使用包的版本css
webpack ->4.3.0
babel-loader ->8.0.5
npm ->6.4.1
webpack-cli ->3.3.1
复制代码
每一个章节对应一个demohtml
参考代码 demo8
node
什么是模块化拆包。好比咱们在项目里面须要引入echarts、xlsx、lodash等比较大的包的时候。若是不作代码拆包,都会打包到一个js文件里面。若是每次打包发版都会生成一个新的js打包文件,用户从新请求页面的时候会再次请求echarts、xlsx、lodash这些不变的代码,就会下降用户体验。模块化拆包将这些不变的依赖包打包成一个新的js文件,每次打包发版的时候用户就不会再次请求echarts、xlsx、lodash这些不变的代码了。react
webpack-bundle-analyzer 可视化显示打包的JS引用的包webpack
npm i webpack-bundle-analyzer -D
复制代码
在项目里面引入echarts、xlsx、lodashnginx
npm i echarts xlsx lodash -S
复制代码
app.jsgit
import "regenerator-runtime/runtime";
import _ from 'lodash';
import echarts from 'echarts';
import xlsx from 'xlsx';
console.log(echarts)
console.log(xlsx);
document.getElementById('app').innerHTML = _.ceil(2,3,4);
复制代码
webpack.base.conf.js 配置可视化如今打包文件。github
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
plugins:[
new BundleAnalyzerPlugin(),
new htmlWebpackPlugin({
filename:'index.html',
template:'./index.html',
inject:true,
minify: {
// 压缩 HTML 文件
removeComments: isPord, // 移除 HTML 中的注释
collapseWhitespace: isPord, // 删除空白符与换行符
minifyCSS: isPord // 压缩内联 css
},
})
],
复制代码
运行打包命令web
npm run build
复制代码
optimization.splitChunks是webpack4新的特性,默认进行代码拆包。算法
chunks:
minSize: 文件最小打包体积,单位byte,默认30000。
好比说某个项目下有三个入口文件,a.js和b.js和c.js都是100byte,当咱们将minSize设置为301,那么webpack就会将他们打包成一个包,不会将他们拆分红为多个包。
automaticNameDelimiter: 链接符。
假设咱们生成了一个公用文件名字叫vendor,a.js,和b.js都依赖他,而且咱们设置的链接符是"~"那么,最终生成的就是 vendor~a~b.js
maxInitialRequests 入口点处的最大并行请求数,默认为3。
若是咱们设置为1,那么每一个入口文件就只会打包成为一个文件
maxAsyncRequests 最大异步请求数量,默认5
若是咱们设置为1,那么每一个入口文件就只会打包成为一个文件
优先级关系。
maxInitialRequest / maxAsyncRequests <maxSize <minSize。
cacheGroups 定制分割包的规则列表
test能够配置正则和写入function做为打包规则。其余属性都可继承splitChunks。
minChunks
依赖最少引入多少次才能进行拆包。
复制默认配置,将chunks 改成'all'。
optimization: {
splitChunks: {
chunks: 'all',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
},
复制代码
运行打包命令
npm run build
复制代码
vendors~app.js是一个1.71mb的文件,太大了,继续拆包。
添加一个新的拆包规则
cacheGroups: {
echarts:{ // 新增拆包规则
name:'echarts', // 规则名字
chunks:'all', // 同步引入和异步引入均可以使用该规则
priority:10,
// 该规则的优先级,好比 webpack中进行拆包的时候,
// echarts包会有先匹配priority高的规则,若是知足这个规则,
// 则将代码导入到该规则里面,不会将代码导入到后面的规则里面了。
test:/(echarts)/, // 正则匹配规则
minChunks:1 // 代码里面最少被引入1次就可使用该规则。
},
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
复制代码
运行打包命令
npm run build
复制代码
参考代码 demo9
这一节只使用splitChunks的默认配置来实现异步加载,理解异步加载的好处。
。 使用react做为开发框架。
redux -> 16.8.6
react-dom -> 16.8.6
react-router-dom -> 5.0.0
复制代码
安装 react
npm i react react-router-dom react-dom -S
复制代码
解析 jsx代码还须要@babel/preset-react
npm i @babel/preset-react -D
复制代码
详细代码请看demo9。
两个路由分别对应不一样的组件。
import About from './src/view/about';
import Inbox from './src/view/inbox';
// ....
<App>
<Route path="/about" component={About} />
<Route path="/inbox" component={Inbox} />
</App>
复制代码
inbox.js引入了echarts而且使用了。
import React from 'react';
import echarts from 'echarts';
class Inbox extends React.Component {
componentDidMount() {
var myChart = echarts.init(document.getElementById('main'));
var option = {
tooltip: {
show: true
},
legend: {
data: ['销量']
},
xAxis: [
{
type: 'category',
data: ["衬衫", "羊毛衫2", "雪纺衫222", "裤子111", "高跟鞋", "袜子"]
}
],
yAxis: [
{
type: 'value'
}
],
series: [
{
"name": "销量",
"type": "bar",
"data": [5, 20, 40, 10, 10, 20]
}
]
};
// 为echarts对象加载数据
myChart.setOption(option);
}
render() {
return (
<div>
<h2>Inbox</h2>
<div style={{ height: '400px' }} id="main"></div>
</div>
)
}
}
export default Inbox;
复制代码
splitChunks 使用默认配置
splitChunks: {
chunks: 'async',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
复制代码
运行打包命令
npm run build
复制代码
打包的时候已经提示了,打包的代码太大影响性能。可使用import()或require来限制包的大小。确保延迟加载应用程序的某些部分。
WARNING in webpack performance recommendations:
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/
复制代码
咱们在首页的时候,并无/inbox路由,可是/inbox路由里面的代码都打包在了app.js这一个js里面。首屏渲染变的很慢。按照官方提示须要作组件的动态引入,只有路由切换到/inbox的时候才加载对应组件的代码。
异步引入组件须要用到babel的一个插件@babel/plugin-syntax-dynamic-impor。
{
test: /\.(js|jsx)?$/, // 正则匹配,全部的.js文件都使用这个规则进行编译
exclude: /node_modules/, // 排除的文件夹。这个文件夹里面的的文件不进行转译
loader: "babel-loader", // 转译的插件
options: { // 转译的规则
presets: [ //转译器配置
[
"@babel/preset-env", {
useBuiltIns: "usage",
corejs: 3
},
],
["@babel/preset-react"]
],
plugins: ["@babel/plugin-syntax-dynamic-import"] // 转译插件配置
}
},
复制代码
新建AsyncComponent.js。
import React, { Component } from "react";
export default function asyncComponent(importComponent) {
class AsyncComponent extends Component {
constructor(props) {
super(props);
this.state = {
component: null
};
}
async componentDidMount() {
const { default: component } = await importComponent();
// 组件引入完成后渲染组件
this.setState({
component: component
});
}
render() {
const C = this.state.component;
return C ? <C {...this.props} /> : null;
}
}
return AsyncComponent;
}
复制代码
更换引入模式
//import Inbox from './src/view/inbox';
const Inbox = asyncComponent(() => import('./src/view/inbox'));
复制代码
运行打包命令
npm run build
复制代码
npm run server
复制代码
首页并无加载1.js
react的动态引入组件就OK了。 按照官方文档,还能够自定义chunk名称
const Inbox = asyncComponent(() => import(/* webpackChunkName: "echarts" */ './src/view/inbox'));
复制代码
其实还能够异步引入依赖包
async componentDidMount() {
const {default:echarts}= await import('echarts');
var myChart = echarts.init(document.getElementById('main'));
}
复制代码
参考代码 demo10
树抖动是JavaScript上下文中经常使用于消除死代码的术语。 它依赖于ES2015模块语法的静态结构,即导入和导出。 名称和概念已由ES2015模块捆绑器汇总推广。
摇树的意思。在webpack4.0里面会自动的把不须要的js在打包的时候剔除。固然须要开发进行配合:按需加载
咱们拿lodash为例。 为了清楚咱们摇掉了多少代码,先将react的包拆出来单独打包。
splitChunks: {
chunks: 'all',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
reactVendors: {
chunks: 'all',
test: /(react|react-dom|react-dom-router)/,
priority: 100,
name: 'react',
},
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
复制代码
在about.js里面使用lodash
import React from 'react';
import _ from 'lodash';
class About extends React.Component {
render() {
const s = _.concat([1],[2,3,1,2,3,4,6,78,41]);
return (
<h3>{s}</h3>
)
}
}
export default About;
复制代码
运行打包命令
npm run build
复制代码
npm i lodash-es -S
复制代码
改变about.js的代码
import React from 'react';
//import _ from 'lodash';
import { concat } from 'lodash-es';
class About extends React.Component {
render() {
const s = concat([1],[2,3,1,2,3,4,6,78,41]);
return (
<h3>{s}</h3>
)
}
}
export default About;
复制代码
运行打包命令
npm run build
复制代码
打开掘金首页。
随便找一个js资源。
Response Headers 里面的 content-encoding 表明着资源传输格式。说明这段资源使用的是Gzip文件。说明不少公司用的是Gzip进行资源传输,由于代码打包成Gzip传输时须要的资源很小。
其实Gzip传输是服务器须要配置的。即便咱们上传代码没有进行Gzip压缩,服务器通过配置后会将资源进行Gzip压缩而后传输。例如nginx的Gzip配置。
Gzip配置都是服务器配置的工做,咱们要作什么呢?其实服务器会寻找资源对应的gzip文件,若是没有则会进行Gzip压缩而后传输资源。这就多出了压缩的时间。若是咱们经过webpack生成gzip文件,能够减小服务器生成gzip文件的时间,也减缓了服务器压力。
这个属性的属性值能够是布尔、数组。
在mode是prduction时,minimiz默认是true。自动使用terser-webpack-plugin进行压缩。
默认的配置不用改,使用压缩插件compression-webpack-plugin。
npm i compression-webpack-plugin -D
复制代码
配置 optimization.minimiz
minimizer: [
new CompressionPlugin({
filename: '[path].gz[query]', // 生成资源的名字 [path].gz[query] 是默认名字
algorithm: 'gzip', // 压缩算法
test: /\.(js|css|html|svg)$/, // 匹配的资源
compressionOptions: { level: 8 }, // 压缩等级 默认9级
threshold: 10240, // 多大资源使用才压缩 10kb
minRatio: 0.8,
//仅处理压缩比此比率更好的资源(minRatio =压缩尺寸/原始尺寸)。
//示例:您拥有1024b大小的image.png文件,压缩版本的文件大小为768b,
//所以minRatio等于0.75。换句话说,当压缩大小/原始大小值小于minRatio值时,
//将处理资源。默认 0.8 。
deleteOriginalAssets: false, // 是否删除原始资产。
}),
new TerserPlugin({
parallel: true,
}),
]
复制代码
npm run build
复制代码