渐进式配置webpack4单页面和多页面(二)
栏目: JavaScript · 发布时间: 5年前
内容简介:使用包的版本每个章节对应一个demo参考代码
使用包的版本
webpack ->4.3.0 babel-loader ->8.0.5 npm ->6.4.1 webpack-cli ->3.3.1 复制代码
每个章节对应一个demo
模块化拆包1
参考代码 demo8
什么是模块化拆包。比如我们在项目里面需要引入echarts、xlsx、lodash等比较大的包的时候。如果不做代码拆包,都会打包到一个js文件里面。如果每次打包发版都会生成一个新的js打包文件,用户重新请求页面的时候会再次请求echarts、xlsx、lodash这些不变的代码,就会降低用户体验。模块化拆包将这些不变的依赖包打包成一个新的js文件,每次打包发版的时候用户就不会再次请求echarts、xlsx、lodash这些不变的代码了。
代码更改
webpack-bundle-analyzer 可视化显示打包的JS引用的包
npm i webpack-bundle-analyzer -D 复制代码
在项目里面引入echarts、xlsx、lodash
npm i echarts xlsx lodash -S 复制代码
app.js
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 配置可视化现在打包文件。
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 }, }) ], 复制代码
运行打包命令
npm run build 复制代码
打包提示代码太大,需要进行拆包。
默认配置
optimization.splitChunks 是webpack4新的特性,默认进行代码拆包。
上图是默认配置。
chunks:
- all: 不管文件是异步还是同步引入,都可以使用splitChunks进行代码拆包。
- async: 只将异步加载的文件分离,首次一般不引入,到需要异步引入的组件才会引入。
- initial:将异步和非异步的文件分离,如果一个文件被异步引入也被非异步引入,那它会被打包两次(注意和all区别),用于分离页面首次需要加载的包。
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。这个js里面储层的就是需要拆分的代码。
再次拆包
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 复制代码
echarts包就被单独拆出来了储层在echarts.js。
其实这个就是Code Splitting的概念之一官方demo模块化拆包2
参考代码 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 复制代码
echarts被引入到1.js里面了。
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')); } 复制代码
总结
- 一般情况下推荐 splitChunks.chunks: 'all' ,其他参数可以不变。'all'的意思是同步引入和异步引入都可以进行拆包。
- 尽量小的引入所需要的插件,比如echarts官方提供了按需加载的方法。
- 在首页没有用的某个插件的时候,尽量使用异步加载的方式进行引入。
JS Tree Shaking
参考代码 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 复制代码合并引入的vendors有95kb。我们只使用了lodash的concat方法,其他的方法没使用。 我们使用lodash的模块化包 lodash-es
。
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 复制代码
现在只有28kb。缩小的部分就是loader的按需加载的部分。
Gzip压缩。
Gzip压缩的好处。
打开 掘金首页 。
随便找一个js资源。
Response Headers 里面的 content-encoding 代表着资源传输格式。说明这段资源使用的是Gzip文件。说明很多公司用的是Gzip进行资源传输,因为代码打包成Gzip传输时需要的资源很小。
其实Gzip传输是服务器需要配置的。即使我们上传代码没有进行Gzip压缩,服务器经过配置后会将资源进行Gzip压缩然后传输。例如nginx的Gzip配置。
Gzip配置都是服务器配置的工作,我们要做什么呢?其实服务器会寻找资源对应的gzip文件,如果没有则会进行Gzip压缩然后传输资源。这就多出了压缩的时间。如果我们通过webpack生成gzip文件,可以减少服务器生成gzip文件的时间,也减缓了服务器压力。
js打包压缩配置optimization.minimiz
这个属性的属性值可以是布尔、数组。
当属性值是布尔时
在mode是 prduction 时,minimiz默认是true。自动使用 terser-webpack-plugin 进行压缩。
当属性值是数组时
如图是默认配置。
配置Gzip压缩
默认的配置不用改,使用压缩插件 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 复制代码.zip
就是打包生成的gzip资源。对比原来的文件压缩了 38/121≈0.3。压缩了很多。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 渐进式配置webpack4单页面和多页面(一)
- 渐进式动画解决方案
- vuex 渐进式教程(一)
- 渐进式 Unbundled 开发工具探索之路
- React Fiber 渐进式组件遍历详解
- 大数据系统云原生渐进式演进最佳实践
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
剑指Offer:名企面试官精讲典型编程题(第2版)
何海涛 / 电子工业出版社 / 2017-5 / 65.00
《剑指Offer:名企面试官精讲典型编程题(第2版)》剖析了80个典型的编程面试题,系统整理基础知识、代码质量、解题思路、优化效率和综合能力这5个面试要点。《剑指Offer:名企面试官精讲典型编程题(第2版)》共分7章,主要包括面试的流程,讨论面试每一环节需要注意的问题;面试需要的基础知识,从编程语言、数据结构及算法三方面总结程序员面试知识点;高质量的代码,讨论影响代码质量的3个要素(规范性、完整......一起来看看 《剑指Offer:名企面试官精讲典型编程题(第2版)》 这本书的介绍吧!