跳转到内容

打包优化

刀刀
0字
0分钟
2026/8/16

项目的构建速度由两个方面决定:

  • 处理的内容数量
  • 进行的操作

优化操作一般有以下几点:

  1. 减少处理内容数量

    指定 includeexclude 排除掉一些不必要的处理; DLL 优化处理

  2. 提高处理效率

    多线程操作;缓存 sourcemap

  3. 减少操作的数量

    去掉一些没意义的操作

减少处理内容数量

排除不必要的处理

先来看看一段 vue.config.js 代码:

vue.config.js
js
const path = require('path')
function resolve(dir) {
  return path.join(__dirname, dir)
}

module.exports = {
  publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
  chainWebpack: (config) => {
    config.resolve.alias.set('@', resolve('src'))
  },
  productionSourceMap: false,
  devServer: {
    open: false,
    port: 5003,
    overlay: {
      errors: true,
      warmimgs: true,
    },
    proxy: {
      '/api': {
        target: 'xxx',
        changeOrigin: true,
      },
    },
  },
}

打包这个项目,打包的时间为 6.4s,接下来测试一下通过 exclude 排除后能减少多少时间。

需要注意的是,不能什么都能排除, js 文件 webpack 本来就认识,但是 tsvuehtml 等文件不能排除,需要处理,通过 babel-loader-es6-es5 转编译。

开发环境中排除掉 js 中的 node_modulessrc ,代码如下:

vue.config.js
js
const path = require('path')
function resolve(dir) {
  return path.join(__dirname, dir)
}

module.exports = {
  publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
  chainWebpack: (config) => {
    if (
      config.module
        .rule('js')
        .exclude 
        .add(resolve('/node_modules')) 
        .add(resolve('/src')) 
    )
      config.resolve.alias.set('@', resolve('src'))
  },
  productionSourceMap: false,
  devServer: {
    open: false,
    port: 5003,
    overlay: {
      errors: true,
      warmimgs: true,
    },
    proxy: {
      '/api': {
        target: 'xxx',
        changeOrigin: true,
      },
    },
  },
}

重新打包,打包时间缩短为 6.19s,这个项目用到的 js 不多,如果 js 文件多的话能减少更多的时间。

DLL 处理

接下来做 DLL 处理,步骤为先把一些第三方库打包,后续打包时候不去打包这些第三方库,而是直接复用。

根目录下新建一个 webpack.dll.js 文件,代码如下:

webpack.dll.js
js
const path = require('path')
const webpack = require('webpack')

// dll 文件存放的目录
const dllPath = 'public/vendor'
module.exports = {
  mode: 'production',
  entry: {
    // 需要提取的库文件
    vendor: ['vue', 'vuex', 'vue-router', 'element-ui', 'echarts']
  },
  // 定义出口
  output: {
    // 当前目录下的 public文件夹下的 vendor
    path: path.join(__dirname, dllPath),
    filename: '[name].dll.js',
    // vendor.dll.js 中暴露出的全局变量名
    // 保持与 webpack.DllPlugin 中名称一致
    library: '[name]_[hash]'
  },
  plugins: [
    // manifest.json 描述动态链接库包含了哪些内容,后续不需要再重复打包了
    new webpack DllPlugin({
     path: path.join(__dirname, dllPath, '[name]-manifest.json'),
     // 保持与 output.library 中名称一致
     name: '[name]_[hash]',
     context:process.cwd()
    })
  ]
}

接着去到 package.json 文件重写打包命令,新增一条 dll 打包命令:

package.json
json
{
  // ...
  "scripts": {
    "dll": "webpack --config webpack.dll.js"
    // ...
  }
}

通过新命令打包,会遇到一个坑,它会提示你是否要下载 webpack-cli ,如果直接点 yes ,会有版本冲突问题,正确做法是先在全局下载 webpack-cli

sh
npm i webpack-cli@3.3.12 --save-dev

然后再运行命令:

sh
npm run dll

运行完毕, public 文件夹下已经有打包好的 vendor 文件夹,接下来回到 vue.config.js 文件,添加 webpack 配置,代码如下:

vue.config.js
js
const path = require('path')
const webpack = require('webpack') 
function resolve(dir) {
  return path.join(__dirname, dir)
}

module.exports = {
  publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
  configureWebpack: {
    plugins: [
      new webpack.DllReferencePlugin({
        context: process.cwd(), 
        // 关键设置,引用的文件地址
        manifest: require('./public/vendor/vendor-manifest.json'), 
      }), 
    ], 
  }, 
  chainWebpack: (config) => {
    if (
      config.module
        .rule('js')
        .exclude.add(resolve('/node_modules'))
        .add(resolve('/src'))
    )
      config.resolve.alias.set('@', resolve('src'))
  },
  productionSourceMap: false,
  devServer: {
    open: false,
    port: 5003,
    overlay: {
      errors: true,
      warmimgs: true,
    },
    proxy: {
      '/api': {
        target: 'xxx',
        changeOrigin: true,
      },
    },
  },
}

