Webpack打包优化
栏目: JavaScript · 发布时间: 5年前
内容简介:对于使用vue开发项目的FE来说,打包上线这个环节相信大家都不陌生。本文主要是介绍如何通过webpack(实践版本:webpack4.16.5)的配置来提高打包构建速度以及减小包的体积。使用使用
一、前言
对于使用vue开发项目的FE来说,打包上线这个环节相信大家都不陌生。本文主要是介绍如何通过webpack(实践版本:webpack4.16.5)的配置来提高打包构建速度以及减小包的体积。
二、优化策略
css,js,html压缩
使用 optimize-css-assets-webpack-plugin
来压缩css。在webpack4的配置项中,需在optimization下的minimizer对象中去实例化使用。
// 压缩css const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin') // 在optimization.minimizer中引用 new OptimizeCSSAssetsPlugin()复制代码
使用 MiniCssExtractPlugin来抽离内联css到外联文件 。
// 抽离内联css到外部css文件 const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 在optimization.minimizer中引用 new MiniCssExtractPlugin({ filename: utils.assetsPath('css/[name].[contenthash:8].css'), chunkFilename: utils.assetsPath('css/[name].[contenthash:8].css') })复制代码
使用 uglifyjs-webpack-plugin 来压缩js,重点属性 parallel, 用来开启多进程并行执行js压缩,大大提高构建速度。
//压缩js const UglifyJsPlugin = require('uglifyjs-webpack-plugin') //在optimization.minimizer中引用 new UglifyJsPlugin({ uglifyOptions: { warnings: false, mangle: { safari10: true }, compress: { drop_debugger: false, drop_console: true, //console pure_funcs: ['console.log'] // 移除console }, output:{ // 去掉注释内容 comments: false } }, sourceMap: false, cache: true, parallel: true })复制代码
使用 html-webpack-plugin来 压缩html代码,并实现自动化注入脚本以及样式文件,对于文件名中包含哈希的Webpack捆绑包尤其有用。
// 压缩html const HtmlWebpackPlugin = require('html-webpack-plugin') // 在plugins中引入 new webpack.HtmlWebpackPlugin({ filename:path.resolve(__dirname, '../dist/index.html'), template: 'index.html', inject: true, favicon: resolve('favicon.ico'), title:'标题', minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: true } }) 复制代码
拆包
webpack4.x中使用splitChunks来进行拆包,抽离第三方依赖库。默认情况下,webpack将会基于以下条件自动分割代码块:
- 新的代码块被共享或者来自node_modules文件夹
- 新的代码块大于30kb(在min+giz之前)
- 按需加载代码块的请求数量应该<=5
- 页面初始化时加载代码块的请求数量应该<=3
默认配置如下:
splitChunks: { chunks: "async", minSize: 30000, // 模块的最小体积 minChunks: 1, // 模块的最小被引用次数 maxAsyncRequests: 5, // 按需加载的最大并行请求数 maxInitialRequests: 3, // 一个入口最大并行请求数 automaticNameDelimiter: '~', // 文件名的连接符 name: true, cacheGroups: { // 缓存组 vendors: { test: /[\\/]node_modules[\\/]/, priority: -10 }, default: { minChunks: 2, priority: -20, reuseExistingChunk: true } } }复制代码
可以通过自定义配置项修改配置,如像这样抽离第三方依赖库(如单独将elementUI打包)
splitChunks: { chunks: 'all', cacheGroups: { lib: { name: 'chunks-libs', test: /[\\/]node_modules[\\/]/, priority: 10, chunks: 'initial' // 只打包初始时依赖的第三方 }, elementUI: { name: 'chunk-elementUI', // 单独将 elementUI 拆包 priority: 20, // 权重要大于 libs 和 app 不然会被打包进 libs 或者 app test: /[\\/]node_modules[\\/]element-ui[\\/]/, chunks: 'all' } } }复制代码
一般还需配合runtimeChunk使用( runtimeChunk的具体作用 )。在抽取 Webpack 运行时代码的时候,要指定 runtimeChunk
属性:
true 'single'
// optimization.runtimeChunk 具体使用规则 详见 //https://webpack.js.org/configuration/optimization/#optimizationruntimechunk runtimeChunk: true复制代码
Happypack
由于 JavaScript 是单线程模型,在webpack构建过程中,我们需要使用Loader对js,css,图片,字体等文件做转换操作,并且转换的文件数据量也是非常大的,且这些转换操作不能并发处理文件,而是需要一个个文件进行处理,HappyPack的基本原理是将这部分任务分解到多个子进程中去并行处理,子进程处理完成后把结果发送到主进程中,从而减少总的构建时间。(ps:对file-loader和url-loader支持不好,所以这两个loader就不需要换成HappyPack)
Happypack运行机制
Happy使用方式
// 多进程并发执行loader 默认为单线程 const HappyPack = require('happypack') const os = require('os')// 根据系统的内核数量 指定线程池个数 也可以其他数量 const happyThreadPool = HappyPack.ThreadPool({size: os.cpus().length})//插件中引入 plugins:[ new HappyPack({ // 基础参数设置 id: 'babel', // 上面loader?后面指定的id loaders: ['babel-loader?cacheDirectory'], // 实际匹配处理的loader 这里加入了缓存控制 threadPool: happyThreadPool, verbose: true }) ] // loader处理器中使用 通过id匹配modules: { test: /\.js$/, use:'happypack/loader?id=babel', exclude: /node_modules/, // 排除不处理的目录 include: [ resolve('src'), resolve('test'), resolve('mock'), resolve('node_modules/webpack-dev-server/client') ] }复制代码
缓存与增量构建
缓存构建 :webpack构建中,一般需要使用许多loader来预处理文件,以babel-loader为例。可以通过设置 cacheDirectory
或 cacheDirectory=true
来达到缓存的目的。
{ test: /\.js$/, loader: 'babel-loader?cacheDirectory', exclude: /node_modules/, // 排除不处理的目录 include: [ resolve('src'), resolve('test'), resolve('mock'), resolve('node_modules/webpack-dev-server/client') ] }复制代码
cacheDirectory对loader转译后的结果进行缓存,之后的webpack进行构建时,都会去尝试读取缓存来避免高耗能的babel重新转译过程。
增量构建 :使用增量构建而不是全量构建有利于构建速度的提升。 全量构建 即每次重新构建都需要重新编译一次(包括未修改部分),而 增量构建 对于未修改的部分不会再重新编译,对于rebuild能够大大提高编译速度。对于开发阶段,可以使用 webpack-dev-server
来达到增量编译的目的,对于生产阶段,可以通过给生成的文件添加 hash
(或 chunkhash
或 contenthash
)来实现增量构建。
output: { path: config.build.assetsRoot, filename: utils.assetsPath('js/[name].[chunkhash:8].js'), chunkFilename: utils.assetsPath('js/[name].[chunkhash:8].js') }复制代码
优化模块查找路径
通过配置resolve.modules来告诉webpack解析模块时应该搜索的目录。默认配置采用向上递归搜索的方式去寻找,设置特定搜索目录有助于webpack更快搜索到目标。
resolve: { extensions: ['.js', '.vue', '.json'], alias: { '@': resolve('src') }, modules: [ resolve('src'), resolve('node_modules') ]} }复制代码
DllPlugin和DllReferencePlugin
dll全称为动态链接库,先通过 dllPlugin
生成清单文件(这个文件包含了从 require
和 import
的request到模块 id 的映射),然后通过 DllReferencePlugin
引用该清单文件,将依赖的名称映射到模块的 id 上。这样每次打包时.先去查找清单里中是否已经存在这个依赖,如果已经存在,则不打包,如果还没存在,则需要打包。与通过 externals 的方式引入第三方库类似,dll主要用于那些没有可以在<script>标签中引入的资源的模块(纯npm包)。
使用方式
新建一个webpack.dll.conf.js文件,并执行 webpack --config ./build/webpack.dll.conf.js
会在static/js文件夹下生成 dll.vendor.js
以及在根目录下生成 mainfest.json
//webpack.dll.conf.js const webpack = require('webpack'); const path = require('path'); const vendors = [ 'vue', 'vue-router', 'vuex', 'axios']; module.exports = { output: { path: path.resolve(__dirname, '../static/js'), filename: 'dll.[name].js', library: '[name]' }, entry: { vendor: vendors, }, plugins: [ new webpack.DllPlugin({ path: 'manifest.json', name: '[name]', context: __dirname }) ] }复制代码
在项目配置文件(如webpack.base.conf.js)中通过DllRe引入
// build/webpack.base.conf.js const manifest = require('../manifest.json') // 插件中引入 plugins: [ new webpack.DllReferencePlugin({ mainfest }) ]复制代码
然后在index.html手动引入该文件
<script type="text/javascript" src="/static/js/dll.vendor.js"></script>复制代码
externals配合cdn加载第三方库
externals的作用是从打包的bundle文件中排除依赖。通俗点讲,就是在项目中通过import引入的依赖在打包的时候不会打包到bundle包中去,而是通过script的方式去访问这些依赖。与Dll不同的是,要去维护cdn
// build/webpack.base.conf.js // externals 对象中的 key代表第三方依赖名(同package.json包中依赖名) // value代表暴露给外部使用的别名 这里element-ui的别名为ELEMENT module.exports ={ externals: { 'vue':'Vue', 'vue-router':'VueRouter', 'axios':'axios', 'vuex':'Vuex', 'element-ui':'ELEMENT' } }复制代码
在index.html文件中通过cdn方式引入
// head中引入 <link href="https://cdn.bootcss.com/element-ui/2.4.6/theme-chalk/index.css" rel="stylesheet"> // body中引入 <script type="text/javascript" src="/static/js/dll.vendor.js"></script> <script src="https://cdn.bootcss.com/vue/2.5.17/vue.min.js"></script> <script src="https://cdn.bootcss.com/element-ui/2.4.6/index.js"></script> <script src="https://cdn.bootcss.com/vue-router/3.0.6/vue-router.min.js"></script> <script src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script> <script src="https://cdn.bootcss.com/vuex/3.0.1/vuex.min.js"></script>复制代码
在相关文件中移除相关引入的依赖
// main.js //通过cdn的方式注释相关依赖 // import Vue from "vue"; // import ElementUI from "element-ui"; // Vue.use(ElementUI); // store/index.js // 注释掉vuex import引入方式 // import Vue from 'vue'; // import Vuex from 'vuex'; // Vue.use(Vuex); // router/router.js // 注释掉vue-router // import Vue from 'vue'; // import Router from 'vue-router'; //使用externals中vue-router暴露的全局对象名VueRouter进行实例化 const router = new VueRouter({ mode: "history", scrollBehavior: () => ({y: 0}), routes: [otherRouter, ...constantRouterMap] });复制代码
分析工具
查看 webpack 打包后所有组件与组件间的依赖关系,可以通过打包分析 工具 来实现。
webpack-bundle-analyzer
webpack-bundle-analyzer是一个webpack打包分析插件,它将打包的依赖关系以树形图的方式呈现,直观方便。
plugins:[ new BundleAnalyzerPlugin({ analyzerPort: 8080, generateStatsFile: false }) ]复制代码
在插件配置项中引入并通过 cnpm run build --report
来生成可视化分析图,效果图如下:
官方分析工具
webpack提供的一个官方工具,可查看你的项目版本信息,有多少modules,多少chunks,中间有多少错误信息、有多少警告,各个模块之间的依赖关系等。
使用方式
首先通过 webpack --config ./build/webpack.prod.conf.js --json > stats.json
生成json文件(ps:如果使用了Happypack,需删除该json文件前两行,否则不是标准的json),然后通过官网分析工具地址将该文件上传,如图所示:
具体分析结果可以通过点击相应的模块查看详情。
总结
webpack优化并没有一个通用方案,这里我只是列出了我使用过的一些策略,具体业务需具体分析,对症下药。希望本文能帮助到一些有需要的小伙伴~
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 【前端打包部署】谈一谈我在SPA项目打包=>部署的处理
- Maven多模块项目打包前的一些注意事项(打包失败)
- tar打包如何不打包某一个文件夹(排除某些文件夹)
- iOS新手用swift写一个macos打包工具 一键打包到指定位置
- Android应用签名打包
- 多渠道打包
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。