Webpack + Vue 多页面项目晋级 Webpack 4 以及打包优化

0. 媒介

早在 2016 年我就宣布过一篇关于在多页面下运用 Webpack + Vue 的设置的文章,当时也是我在做本身一个个人项目时碰到的设置题目,想到他人也能够碰到跟我一样的题目,就把设置的思绪分享出来了,传送门在这里。

因为那份设置直到如今另有人在关注,同时近来公司协助项目晋级了 Webpack 4,乘隙也把之前的设置也晋级了一下,而且博客荒废了这么久,都快 9102 年了,不能比年均一篇博文都不到,所以有了下面的分享。

下面的设置主假如给在多页面下运用 Webpack 的同砚在晋级 Webpack 时供应一点思绪,多页面的设置思绪请点击上面的传送门。

下面代码的地点 https://github.com/cnu4/Webpack-Vue-MultiplePage

1. Webpack 晋级 4.x

1.1. 晋级和装置相干依靠

  • webpack 晋级
  • webpack-cli webapck4.x 须要新加的依靠
  • mini-css-extract-plugin 庖代 extract-text-webpack-plugin
  • 其他相干 loader 和 plugin

    • css-loader
    • file-loader
    • url-loader
    • vue-style-loader
    • vue-template-compiler(注重要坚持与 vue 版本一向)
    • html-webpack-plugin@next

1.2 修正设置

mode 构建形式

设置 mode 构建形式,比方 development 会将 process.env.NODE_ENV 的值设为 development

mini-css-extract-plugin

删除原 extract-text-webpack-plugin 设置,增添 mini-css-extract-plugin 设置

module.exports = {
  plugins: [
    new  MiniCssExtractPlugin({
      filename:  'css/[name].css'
    }),
  ],
}

module.exports = {
  module: {
    rules: [
      {
        test:/\.vue$/,
        loader: 'vue-loader',
      },
      { test: /\.css$/,
        use: [
          // 开辟形式下运用 vue-style-loader,以便运用热重载
          process.env.NODE_ENV !== 'production' ?
            'vue-style-loader' : MiniCssExtractPlugin.loader,
          'css-loader' ] },
    ]
  }
}

optimization

这是 webpack 4 一个比较大的变动点,webpack 4 中删除了 webpack.optimize.CommonsChunkPlugin,而且运用 optimization 中的splitChunk来替换,下面的设置替代了之前的 CommonsChunkPlugin 设置,赞同能提取 JS 和 CSS 文件

module.exports = {
  optimization: {
    splitChunks: {
      vendors: {
        name:  'venders',
        chunks:  'all',
        minChunks: chunks.length
    }
  }
}

vue-loader 晋级

vue-loader 15 注重要合营一个 webpack 插件才准确运用

const { VueLoaderPlugin } = require('vue-loader') 

module.exports = {
  plugins: [ new VueLoaderPlugin() ]
}

html-webpack-plugin 晋级

晋级到 next,不然开辟下没法一般注入资本文件

文件紧缩

  • optimize-css-assets-webpack-plugin
  • terser-webpack-plugin

紧缩的设置也挪动到了 optimization 选项下,值得注重的是紧缩东西换成了 terser-webpack-plugin,这是 webpack 官方也引荐运用的,预计在 webpack 5 中会变成默许的设置,实测打包速率确切变快了许多。

设置

module.exports = {
    minimizer: [
      new TerserPlugin({ // 紧缩js
          cache:  true,
          parallel:  true
        }
      }),
      new OptimizeCSSAssetsPlugin({ // 紧缩css
        cssProcessorOptions: {
          safe: true
        }
      })
    ]
  }
}

2. 打包速率优化

能够运用下面的插件看看打包时刻重要耗时在哪

speed-measure-webpack-plugin

2.1 相干 plugin 开启 parallel 选项

TerserPlugin 紧缩插件能够开启多线程,见上面设置

2.2 HappyPack 和 thread-loader 开启 Loader 多历程转换

github 的 Demo 中没有引入,有兴致的同砚能够尝试,在一些耗时的 Loader 确切能够进步速率

