扶着你玩转webpack4——优化

本章以 vue-cli 2.x 生成的项目结构为例。

缩小文件搜索范围

Webpack 启动后会从配置的 Entry 出发,解析出文件中的导入语句,再递归的解析。 在遇到导入语句时 Webpack 会做两件事情:

  1. 根据导入语句去寻找对应的要导入的文件。例如 require(‘react’) 导入语句对应的文件是 ./node_modules/react/react.js, require(’./util’) 对应的文件是 ./util.js。
  2. 根据找到的要导入文件的后缀,使用配置中的 Loader 去处理文件。例如使用 ES6 开发的 JavaScript 文件需要使用 babel-loader 去处理。
  1. 优化 loader 的配置
    loader 中可以使用 testincludeexclude 来命中 loader 要应用规则的文件。所以为了提高构建速度,我们要尽可能少和准确的让文件被 loader 处理。
function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

rules: [
	{
	   test: /\.js$/,
	   loader: 'babel-loader',
	   include: resolve('src')
	}
]
  1. resolve.alias 配置别名
    vue-cli 中,默认会有一个 vue$@ 的别名,对我们构建形成直接影响的使 vue$ ,这里直接指向的是 vue/dist/esm.js,这样就减少了递归解析的操作。
resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
  }
  1. resolve.extensions 配置后缀名
module.exports = {
  resolve: {
    // 尽可能的减少后缀尝试的可能性 以及 把需要优先匹配的放到前面
    extensions: ['js', 'css']
  },
}

使用DllPlugin

大量复用模块的动态链接库只需要编译一次,在之后的构建过程中被动态链接库包含的模块将不会在重新编译,而是直接使用动态链接库中的代码。 由于动态链接库中大多数包含的是常用的第三方模块,例如 react、react-dom,只要不升级这些模块的版本,动态链接库就不用重新编译。

  1. build 文件夹下新建一个 webpack.dll.config.js 文件。
const path = require('path')
const webpack = require('webpack')
const AssetsPlugin = require('assets-webpack-plugin') // 生成带有资源路径的json文件
module.exports = {
  entry: {
    // 把这些依赖的包打包生成一个 libs开头 的文件夹,也可以更细粒度的拆分
    libs: [
      'vue/dist/vue.esm.js',
      'vue-router',
      'vuex',
      'element-ui',
      'moment',
      'lodash',
      'qs',
      'axios',
      'vue-amap',
      'ant-design-vue'
    ]
  },
  output: {
    path: path.resolve(__dirname, '../static'),
    filename: '[name].[chunkhash:7].js',
    library: '[name]_library'
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.resolve(__dirname, '../static/[name]-manifest.json'),
      name: '[name]_library', // 这里需要和 output.library 保持一致
      context: __dirname
    }),
    new AssetsPlugin({
      filename: 'bundle-config.json',
      path: './static'
    })
  ],
  optimization: {
    minimize: true
  }
}

  1. package.json 中添加一条命令
"scripts": {
    "build:dll": "webpack --config build/webpack.dll.config.js"
  }

然后执行

yarn build:dll

这样会在 static 中生成 bundle.config.jsonlibs-manifest.jsonlibs.[hash:7].js 三个文件。

  1. webpack.dev.config.js 中加上一个插件
plugins: [
  new webpack.DllReferencePlugin({
    context: __dirname,
    manifest: require('../static/libs-manifest.json')
  })
]
  1. 这里还需要把生成后的文件引入到我们的 html 中,利用 html-webpack-plugin 来动态解决
    修改 webpack.prod.conf.js 文件
plugins: [
	new HtmlWebpackPlugin({
      ...
      libJsName: bundleConfig.libs.js
    })
]
  1. index.html 中修改,这里是内置的 ejs 语法。
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="shortcut icon" href="static/favicon.icon" type="image/icon" />
    <title>项目</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <script src="./static/<%= htmlWebpackPlugin.options.libJsName %>"></script>
  </body>
</html>

然后重新打包,即可。

HappyPack 开启多进程

文件读写和计算操作是无法避免的,那能不能让 Webpack 同一时刻处理多个任务,发挥多核 CPU 电脑的威力,以提升构建速度呢?
HappyPack 就能让 Webpack 把任务分解给多个子进程去并发的执行,子进程处理完后再把结果发送给主进程。