改完配置文件后因为没有缓存了,所以第一次打包速度会比较慢,以后续打包时间为准。打包后发现时间已经缩短为 3.15s 。

不过现在还没结束,打包完毕后此时项目如果在浏览器上打开,页面一片空白。这是因为前面打包的时候把这些资源排除掉不打包了,解决方法为去到 index.html 文件中,把那些排除出去的 js 文件手动引入。

index.html
html
<script src="<%= BASE_URL %>vendor/vendor.dll.js"></script>

提高处理效率

多线程处理

多线程打包的收益在项目较小的时候其实不是很明显,因为开启多线程也需要额外的操作。

想要使用多线程需要下载一个 loader

sh
npm i thread-loader --save

下载同时来写代码,在 vue.config.js 文件中引入使用:

vue.config.js
js
const os = require('os') 
const webpack = require('webpack')
const path = require('path')
function resolve(dir) {
  return path.join(__dirname, dir)
}

module.exports = {
  publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
  configureWebpack: {
    plugins: [
      new webpack.DllReferencePlugin({
        context: process.cwd(),
        // 关键设置,引用的文件地址
        manifest: require('./public/vendor/vendor-manifest.json'),
      }),
    ],
  },
  chainWebpack: (config) => {
    if (
      config.module
        .rule('js')
        .exclude.add(resolve('/node_modules'))
        .add(resolve('/src'))
    )
      config.module
        .rule('vue')
        .use('thread-loader') 
        .loader('thread-loader') 
        .tap(() => {
          return {
            // 根据当前cpu数量
            worker: os.cpus().length, 
          } 
        })
        .before('vue-loader') // 在vue-loader处理完后再处理
    config.resolve.alias.set('@', resolve('src'))
  },
  productionSourceMap: false,
  devServer: {
    open: false,
    port: 5003,
    overlay: {
      errors: true,
      warmimgs: true,
    },
    proxy: {
      '/api': {
        target: 'xxx',
        changeOrigin: true,
      },
    },
  },
}

但是这样可能不会生效,甚至会有反效果,这是因为优化之后也有可能会增多处理。

上方开多线程这个举动也是需要时间,多线程处理的时间比开启多线程的时间要少,因此最后打包时间也会增多,不太推荐使用该方法。

sourceMap

举个例子,我在 login.vue 页面 console.log() 输出一行代码,开启 sourceMap 后可以根据这个输出定位到是哪个文件哪行代码输出的。在生产模式时没必要打开。

Vue-cli 中配置的默认 evalsourceMap ,可以关闭,代码如下:
vue.config.js
js
module.exports = {
  // ...
  css: {
    sourceMap: false,
  },
  configureWebpack: {
    devtool: 'none',
    plugins: [
      // ...
    ],
  },
}

关闭之后打包时间确实有减少,但是打开项目之后查看控制台的打印,会定位到打包好的文件,不利于我们排错,因此不推荐关闭。

注意

CSS 的 sourceMap 默认是关闭的,可以打开,但是最后打包时间会增加,因为增加了要处理的内容。开启方式为在 vue.config.js 文件设置以下代码:

vue.config.js
js
module.exports = {
  // ...
  css: {
    sourceMap: true,
  },
}

完整代码

vue.config.js 完整代码如下:

vue.config.js
js
const os = require('os') 
const webpack = require('webpack')
const path = require('path')
function resolve(dir) {
  return path.join(__dirname, dir)
}

module.exports = {
  publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
  configureWebpack: {
    plugins: [
      new webpack.DllReferencePlugin({
        context: process.cwd(),
        // 关键设置,引用的文件地址
        manifest: require('./public/vendor/vendor-manifest.json'),
      }),
    ],
  },
  chainWebpack: (config) => {
    if (
      config.module
        .rule('js')
        .exclude.add(resolve('/node_modules'))
        .add(resolve('/src'))
    )
      config.module
        .rule('vue')
        .use('thread-loader') 
        .loader('thread-loader') 
        .tap(() => {
          return {
            // 根据当前cpu数量
            worker: os.cpus().length, 
          } 
        })
        .before('vue-loader') // 在vue-loader处理完后再处理
    config.resolve.alias.set('@', resolve('src'))
  },
  css: {
    sourceMap: false,
  },
  productionSourceMap: false,
  devServer: {
    open: false,
    port: 5003,
    overlay: {
      errors: true,
      warmimgs: true,
    },
    proxy: {
      '/api': {
        target: 'xxx',
        changeOrigin: true,
      },
    },
  },
}

贡献者

The avatar of contributor named as duyidao duyidao
The avatar of contributor named as v_duyilin v_duyilin
The avatar of contributor named as 刀刀 刀刀
The avatar of contributor named as 平安喜乐 平安喜乐

页面历史

刀刀博客累计访客 人;文档累计访问量共