vue-loader 不支撑 HappyPack,官方发起用 thread-loader

const HappyPack = require('happypack');

exports.module = {
  rules: [
    {
      test: /.js$/,
      // 1) replace your original list of loaders with "happypack/loader":
      // loaders: [ 'babel-loader?presets[]=es2015' ],
      use: 'happypack/loader',
      include: [ /* ... */ ],
      exclude: [ /* ... */ ]
    }
  ]
};

exports.plugins = [
  // 2) create the plugin:
  new HappyPack({
    // 3) re-add the loaders you replaced above in #1:
    loaders: [ 'babel-loader?presets[]=es2015' ]
  })
];

2.3 提早打包大众代码

DllPlugin

运用 DllPlugn 将 node_modules 或许本身编写的不常变的依靠包打一个 dll 包,进步速率和充分利用缓存。相当于 splitChunks 提取了大众代码,但 DllPlugn 是手动指定了大众代码,提早打包好,免去了后续 webpack 构建时的从新打包。

起首须要增添一个 webpack 设置文件 webpack.dll.config.js 特地针对 dll 打包设置,个中用到 webpack.DllPlugin

实行 webpack --config build/webpack.dll.config.js 后,webpack会自动天生 2 个文件,个中vendor.dll.js 即兼并打包后第三方模块。别的一个 vendor-mainifest.json 存储各个模块和所需公用模块的对应关联。

接着修正我们的 webpack 设置文件,在 plugin 设置中增添 webpack.DllReferencePlugin,设置中指定上一步天生的 json 文件,然后手动在 html 文件中援用上一步的 vendor.dll.js 文件。

背面假如增删 dll 中的依靠包时都须要手动实行上面打包敕令来更新 dll 包。下面插件能够自动完成这些操纵。

AutoDllPlugin

装置依靠 autodll-webpack-plugin

AutoDllPlugin 自动同时相当于完成了 DllReferencePlugin 和 DllPlugin 的事情,只须要在我们的 webpack 中增加设置。AutoDllPlugin 会在实行 npm install / remove / update package-name 或转变这个插件配件时从新打包 dll。须要注重的是转变 dll 中指定的依靠包不会触发自动从新打包 dll。

现实打包中天生环境是没题目的,但开辟形式下在有缓存的情况下,autodll 插件不会天生新的文件,致使 404,所以在 Demo 中临时关了这个插件。不过 dll 提早打包了大众文件,确切能够进步打包速率,有兴致的同砚能够研讨下开辟形式下的缓存题目,迎接在批评中分享。

module.exports.plugins.push(new AutoDllPlugin({
  inject: true, // will inject the DLL bundles to html
  context: path.join(__dirname, '.'),
  filename: '[name].dll.js',
  debug: true,
  inherit: true,
  // path: './',
  plugins: [
    new TerserPlugin({
      cacheL true,
      parallel: true
    }),
    new MiniCssExtractPlugin({
      filename: '[name].css'
    })
  ],
  entry: {
    vendor: ['vue/dist/vue.esm.js', 'axios', 'normalize.css']
  }
}));

3. 增添 ES6+ 支撑

3.1 装置依靠

  • @babel/core
  • @babel/plugin-proposal-class-properties
  • @babel/plugin-proposal-decorators
  • @babel/plugin-syntax-dynamic-import
  • @babel/plugin-transform-runtime
  • @babel/preset-env
  • @babel/runtime
  • babel-loader
  • @babel/polyfill

因为项目中是第一次设置 babel,一步到位直接运用新版 7,新版 babel 运用新的定名空间 @babel,假如是老项目晋级 babel 7,能够运用东西 babel-upgrade,读一下 晋级文档

这里说下上面依靠的作用和晋级 babel 7 的修改。

@babel/runtime, @babel/plugin-transform-runtime

新版中 @babel/runtime 只包含了一些 helpers,假如须要 core-js polyfill 浏览器不支撑的 API,能够用 transform 供应的选项 {"corejs": 2} 并装置依靠 @babel/runtime-corejs2。纵然默许的 polyfill 没了,但 @babel/plugin-transform-runtime 依旧能够为我们星散辅佐函数,削减代码体积

