webpack 最好的日志_体验webpack5个人总结

介绍webpck5

距离webpack5推出也快有一年多的时间,现在还处在测试版本的阶段,目前已经可以安装使用了

安装方式

```

npm install webpack@next webpack-cli -D

yarn add webpack@next webpack-cli -D

```

复制代码

HTML插件兼容HTML

需要将html-webpack-plugin升级到4.3.0版本

环境要求

最新的官方文档要求node至少在10.13.0版本以上

新特性介绍

持久化缓存

moduleIds & chunkIds的优化

更智能的tree shaking

nodeJs的polyfill脚本被移除

支持生成e6/es2015的代码

SplitChunk和模块大小

Module Federation

使用体验

经过上手使用,webpack5打包体积大小,持续编译速度都有很不错的提升,对webpack4兼容也很平缓,Module Federation也对项目中如何使用微型前端应用提供一种解决方案。如果升级过程中有遇不兼容的情况,可以去webpack5变更日志上查阅。

持久化缓存

在webpack5之前,可以使用cache-loader将编译结构写入硬盘缓存,还可以使用babel-loader,设置option.cacheDirectory将babel-loader编译的结果写进磁盘。

在webpack5中,默认开启缓存,缓存默认是在内存里。可以对cache进行设置

module.export={

cache{

type:'filesystem', // 'memory' | 'filesystem'

cacheDirectory: 'node_modules/.cache/webpack', // 默认将缓存存储在 node_modules/.cache/webpack

// 缓存依赖,当缓存依赖修改时,缓存失效

buildDependencies:{

// 将你的配置添加依赖,更改配置时,使得缓存失效

config: [__filename]

}

}

}

复制代码默认情况下webpack 会假定其所处的 node_modules 目录仅由包管理器修改,将会跳过hash和时间戳处理,出于性能考虑,仅使用package的名称和版本。

当设置 cache.type: "filesystem" 时,webpack 会在内部以分层方式启用文件系统缓存和内存缓存。从缓存读取时,会先查看内存缓存,如果内存缓存未找到,则降级到文件系统缓存。写入缓存将同时写入内存缓存和文件系统缓存。

文件系统缓存不会直接将对磁盘写入的请求进行序列化。它将等到编译过程完成且编译器处于空闲状态才会执行。如此处理的原因是序列化和磁盘写入会占用资源,并且我们不想额外延迟编译过程。

缓存淘汰策略设计:文件缓存存储在 node_modules/.cache/webpack,对于一个缓存集合,最大限度应该不超过 5 个缓存内容,最大累积资源占用不超过 500 MB,当逼近或超过 500MB 的阈值时,优先删除最老的缓存内容。同时,也设计了缓存的有效时长为 2 个星期。

moduleIds & chunkIds的优化

chunk 和 module

chunk:webpack打包最终生成的单独文件块,最终生成的单独文件,一个文件对应一个chunk。

module:每一个源码 js 文件其实都可以看成一个 module。

chunkId的缺点

webpack5改进了moduleIds 和 chunkIds的确定。在webpack5之前,没有从entry打包的chunk文件,都会以1,2,3...的文件命名方式输出。(文件名称后的hash值是用chunkhash生成的)

这样会造成一个后果是,当删除或者暂时不使用1.js这个文件后,那么2.js->1.js,3.js->2.js,这样就会造成原本线上的2.js请求时会造成缓存失效.

在webpack5之前也是可以通过webpackChunkName来解决命名问题

...

Loadable({

loader: () => import(/* webpackChunkName: "home" */ './home'),

loading: (

loadding
)

})

}/>

Loadable({

loader: () => import(/* webpackChunkName: "page1" */'./page1'),

loading: () => (

loadding
)

})

} />

Loadable({

loader: () => import(/* webpackChunkName: "page2" */'./page2'),

loading: () => (

loadding
)

})

} />

....

复制代码这样似乎解决了缓存失效的问题,但我们打开编译后的home.js 会发现还是存有chunkId。如果删除掉home这个菜单,page1,page2打包后的chunkId还是会发生变化,

page1.js打包的文件

删除完home.js后page1.js打包的文件

即使page1.js没有做任何修改,但是由于home.js删除导致的chunkId的变化,所以page1.js的chunkhashi还是会发生变化,缓存失效的问题依旧存在。

webapck5 的改进

采用新的算法,在生产模式下,默认启用这些功能chunkIds: "deterministic", moduleIds: "deterministic"。

此算法采用确定性的方式将短数字 ID(3 或 4 个字符)分配给 modules 和 chunks。这是基于 bundle 大小和长效缓存间的折中方案。

optimization.moduleIds:

可选值:

1. false 告诉webpack不应使用任何内置算法,通过插件提供自定义算法

2.natural 按使用顺序的数字ID。

3.named 方便调试的高可读性id

4.deterministic 根据模块名称生成简短的hash值

