VUE项目开发-webpack配置第一讲(vue-cli <3.0)

457 阅读7分钟

第一步:先上个截图,这次主要讲这四个文件,作用都在代码中添加了注释

第二步:package.json讲解

{
  "name": "item_name",//项目名称
  "version": "1.0.0", //项目版本号
  "description": "A Vue.js project", //项目描述
  "author": "feiyu", //项目作者
  "private": true, // 私有的
  "scripts": {
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
    // webpack-dev-server:建立软连webpack-dev-server/bin/webpack-dev-server.js
    // --inline 开启修改保存后刷新页面 禁止:--inline=false
     //--progress 将运行进度输出到控制台。
     //其实这里也可以不写这些配置,直接在build/webpack.dev.conf.js的devServer对象中修改
     //找到 build/webpack.dev.conf.js文件,开始启动服务的配置  不写的话默认查找webpack.config.js
     
    "start": "npm run dev",//先运行 npm run dev 再重复上一步内容
    "build": "node build/build.js"  //运行项目打包文件
  },
  "dependencies": {  //生产环境依赖包 安装 npm i xxx -S
    "axios": "^0.19.0", 
    // 第一个数字代表大版本:大版本号改变,有可能存在不向后兼容问题
    // 第二个数字是次要版本:一般代表新功能的增加
    // 第三个数字是小版本:一般是修了复某些bug 
    //在依赖版本我们会看到插入符号(^)或者波浪号(~),这是SemVer中定义的版本范围的表示法
    //^代表 安装依赖时,大版本不可变,次要版本和小版本下载最新版本
    //~代表 安装依赖时,大版本和次要版本不可变,小版本下载最新版本
    "babel-polyfill": "^6.26.0",
    "echarts": "^4.2.1",
    "element-ui": "^2.3.9",
    "moment": "^2.24.0",
    "qs": "^6.7.0",
    "vue": "^2.5.2",
    "vue-json-excel": "^0.2.98",
    "vue-router": "^3.0.1",
    "vuex": "^3.1.3"
  },
  "devDependencies": {  //开发环境依赖包 安装 npm i xxx -D 
    "autoprefixer": "^7.1.2",
    "babel-core": "^6.22.1",
    "babel-helper-vue-jsx-merge-props": "^2.0.3",
  },
  "engines": { //启动的最低版本限制
    "node": ">= 6.0.0",
    "npm": ">= 3.0.0"
  },
  "browserslist": [//浏览器版本支持
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]
}

第三步:执行npm run dev 进入 build/webpack.dev.conf.js

const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
//合并资源的模块
const merge = require('webpack-merge')
//node的模块,用来查找路径
const path = require('path') 
//开发环境和生产环境都需要的公共配置项
const baseWebpackConfig = require('./webpack.base.conf') 
 //把static中一些文件原封不动的拷贝到打包文件中去dist/static
const CopyWebpackPlugin = require('copy-webpack-plugin')
//把根目录中的index.html拷贝到打包文件,并自动引入打包后的文件路径的插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')// 识别某些类别的webpack错误,并清理,聚合和优先级,以提供更好的开发人员体验。
const portfinder = require('portfinder') //端口查找器

const HOST = process.env.HOST //获取环境host
const PORT = process.env.PORT && Number(process.env.PORT) //获取进程环境端口号

