一.可视化 webpack 输出文件的大小
添加webpack-bundle-analyzer
yarn add webpack-bundle-analyzer --dev
可视化只在项目本地打包的时候,在测试或者语法或者生产打包的时候,不应该添加可视化配置,因此需用cross-env配置可视化环境变量
package.json scripts 添加:
"analyze": "cross-env TC_NODE_ENV=analyze react-app-rewired build",
webpack配置文件:
const{BundleAnalyzerPlugin}=require('webpack-bundle-analyzer');if(process.env.TC_NODE_ENV==='analyze'){config.plugins.push(newBundleAnalyzerPlugin(),);}
运行 yarn run analyze 即可看到,剩下的事情就是想办法减小这些包的体积
二.在不改动业务代码的情况下,减小公共依赖
生产环境启用压缩等插件,去除不必要插件
webpack 开启 gzip 压缩
拆分业务代码与第三方库及公共模块
按需加载
1 压缩
uglifyjs-webpack-plugin
gzip压缩:比较耗性能,所以在开发环境并不开启,只在生产环境开启:
if(env==='production'){config.plugins.push(newUglifyESPlugin({uglifyOptions:{compress:{warnings:false,drop_console:true,collapse_vars:true,reduce_vars:true,},output:{beautify:false,comments:false,},},sourceMap:true,}),// gzipnewCompressionPlugin({filename:'[path].gz[query]',algorithm:'gzip',test:newRegExp('\\.('+['js','css'].join('|')+')$'),threshold:10240,minRatio:0.8,}));}
2.这些包不可能不引入,但是他们都是全局依赖的,可以使用浏览器自己的缓存来实现不重复加载。将项目中需要的一些公共依赖包,并且不常变动的,单独取出,不再每次都打包编译(如React,Redux等)。而是通过使用 script 标签形式 cdn 引入,这样在有缓存的情况下,这些资源均走缓存,不必加载。但如 antd 这样的库,整个包体积较大,库文件也会按需加载,不必单独取出,并且里面可能会有 bug,需要更新。
2.1 cdn 引入方式
找到对应的cdn加速服务,如在https://www.bootcdn.cn/中找,配置 webpack,使其打包的时候不在将这些资源打包:
config.externals={'react':'React','react-dom':'ReactDOM',}
键表示 require 或者 import 时候的字符串,值表示的当前环境下的变量,比如引入 React 之后,React 被作为全局对象,webpack 就回去寻找React对象。
2.2 公共模块CommonsChunkPlugin
why? 路由按需加载之后,每个页面的js有相同的包引入,导致每个js文件都有重复的代码,抽离这部分代码到公共模块。
在webpack配置文件中entry加入,例:
plugin加入:
newwebpack.optimize.CommonsChunkPlugin({name:'vendor',filename:'[name].[hash].js',minChunks:({resource})=>(resource&&resource.indexOf('node_modules')>=0&&resource.match(/\.js$/)),}),// 完成打包并发送到浏览器时,会在运行时通过 Manifest 来解析和加载模块newwebpack.optimize.CommonsChunkPlugin({name:'manifest',filename:'[name].[hash].js',minChunks:Infinity,}),newwebpack.optimize.CommonsChunkPlugin({name:'main',async:'vendor-async',children:true,minChunks:({resource},count)=>(resource&&resource.indexOf('node_modules')>=0&&resource.match(/\.js$/)&&count>=2),}),newwebpack.optimize.CommonsChunkPlugin({name:'main',async:'common',minChunks:(module,count)=>(count>=2),}),
2.3 DLLPlugin
DLLPlugin 是能把第三方代码完全分离开,即每次只打包项目自身的代码。Dll这个概念是借鉴了Windows系统的dll,一个dll包,就是一个纯纯的依赖库,它本身不能运行,是用来给你的app引用的。
新建 webpack.config.dll.js
constpath=require('path');constwebpack=require('webpack');constCompressionPlugin=require('compression-webpack-plugin');module.exports={entry:{vendor:['echarts'],},output:{path:path.resolve('./public/static'),filename:'[name].dll.js',library:'[name]_[hash]',},plugins:[newwebpack.DllPlugin({path:path.resolve('./public/static','[name]-manifest.json'),name:'[name]_[hash]',}),newwebpack.optimize.UglifyJsPlugin({compress:{warnings:false,},}),newCompressionPlugin({filename:'[path].gz[query]',algorithm:'gzip',test:newRegExp('\\.('+['js','css'].join('|')+')$'),threshold:10240,minRatio:0.8,}),],};
在webpcak配置文件中加入
newwebpack.DllReferencePlugin({manifest:require('./public/static/vendor-manifest.json')})
在入口html文件中引入 vendor.dll.js
<scripttype="text/javascript"src="/static/vendor.dll.js"></script>
package.json加入
"build:dll":"webpack --config webpack.dll.config.js"
打包的时候先执行 npm run build:dll,会在打包目录下生成 vendor-manifest.json 文件与 vendor.dll.js 文件。
再执行npm run build
注意:npm run build:dll 为开发人员本地打包vendor,打包完成之后会在/public/static文件下生成vendor.dll.js、vendor.dll.js.gz和vendor-manifest.json三个文件,线上打包不必在执行此命令,如果vendor没有更新,不必在 npm run build:dll,利用浏览器缓存。
已加入cdn的不必再配置vendor。
2.4 抽离css样式(extract-text-webpack-plugin)
constExtractTextPlugin=require('extract-text-webpack-plugin');newExtractTextPlugin({filename:'static/css/[name].[contenthash:8].css',allChunks:true,}),
3.按需引入
如antd、lodash、echarts都有按需加载的方式,可以去官方看
4.按需加载
思路:import(*).then(),返回一个Promise,加载成功后可以在then方法中获取加载的内容,Webpack内置对import( *)语句的支持。
asyncComponent.js
importReactfrom'react';exportdefaultfunctionasyncComponent(importComponent){classAsyncComponentextendsReact.Component{constructor(props){super(props);this.state={Component:null,};}asynccomponentDidMount(){const{default:Component}=awaitimportComponent();this.setState({Component,});}render(){const{Component}=this.state;returnComponent?<Component{...this.props}/>:null;}}returnAsyncComponent;}
路由引入方式:
AsyncComponent(()=>import('../containers/Home/Home'))