分解任务和管理线程的事情 HappyPack 都会帮你做好,你所需要做的只是接入 HappyPack。

yarn add happypack -D
const HappyPack = require('happypack')
module.exports = {
	modules: {
		rules: [
			 {
		        test: /\.js$/,
		        loader: 'happypack/loader?id=babel',
		        include: resolve('src')
		      }
		]
	},
	plugins: [
		new HappyPack({
	        id: 'babel', // 默认是 '1'
	        loaders: ['babel-loader?cacheDirectory'] // 为了尽可能地优化 开启 babel 的缓存
	    })
	]
}

使用ParallelUglifyPlugin来多进程进行代码压缩

webpack 中内置的是 UglifyJs 来压缩代码,但是它是一个一个地去压缩,很耗费时间。
ParallelUglifyPlugin 是开启多个进程来并行压缩代码,内部还是使用 UglifyJs 来处理。

yarn add webpack-parallel-uglify-plugin -D

然后在 webpack.pro.config.js 中修改即可

const UglifyJsParallelPlugin = require('webpack-parallel-uglify-plugin')
module.exports = {
	plugins: [
		new UglifyJsParallelPlugin({
	        cacheDir: '.cache/',  // 开启缓存
	        uglifyJS:{
		        output: {
		            comments: false
		        },
		        compress: false
	        }
	    })
	]
}

在通过 new ParallelUglifyPlugin() 实例化时,支持以下参数:

  1. test:使用正则去匹配哪些文件需要被 ParallelUglifyPlugin 压缩,默认是 /.js$/,也就是默认压缩所有的 .js 文件。
  2. include:使用正则去命中需要被 ParallelUglifyPlugin 压缩的文件。默认为 []。
  3. exclude:使用正则去命中不需要被 ParallelUglifyPlugin 压缩的文件。默认为 []。
  4. cacheDir:缓存压缩后的结果,下次遇到一样的输入时直接从缓存中获取压缩后的结果并返回。cacheDir 用于配置缓存存放的目录路径。默认不会缓存,想开启缓存请设置一个目录路径。
  5. workerCount:开启几个子进程去并发的执行压缩。默认是当前运行电脑的 CPU 核数减去1。
  6. sourceMap:是否输出 Source Map,这会导致压缩过程变慢。
  7. uglifyJS:用于压缩 ES5 代码时的配置,Object 类型,直接透传给 UglifyJS 的参数。
  8. uglifyES:用于压缩 ES6 代码时的配置,Object 类型,直接透传给 UglifyES 的参数。

使用 externals 减少应用的依赖

当我们使用 jQuery 或者 vue 等时,代码量很大,打包起来比较耗时,那我们也可以使用 script 引用。
webpack.base.config.js 中修改

module.exports = {
	externals: {
	    'vue': 'Vue',
	    'vue-router': 'VueRouter'
	}
}

然后在 index.html 中修改

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>title</title>
    <script src="https://cdn.bootcss.com/vue/2.5.15/vue.min.js"></script>
    <script src="https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

这时,还需要手动在模块引入的地方进修引入修改
src/main.js

// import Vue from 'vue'
const Vue = require('vue')

src/router

// import Router from 'vue-router'
// import Vue from 'vue'
// Vue.use(Router)
const Router = require('vue-router')

考虑是否提取CSS

我们知道,我们最后打包生成的 bundle 中,可以在 js 中来动态引用样式,也可以单独生成一个 css 文件来引入。这两种方案各有千秋,通常我们希望在生产环境中提取 css ,这样当我们修改 js 的时候,不会影响 csscontenthash,使得我们的静态资源能够尽可能地长缓存。但是需要的深究一点的是,如果我们的 css 很小,也被抽离成单独的一个 css 文件,那就意味还需要发一次 http 请求,这样是不合理的。遗憾的是 mini-css-extract-plugun 现阶段还不支持 min-size 的功能。如果未来实现了该功能,我们就可以指定 min-size 来决定是需要提取 css 还是需要内联到 js 中。
现阶段我个人觉得还是不需要提取 css 。因为通常来说我们的 css 文件都还是很小,而一次 http 请求是相当昂贵的。我们可以在 webpack.pro.config.js 中修改
extract 改为 false

module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: false,
      usePostCSS: true
    })
  },

剩下的诸如 Tree-ShakingScope-HoistingCode-Splitting提取公共代码 等就不赘述了,之前的章节都提到过。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值