@babel/polyfill

运用 @babel/runtime 的 polyfill 不会污染全局 API,因为不会修改原生对象的原型,它只是建立一个辅佐函数在当前作用于见效,所以诸如 [1, 2].includes(1) 如许的语法也没法被 polyfill。假如不是开辟第三方库,能够运用 @babel/polyfill,相反他的 polyfill 会影响到浏览器全局的对象原型

@babel/preset-env 供应了一个 useBuiltIns 选项来按需引入 polyfill,而不须要引入悉数。另一种方法是直接援用 core-js 包下的特定 polyfill。

stage presets

如今须要手动装置 @babel/plugin-proposal 开首的依靠是因为 babel 在新版中移除了 stage presets,为的是后续更好保护处于 proposal 阶段的语法。想要运用 proposal 阶段的语法须要零丁援用对应的 plugin, 上面的设置只加了几个处于 stage 3 阶段的 plugin,老项目发起运用 babel-upgrade 晋级,自动增加依靠

3.2 增加设置文件 .babelrc

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": false,
        "targets": {
          "browsers": [
            "> 1%",
            "last 2 versions",
            "ie >= 11"
          ]
        },
        "useBuiltIns": "usage" // 按需引入 polyfill
      }
    ]
  ],
  "plugins": [
    "@babel/plugin-transform-runtime",
    "@babel/plugin-syntax-dynamic-import",
    ["@babel/plugin-proposal-class-properties", { "loose": false }],
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
  ]
}

3.3 增添 webpack 设置

module.exports = {
  modules: {
    rules: [
      {
        test:  /\.js$/,
        loader:  'babel-loader',
        exclude:  /node_modules/
      }
    ]
  }
}

4. 其他题目

下面是我公司项目中碰到的题目,列位晋级过程当中假如碰到一样的题目能够参考一下处置惩罚思绪。

4.1 json-loader

webpack4 内置的json-loader 有点兼容性题目,装置 json-loader 依靠和变动设置

处置惩罚:

{
  test: /\.json$/,  //用于婚配loaders所处置惩罚文件拓展名的正则表达式
  use: 'json-loader', //详细loader的称号
  type: 'javascript/auto',
  exclude: /node_modules/
}

4.2 vue-loader

vue-loader 晋级到 15.x 后,会致使旧的 commonjs 写法加载有题目,须要运用 require('com.vue').default 的体式格局援用组件

13的版本还能够设置 esModule,14 今后的版本不能设置了,vue 文件导出的模块一定是 esModule

处置惩罚:运用 require('com.vue').default 或许 import 的体式格局援用组件

esModule option stopped working in version 14 · Issue #1172 · vuejs/vue-loader · GitHub

尤大大发起能够本身写一个 babel 插件,碰到 require vue 文件的时刻自动加上 default 属性,如许就不必修改一切代码,我们在项目中也是如许处置惩罚的。

4.3 提取大众 css 代码

scss 中 import 的代码不能被提取到大众 css 中。scss 中的 @import 是运用 sass-loader 处置惩罚的,处置惩罚后已变成 css 文件,webpack 已不能推断是不是是同一个模块,所以不能提取到大众的 css 中,但多页面中我们照样愿望一些大众的 css 能被提取到大众的文件中。

处置惩罚:将须要提取到大众文件的 css 改到 js 中引入就能够,详见下面 issue

mini-css-extract-plugin + sass-loader + splitChunks · Issue #49

4.4 mini-css-extract-plugin filename 不支撑函数

mini-css-extract-plugin 的 filename 选项不支撑函数,但我们有时刻照样愿望能零丁掌握大众 css 文件的位置,而不是和其他进口文件的 css 运用一样的目次花样

处置惩罚:运用插件 FileManagerPlugin 在构建后挪动文件,等 filename 支撑函数后再优化

feat: allow the option filename to be a function · Issue #143 · webpack-contrib/mini-css-extract-plugin · GitHub

    原文作者:cnu4
    原文地址: https://segmentfault.com/a/1190000017569409
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