webpack/vue-cli构建速度和打包体积优化

编译分析插件

webpack-bundle-analyzer

webpack-bundle-analyzer可以生成代码分析报告,可以直观地分析打包出的文件有哪些,及它们的大小、占比情况、各文件 Gzipped 后的大小、模块包含关系、依赖项等

webpack/vue-cli构建速度和打包体积优化_第1张图片

npm i -D webpackbar webpack-bundle-analyzer


javascript

复制代码

const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); ​ module.exports = {  // ...  plugins: [    new BundleAnalyzerPlugin(), ] } //package.json "scripts": {   "analyz": "webpack-bundle-analyzer --port 8888 ./build/stats.json", }

新版的 vue-cli 也内置了webpack-bundle-analyzer


json

复制代码

"scripts": { "analyz": "vue-cli-service build --report", },

配置:

  • analyzerMode:server / static / json / disabled

    默认值:server。 在server 模式下,分析器将启动 HTTP 服务器以显示 bundle 报告。 在 static 模式下,将生成带有 bundle 报告的单个 HTML 文件。 在 json 模式下,将生成带有捆绑报告的单个 JSON 文件。 在 disable 模式下,您可以使用此插件通过将 generateStatsFile 设置为 true 来生成 Webpack Stats JSON 文件。

  • analyzerHost:默认值:127.0.0.1。 在 server 模式下用于启动 HTTP 服务器的主机。

  • analyzerPort:默认值:8888。在 server 模式下用于启动 HTTP 服务器的端口

  • reportFilename:默认值:report.html。 在 static 模式下生成的捆绑报告文件的路径。 它可以是绝对路径,也可以是相对于 bundle 文件输出目录的路径(在 webpack 配置中是 output.path)。

  • defaultSizes:stat / parsed / gzip

    默认值:parsed。 默认情况下在报告中显示的模块大小。

    stat:这是文件的“输入”大小,在进行任何转换(如缩小)之前。之所以称为“stat size”,是因为它是从 Webpack 的 stats 对象中获取的。

    parsed:这是文件的“输出”大小。 如果你使用的是 Uglify 之类的 Webpack 插件,那么这个值将反映代码的缩小后的大小。

    gzip:这是通过 gzip 压缩运行解析的包/模块的大小。

  • openAnalyzer:默认值:true。 在默认浏览器中自动打开报告。

  • genarateStatsFile:默认值:false。 如果为 true,将在 bundle 输出目录中生成 webpack stats JSON 文件

webpackbar

webpackbar提供了友好的编译进度提示


ini

复制代码

const WebpackBar = require('webpackbar'); ​ module.exports = { // ... plugins: [   new WebpackBar(), ] }

speed-measure-webpack-plugin

优化 webpack 构建速度,首先需要知道是哪些插件、哪些 loader 耗时长,方便我们针对性的优化。通过 speed-measure-webpack-plugin 插件进行构建速度分析,可以看到各个 loader、plugin 的构建时长,后续可针对耗时 loader、plugin 进行优化。


css

复制代码

npm i -D speed-measure-webpack-plugin

构建速度优化

缓存

Webpack

Webpack 中几种缓存方式:

  • cache-loader
  • hard-source-webpack-plugin

以上这些缓存方式都有首次启动时的开销,即它们会让 “冷启动” 时间会更长,但是二次启动能够节省很多时间

babel-loader

babel-loader的options设置中增加cacheDirectory属性,属性值为true。表示:开启babel缓存,第二次构建时会读取之前的缓存,构建速度会更快一点。


yaml

复制代码

{ test: /.js$/, loader: 'babel-loader', options: { cacheDirectory: true } }

cache-loader

webpack.docschina.org/loaders/cac…

在一些性能开销较大的 loader 之前添加 cache-loader,将结果缓存中磁盘中。默认保存在 node_modueles/.cache/cache-loader 目录下。


java

复制代码

module.exports = { //... module: { //我的项目中,babel-loader耗时比较长,所以我给它配置了`cache-loader` rules: [ { test: /.jsx?$/, use: ['cache-loader','babel-loader'] } ] } }

如果你跟我一样,只打算给 babel-loader 配置 cache 的话,也可以不使用 cache-loader,给 babel-loader 增加选项 cacheDirectory。

持久化缓存

通过配置 webpack 持久化缓存 cache: filesystem,来缓存生成的 webpack 模块和 chunk,改善构建速度。

简单来说,通过 cache: filesystem 可以将构建过程的 webpack 模板进行缓存,大幅提升二次构建速度、打包速度,当构建突然中断,二次进行构建时,可以直接从缓存中拉取,可提速 90% 左右。


java

复制代码

module.exports = { cache: { type: 'filesystem', // 使用文件缓存 }, }

hard-source-webpack-plugin

HardSourceWebpackPlugin 和 speed-measure-webpack-plugin 不能一起使用

vue

cache-loader

Vue-Cli自带cache-loader ,会默认为 Vue/Babel/TypeScript 编译开启。文件会缓存在 node_modules/.cache 中。

cache-loader 进行以下两个的缓存了

  • babel-loader 的 cacheDirectory 标志
  • vue-loader 的 cacheDirectory 标志
hard-source-webpack-plugin

这个插件能正常使用的版本是webpack5以下的版本。

npm install --save-dev hard-source-webpack-plugin

为模块提供中间缓存,缓存路径是:node_modules/.cache/hard-source


php

复制代码

const HardSourceWebpackPlugin = require('hard-source-webpack-plugin') module.exports = { configureWebpack: config => { config.plugin.push( // 为模块提供中间缓存,缓存路径是:node_modules/.cache/hard-source new HardSourceWebpackPlugin({ root: process.cwd(), directories: [], environmentHash: { root: process.cwd(), directories: [], files: ['package.json', 'yarn.lock'] } }) // 配置了files的主要原因是解决配置更新,cache不生效了的问题,配置后有包的变化,plugin会重新构建一部分cache ) } }

hash缓存

防止编译文件名字重复,部署版本的时候,浏览器使用缓存文件。同时,如果编译时文件未改动,不会改变文件名和文件的

hash、chunkhash、contenthash

hash是一整个项目,一次打包,只有一个hash值,是项目级的

chunhash是从入口entry出发,到它的依赖,以及依赖的依赖,依赖的依赖的依赖,等等,一直下去,所打包构成的代码块(模块的集合)叫做一个chunk,也就是说,入口文件和它的依赖的模块构成的一个代码块,被称为一个chunk。

contenthash是哈希只跟内容有关系,内容不变,哈希值不变。与chunkhash的区别可以举上面contenthash的例子,同时可以说明contenthash跟内容有关,但是chunkhash会考虑很多因素,比如模块路径、模块名称、模块大小、模块id等等。


lua

复制代码

output: { filename: '[name].[contenthash].js', // contenthash 只有在内容发生改变才会变 path: path.resolve(__dirname, 'dist'), //输出路径 __dirname 代表当前文件的绝对路径 clean: true, //在生成文件之前清空 output 目录 }, vue-cli configureWebpack -> config.output.filename = `js/[name].[contenthash].js`; config.output.chunkFilename = `js/[name].[contenthash].js`;

在提取css时我们也可以这么命名文件名


css

复制代码

// css 提取 plugins: [ new MiniCssExtractPlugin({ filename: 'css/[name].[contenthash:10].css', }), ]

dll

将我们项目中的依赖使用dll插件进行动态链接,这样依赖就不会进行编译,从而极大地提高编译速度

webpack5 开箱即用的持久缓存是比 dll 更优的解决方案

将dll和缓存进行对比可以发现:

缓存 DLL
把常用的文件存储到内存或硬盘中 把公共代码打包为dll文件放到硬盘中
再次打包时,直接取读取缓存 再次打包时,读取dll文件,不重新打包
加载时间减少 打包时间减少

autodll-webpack-plugin

多线程

将文件解析任务分解成多个子进程并发执行,发挥多核 CPU 电脑的威力。子进程处理完任务后再将结果发送给主进程。所以可以大大提升 Webpack 的项目构建速度

happypack

happypack 同样是用来设置多线程,但是在 webpack5 就不要再使用 happypack 了,官方也已经不再维护了,推荐使用 thread-loader。

npm install happypack -D

HappyPack 参数

  • id: String 用唯一的标识符 id 来代表当前的 HappyPack 是用来处理一类特定的文件.
  • loaders: Array 用法和 webpack Loader 配置中一样.
  • threads: Number 代表开启几个子进程去处理这一类型的文件,默认是3个,类型必须是整数。
  • verbose: Boolean 是否允许 HappyPack 输出日志,默认是 true。
  • threadPool: HappyThreadPool 代表共享进程池,即多个 HappyPack 实例都使用同一个共享进程池中的子进程去处理任务,以防止资源占用过多。
  • verboseWhenProfiling: Boolean 开启webpack --profile ,仍然希望HappyPack产生输出。
  • debug: Boolean 启用debug 用于故障排查。默认 false

javascript

复制代码

//提升 Webpack 构建速度 const HappyPack = require('happypack'); //安装 OS 模块 这个主要是拿到当前电脑的CPU核数 const os = require('os'); //这个是设置共享线程池中的数量 size 控制设置数量 类型 只能是 整数类型 const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length }); ​ module.exports = {  module: {    rules: [     {        test: /.js$/,        //把对.js 的文件处理交给id为happyBabel 的HappyPack 的实例执行        loader: 'happypack/loader?id=happyBabel',        //排除node_modules 目录下的文件        exclude: /node_modules/     },     {            test: /.(css|less)$/,            use: 'happypack/loader?id=styles'     },   ] }, plugins: [    new HappyPack({       //用id来标识 happypack处理那里类文件      id: 'happyBabel',      //用法和loader 的配置一样      loaders: [{        loader: 'babel-loader?cacheDirectory=true',     }],      //共享进程池      threadPool: happyThreadPool,      //允许 HappyPack 输出日志      verbose: true,   }),    new HappyPack({        id: 'styles',        loaders: [ 'style-loader', 'css-loader', 'less-loader' ],        //共享进程池        threadPool: happyThreadPool,   }); ] }

vue


lua

复制代码

  //把对.js 的文件处理交给id为happyBabel 的HappyPack 的实例执行   // config.module.rule('js').test(/.js$/)   //   .include.add('/src/').end()   //   .exclude.add('/node_modules/').end()   //   .use().loader('happypack/loader?id=happyBabel').end()

thread-loader

  • Webpack

    npm install --save-dev thread-loader

     javascript 

    复制代码

    const path = require("path"); module.exports = { module: {   rules: [     {       test: /.js$/,       include: path.resolve('src'),       use: [         "thread-loader",         // 耗时的 loader (例如 babel-loader)       ],     },   ], }, };
  • Vue-Cli已经内置thread-loaderthread-loader 会在多核 CPU 的机器上为 Babel/TypeScript 转译开启。

     java 

    复制代码

    module.exports = { parallel: true, }
    • Type: boolean

    • Default: require('os').cpus().length > 1

      是否为 Babel 或 TypeScript 使用 thread-loader

      该选项在系统的 CPU 有多于一个内核时自动启用,仅作用于生产构建。

缩小文件检索解析范围

alias

  • 为避免无用的检索与递归遍历,可以使用alias指定引用时候的模块

extensions

  • extensions 表示需要解析的文件类型列表。

    根据项目中的文件型,定义 extensions,以覆盖 webpack 默认的 extensions,加快解析速度。

    由于 webpack 的解析顺序是从左到右,因此要将使用频率高的文件类型放在左侧,如下我将 tsx 放在最左侧

     java 

    复制代码

    module.exports = {   resolve: {       extensions: ['.tsx', '.js'], // 因为我的项目只有这两种类型的文件,如果有其他类型,需要添加进去。   } }

noParse

  • noParse,对不依赖本地代码的第三方依赖不进行解析,比如CDN引用的第三方依赖。

include

  • 为 loader 指定 include,减少 loader 应用范围,仅应用于最少数量的必要模块,。

import优化

运用这个插件能在代码使用了import语法的情况下,大大提高代码的编译速度。

安装 babel-plugin-dynamic-import-node


kotlin

复制代码

npm install --save-dev babel-plugin-dynamic-import-node

vue-cli3

修改babel.config.js文件


css

复制代码

module.exports = { presets: ["@vue/cli-plugin-babel/preset"], env: {   development: {     plugins: ["dynamic-import-node"]   } } };

vue.cli2

.babelrc文件


json

复制代码

"env": {       "test": {                 "plugins": []               },       "development":{                       "presets": ["env", "stage-2"],                       "plugins": ["dynamic-import-node"]                     }       }

打包体积优化

某些 utility, plugin 和 loader 都只用于生产环境。例如,在开发环境下使用 TerserPlugin 来 minify(压缩) 和 mangle(混淆破坏) 代码是没有意义的。通常在开发环境下,应该排除以下这些工具:

  • TerserPlugin
  • [fullhash]/[chunkhash]/[contenthash]

代码分离

www.cnblogs.com/Mr-Hou88888…

blog.csdn.net/qq_41887214…

代码分离code splitting是 webpack 中最引人注目的特性之一。此特性能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的 bundle,加快打包速度,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。

多入口起点

入口起点(entry points)

src/index.js


arduino

复制代码

console.log('Hello world!');

src/another-module.js


javascript

复制代码

import _ from 'lodash' ​ console.log(_.join(['another', 'module', 'chunk'], ' '));

这个模块依赖了 lodash ,需要安装一下:


复制代码

npm install lodash

webpack.config.js


css

复制代码

module.exports = { mode: 'development', entry: { // 配置多入口文件   index: './src/index.js',   another: './src/another_module.js' },   output: {     filename: 'bundle.js',     path: path.resolve(__dirname, './dist'),   }, } ​

执行webpack命令,可以看到报错了 ̄□ ̄||

webpack/vue-cli构建速度和打包体积优化_第2张图片


css

复制代码

module.exports = { mode: 'development', entry: {   index: './src/index.js',   another: './src/another_module.js' }, output: {   filename: '[name].bundle.js', // 对应多个出口文件名   path: path.resolve(__dirname, './dist'), }, }

执行webpack命令,可以看到不报错了,并且dist输出了两个js文件

webpack/vue-cli构建速度和打包体积优化_第3张图片

在这里插入图片描述

文件another.bundle.js来源于entry.another,即src/another.js,文件大小为554kb,因为被lodash被打包进去了

文件index.bundle.js来源于entry.index,即src/index.js,文件大小为1.21kb

但是,如果我们的其他入口也需要使用lodash呢?


javascript

复制代码

src/index.js import _ from 'lodash' ​ console.log(_.join(['index', 'module', 'chunk'], ' '));

在这里插入图片描述

lodash在两个引用文件中都被打包了,我们期望lodash应该是公用的

配置 dependOn option 选项,这样可以在多个 chunk 之间共享模块


css

复制代码

module.exports = { mode: 'development', entry: {   index: {     import: './src/index.js', // 启动时需加载的模块     dependOn: 'common_chunk', // 当前入口所依赖的入口   },   another: {     import: './src/another_module.js',     dependOn: 'common_chunk',   }, ​   common_chunk: 'lodash' // 当上面两个模块有lodash这个模块时,就提取出来并命名为shared chunk }, output: {   filename: '[name].bundle.js', // 对应多个出口文件名   path: path.resolve(__dirname, './dist'), }, } ​

执行webpack命令,可以看到打包结果

在这里插入图片描述

已经提取出来common_chunk.bundle.js,即为提取打包了lodash公用模块

index.bundle.js another.bundle.js体积也变小

分离 Vendor

chunk-vendors.js :顾名思义,chunk-vendors.js 是捆绑所有不是自己的模块,而是来自其他方的模块的捆绑包,它们称为第三方模块或供应商模块。

通常,它意味着(仅和)来自项目 /node_modules 目录的所有模块,会将所有 /node_modules 中的第三方包打包到 chunk-vendors.js 中。

将所有的第三方包集中到一个文件,自然也会出现文件过大的问题。

webpack/vue-cli构建速度和打包体积优化_第4张图片

可以看到,当前只有一个 chunk 也就是 app.js ,他是一个 entry chunk 。因为我们的 webpack 配置是这样子的:


java

复制代码

// webpack.config.js module.exports = {  entry: {     app: './src/main.js', // entry chunk } }

app.js 包含了我们的第三方库 vue 和 axios ,以及我们的业务代码 src 。

分离 Vendor,最简单方法就是:加一个 entry ( File Changes ):


java

复制代码

// webpack.config.js module.exports = {  entry: {    app: './src/main.js',    vendor: ['vue', 'axios'], }, }

webpack/vue-cli构建速度和打包体积优化_第5张图片

虽然 vendor.js 这个 entry chunk 包含了我们想要的 vue 和 axios ,但是细心的同学会发现, app.js 也包含了他们!为什么!?

其实这是很正常的事情:每个 entry 都包含了他自己的依赖,这样他才能作为一个入口,独立地跑起来。

很难受,事实上我们并不想 app.js 还包含了 vue 和 axios 。如果可以把他们俩相同的依赖提取出来就好了,就像这样:

webpack/vue-cli构建速度和打包体积优化_第6张图片

SplitChunksPlugin

SplitChunksPlugin 插件可以将公共的依赖模块提取到已有的 chunk 中,或者提取到一个新生成的 chunk。让我们使用这个插件,将之前的示例中重复的 lodash 模块去除:

webpack.config.js


css

复制代码

module.exports = { entry: { // 多入口   index: './src/index.js',   another: './src/another_module.js', }, output: {   filename: '[name].bundle.js', // 对应多个出口文件名   path: path.resolve(__dirname, './dist'), }, optimization: {   splitChunks: { // 代码分割     // include all types of chunks     chunks: 'all'   } }, } ​

使用 optimization.splitChunks 配置选项之后,现在应该可以看出,index.bundle.jsanother.bundle.js 中已经移除了重复的依赖模块。需要注意的是,插件将 lodash 分离到单独的 chunk,并且将其从 main bundle 中移除,减轻了大小

在这里插入图片描述

CommonsChunkPlugin(已废弃)

现在,修改我们的 webpack 配置文件( File Changes ):


php

复制代码

new webpack.optimize.CommonsChunkPlugin({  name: 'vendor', })

但是!随着业务的增长,我们依赖的第三方库代码很可能会越来越多,这时候我们的 webpack.config.js 就变成这样了:


java

复制代码

module.exports = {  entry: {    app: './src/main.js',    vendor: [      'vue',      'axio',      'vue-router',      'vuex',      'element-ui',      // 很长很长   ], }, }

vendor entry 会变成很长很长,更糟糕的是,我们每次引入了新的第三方库,都需要在 vendor 手动增加对应的包名。

动态导入懒加载

  • import() 为动态加载脚本,webpack 会生成类似以上动态创建 script 标签的代码
  • import 里的注释为特殊含义的魔法注释,如果不设置 webpackChunkName,加载的脚本将被按数字次序命名

如果我们想「按需加载」路由组件的话,只要改几行代码就好了。


javascript

复制代码

//index.js setTimeout(function () {  //文件会等5秒后加载,实现懒加载  // webpackChunkName: "dynamicImport":这是webpack动态导入模块命名的方式。  //浏览器看到的文件名:dynamicImport.chunk-test.js。output.chunkFilename配置命名格式  const add = () => import(    /* webpackChunkName: "dynamicImport" */    './dynamicImport.js')  console.log(add(1, 2)); }, 5000) ​ //dynamicImport.js export default function add(a,b){  return a+b; }

动态import使用最多的一个场景是懒加载(比如路由懒加载)

  • 封装一个component.js,返回一个component对象;
  • 我们可以在一个按钮点击时,加载这个对象;

webpack/vue-cli构建速度和打包体积优化_第7张图片

webpack/vue-cli构建速度和打包体积优化_第8张图片

如果你用了 Babel ,就需要装上这个插件@babel/plugin-syntax-dynamic-import:babel plugin syntax dynamic import 来解析 import() 语法。

预加载

在声明 import 时,使用下面这些内置指令,可以让 webpack 输出 "resource hint(资源提示)",来告知浏览器:

  • prefetch(预获取):将来某些导航下可能需要的资源。只会缓存资源不会解析
  • preload(预加载):当前导航下可能需要资源。会缓存资源并解析
prefetch

下面这个 prefetch 的简单示例中,有一个 HomePage 组件,其内部渲染一个 LoginButton 组件,然后在点击后按需加载 LoginModal 组件。

LoginButton.js


go

复制代码

//... import(/* webpackPrefetch: true */ './path/to/LoginModal.js');

这会生成 并追加到页面头部,指示着浏览器在闲置时间预取 login-modal-chunk.js 文件。

只要父 chunk 完成加载,webpack 就会添加 prefetch hint(预取提示)。

不同

与 prefetch 指令相比,preload 指令有许多不同之处:

  • preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
  • preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
  • preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。
  • 浏览器支持程度不同。

babel

动态导入

如果你用了 Babel ,就需要装上这个插件@babel/plugin-syntax-dynamic-import:babel plugin syntax dynamic import 来解析 import() 语法。


json

复制代码

// .babelrc {  "plugins": ["syntax-dynamic-import"] }

资源模块

使用 webpack 资源模块 (asset module) 代替旧的 assets loader(如 file-loader/url-loader/raw-loader 等),减少 loader 配置数量。

配置方式如下:


bash

复制代码

module.exports = {   rules: [       {       test: /.(png|svg|jpg|jpeg|gif)$/i,       include: [         paths.appSrc,       ],       type: 'asset/resource',     },   ] }

SourceMap

最佳选择是 eval-cheap-module-source-map详细区分可至 webpack devtool 查看。

css抽离

mini-css-extract-plugin插件会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载


ini

复制代码

const MiniCssExtractPlugin = require("mini-css-extract-plugin"); plugins: [ new MiniCssExtractPlugin({      filename: "css/[name].[hash].css", // 定义抽离的入口文件的文件名      chunkFilename: "css/[name].[hash].css", // 定义非入口块文件的名称,如动态导入的文件   }) ],

css压缩


ini

复制代码

const MiniCssExtractPlugin = require("mini-css-extract-plugin"); + const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); ​ module.exports = { module: {   rules: [     {       test: /.(css|less)$/,       use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"],     },   ], }, + optimization: { +   minimizer: [ +     new CssMinimizerPlugin(), +   ], + }, plugins: [new MiniCssExtractPlugin()], };

这将仅在mode: production 生产环境开启 CSS 优化

如果还想在开发环境下启用 CSS 优化,optimization.minimize 设置为 true

gzip压缩

前端将文件打包成 .gz 文件,然后通过 nginx 的配置,让浏览器直接解析 .gz 文件,可以大大提升文件加载的速度,浏览器可以直接解析 .gz 文件并解压。


javascript

复制代码

启用gzip压缩(需要配置nginx,可以看出压缩后的文件大小明显变化) highlighter- PHP ​ const CompressionWebpackPlugin = require('compression-webpack-plugin') chainWebpack(config) { // 生产模式下启用gzip压缩 需要配置nginx支持gzip   if (process.env.NODE_ENV === 'production') {     config.plugin('CompressionWebpackPlugin').use(CompressionWebpackPlugin, [       {         filename: '[path][base].gz',         algorithm: 'gzip',         test: new RegExp('\.(js|css)$'),         // 只处理大于xx字节 的文件,默认:0         threshold: 10240,         // 示例:一个1024b大小的文件,压缩后大小为768b,minRatio : 0.75         minRatio: 0.8, // 默认: 0.8         // 是否删除源文件,默认: false         deleteOriginalAssets: false       }     ])   } }

js压缩

blog.csdn.net/qq_29722281…

  • terser-webpack-plugin和uglifyjs-webpack-plugin(不推荐)

    不再维护 uglify-es ,并且 uglify-js 不支持 ES6 +。

    terser 是 uglify-es 的一个分支,主要保留了与 uglify-es 和 uglify-js@3 的 API 和 CLI 兼容性。

    webpack5 自带最新的 terser-webpack-plugin,无需手动安装。

    terser-webpack-plugin 默认开启了 parallel: true 配置,并发运行的默认数量: os.cpus().length - 1 ,本文配置的 parallel 数量为 4,使用多进程并发运行压缩以提高构建速度。

     yaml 

    复制代码

    const TerserPlugin = require('terser-webpack-plugin'); module.exports = {    optimization: {        minimizer: [            new TerserPlugin({              parallel: 4,              terserOptions: {                parse: {                  ecma: 8,               },                compress: {                  ecma: 5,                  warnings: false,                  comparisons: false,                  inline: 2,               },                mangle: {                  safari10: true,               },                output: {                  ecma: 5,                  comments: false,                  ascii_only: true,               },             },           }),       ]   } }
  • vue-cli

     arduino 

    复制代码

    config.optimization.minimize(true)// 开启压缩js代码     config.optimization.splitChunks({ // 开启代码分割     chunks: 'all' })

img压缩

image-minimizer-webpack-plugin:用来压缩图片的插件

npm i image-minimizer-webpack-plugin imagemin -D 还有剩下包需要下载,有两种模式:

无损压缩 npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D 有损压缩 npm install imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D

配置CDN

线上使用 cdn ,如何库有问题,项目就会有问题,除非公司有自己的 cdn 库,不过这确实也是一种优化方案,效果也还不错。它的配置也很简单,在 externals 中配置,例子:


arduino

复制代码

module.exports = {  configureWebpack: config => {    if (process.env.NODE_ENV === 'production') {      // 配置 cdn,这里将 vue,vue-router 和 axios 三个包配置成 cdn 引入      // 其中 Vue,VueRouter 等名称是该库暴露在全局中的变量名      config.externals = {        vue: 'Vue',        'vue-router': 'VueRouter',        axios: 'axios'     }   } } }

然后在 public/index.html 模板文件中引入 cdn 地址:


xml

复制代码

                                             

 

我这里使用的是 bootcdn 的地址,需要注意版本问题。

也可以借助 HtmlWebpackPlugin 插件来方便插入 cdn 的引入。

使用 cdn 引入的方式虽然能极大改善网页加载速度,但我还是不会用这个功能,项目还不需要非得这样的优化,也怕 cdn 不稳定。

借助 HtmlWebpackPlugin 插件来方便插入 cdn 的引入


ini

复制代码

//生产环境标记 const IS_PRODUCTION = process.env.NODE_ENV === "production"; const path = require("path"); // 生产配置 const cdn_production = {  js: ["/librarys/[email protected]/vue.min.js"] }; // 开发配置 const cdn_development = {  js: ["/librarys/[email protected]/vue.js"] }; ​ module.exports = {  configureWebpack: {    externals: {      vue: "Vue",   }, },  chainWebpack: config => {    config.plugin("html").tap(args => {      args[0].cdn = IS_PRODUCTION ? cdn_production : cdn_development;      return args;   }); } };

index.html中添加


javascript

复制代码

<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %> <% } %>

按需加载

1. lodash

类似 import { throttle } from 'lodash' 就属于有副作用的引用,会将整个 lodash 文件进行打包。

优化方式是使用 import { throttle } from 'lodash-es' 代替 import { throttle } from 'lodash', lodash-es 将 Lodash 库导出为 ES 模块,支持基于 ES modules 的 tree shaking,实现按需引入。

2. ant-design

ant-design 默认支持基于 ES modules 的 tree shaking,对于 js 部分,直接引入 import { Button } from 'antd' 就会有按需加载的效果。

假如项目中仅引入少部分组件,import { Button } from 'antd' 也属于有副作用,webpack不能把其他组件进行tree-shaking。这时可以缩小引用范围,将引入方式修改为 import { Button } from 'antd/lib/button' 来进一步优化。

Tree Shaking


javascript

复制代码

tree shaking` 是一个术语,用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块语法的 静态结构 特性,例如 `import` 和 `export

babel

Babel 为编译的每个文件都插入了辅助代码,使代码体积过大!默认情况下会被添加到每一个需要它的文件中。可以将这些辅助代码作为一个独立模块,来避免重复引入。

@babel/plugin-transform-runtiome:

禁用了 Babel 自动对每个文件的 runtime 注入,而是引入 @babel/plugin-transform-runtiome 并且使所有辅助代码从这里引用

先下载包:

npm i @babel/plugin-transform-runtime -D

webpack/vue-cli构建速度和打包体积优化_第9张图片

sideEffects(不推荐)

sideEffects 有三种情况

sideEffects:true 所有文件都有副作用,全都不可 tree-shaking sideEffects:false 有这些文件有副作用,所有其他文件都可以 tree-shaking,但会保留这些文件 sideEffects:[] 部分 tree-shaking , 除了数组外都 tree-shaking

"side effect(副作用)" 的定义是,在导入时会执行特殊行为的代码,而不是仅仅暴露一个 export 或多个 export。举例说明,例如 polyfill,它影响全局作用域,并且通常不提供 export。

如果你的代码确实有一些副作用,可以改为提供一个数组:


json

复制代码

{  "name": "your-project",  "sideEffects": ["./src/some-side-effectful-file.js"] }

所有导入文件都会受到 tree shaking 的影响。这意味着,如果在项目中使用类似 css-loader 并 import 一个 CSS 文件,则需要将其添加到 side effect 列表中,以免在生产模式中无意中将它删除:


json

复制代码

{  "name": "your-project",  "sideEffects": ["./src/some-side-effectful-file.js", "*.css"] }

IgnorePlugin

  1. 这是webpack内置插件
  2. 这个插件的作用是:忽略第三方包指定目录,让这些指定目录不要被打包进去

javascript

复制代码

//虽然我设置了语言为中文,但是在打包的时候,是会将所有语言都打包进去的。这样就导致包很大,打包速度又慢 plugins:[ new Webpack.IgnorePlugin(/./locale/,/moment/),//moment这个库中,如果引用了./locale/目录的内容,就忽略掉,不会打包进去 ]

css

打包时把没有用的 CSS 代码摇走,可以大幅减少打包后的 CSS 文件大小。

使用 purgecss-webpack-plugin 对 CSS Tree Shaking。

插件

autoprefixer兼容css

webpak 引入autoprefixer,自动加上各种前缀让不同的浏览器得以支持

你可能感兴趣的:(webpack,vue.js,前端)