webpack升级

前情描述

项目从2017年开始,采用的是webpack2.2.1版本+vue等的技术架构,由于工作较忙及业务较多项目越来越大,一直疏于对webpack进行升级,导致构建时间越来越长,最近终于有时间,构建时间也有了一个质的飞跃,从5分钟的打包时间缩短到了1分钟。当然再此之前也有过相应的打包优化升级历史。

优化历史

dll方式也就是通过配置,提前把不会变动的公共依赖单独构建打包,告诉webpack指定库在项目中的位置,从而直接引入,不将其打包在内。

webpack通过webpack.DllPlugin与webpack.DllReferencePlugin两个内嵌插件实现此功能。

1、提取webpack.dll.config.js文件

/*
 * @Description:
 * @Author: (jiaobingqian)
 * @Email: jiaobq123@163.com
 * @Date: 2018-12-11 14:25:36
 * @LastEditTime: 2019-02-19 15:16:20
 * @LastEditors: (jiaobingqian)
 */
//快速打包优化方案:
const path = require('path');
const webpack = require('webpack');
module.exports = {
  entry: {
    vendor: [
      'vue/dist/vue.esm.js',
      'vue-router',
      'element-ui',
      'clipboard',
      'file-saver',
      'xlsx'
    ]
  },
  output: {
    path: path.join(__dirname, '../static/js'), //放在项目的static/js目录下面
    filename: '[name].dll.js', //打包文件的名字
    library: '[name]_library' //可选 暴露出的全局变量名
    // vendor.dll.js中暴露出的全局变量名。
    // 主要是给DllPlugin中的name使用,
    // 故这里需要和webpack.DllPlugin中的`name: '[name]_library',`保持一致。
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.join(__dirname, '.', '[name]-manifest.json'), //生成上文说到清单文件,放在当前build文件下面,这个看你自己想放哪里了。
      name: '[name]_library'
    }),
    //压缩 只是为了包更小一点
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false,
        drop_console: true,
        drop_debugger: true
      },
      output: {
        // 去掉注释内容
        comments: false
      },
      sourceMap: true
    })
  ]
};

2、打包构建dll公共模块(生成到一个独立目录,正式打包copy到build正式目录dist)

webpack --config build/webpack.dll.conf.js

3、配置webpack.pro.conf.js文件

webpack.DllReferencePlugin的选项中:

  • context:需要跟之前保持一致,这个用来指导webpack匹配manifest.json中库的路径;

  • manifest:用来引入刚才输出的manifest.json文件。

    plugins: [

    .... //此处省略其他插件配置


    //begin: 打包部分代码,提高打包效率
    new webpack.DllReferencePlugin({
      context: path.resolve(__dirname, '..'),
      manifest: require('./vendor-manifest.json')
    }),
    // 这个主要是将生成的vendor.dll.js文件加上hash值插入到页面中。
    new AddAssetHtmlPlugin([{
      filepath: path.resolve(__dirname, '../static/js/vendor.dll.js'),
      outputPath: utils.assetsPath('js'),
      publicPath: path.posix.join(config.build.assetsPublicPath, 'static/js'),
      includeSourcemap: false,
      hash: true,
    }]),
    // end
    ]

方案调研

升级方案

此处省略掉中间无数试错、解决相关构建错误的心酸历程😔。。。

1、升级相应依赖包(更改package.json)

"devDependencies": {
    "clean-webpack-plugin": "^2.0.1",

    "babel-loader": "^7.1.3",

    "css-loader": "^2.1.1",

    "eslint-loader": "^2.0.0",

    "extract-text-webpack-plugin": "^4.0.0-beta.0",
    "file-loader": "^1.1.10",

    "html-webpack-plugin": "^3.2.0",

    "script-loader": "^0.7.2",

    "vue-loader": "^15.0.0",

    "vue-style-loader": "^4.1.0",

    "webpack": "^4.29.6",

    "webpack-cli": "^3.3.0",

  },

附:完整package.json文件