const devWebpackConfig = merge(baseWebpackConfig, { //通过merge与./webpack.base.conf中的配置合并
  module: {
    // 加载loader  loader的作用就是处理不被浏览器识别的文件,生成可被识别文件
    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
  },
  ////开启或者关闭 source-map   关闭:none  如果不写这个属性,开发环境默认打开,生产环境默认关闭 
  //source-map 作用 /将编译后的代码映射会原始源代码,方便查看是哪里出的错,追踪到错误和警告在源代码中的原始位置
  devtool: config.dev.devtool, 

  // 配置启动的服务相关配置
  //相关配置官网 https://webpack.docschina.org/configuration/dev-server/#devserver
  devServer: {
    //重新加载之前的控制台的信息输出 //'none' | 'info' | 'error' | 'warning'
    clientLogLevel: 'warning', 
    historyApiFallback: {
      rewrites: [
        { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
      ],
    },
    //是否启用模块的热替换
    hot: true, 
     //告诉服务器从哪个目录中提供内容。只有在你想要提供静态文件时才需要 false string [string] number
    contentBase: false,
     //是否启用  gzip 压缩
    compress: true, 
    //是否允许浏览器使用本地 IP 打开。
    useLocalIp:true,
    //HOST
    host: HOST || config.dev.host,
    //端口号
    port: PORT || config.dev.port,
    //是否直接浏览器
    open: config.dev.autoOpenBrowser,
    //当出现编译器错误或警告时,在浏览器中显示全屏覆盖层。默认禁用
    overlay: config.dev.errorOverlay
      ? { warnings: false, errors: true }
      : false,
     // 此路径下的打包文件可在浏览器中访问,默认是'/'。   127.0.0.1:8080/
    publicPath: config.dev.assetsPublicPath,
    //代理 一般处理后端接口的跨域问题
    proxy: config.dev.proxyTable,
    // 初始启动信息之外的任何内容都不会被打印到控制台。这也意味着来自 webpack 的错误或警告在控制台不可见。
    quiet: true, 
    // 获取文件改动的通知,重新构建
    watchOptions: {
      poll: config.dev.poll,
    }
  },
  //插件 用来处理webpack无法处理的一些事情,
  // https://www.webpackjs.com/plugins/copy-webpack-plugin/
  plugins: [
    // DefinePlugin可以为webpack提供一个在编译时可以配置的全局常量
    // 在这里我们可以通过"process.env"这个全局变量的值来判定所处的环境
    new webpack.DefinePlugin({ 
      'process.env': require('../config/dev.env')
    }),
    //启用热替换模块(Hot Module Replacement),也被称为 HMR。
    new webpack.HotModuleReplacementPlugin(),
    // HMR更新时在控制台中显示正确的文件名
    new webpack.NamedModulesPlugin(), 
    // 页面中的报错不会阻塞编译,但是会在编译结束后报错
    new webpack.NoEmitOnErrorsPlugin(),
    //生成html模版文件
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index.html',
      inject: true
    }),
    // 最上边有介绍
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.dev.assetsSubDirectory,
        ignore: ['.*']
      }
    ])
  ]
})

module.exports = new Promise((resolve, reject) => {
  portfinder.basePort = process.env.PORT || config.dev.port
  portfinder.getPort((err, port) => {
    if (err) {
      reject(err)
    } else {
      // publish the new Port, necessary for e2e tests
      process.env.PORT = port
      // add port to devServer config
      devWebpackConfig.devServer.port = port

      // 错误提示插件 
      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
        // 运行成功
        compilationSuccessInfo: {
          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
        },
        onErrors: config.dev.notifyOnErrors
        ? utils.createNotifierCallback()
        : undefined
      }))

      resolve(devWebpackConfig)
    }
  })
})

第四步:开发和生产环境的基础配置 build/webpack.base.conf.js

'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')

function resolve (dir) {
  return path.join(__dirname, '..', dir)
}



