大家在开发前端项目的时候,随着项目的不断更新迭代以及产品经理的不断提需求,势必会用到各种各样的第三方库,那么我们用到的第三方库越来越多,整体项目的依赖也就会越来越多,整体项目打包的时候体积也就会越来越大,用webpack去build的时候时间也会越长。 这些倒不是最主要的,我们做的一般都是单页面应用,如果随着第三方库越来越多,咱们项目首页的白屏时间也会加长,用户体验感会很差,这是我们不得不面对的一个问题,所以我们要优化webpack的打包方法,具体如何做呢?咱们接着往下看。 一、webpack-bundle-analyzer的使用 webpack-bundle-analyzer是一个工具,它能够将webpack打包后的内容进行扫码并形成一个可视化的界面,在这个界面中我们可以看到打包后项目的依赖项,我们可以从中找到一些我们项目中并不需要的依赖包或者体积较大的依赖包来进行针对性的优化。 以下我们针对Vue框架来进行叙述。 1. vue-cli2 如果你用的是vue脚手架2版本,那么恭喜你,它已经集成了webpack-bundle-analyzer,你只需运行npm run build --report这个命令,就可以看到如下界面。 从上面的图我们可以知道打包的内容有哪些,以及它们的大小,还有各个依赖包的耗时以及总的耗时是多久。 2. vue-cli3 如果你用的是vue脚手架3版本,那你还需要进行如下配置: 首先需要下载依赖: npm install webpack-bundle-analyzer -D 然后配置webpack.config.js 文件: const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); plugins:[ new BundleAnalyzerPlugin(), ] 二、进行优化 知道了webpack-bundle-analyzer这个插件给我们带来哪些信息后,我们就可以有针对性的进行优化了,具体如何优化呢?我们从以下几个方面来进行 1. 按需加载 单页面应用是将整个工程作为一个入口打包成一个模块,所以在首页会加载一些没用到的资源,造成首页渲染速度慢,“白屏时间”过长,给用户不好的体验。我们可以从以下几个方面进行按需加载: const router = [ { path: '/index', component: resolve => require.ensure([], () => resolve(require('@/components/index'))) }, { path: '/login', component: resolve => require.ensure([], () => resolve(require('@/components/login'))) } ] // 引入全部组件 import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' Vue.use(ElementUI) // 按需引入组件 import { Button } from 'element-ui' Vue.component(Button.name, Button) // 在main.js引入 import Vue from vue import Vuelidate from 'vuelidate' Vue.use(Vuelidate) // 在组件中按需引入 import { Vuelidate } from 'vuelidate' 2. 优化loader的配置 我们可以从如下几个方面对loader进行优化: module: { rules: [ { test: /\.js$/, loader: 'babel-loader?cacheDirectory', include: [resolve('src')] } ] } 3. 优化文件路径 优化文件路径可以省下搜索文件的时间,这里有两个属性: 配置该属性之后可以不用在 require 或是 import 的时候加文件扩展名,会依次尝试添加扩展名进行匹配。 通过配置别名可以加快 webpack 查找模块的速度。 resolve: { extensions: ['.js', '.vue', '.json'], alias: { '@': resolve('src'), } }, 4. 利用IgnorePlugin,避免引入无用模块 比如有些库包含多个国家的语言包(moment.js),我们只需要中文包或者英文报,所以我们需要避免引入不需要的语言包。 plugins:[ //忽略moment下的/locale目录,再手动import locale内部我们需要使用到的语言包 new webpack.IgnorePlugin(/\.\/local/,/moment/) ] 5. 利用noParse,避免重复模块化解析 代码中如果有xx.min.js之类的文件,基本已经采用模块化处理过的,无需在对类似文件进行递归解析处理,会引入进项目 module: { noParse: [/xx\.min\.js$/] } 6. happyPack 多进程打包 在整个 Webpack 构建流程中,最耗时的流程可能就是 Loader 对文件的转换操作了,因为要转换的文件数据巨多,而且这些转换操作都只能一个个挨着处理。HappyPack 的核心原理就是把这部分任务分解到多个进程去并行处理,从而减少了总的构建时间。 rules: [ { test: /\.js$/, //把对.js文件的处理转交给id为babel的HappyPack实例 use: ['happypack/loader?id=babel'], include: srcPath } ], plugins: [ new HappyPack({ //用唯一的标识符id来代表当前的HappyPack是用来处理一类特定的文件 id: 'babel', //如何处理.js文件,用法和loader配置中一样 loaders: ['babel-loader?cacheDirectory'] }) ] 7. 热更新 相信大家对热更新肯定不陌生,即改完代码,浏览器无需刷新,新代码生效,状态保留。 entry: { index: [ 'webpack-dev-server/client?http://localhost:8080/', 'webpack/hot/dev-server', path.join(srcPath, 'index.js') ], other: path.join(srcPath, 'other.js') } plugins: [ new HotModuleReplacementPlugin() ] devServer: { xxx: 'xx' hot: true //开启热更新 } 配置完后,修改样式,热更新生效了,但修改js,热更新没有生效,接下来还需要在js中设置允许热更新监听的模块。 if(module.hot){ module.hot.accept(['xxx文件名'], () => { //在注册了热更新的js文件修改后,才会进行热更新 }) } 8. 生产环境关闭 sourceMap sourceMap 本质上是一种映射关系,打包出来的 js 文件中的代码可以映射到代码文件的具体位置,这种映射关系会帮助我们直接找到在源代码中的错误。打包速度减慢,生产文件变大,所以开发环境使用 sourceMap,生产环境则关闭。 9. 代码压缩 uglifyJsPlugin 是 vue-cli 默认使用的压缩代码方式,用来对js文件进行压缩,从而减小js文件的大小,加速load速度。它使用的是单线程压缩代码,打包时间较慢,所以可以在开发环境将其关闭,生产环境部署时再把它打开。 plugins: [ new UglifyJsPlugin({ uglifyOptions: { compress: { warnings: false } }, sourceMap: true, parallel: true }) ] 我们还可以使用ParallelUglifyPlugin 开启多个子进程,把对多个文件压缩的工作分别给多个子进程去完成,每个子进程其实还是通过 UglifyJS 去压缩代码,但是变成了并行执行。 ParallelUglifyPlugin 还可以缓存压缩后的结果,下次遇到一样的输入时直接从缓存中获取压缩后的结果并返回。 plugins: [ new ParallelUglifyPlugin({ //cacheDir 用于配置缓存存放的目录路径。 cacheDir: '.cache/', sourceMap: true, uglifyJS: { output: { comments: false }, compress: { warnings: false } } }) ] 10. DllPlugin 这是在一个额外的独立的 webpack 设置中创建一个只有 dll 的 bundle(dll-only-bundle)。这个插件会生成一个名为 manifest.json 的文件,这个文件是用来让 DLLReferencePlugin 映射到相关的依赖上去的。使用步骤如下: 在build下创建 webpack.dll.config.js const path = require('path') const webpack = require('webpack') module.exports = { entry: { vendor: [ 'vue-router', 'vuex', 'vue/dist/vue.common.js', 'vue/dist/vue.js', 'vue-loader/lib/component-normalizer.js', 'vue', 'axios', 'echarts' ] }, output: { path: path.resolve('./dist'), filename: '[name].dll.js', library: '[name]_library' }, plugins: [ new webpack.DllPlugin({ path: path.resolve('./dist', '[name]-manifest.json'), name: '[name]_library' }), // 建议加上代码压缩插件,否则dll包会比较大。 new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ] } 在 webpack.prod.conf.js 的 plugin 后面加入配置 new webpack.DllReferencePlugin({ manifest: require('../dist/vendor-manifest.json') }) package.json文件中添加快捷命令(build:dll) "scripts": { "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", "start": "npm run dev", "lint": "eslint --ext .js,.vue src", "build": "node build/build.js", "build:dll": "webpack --config build/webpack.dll.conf.js" } 生产环境打包的时候先npm run build:dll命令会在打包目录下生成 vendor-manifest.json 文件与 vendor.dll.js 文件。然后npm run build生产其他文件。 根目录下的入口index.html加入引用 <script type="text/javascript" src="./vendor.dll.js"></script> 11. CDN优化 CDN 的全称是 Content Delivery Network,即内容分发网络。CDN 是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN 的关键技术主要有内容存储和分发技术。 随着项目越做越大,依赖的第三方 npm 包越来越多,构建之后的文件也会越来越大。再加上又是单页应用,这就会导致在网速较慢或者服务器带宽有限的情况出现长时间的白屏。此时我们可以使用CDN的方法,优化网络加载速度。 将 vue、vue-router、vuex、element-ui 和 axios 这五个库,全部改为通过 CDN 链接获取,在 index.html里插入相应链接。 <head> <link rel="stylesheet" href="https://cdn.bootcss.com/element-ui/2.0.7/theme-chalk/index.css" /> </head> <body> <div id="app"></div> <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script> <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js"></script> <script src="https://cdn.bootcss.com/vuex/3.1.0/vuex.min.js"></script> <script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.min.js"></script> <script src="https://cdn.bootcss.com/element-ui/2.6.1/index.js"></script> <!-- built files will be auto injected --> </body> 在 webpack.config.js 配置文件 module.exports = { ··· externals: { 'vue': 'Vue', 'vuex': 'Vuex', 'vue-router': 'VueRouter', 'element-ui': 'ELEMENT', 'Axios':'axios' } }, 卸载依赖的 npm 包 npm uninstall axios element-ui vue vue-router vuex 修改 main.js 文件里之前的引包方式 // import Vue from 'vue' // import ElementUI from 'element-ui' // import 'element-ui/lib/theme-chalk/index.css' // import VueRouter from 'vue-router' import App from './App.vue' import routes from './router' import utils from './utils/Utils' Vue.use(ELEMENT) Vue.use(VueRouter) const router = new VueRouter({ mode: 'hash', //路由的模式 routes }) new Vue({ router, el: '#app', render: h => h(App) }) 总结 大致我们需要优化的地方就这么多,那么咱们来总结一下这些方法:
至此,webpack的打包优化就结束了,相信认真阅读的你一定有所收获。