5.size 根据模块大小生成的数字id

optimization.chunkIds:

可选值:

1. false 告诉webpack不应使用任何内置算法,通过插件提供自定义算法

2.natural 按使用顺序的数字ID。

3.named 方便调试的高可读性id

4.deterministic 根据模块名称生成简短的hash值

5.size 根据请求到的初始资源size计算的id

6..total-size:根据请求到的解析资源size计算的id

复制代码

更智能的tree shaking

开启tree shaking的条件跟webpack4一样,必须使用ES6模块化,开启production环境

例子

// a.js

export const a =22;

export const b = 33;

// b.js

import * as a from './a.js'

export {a};

// index.js

import * as b from './b.js'

console.log(b.a.a) // 输出22

复制代码webpack4:打包时,会把const b =33也打包进去

webpack5:打包的出来的代码则更精简

nodeJs的polyfill脚本被移除

webpack <= 4 附带了许多 Node.js 核心模块的 polyfil,一旦模块中使用了任何核心模块(即 ”crypto“ 模块),这些模块就会被自动启用。

虽然这使得为 Node.js 编写模块变得简单,但它会将超大的 polyfill 添加到 package 中。在许多情况下,这些 polyfill 并非必要。

import CryptoJS from 'crypto-js';

console.log(CryptoJS.MD5('123123'));

复制代码webpack4 :

webpack5:

支持生成e6/es2015的代码

webpack5 在output中新添加了ecmaVersion,设置output: { ecmaVersion: 6 }就可以使用,在webpack中默认编译生成的代码对应的是es5版本。

两种设置方式:5 =< ecmaVersion <= 11 2009 =< ecmaVersion <= 2020

SplitChunk和模块大小

模块现在能够以更好的方式表示大小,而不是显示单个数字和不同类型的大小。

默认情况下,只能处理 javascript 的大小,但是你现在可以传递多个值来管理它们:

optimization{

splitChunks{

minSize: {

javascript: 30000,

style: 50000,

}

}

}

复制代码

Module Federation

Module Federation 使 JavaScript 应用得以从另一个 JavaScript 应用中动态地加载代码 —— 同时共享依赖。让代码直接在项目间利用 CDN 直接共享,不再需要本地安装 Npm 包、构建再发布了!

动态加载 可以允许代码在运行时按需加载另一个应用的代码。

例子:在app1中引用app2中暴露出来的一个按钮组件

// app1 - webpack.config.js

const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");

module.exports = {

//....

plugins: [

new ModuleFederationPlugin({

name: "app1", // 应用名称 唯一

library: { type: "var", name: "app1" },

remotes: { // 需要引用远程应用,与app2中library的name字段保持一致

app2: "app2",

},

shared: ["react", "react-dom"], // 先判断存不再存这个包,如果不存在就使用app2里的依赖

}),

],

};

复制代码// app2 - webpack.config.js

const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");

module.exports = {

//....

plugins: [

new ModuleFederationPlugin({

name: "app2", // 应用名称 唯一

library: { type: "var", name: "app2" }, // UMD标准导出,和name保持一致即可。

filename: "remoteEntry.js", 暴露出去的chunkname

exposes: { 要暴露出去的模块

"./Button": "./src/Button",

},

shared: ["react", "react-dom"], 与app1共享的依赖,如果app1中有,则会优先使用app1中的依赖。(注:被app1引用)时候会按照app1的/package.json中的版本要求来加载

}),

],

};

复制代码// app1/index.html

// 引入app2暴露的文件

...

...

app1/app.js

import AppTwoButton from 'app/Button';

const App = ()=>{

return (

)

}

app1/bootstrap.js

ReactDOM.render(, document.getElementById("root"));

app1/bootstrap.js

import("./bootstrap");

复制代码从加载流程看懂Module Federation

首先加载的是 app2/remoteEntry.js ,因为是在html中加载的,通过观remoteEntry.js 可以看出,webpakc是将app2定义在了全局变量里面了,这样app1就能够加载app2里面的模块了。

加载app1的打包后的main.js,进入到bootstrap.js寻找依赖前置,分析

然后在加载main.js里的react.js以及react-dom,react依赖的prop-types.js

接着经过remote的时候,发现依赖app2的Button组件

bootstrap.js所有依赖都加载完之后,异步加载完成,执行then逻辑,启动应用

总结

Module Federation给微前端,代码细分,共享依赖,按需加载提供新的思路,而且能做到运行时加载。但缺点公用全局变量和全局style,以及将模块暴露在全局变量下不够优雅。

webpack5还在不断合并代码的过程中,目前版本bate22,webpack5正式版出来后,相信Module Federation功能还会继续优化。总之升级webpack5之后,构建速度和打包体积都有不错的提升。

参考资料

1.三大应用场景调研,Webpack 新功能 Module Federation 深入解析:developer.aliyun.com/article/755…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值