module.exports = {
  context: path.resolve(__dirname, '../'), //上下文路径
  entry: {  ////入口文件  可以使数组或者对象
    app: './src/main.js'
  },
  output: {
    path: config.build.assetsRoot, //文件存放的目录,有则替换,没有则生成
    filename: '[name].js',//输出文件需要使用占位符 [name].js  , [name][hash:6].js [name][chunkhash].js
    publicPath: process.env.NODE_ENV === 'production'  //此选项指定在浏览器中所引用的文件路径
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  resolve: {
    //自动解析确定的扩展
    extensions: ['.js', '.vue', '.json'], 
    alias: {  //创建 import 或 require 的别名,来确保模块引入变得更简单
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      '@static': resolve('static'),
    }
  },
  module: {
    rules: [
      {
        test: /\.vue$/, 
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        test: /\.js$/, //匹配特定条件
        loader: 'babel-loader', //加载的依赖包
        //匹配特定条件
        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client'),resolve('static/js'),]
       // exclude: Condition   排除特定条件
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          //文件大小限时
          limit: 10000,
          //文件名字命名方式
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('media/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  },
  node: {
    // prevent webpack from injecting useless setImmediate polyfill because Vue
    // source contains it (although only uses it if it's native).
    setImmediate: false,
    // prevent webpack from injecting mocks to Node native modules
    // that does not make sense for the client
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  }
}

第五步:项目打包的配置 webpack.prod.conf.js

const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

const env = require('../config/prod.env')

const webpackConfig = merge(baseWebpackConfig, {
  module: {
    // 将.vue外部的css或css预处理器文件进行处理
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: true,
      usePostCSS: true
    })
  },
  // 是否开启调试
  devtool: config.build.productionSourceMap ? config.build.devtool : false,
  output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    // 定义在非入口文件引用的文件的名称
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  plugins: [
    // 定义一个在编译时间内可以使用的全局变量,用来关闭vue的所有警告功能
    new webpack.DefinePlugin({
      'process.env': env
    }),
    // 最小化所有JavaScript输出块,消除无作用的代码
    new UglifyJsPlugin({
      uglifyOptions: {
        compress: {
          warnings: false
        }
      },
      sourceMap: config.build.productionSourceMap,
      parallel: true
    }),
    // // ExtractTextPlugin会将所有的「入口 chunk(entry chunks)」中的 require("style.css") 移动到独立分离的css文件。因此,样式不再内联到javascript里面,但会放到一个单独的css包文件 (styles.css)当中。 如果样式文件较大,这会更快,因为样式文件会跟javascript包并行加载
    new ExtractTextPlugin({
      filename: utils.assetsPath('css/[name].[contenthash].css'),
      allChunks: true,
    }),
    // 压缩提取出来的CSS,并且进行css的复用以解决extract-text-webpack-plugin将css处理后会出现的css重复的情况
    new OptimizeCSSPlugin({
      cssProcessorOptions: config.build.productionSourceMap
        ? { safe: true, map: { inline: false } }
        : { safe: true }
    }),
    // 构建要编译输出的index.html,并在文件中嵌入资源文件
    new HtmlWebpackPlugin({
      filename: config.build.index,
      template: 'index.html',
      // 设置为true或body可以将js代码放到<body>元素最后
      // 设置为head将js代码加到<head>里面
      // 设置为false所有静态资源css和JavaScript都不会注入到模板文件中
      inject: true,
      minify: {
         // 删除注释
        removeComments: true,
         // 合并空白
        collapseWhitespace: true,
        // 删除属性的引号
        removeAttributeQuotes: true
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
      },
      // 通过CommonsChunkPlugin控制chunks在html文件中添加的顺序  
      // 设置为dependency即按照它们之间的依赖关系添加
      chunksSortMode: 'dependency'
    }),
    //稳定 chunkhash
    new webpack.HashedModuleIdsPlugin(),
    // enable scope hoisting
    //启用作用域提升,作用是让代码文件更小、运行的更快
    new webpack.optimize.ModuleConcatenationPlugin(),
    // split vendor js into its own file
    //// webpack将公共模块打包的vendor.js里面使用CommonsChunkPlugin将vendor.js分离到单独的文件
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks (module) {
        // any required modules inside node_modules are extracted to vendor
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, '../node_modules')
          ) === 0
        )
      }
    }),
    // 使用CommonsChunkPlugin可以保证如果我们的第三方插件没有变动,在打包的时候可以不被重新的打包
    // 待到上线后就不会重新的加载以达到缓存的目的
    // 我们会获得webpack执行时间和输出一份json文件保存chunk的id和最终引用它们的文件映射关系
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity
    }),
    // This instance extracts shared chunks from code splitted chunks and bundles them
    // in a separate chunk, similar to the vendor chunk
    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
    new webpack.optimize.CommonsChunkPlugin({
      name: 'app',
      async: 'vendor-async',
      children: true,
      minChunks: 3
    }),

   // 复制static文件夹内的静态资源到打包好的文件中
    // 具体的路径是之前我们设置的"config.build.assetsSubDirectory"
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.build.assetsSubDirectory,
        ignore: ['.*']
      }
    ])
  ]
})
// 如果开启了Gzip压缩,使用以下配置
if (config.build.productionGzip) {
  const CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      threshold: 10240,
      minRatio: 0.8
    })
  )
}
// 如果开启了编译报告,使用以下配置
if (config.build.bundleAnalyzerReport) {
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}

module.exports = webpackConfig