{
  "name": "pdl_cms",
  "version": "1.0.0",
  "description": "haohuan cms system web front end",
  "author": "<jiaobq123@163.com>",
  "private": true,
  "scripts": {
    "dev:new": "webpack --mode development",
    "build:new": "webpack --mode production",
    "watch:new": "webpack --watch",
    "dev": "node build/dev-server.js",
    "build": "node build/build.js",
    "build:dll": "webpack --config build/webpack.dll.conf.js",
    "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
    "e2e": "node test/e2e/runner.js",
    "test": "npm run unit && npm run e2e",
    "lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs"
  },
  "dependencies": {
    "clipboard": "^1.6.1",
    "element-ui": "^1.4.4",
    "file-saver": "^1.3.3",
    "killport": "^1.0.1",
    "vue": "^2.4.4",
    "vue-clipboard2": "^0.2.1",
    "vue-emoji-picker": "^1.0.1",
    "vue-html5-editor": "^1.1.1",
    "vue-router": "^2.2.0",
    "xl_close_port": "^1.0.1",
    "xlsx": "^0.9.13"
  },
  "devDependencies": {
    "add-asset-html-webpack-plugin": "^2.1.3",
    "autoprefixer": "^6.7.2",
    "axios": "^0.16.2",
    "babel-core": "^6.22.1",
    "babel-eslint": "^7.1.1",
    "babel-helper-vue-jsx-merge-props": "^2.0.2",
    "babel-loader": "^7.1.3",
    "babel-plugin-istanbul": "^5.1.1",
    "babel-plugin-syntax-jsx": "^6.18.0",
    "babel-plugin-transform-runtime": "^6.22.0",
    "babel-plugin-transform-vue-jsx": "^3.5.0",
    "babel-preset-es2015": "^6.22.0",
    "babel-preset-latest": "^6.24.0",
    "babel-preset-stage-2": "^6.22.0",
    "babel-register": "^6.22.0",
    "babili-webpack-plugin": "0.0.11",
    "chai": "^3.5.0",
    "chalk": "^1.1.3",
    "chromedriver": "^2.27.2",
    "clean-webpack-plugin": "^2.0.1",
    "connect-history-api-fallback": "^1.3.0",
    "cross-env": "^3.1.4",
    "cross-spawn": "^5.0.1",
    "css-loader": "^2.1.1",
    "eslint": "^3.14.1",
    "eslint-config-standard": "^6.2.1",
    "eslint-friendly-formatter": "^2.0.7",
    "eslint-loader": "^2.0.0",
    "eslint-plugin-html": "^2.0.0",
    "eslint-plugin-import": "^2.2.0",
    "eslint-plugin-jsx-a11y": "^4.0.0",
    "eslint-plugin-promise": "^3.4.0",
    "eslint-plugin-react": "^6.10.0",
    "eslint-plugin-standard": "^2.0.1",
    "eventsource-polyfill": "^0.9.6",
    "express": "^4.14.1",
    "extract-text-webpack-plugin": "^4.0.0-beta.0",
    "file-loader": "^1.1.10",
    "friendly-errors-webpack-plugin": "^1.1.3",
    "function-bind": "^1.1.0",
    "html-webpack-plugin": "^3.2.0",
    "http-proxy-middleware": "^0.19.1",
    "inject-loader": "^2.0.1",
    "jquery": "^3.1.1",
    "karma": "^4.0.0",
    "karma-coverage": "^1.1.1",
    "karma-mocha": "^1.3.0",
    "karma-phantomjs-launcher": "^1.0.2",
    "karma-sinon-chai": "^1.2.4",
    "karma-sourcemap-loader": "^0.3.7",
    "karma-spec-reporter": "0.0.26",
    "karma-webpack": "^2.0.2",
    "lolex": "^1.5.2",
    "mocha": "^6.0.2",
    "mockjs": "^1.0.1-beta3",
    "nightwatch": "^1.0.19",
    "opn": "^4.0.2",
    "ora": "^1.1.0",
    "phantomjs-prebuilt": "^2.1.14",
    "rimraf": "^2.6.1",
    "script-loader": "^0.7.2",
    "selenium-server": "^3.0.1",
    "semver": "^5.3.0",
    "shelljs": "^0.7.6",
    "sinon": "^2.2.0",
    "sinon-chai": "^2.8.0",
    "url-loader": "^1.1.2",
    "vue-file-change-upload": "^1.0.3",
    "vue-loader": "^15.0.0",
    "vue-simple-upload-component": "^0.5.43",
    "vue-style-loader": "^4.1.0",
    "vue-template-compiler": "^2.1.10",
    "webpack": "^4.29.6",
    "webpack-bundle-analyzer": "^2.2.1",
    "webpack-cli": "^3.3.0",
    "webpack-dev-middleware": "^1.10.0",
    "webpack-hot-middleware": "^2.16.1",
    "webpack-merge": "^2.6.1"
  },
  "engines": {
    "node": ">= 4.0.0",
    "npm": ">= 3.0.0"
  }
}

