Next.js的Babel及拆包优化
<div class="lake-content"><h2 id="62cea749">1 Babel兼容</h2>
<p id="u27a0f6bf" class="ne-p"> </p>
<h3 id="2d5cc7fa"><span class="ne-text">1.2 Babel按需加载规则(useBuiltIns)</span></h3>
<p id="u3878b480" class="ne-p"> </p>
<p id="u575dc475" class="ne-p"><span class="ne-text">在Babel>7的条件下,支持通过useBuiltIns参数来实现按需加载必要的垫片。</span></p>
<p id="ufb413a9d" class="ne-p"> </p>
<h4 id="74d51104"><span class="ne-text">useBuiltIns=false</span></h4>
<p id="u227535cd" class="ne-p"> </p>
<p id="u630c7627" class="ne-p"><span class="ne-text">不会自动引入垫片。</span></p>
<p id="u5b475e4a" class="ne-p"> </p>
<h4 id="7c0d3353"><span class="ne-text">useBuiltIns=entry</span></h4>
<p id="ud7f5660c" class="ne-p"> </p>
<p id="u1509c358" class="ne-p"><span class="ne-text">通过<code class="ne-code"><span class="ne-text">@babel/preset-env</span></code><span class="ne-text">插件,按照浏览器环境按需加载需要的模块来替换对<code class="ne-code"><span class="ne-text">core-js</span></code><span class="ne-text">的直接引用。</span></span></span></p>
<p id="uea42193e" class="ne-p"> </p>
<div class="ne-quote">
<p id="ud15a817a" class="ne-p"><span class="ne-text">该方式会以浏览器环境配置为准,全量引入最低浏览器所需要的所有垫片。</span></p>
</div>
<p id="u49341814" class="ne-p"> </p>
<p id="u125f5dd9" class="ne-p"><span class="ne-text">例如输入源码为:</span></p>
<p id="u54600d5b" class="ne-p"> </p>
<pre class="ne-codeblock language-plain" data-language="plain">import "core-js/stable";
import "regenerator-runtime/runtime";</pre>
<p id="uf6054a1b" class="ne-p"> </p>
<p id="u82be1baf" class="ne-p"><span class="ne-text">再Chrome72中,上面代码会被<code class="ne-code"><span class="ne-text">@babel/preset-env</span></code><span class="ne-text">输出为:</span></span></p>
<p id="uf9d17d51" class="ne-p"> </p>
<pre class="ne-codeblock language-plain" data-language="plain">import "core-js/modules/es.array.unscopables.flat";
import "core-js/modules/es.array.unscopables.flat-map";
import "core-js/modules/es.object.from-entries";
import "core-js/modules/web.immediate";</pre>
<p id="ue3a840d6" class="ne-p"> </p>
<h4 id="7d671158"><span class="ne-text">useBuiltIns=usage</span></h4>
<p id="u6a439edb" class="ne-p"> </p>
<p id="ud03aff94" class="ne-p"><span class="ne-text">该方式会根据不支持的浏览器环境且根据扫描到的代码中需要的垫片,自动引入不支持的模块。</span></p>
<p id="u201b5e16" class="ne-p"> </p>
<p id="u47ead494" class="ne-p"><span class="ne-text">例如输入为:</span></p>
<p id="ud8b55593" class="ne-p"> </p>
<pre class="ne-codeblock language-plain" data-language="plain">const set = new Set();
.includes(2);</pre>
<p id="u6bb6d414" class="ne-p"> </p>
<p id="uf557fd91" class="ne-p"><span class="ne-text">再IE11中会替换为:</span></p>
<p id="u04f3c10b" class="ne-p"> </p>
<pre class="ne-codeblock language-plain" data-language="plain">import "core-js/modules/es.array.includes";
import "core-js/modules/es.array.iterator";
import "core-js/modules/es.object.to-string";
import "core-js/modules/es.set";
const set = new Set();
.includes(2);</pre>
<p id="ue34652e0" class="ne-p"> </p>
<h3 id="6e7198c4"><span class="ne-text">1.2 浏览器环境配置</span></h3>
<p id="u1fd53834" class="ne-p"> </p>
<p id="ue86cf060" class="ne-p"><span class="ne-text">Babel是根据<span class="ne-text">browserslist</span><span class="ne-text">来配置浏览器环境的。</span></span></p>
<p id="u25b29e12" class="ne-p"> </p>
<p id="u7a94d070" class="ne-p"><span class="ne-text">browserslist的查询规则如下:</span></p>
<p id="u7b302fda" class="ne-p"> </p>
<ol class="ne-ol">
<li id="ub6d01301"><code class="ne-code"><span class="ne-text">browserslist</span></code><span class="ne-text"> key in <code class="ne-code"><span class="ne-text">package.json</span></code><span class="ne-text"> file in current or parent directories.<span class="ne-text"><br><strong>We recommend this way.</strong></span></span></span></li>
<li id="uc08485e4"><code class="ne-code"><span class="ne-text">.browserslistrc</span></code><span class="ne-text"> config file in current or parent directories.</span></li>
<li id="u95d7bdd0"><code class="ne-code"><span class="ne-text">browserslist</span></code><span class="ne-text"> config file in current or parent directories.</span></li>
<li id="u2295cc62"><code class="ne-code"><span class="ne-text">BROWSERSLIST</span></code><span class="ne-text"> environment variable.</span></li>
<li id="u6f022d09"><span class="ne-text">If the above methods did not produce a valid result<span class="ne-text"><br><span class="ne-text">Browserslist will use defaults:<span class="ne-text"><br><code class="ne-code"><span class="ne-text">> 0.5%, last 2 versions, Firefox ESR, not dead</span></code><span class="ne-text">.</span></span></span></span></span></li>
</ol>
<p id="uced97017" class="ne-p"> </p>
<h3 id="c9584509"><span class="ne-text">1.3 Next默认配置</span></h3>
<p id="u189465bc" class="ne-p"> </p>
<p id="uccec525f" class="ne-p"><span class="ne-text">看了Next.js(8.1.0)的源码,没有找到浏览器兼容的配置,所以可以认为是默认使用了规则 <code class="ne-code"><span class="ne-text">> 0.5%, last 2 versions, Firefox ESR, not dead</span></code><span class="ne-text">.<span class="ne-text"><br><span class="ne-text">而且Babel配置为<code class="ne-code"><span class="ne-text">useBuiltIns: false</span></code></span></span></span></span></p>
<p id="ufd74234d" class="ne-p"> </p>
<h3 id="3dfca923"><span class="ne-text">1.4 优化</span></h3>
<p id="ud38682df" class="ne-p"> </p>
<p id="u0da2706f" class="ne-p"><span class="ne-text">根据Next.js文档对于Babel的描述:</span></p>
<p id="u0eb7cb29" class="ne-p"> </p>
<ol class="ne-ol">
<li id="u012d6453"><span class="ne-text">可以通过<code class="ne-code"><span class="ne-text">.babelrc</span></code><span class="ne-text">来配置</span></span></li>
<li id="uf3536ea8"><span class="ne-text">可以通过配置<code class="ne-code"><span class="ne-text">next/babel</span></code><span class="ne-text">的参数来进行配置</span></span></li>
<li id="uf8a629f2"><span class="ne-text">Next.js8.1.0对应的依赖为:<code class="ne-code"><span class="ne-text">"@babel/core": "7.1.2"</span></code><span class="ne-text">,<code class="ne-code"><span class="ne-text">"webpack": "4.29.0"</span></code><span class="ne-text">,<code class="ne-code"><span class="ne-text">"@babel/runtime-corejs2": "7.1.2"</span></code></span></span></span></li>
<li id="udcf4110f"><span class="ne-text">The modules option on <code class="ne-code"><span class="ne-text">"preset-env"</span></code><span class="ne-text"> should be kept to <code class="ne-code"><span class="ne-text">false</span></code><span class="ne-text"> otherwise webpack code splitting is disabled.</span></span></span></li>
</ol>
<p id="u933bf407" class="ne-p"> </p>
<h4 id="5d3bfbce"><span class="ne-text">1.4.1 优化策略v1</span></h4>
<p id="ube6842d9" class="ne-p"> </p>
<p id="u330cf803" class="ne-p"><span class="ne-text">通过对比useBuiltIns的选项,可以发现useBuiltIns=usage是最优的方案,它可以真正只加载我们需要的垫片。</span></p>
<p id="uf0d30539" class="ne-p"> </p>
<p id="u4e50fe71" class="ne-p"><code class="ne-code"><span class="ne-text">.babelrc</span></code><span class="ne-text">:</span></p>
<p id="ueafbf6d4" class="ne-p"> </p>
<pre class="ne-codeblock language-plain" data-language="plain">{
"presets": [
[
"next/babel",
{
"preset-env": {
"useBuiltIns": "usage",
"modules": false,
"debug": true,
},
}
]
]
...
}</pre>
<p id="u02db9aca" class="ne-p"> </p>
<p id="u3e7cbda0" class="ne-p"><code class="ne-code"><span class="ne-text">.browserslistrc</span></code><span class="ne-text">:<span class="ne-text"><br><span class="ne-text">为什么是 > 0.25%?</span></span></span></p>
<p id="uf8cfd2ca" class="ne-p"> </p>
<p id="u50385994" class="ne-p"><span class="ne-text">其他情况按照具体业务来配置,原则当然是版本越高越好。</span></p>
<p id="u738de7ea" class="ne-p"> </p>
<pre class="ne-codeblock language-plain" data-language="plain">> 0.25%
last 4 versions
ie 8-11
Android >=4.4
iOS >= 8.4
Firefox > 20</pre>
<p id="u4835701e" class="ne-p"> </p>
<h4 id="debb4552"><span class="ne-text">1.4.2 优化策略v2</span></h4>
<p id="u3fe477e0" class="ne-p"> </p>
<div class="ne-quote">
<p id="u839be9f6" class="ne-p"><span class="ne-text">没有银弹🙁</span></p>
</div>
<p id="u7b52b7ba" class="ne-p"> </p>
<p id="u4e7b5dd1" class="ne-p"><span class="ne-text">上面配置完,基本上我们源码里面的垫片都能自动挂上了。但是....,在IE9/Android 4.4/iOS8上还是会报错。<span class="ne-text"><br><span class="ne-text">Set/Map,找不到,数组没有includes等。</span></span></span></p>
<p id="u37235d9d" class="ne-p"> </p>
<p id="u39127f6b" class="ne-p"><span class="ne-text">这是因为:</span></p>
<p id="uf03bb8e1" class="ne-p"> </p>
<ol class="ne-ol">
<li id="uac49a97c"><span class="ne-text">只有Webpack通过Babel的目录才会处理</span></li>
<li id="u40e585bc"><span class="ne-text">如果使用<code class="ne-code"><span class="ne-text">useBuiltIns=usage</span></code><span class="ne-text">,Babel 默认会假设处理的文件使用的是ES modules规范,也就是使用(import和export)语法。node_modules里面的第三方库一般都是以CommonJS的规范发布的。</span></span></li>
</ol>
<p id="u4e240cda" class="ne-p"> </p>
<p id="u0f232466" class="ne-p"><span class="ne-text">所以因为React位于node_modules中,而且react发布版本已经变成了CommonJS的规范。所以需要真正的适配node_modules里面的所有模块还需要进一步配置。例如下面这个配置,但是实际上,node_modules的发布风格百花齐放,按照下面这个链接配置完之后会有各种问题。</span></p>
<p id="u9daeccf1" class="ne-p"> </p>
<p id="u70d3196c" class="ne-p"><span class="ne-text">how-do-i-use-babels-usebuiltins-usage-option-on-the-vendors-bundle</span></p>
<p id="u708b9afa" class="ne-p"> </p>
<p id="uff0f487f" class="ne-p"><span class="ne-text">对比过网上为了解决第三方库中的垫片问题,基本上有三种方式:</span></p>
<p id="u22d71440" class="ne-p"> </p>
<ol class="ne-ol">
<li id="ude1510c9"><span class="ne-text">最简单的就是使用<code class="ne-code"><span class="ne-text">useBuiltIns=entry</span></code><span class="ne-text">,根据当前的需要支持的浏览器环境,全局导入所有垫片。</span></span></li>
<li id="u5770fea0"><span class="ne-text">为第三方库单独添加全局垫片,一般第三方库会说明需要什么垫片,例如<span class="ne-text">React JavaScript Environment Requirements</span></span></li>
<li id="u20f4e8b0"><span class="ne-text">使用垫片服务,<span class="ne-text">polyfill.io</span><span class="ne-text">,会自动根据当前浏览器的UA自动添加缺失的功能,缺点就是代码不需要的也会加上。这种方案属于方案1的优化版。</span></span></li>
</ol>
<p id="u790832ab" class="ne-p"> </p>
<p id="ub3951529" class="ne-p"><span class="ne-text">综合下来,目前使用了方案2。</span></p>
<p id="u08124951" class="ne-p"> </p>
<p id="u7d1896f7" class="ne-p"><span class="ne-text">在<code class="ne-code"><span class="ne-text">next.config.js</span></code><span class="ne-text">中的<code class="ne-code"><span class="ne-text">webpack</span></code><span class="ne-text">加入:</span></span></span></p>
<p id="ue5d623ae" class="ne-p"> </p>
<pre class="ne-codeblock language-plain" data-language="plain">config.entry = async () => {
const entries = await originalEntry();
if (entries['main.js']) {
entries['main.js'].unshift('./static/js/polyfills-legacy.js');
}
return entries;
};</pre>
<p id="ua9a48bba" class="ne-p"> </p>
<p id="u73688c41" class="ne-p"><code class="ne-code"><span class="ne-text">static/js/polyfills-legacy.js</span></code><span class="ne-text">:</span></p>
<p id="u9dba7a12" class="ne-p"> </p>
<pre class="ne-codeblock language-plain" data-language="plain">// React
import 'core-js/es6/map';
import 'core-js/es6/set';
// Next.js
import 'core-js/fn/string/starts-with';</pre>
<p id="u8a5017ee" class="ne-p"> </p>
<h3 id="2d6347ef"><span class="ne-text">1.5 小结</span></h3>
<p id="ua81e2851" class="ne-p"> </p>
<ol class="ne-ol">
<li id="u651320a3"><span class="ne-text">通过配置<code class="ne-code"><span class="ne-text">.babelrc</span></code><span class="ne-text">文件中的<code class="ne-code"><span class="ne-text">preset-env</span></code><span class="ne-text">给业务代码自动加上垫片</span></span></span></li>
<li id="udb8e0c9b"><span class="ne-text">通过全局垫片<code class="ne-code"><span class="ne-text">polyfills-legacy.js</span></code><span class="ne-text">按照实际业务需要补上<code class="ne-code"><span class="ne-text">node_modules</span></code><span class="ne-text">中模块的垫片。</span></span></span></li>
</ol>
<p id="ufed96780" class="ne-p"> </p>
<h2 id="e2626ed5"><span class="ne-text">2 Webpack打包优化</span></h2>
<p id="u03cfe64e" class="ne-p"> </p>
<h3 id="4545c35b"><span class="ne-text">2.1 目前打包分析</span></h3>
<p id="u90d3966c" class="ne-p"> </p>
<p id="u98612eca" class="ne-p"><span class="ne-text">以insure页面为例,nextjs对应的资源有:</span></p>
<p id="u300145ec" class="ne-p"> </p>
<ol class="ne-ol">
<li id="u8f38f35e"><code class="ne-code"><span class="ne-text">https://res.qixin18.com/v3/h5/_next/static/RRt1cbeJiwK-C3QWNcKTC/pages/product/insure.js</span></code><span class="ne-text">(547.82 KB)</span></li>
<li id="u906db09e"><code class="ne-code"><span class="ne-text">https://res.qixin18.com/v3/h5/_next/static/RRt1cbeJiwK-C3QWNcKTC/pages/_app.js</span></code><span class="ne-text"><br><span class="ne-text">(341.34 KB)</span></span></li>
<li id="u32eba987"><code class="ne-code"><span class="ne-text">https://res.qixin18.com/v3/h5/_next/static/chunks/commons.e06dbd81189492fbc49d.js</span></code><span class="ne-text"><br><span class="ne-text"> (205.13 KB)</span></span></li>
<li id="u17ce6a16"><code class="ne-code"><span class="ne-text">https://res.qixin18.com/v3/h5/_next/static/runtime/main-17f8ae1732f73a182272.js</span></code><span class="ne-text"><br><span class="ne-text">(63.6 KB)</span></span></li>
</ol>
<p id="u10f15b5e" class="ne-p"><img width="960" id="u104af25a" class="ne-image lazyload" data-src="https://cdn.nlark.com/yuque/0/2021/png/371248/1620704096404-26e49cac-3380-4548-a934-9fe9d3428f21.png"></p>
<p id="uece2d57a" class="ne-p"> </p>
<p id="ued72f47c" class="ne-p"><span class="ne-text">从上图可以看出存在大量重复的包引用</span></p>
<p id="u290dc94f" class="ne-p"> </p>
<h4 id="61034a81"><span class="ne-text">2.1.1 main.js</span></h4>
<p id="u248d8c44" class="ne-p"><img width="962" id="u57602ee8" class="ne-image lazyload" data-src="https://cdn.nlark.com/yuque/0/2021/png/371248/1620704113076-aa310552-245d-4476-860f-217e4f6f978a.png"></p>
<p id="u0a58e19d" class="ne-p"> </p>
<p id="u79b5b346" class="ne-p"><span class="ne-text">main.js 主要是Next.js核心。<span class="ne-text"><br><span class="ne-text">从上图可以看出包含了:</span></span></span></p>
<p id="uacc8078b" class="ne-p"> </p>
<ul class="ne-ul">
<li id="ua632ba03"><span class="ne-text">垫片(core-js/@babel/runtime-core2)</span></li>
<li id="udf480752"><span class="ne-text">Next核心(next-server/next)</span></li>
<li id="u12cab68e"><span class="ne-text">工具类(url/querystring/unfetch.mjs)</span></li>
</ul>
<p id="u012d03ec" class="ne-p"> </p>
<h4 id="e78f2a37"><span class="ne-text">2.1.2 _app.js</span></h4>
<p id="uce22dae6" class="ne-p"><img width="837" id="u4f49ae27" class="ne-image lazyload" data-src="https://cdn.nlark.com/yuque/0/2021/png/371248/1620704137662-6428eac0-6921-40ed-8b85-c4f813be010e.png"></p>
<p id="uc6a0ee5f" class="ne-p"> </p>
<p id="ufacf356d" class="ne-p"><em>app.js 主要是对应业务代码中的</em><span class="ne-text">app.js文件<span class="ne-text"><br><span class="ne-text">从上图可以看出包含了:</span></span></span></p>
<p id="u1dae2faa" class="ne-p"> </p>
<ul class="ne-ul">
<li id="u5796e9d2"><span class="ne-text">垫片(core-js/@babel/runtime-core2)</span></li>
<li id="u368a6b9f"><span class="ne-text">Redux(<code class="ne-code"><span class="ne-text">pages/*/store</span></code><span class="ne-text">)</span></span></li>
<li id="u1306d2d8"><span class="ne-text">工具类(immutable.js/moment.js/loadash/axios/antd/exif.js...)</span></li>
</ul>
<p id="udf1bc9d5" class="ne-p"> </p>
<h4 id="cabd4d1d"><span class="ne-text">2.1.3 commons.js</span></h4>
<p id="u9e2fb6df" class="ne-p"><img width="809.5" id="u92694416" class="ne-image lazyload" data-src="https://cdn.nlark.com/yuque/0/2021/png/371248/1620704162941-0cd80d4d-e168-411e-858d-3d3782eda49b.png"></p>
<p id="uda5a3e9a" class="ne-p"><span class="ne-text">commons.js主要是公用的库<span class="ne-text"><br><span class="ne-text">从上图可以看出包含了:</span></span></span></p>
<p id="u13103287" class="ne-p"> </p>
<ul class="ne-ul">
<li id="u7a0c0f58"><span class="ne-text">垫片(core-js/@babel/runtime-core2)</span></li>
<li id="u5ae00017"><span class="ne-text">react</span></li>
<li id="u0472ea2e"><span class="ne-text">antd</span></li>
</ul>
<p id="ub79be132" class="ne-p"> </p>
<h4 id="368323f0"><span class="ne-text">2.1.4 insure.js</span></h4>
<p id="u6d54b007" class="ne-p"><img width="933.5" id="u87d50ee1" class="ne-image lazyload" data-src="https://cdn.nlark.com/yuque/0/2021/png/371248/1620704183078-7290e97d-ae94-4181-8dc6-b06f26e81468.png"></p>
<p id="u3e887769" class="ne-p"> </p>
<p id="udac5fb6b" class="ne-p"><span class="ne-text">insure.js主要页面的业务代码<span class="ne-text"><br><span class="ne-text">从上图可以看出包含了:</span></span></span></p>
<p id="u9d159e01" class="ne-p"> </p>
<ul class="ne-ul">
<li id="uecb2ebab"><span class="ne-text">垫片(core-js/@babel/runtime-core2)</span></li>
<li id="u0899fb50"><span class="ne-text">antd</span></li>
<li id="ufa471b67"><span class="ne-text">工具类(moment.js/immutable.js/redux)</span></li>
<li id="u56eac0ac"><span class="ne-text">业务代码</span></li>
</ul>
<p id="uff6c85f7" class="ne-p"> </p>
<h3 id="eca2dfcf"><span class="ne-text">2.2 Webpack SplitChunksPlugin</span></h3>
<p id="u9fe2df01" class="ne-p"> </p>
<p id="u57a488e0" class="ne-p"><span class="ne-text">Webpack SplitChunksPlugin默认打包策略</span></p>
<p id="u5683f78a" class="ne-p"> </p>
<ul class="ne-ul">
<li id="uacb435c8"><span class="ne-text">New chunk can be shared OR modules are from the node_modules folder</span></li>
<li id="u4e15280e"><span class="ne-text">New chunk would be bigger than 30kb (before min+gz)</span></li>
<li id="u53ca7e66"><span class="ne-text">Maximum number of parallel requests when loading chunks on demand would be lower or equal to 5</span></li>
<li id="u8ee9751b"><span class="ne-text">Maximum number of parallel requests at initial page load would be lower or equal to 3</span></li>
</ul>
<p id="ud6aee191" class="ne-p"> </p>
<pre class="ne-codeblock language-plain" data-language="plain">module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
automaticNameMaxLength: 30,
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}};</pre>
<p id="uc8da1bed" class="ne-p"> </p>
<h3 id="8109428e"><span class="ne-text">2.3 客户端默认打包策略</span></h3>
<p id="u61f4f3d9" class="ne-p"> </p>
<pre class="ne-codeblock language-plain" data-language="plain">if (!isServer) {
const cacheGroups = config.optimization.splitChunks;
console.log(cacheGroups);
}
// 默认值:
splitChunks {
chunks: 'all',
cacheGroups: {
default: false,
vendors: false,
commons: { name: 'commons', chunks: 'all', minChunks: 78.5 },
react: {
name: 'commons',
chunks: 'all',
test: /[\\\/]node_modules[\\\/](react|react-dom)[\\\/]/
},
styles: { name: 'styles', test: /\.+(css)$/, chunks: 'all', enforce: true }
}
}</pre>
<p id="ubc7c067f" class="ne-p"> </p>
<div class="ne-quote">
<p id="ueacd060d" class="ne-p"><span class="ne-text">splitChunks.chunks: 'all'</span></p>
</div>
<p id="u6af79295" class="ne-p"> </p>
<p id="u5e3f6102" class="ne-p"><span class="ne-text">定义如何根据<code class="ne-code"><span class="ne-text">静态引用</span></code><span class="ne-text">和<code class="ne-code"><span class="ne-text">动态引用</span></code><span class="ne-text">来<code class="ne-code"><span class="ne-text">复用模块</span></code><span class="ne-text">。详情看上面的链接,配置为<code class="ne-code"><span class="ne-text">all</span></code><span class="ne-text">表示使用最优策略来复用模块。</span></span></span></span></span></p>
<p id="udb26978d" class="ne-p"> </p>
<div class="ne-quote">
<p id="udc2970d9" class="ne-p"><span class="ne-text">splitChunks.cacheGroups.default: false</span></p>
</div>
<p id="ucda288a6" class="ne-p"> </p>
<p id="u09038808" class="ne-p"><span class="ne-text">把Webpack中的默认<code class="ne-code"><span class="ne-text">splitChunks.cacheGroups.default</span></code><span class="ne-text">禁用。</span></span></p>
<p id="u9808e1f8" class="ne-p"> </p>
<div class="ne-quote">
<p id="u7c5fdc07" class="ne-p"><span class="ne-text">splitChunks.cacheGroups.vendors: false</span></p>
</div>
<p id="uecef3432" class="ne-p"> </p>
<p id="u9754c6bd" class="ne-p"><span class="ne-text">把Webpack中的默认<code class="ne-code"><span class="ne-text">splitChunks.cacheGroups.vendors</span></code><span class="ne-text">禁用。</span></span></p>
<p id="uac353c1c" class="ne-p"> </p>
<div class="ne-quote">
<p id="uc8aadf2b" class="ne-p"><span class="ne-text">splitChunks.cacheGroups.commons</span></p>
</div>
<p id="ub1699727" class="ne-p"> </p>
<p id="u3f265b39" class="ne-p"><span class="ne-text">配置<code class="ne-code"><span class="ne-text">commons</span></code><span class="ne-text">缓存组,包名为<code class="ne-code"><span class="ne-text">commons</span></code><span class="ne-text">,</span></span></span></p>
<p id="u406d8cf0" class="ne-p"> </p>
<pre class="ne-codeblock language-plain" data-language="plain">splitChunks.cacheGroups.commons.minChunks = totalPages > 2 ? totalPages * 0.5 : 2</pre>
<p id="u047f0b81" class="ne-p"> </p>
<p id="u61682458" class="ne-p"><span class="ne-text">这里表示当一个模块被一半以上的页面引用的时候,打包到<code class="ne-code"><span class="ne-text">common.js</span></code><span class="ne-text">中,否则待在单独页面的包中。</span></span></p>
<p id="u7d7ebbf9" class="ne-p"> </p>
<div class="ne-quote">
<p id="ub18e3c91" class="ne-p"><span class="ne-text">splitChunks.cacheGroups.react</span></p>
</div>
<p id="u3f982af2" class="ne-p"> </p>
<p id="u0b87e338" class="ne-p"><span class="ne-text">这里的配置是把所有的React打包在common.js中。</span></p>
<p id="u4b074905" class="ne-p"> </p>
<h3 id="4fbba82c"><span class="ne-text">2.4 优化思路</span></h3>
<p id="u594af649" class="ne-p"> </p>
<p id="uc37d43f0" class="ne-p"><span class="ne-text">从2.1中可以知道,大部分重复的包在于Moment.js/Core.js/exif.js/immutable.js/redux.js等。</span></p>
<p id="uf1e12d1a" class="ne-p"> </p>
<div class="ne-quote">
<p id="ufb8cd2fb" class="ne-p"><span class="ne-text">思路一:所有在node_module中的包都打包在common.js中</span></p>
</div>
<p id="u2a05336d" class="ne-p"> </p>
<p id="ufa080436" class="ne-p"><span class="ne-text">有点,简单粗暴,但是无法控制真正的按需加载,有可能一个node_modules中的一个小模块只被一个页面引用,实际上也会加载到commons.js中,这样导致很浪费流量。这也是为什么在默认的Next.js中,使用只有一半以上页面引用才会放到commons.js中的原因。</span></p>
<p id="uc5ba3276" class="ne-p"> </p>
<div class="ne-quote">
<p id="ub204c6c5" class="ne-p"><span class="ne-text">思路二:按照实际情况做配置。</span></p>
</div>
<p id="ud6674384" class="ne-p"> </p>
<ol class="ne-ol">
<li id="ue50c0fb4"><span class="ne-text">core.js/redux.js/immutable.js等基础库放到一个组<code class="ne-code"><span class="ne-text">main.js</span></code><span class="ne-text">。</span></span></li>
<li id="u6916f751"><span class="ne-text">工具类,moment.js/axios.js等工具类放到一个组<code class="ne-code"><span class="ne-text">vendors.js</span></code><span class="ne-text">。</span></span></li>
<li id="uc5b12a44"><span class="ne-text">其他的包保留默认策略,只有一半以上的页面引用才打包的<code class="ne-code"><span class="ne-text">commons.js</span></code><span class="ne-text">。</span></span></li>
</ol>
<p id="ue60fdc80" class="ne-p"> </p>
<p id="u1949ae39" class="ne-p"><span class="ne-text">最终代码如下:</span></p>
<p id="uc14fc791" class="ne-p"> </p>
<pre class="ne-codeblock language-plain" data-language="plain">if (!isServer) {
const cacheGroups = config.optimization.splitChunks.cacheGroups;
delete cacheGroups.react;
cacheGroups.default = false;
// 1.基础库和next.js的基础main放到一起:next-server
cacheGroups.main = {
name: 'main',
test: /[\\/]node_modules[\\/](next-server|next|core-js|regenerator-runtime|@babel)[\\/]/,
enforce: true,
chunks: 'all',
priority: 20,
};
// 1.基础库和next.js的基础main放到一起:react
cacheGroups.react = {
name: 'main',
test: /[\\/]node_modules[\\/](react|react-dom|react-redux|redux|immutable|redux-immutable)[\\/]/,
enforce: true,
chunks: 'all',
priority: 20,
};
// 2.工具类
cacheGroups.vendors = {
name: 'vendors',
test: /[\\/]node_modules[\\/](axios|lodash|moment)[\\/]/,
enforce: true,
chunks: 'all',
priority: 20,
};
// 3. 其他的包保留默认策略,只有一半以上的页面引用才打包的commons.js。
// commons: { name: 'commons', chunks: 'all', minChunks: 78.5 },
}</pre>
<p id="uc7ae545c" class="ne-p"> </p>
<p id="u5d724c1b" class="ne-p"><span class="ne-text">另外一些没用的包已经清理掉:<code class="ne-code"><span class="ne-text">exif-js</span></code></span></p>
<p id="u6cc3b9f6" class="ne-p"> </p>
<h3 id="6a3833d1"><span class="ne-text">2.5 优化结果</span></h3>
<p id="u2a979f65" class="ne-p"> </p>
<p id="ucecffe7b" class="ne-p"><span class="ne-text">大小是<code class="ne-code"><span class="ne-text">Parsed size</span></code></span></p>
<table id="975fe128" class="ne-table">
<tbody>
<tr>
<td width="150">
<p id="u70a77101" class="ne-p"><span class="ne-text">文件名称</span></p>
</td>
<td width="150">
<p id="ucbc2110a" class="ne-p"><span class="ne-text">优化前</span></p>
</td>
<td width="150">
<p id="u6afba1b6" class="ne-p"><span class="ne-text">优化后</span></p>
</td>
<td width="150">
<p id="u2c038e72" class="ne-p"><span class="ne-text">变化</span></p>
</td>
<td width="150">
<p id="ubfd852af" class="ne-p"><span class="ne-text">优化百分比</span></p>
</td>
</tr>
<tr>
<td width="150">
<p id="u69ab49e7" class="ne-p"><code class="ne-code"><span class="ne-text">insure.js</span></code></p>
</td>
<td width="150">
<p id="u61c22f29" class="ne-p"><span class="ne-text">547.82 KB</span></p>
</td>
<td width="150">
<p id="u725b5735" class="ne-p"><span class="ne-text">364.25 KB</span></p>
</td>
<td width="150">
<p id="ua315d5e6" class="ne-p"><code class="ne-code"><span class="ne-text">-183.57 KB</span></code></p>
</td>
<td width="150"> </td>
</tr>
<tr>
<td width="150">
<p id="u3874574b" class="ne-p"><code class="ne-code"><span class="ne-text">_app.js</span></code></p>
</td>
<td width="150">
<p id="uda78502c" class="ne-p"><span class="ne-text">341.34 KB</span></p>
</td>
<td width="150">
<p id="u30fde1bd" class="ne-p"><span class="ne-text">152.6 KB</span></p>
</td>
<td width="150">
<p id="u8b19fd09" class="ne-p"><code class="ne-code"><span class="ne-text">-188.74 KB</span></code></p>
</td>
<td width="150"> </td>
</tr>
<tr>
<td width="150">
<p id="ue722a58e" class="ne-p"><code class="ne-code"><span class="ne-text">commons.js</span></code></p>
</td>
<td width="150">
<p id="uc0e0b048" class="ne-p"><span class="ne-text">205.13 KB</span></p>
</td>
<td width="150">
<p id="uf67cde91" class="ne-p"><span class="ne-text">0</span></p>
</td>
<td width="150">
<p id="uf1efd4a5" class="ne-p"><code class="ne-code"><span class="ne-text">-205.13 KB</span></code></p>
</td>
<td width="150"> </td>
</tr>
<tr>
<td width="150">
<p id="u657bc87c" class="ne-p"><code class="ne-code"><span class="ne-text">main.js</span></code></p>
</td>
<td width="150">
<p id="u0ba80ac6" class="ne-p"><span class="ne-text">63.6 KB</span></p>
</td>
<td width="150">
<p id="u6bbb4463" class="ne-p"><span class="ne-text">311.43 KB</span></p>
</td>
<td width="150">
<p id="u019d08d8" class="ne-p"><code class="ne-code"><span class="ne-text">+247.83 KB</span></code></p>
</td>
<td width="150"> </td>
</tr>
<tr>
<td width="150">
<p id="ue259b810" class="ne-p"><code class="ne-code"><span class="ne-text">vendors.js</span></code></p>
</td>
<td width="150">
<p id="ub31fa7be" class="ne-p"><span class="ne-text">0</span></p>
</td>
<td width="150">
<p id="ub42cae64" class="ne-p"><span class="ne-text">103.17 KB</span></p>
</td>
<td width="150">
<p id="u7098424e" class="ne-p"><code class="ne-code"><span class="ne-text">+103.17 KB</span></code></p>
</td>
<td width="150"> </td>
</tr>
<tr>
<td width="150">
<p id="ub35f32be" class="ne-p"><code class="ne-code"><span class="ne-text">insure页面</span></code></p>
</td>
<td width="150">
<p id="uf7be81db" class="ne-p"><span class="ne-text">1,157.89 KB</span></p>
</td>
<td width="150">
<p id="u034776e6" class="ne-p"><span class="ne-text">931.45 KB</span></p>
</td>
<td width="150">
<p id="u2b693147" class="ne-p"><code class="ne-code"><span class="ne-text">-226.44 KB</span></code></p>
</td>
<td width="150">
<p id="u880e34ac" class="ne-p"><code class="ne-code"><span class="ne-text">-19.55 %</span></code></p>
</td>
</tr>
<tr>
<td width="150">
<p id="ub2f8cbf3" class="ne-p"><code class="ne-code"><span class="ne-text">全部</span></code></p>
</td>
<td width="150">
<p id="u92eb3c4b" class="ne-p"><span class="ne-text">20.61 MB</span></p>
</td>
<td width="150">
<p id="ubea098e3" class="ne-p"><span class="ne-text">12.39 MB</span></p>
</td>
<td width="150">
<p id="ueab490be" class="ne-p"><code class="ne-code"><span class="ne-text">-8.22 MB</span></code></p>
</td>
<td width="150">
<p id="u6d3a6abe" class="ne-p"><code class="ne-code"><span class="ne-text">-39.88 %</span></code></p>
</td>
</tr>
</tbody>
</table>
<p id="u5f600a9c" class="ne-p"> </p>
<p id="u0c0298dc" class="ne-p"><span class="ne-text">前后Webpack Bundle Analyzer文件:</span></p>
<p id="ud35960a5" class="ne-p"> </p>
<p id="uc6fdaa21" class="ne-p"><span class="ne-text">优化前:</span></p>
<p id="u5df6d93f" class="ne-p"> </p>
<p id="u9112a215" class="ne-p"><span class="ne-text">Webpack-Bundle-Analyzer-client.html</span></p>
<p id="u4e88a3ec" class="ne-p"> </p>
<p id="u365e22db" class="ne-p"><span class="ne-text">优化后:</span></p>
<p id="u563c8d65" class="ne-p"> </p>
<p id="u3fb07292" class="ne-p"><span class="ne-text">Webpack-Bundle-Analyzer-client-optimization</span></p>
<p id="u48eb770a" class="ne-p"> </p>
<h3 id="708cce37"><span class="ne-text">2.6 小结</span></h3>
<p id="ued9e6279" class="ne-p"> </p>
<p id="u2d7e950e" class="ne-p"><span class="ne-text">从Next.js的源码可以看出,Next.js对于拆包这块做得还是很粗糙的。</span></p>
<p id="uafa70e60" class="ne-p"> </p>
<div class="ne-quote">
<p id="u6003dc85" class="ne-p"><span class="ne-text">优化策略:</span></p>
</div>
<p id="ua29e38ac" class="ne-p"> </p>
<ol class="ne-ol">
<li id="ufd4de1f9"><span class="ne-text">对于基础包,包括<code class="ne-code"><span class="ne-text">Next.js</span></code><span class="ne-text">、<code class="ne-code"><span class="ne-text">babel</span></code><span class="ne-text">、<code class="ne-code"><span class="ne-text">React.js相关包</span></code><span class="ne-text">放到<code class="ne-code"><span class="ne-text">main.js</span></code><span class="ne-text">中,这一层应该包含<code class="ne-code"><span class="ne-text">几乎所有页面都共用的包</span></code><span class="ne-text">。</span></span></span></span></span></span></li>
<li id="u5dfd7e1e"><span class="ne-text">对于工具包,包括<code class="ne-code"><span class="ne-text">moment.js</span></code><span class="ne-text">、<code class="ne-code"><span class="ne-text">lodash</span></code><span class="ne-text">、<code class="ne-code"><span class="ne-text">axios</span></code><span class="ne-text">等放到<code class="ne-code"><span class="ne-text">vendor.js</span></code><span class="ne-text">中,这一层应该包含<code class="ne-code"><span class="ne-text">大部分页面都公用的包</span></code><span class="ne-text">。</span></span></span></span></span></span></li>
<li id="u57133908"><span class="ne-text">其他情况公用的代码,包括node_modules中的和业务中的,放到<code class="ne-code"><span class="ne-text">common.js</span></code><span class="ne-text">中,这一层应该包含了<code class="ne-code"><span class="ne-text">一半页面用到的公用包</span></code><span class="ne-text">。</span></span></span></li>
</ol>
<p id="u91cc12e0" class="ne-p"> </p>
<div class="ne-quote">
<p id="u60c97d77" class="ne-p"><span class="ne-text">从上面的策略可以看出,各个层次之间其实没有严格的区分,更多是根据项目的实际情况来决定,另外有一些思考:</span></p>
</div>
<p id="uc66f8feb" class="ne-p"> </p>
<ol class="ne-ol">
<li id="u624d06d9"><span class="ne-text">实际上拆包不仅要从单个页面去看,还要从整个项目去看,例如有些小页面不需要依赖太多的外部包,但是很多外部包又打包在<code class="ne-code"><span class="ne-text">main.js</span></code><span class="ne-text">等基础包中,会影响原来的单个页面的效率。</span></span></li>
<li id="ub761bccf"><span class="ne-text">另外不仅要从node_modules去看,还要从业务代码去看,目前业务代码的打包策略是<code class="ne-code"><span class="ne-text">只有一半以上的页面引用才打包的commons.js</span></code><span class="ne-text">,这只是大概的优化策略,如果想要做到更好,更精细的效果,还需要从实际业务出发去拆包。</span></span></li>
<li id="u64843083"><span class="ne-text">最后还需要考虑文件大小和包大小之间的平衡。</span></li>
</ol></div><br><br>
来源:https://www.cnblogs.com/whw666/p/16033765.html
頁:
[1]