2、webpack.base.js修改

  • vue-loader 15引用配置

const VueLoaderPlugin = require("vue-loader/lib/plugin"); 
...//省略
plugins: [new VueLoaderPlugin()],
  • optimization配置

  // 提取公共代码
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendor: {
          // 抽离第三方插件
          test: /node_modules/, // 指定是node_modules下的第三方包
          chunks: "initial",
          name: "vendor", // 打包后的文件名,任意命名
          // 设置优先级,防止和自定义的公共代码提取时被覆盖,不进行打包
          priority: 10,
        },

        commons: {
          // 选择全部chunk
          chunks: "all",
          // 生成的公共代码文件名,惯用vendor
          name: "vendor",
          // 作用于
          test: /[\\/]node_modules[\\/]/,
        },
      },
    },
  },

3、webpack.dev.conf.js修改

webpack4.0开始开发环境和生产环境通过mode参数区别,mode与module属性同级

mode: "development",

4、webpack.pro.conf.js修改

  • 添加生产环境环境变量控制参数

mode: "production",
  • 生成css文件的命名配置修改,否则编译报错

// filename: utils.assetsPath("css/[name].[contenthash].css"),//修改前
filename: utils.assetsPath("css/[name].[hash].css"), //webpack 4.0 修改后
  • 删除或者注释掉CommonsChunkPlugin相关插件,

    // new webpack.optimize.CommonsChunkPlugin({
    //   name: 'vendor',
    //   minChunks: function (module, count) {
    //     // 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
    //     )
    //   }
    // }),
    // extract webpack runtime and module manifest to its own file in order to
    // prevent vendor hash from being updated whenever app bundle is updated
    // new webpack.optimize.CommonsChunkPlugin({
    //   name: 'manifest',
    //   chunks: ['vendor']
    // }),
  • 删除Dll打包配置的相关插件add-asset-html-webpack-plugin及webpack.DllReferencePlugin

// const AddAssetHtmlPlugin = require("add-asset-html-webpack-plugin");

    // //begin: 打包部分代码,提高打包效率
    // new webpack.DllReferencePlugin({
    //   context: path.resolve(__dirname, ".."),
    //   manifest: require("./vendor-manifest.json"),
    // }),
    // // 这个主要是将生成的vendor.dll.js文件加上hash值插入到页面中。
    // new AddAssetHtmlPlugin([
    //   {
    //     filepath: path.resolve(__dirname, "../static/js/vendor.dll.js"),
    //     outputPath: utils.assetsPath("js"),
    //     publicPath: path.posix.join(config.build.assetsPublicPath, "static/js"),
    //     includeSourcemap: false,
    //     hash: true,
    //   },
    // ]),
    // // end

5、重新安装依赖包(确保已删除node_modules目录)

cnpm install

参考文档

主要参考:https://blog.csdn.net/harsima/article/details/80819747

https://www.jianshu.com/p/55404376b26e

https://vue-loader.vuejs.org/zh/spec.html#%E7%AE%80%E4%BB%8B

Last updated

Was this helpful?