大家好,欢迎来到IT知识分享网。
一、Library
webpack 除了可以打包应用程序外,还可以打包 JavaScript 的 library;当我们想自己开发一个组件库、工具、框架的时候,也就是说我们想自己造轮子给别人用的时候,免不了要开发很多的模块,最终我们都可以借助 webpack 来进行打包;
1、如何构建 library
//index.js export const add = function(a,b){
return a+b
webpack.config.js
const path = require('path') module.exports={
mode:"production", entry:"./src/index", output:{
path:path.resolve(__dirname,"dist"), filename:"myTest.js", bibrary:{
name:"myTest", type:"umd" }, clean:true, globalObject:"globalThis" //解决commonJS打包找不到self的问题 } }
由于是生产环境下的打包,webpack 会自动进行 Tree Sharking ,将未被使用的包自动剔除;我们在 index.js 里面的代码没有被使用,所以直接进行打包, main.js 里面是空的;
那么我们如何才能让我们 index.js 文件作为一个 library 来进行打包呢?让代码不被 Tree Sharking ?
我们可以使用 output.library
来指定包名;这样我们的 library 就可以正常打包然后提供使用了;
注意: type 是为了设置使用我们的 library 时的引入方式;umd 支持:script 标签、CommonJS、AMD 这几种方式的引入;
//script标签 <script src="../dist/myTest.js"></script> <script> console.log(myTest.add(1,2)) </script> //CommonJS const {
add}= require("../dist/myTest") console.log(add.(1,2))
当然我们也可以使用 import 来进入文件,但是这样会更麻烦一点,需要下面的配置:
//webpack.config.js const path = require('path') module.exports = {
mode:'production', entry:{
app1:'./src/app.js', }, experiments:{
outputModule:true }, output:{
path:path.resolve(__dirname,"dist"), filename:"app.js", library:{
type:"module" }, clean:true, }, } //index.html <script type="module"> import {
add } from './dist/app.js' console.log(add) </script>
在 webpack 配置中添加 experiments 的配置,然后删除 library 的 name 属性;在使用的时候需要把 import 放在 type 为 module 的 script 标签内部;
2、发布为 npm-package
执行命令行:
npm config get registry //获取注册地址,必须是 https://registry.npmjs.org/ npm adduser //新增用户,填写用户名、密码、邮箱信息 npm publish //发包
每次发新的包,包的名称必须保持唯一性。
二、模块联邦(Module Federation)
多个独立的构建我们可以组成一个应用程序,这些独立的构件之间不存在任何的依赖关系,因此我们可以单独的开发和部署他们,这种通常可以称之为:微前端;
webpack 可以通过 dll 或者是 externals 来做到代码共享时的一个 common chunk;但是不同应用和项目之间这个共享任务就变得非常困难了,webpack5 提供的模块联邦可以让代码直接在项目之间利用 CDN 直接共享,不再需要本地安装 npm 包构建在发布了;
1、模块共享管理方式对比
1、npm:以前我们代码的共享是依靠 npm ,将依赖作为一个 labrary 安装到我们的项目里,进行 webpack 打包,构建上线;
2、UMD:我们还可以通过 UMD、将模块用 webpack umd 的模式打包,并且输出到其他的应用程序当中;(包的体积无法达到本地编译时的优化效果,库之间容易产生冲突)
3、微前端:(MFE )子应用独立打包模块实现解耦,但是无法抽取公共的依赖;整体应用打包,但是打包是速度太慢了;
4、模块联邦:webpack5 内置的一个核心特性,可以直接将一个线上的应用共享给其他应用使用,具备整体应用打包、公共依赖抽取的能力;
2、使用模块联邦
如果两个线上的项目(a、b),a 项目想访问 b 项目里的某一个模块,这个时候就需要使用到模块联邦了;模块联邦是一个独立的插件,不需要安装,可以在 webpack 里面获取到;
2.1、a 项目中暴露 header 组件
//a项目中 webpack.config.js const {
ModuleFederationPlugin }= require('webpack').container module.exports={
mode:'production', entry:'./src/index.js', plugins:[ new ModuleFederationPlugin({
name:'header', filename:'remoteEntry.js', remotes:{
}, exports:{
'./header':'./src/header.js' }, shared:{
} }) ] }
模块联邦下属性的含义:
name:标识模块联邦的名字,提供给其他应用使用;
filename:远端入口,由于项目已经部署到线上,像访问需要有一个js文件的路径;
remotes:引用其他项目暴露的组件;
exports:暴露一些组件给其他项目使用,暴露以key-value 形式,key 是别的项目使用的时候基于这个路径拼接 url ,value 才是组件在当前项目下的路径;
shared:把模块中共享的第三方模块放在这里,在打包的时候会打包到一个单独的包里面;
2.2、b 项目中引入组件
//b项目中引入 webpack.config.js const {
ModuleFederationPlugin }= require('webpack').container module.exports={
mode:'production', entry:'./src/index.js', plugins:[ new ModuleFederationPlugin({
name:'footer', filename:'remoteEntry.js', remotes:{
header:'header@http://xxxx/remoteEntry.js' }, exports:{
'./footer':'./src/footer.js' }, shared:{
} }) ] }
在 remotes 中引入 a 项目中暴露的组件,key 是 b 项目中引入组件的一个别名,value 是一个组合的字符串,分为三个部分:a 项目暴露模块联邦的 name、a 项目的服务域名加端口号、a 项目模块联邦的 filename;
2.3、b 项目使用组件
由于网络共享、模块导入是有延迟的,所以我们使用异步的方式来引入它;
//index.js import('header/header').then((header)=>{
//在这里就可以直接使用 a 项目中的 header 组件了; })
这里 import 的 header/header 是什么意思呢? 第一个 header 是当前项目引入 其他项目的时候 在 remotes 中配置的 key,第二个 header 则是 a 项目中在暴露的时候 exports 中 配置的路径 key ;
注意:如果引入多个项目暴露出来的组件的话,我们可以使用 Promise.all 方法;
三、构建性能优化
webpack 的性能提升可以分为两类:1、提升项目性能,如网站的首屏加载时间,针对用户;2、提高打包速度,降低打包时间,针对开发者;每个版本的 webpack 构建优化点都是不一样的,所以建议参照官网的优化点进行优化;
1、通用环境
通用环境是指开发环境加生产环境;
1.1、更新到最新版本(webpack、node.js)
这两个工具在升级版本的时候会内置的提升性能;除此之外也可以把我们的 npm、 yarm 更新到最新版本;
1.2、将 loader 应用于最少数量的必要模块
精准解析需要解析的文件,可以大大提高打包速度;
1.3、引导 bootstrap
每个额外的 loader、plugin 都有其启动时间,尽量少的使用工具;
1.4、解析
1、减少 resolve.modules、resolve.extensions、resolve.mainFiles、resolve.descriptionFiles
中条目数量,因为他们会增加文件系统调用的次数;
2、如果不使用 symlinks (例如 npm link 、yarm link ),可以设置 resolve.symlinks:false
;
3、如果使用了自定义 rsolve.plugin 规则,但是没有指定 context 上下文,可以设置 resolve.cacheWithContext:false
;
1.5、小就是快
减少编译结果的整体大小,尽量保持 chunk 体积小:
1、使用数量更小、体积更新的 library
2、在多页面应用程序中使用 SplitChunkPlugin,并开启 async 模式
3、移除未引用的代码
4、只编译你当前正在开发的代码
1.6、持久化缓存
在 webpack 中,使用 chache 选项,在 package.json 中使用 postinstall 清除缓存目录;
1.7、自定义 plugin、loader
对它进行概要分析,以免引入性能问题
1.8、progress plugin
将 ProgressPlugin 从 webpack 中删除,可以缩短构建时间,ProgressPlugin 可以显示 webpack 打包的进度,这个只是一种方案,真实的性能提升效果不大(不建议删除);
1.9、dll
使用 DllPlugin 为更改不频繁的代码生成单独的编译结果,这样可以提高应用程序的编译速度,尽管增加看构建过程的复杂程度;
新建一个 webpack.dll.config.js 配置文件使用 webpack.DllPlugin 来打包 jquery;
const path = require('path') const webpack = require('webpack') module.exports={
mode:'production', entry:{
jquery:['jquery'] //这里引入的是jquery 模块 }, output:{
filename:'[name].js', path:path.resolve(__dirname,'dll'), library:'[name]_[hash]' }, plugins:[ new wenbpack.DllPlugin({
name:'[name]_[hash]', path:path.resolve(__dirname,'dll/manifest.json') }) ] }
在 package.json 把它作为一个 npm 脚本执行,配置 dll 运行命令来执行 jquery 的 dll 编译打包;
"script":[ "dll":"webpack --config ./webpack.dll.config.js" ]
然后执行 npm run dll 就可以执行打包,打包完成之后在项目中生成一个 dll 文件夹,里面是 jquery 相关的代码以及 manifest.json 文件;这个 json 文件让我们可以在项目中引入并使用 jquery;我们项目中使用:webpack.config.js
const path = require('path') const webpack = require('webpack') const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin') module.exports = {
plugins:[ new webpack.DllReferencePlugin({
manifest:path.resolve(__dirname,'./dll/manifest.json') //引入dll打包生成的manifest.json }), new AddAssetHtmlPlugin({
filepath:path.resolve(__dirname,'./dll/jquery.js'), publicPath:'./' }) ] }
打包使用 jquery 之前还需要借助插件 add-asset-html-webpack-plugin ,将 dll 打包后的 js 引入到打包后的 html 文件中;然后项目打包,生成的 dist 文件夹下会生成一个 jquery 文件,并在 html 文件中引入;
1.10、worker 池
thread-loader 可以将非常消耗资源的 loader 分流给一个 worker pool;npm i thread-loader -D
提升打包速度;
module:{
rules:[ {
test:/\.js$/, exclude:/node_modules/, use:[ {
loader:'babel-loader', options:{
presets:['@babel-preset-env'] } }, {
loader:'thread-loader', options:{
workers:2 } } ] } ] }
想要将 babel-loader 放到一个 worker pool 中,需要在 babel-loader 运行前先执行 thread-loader;注意 thread-loader 自身也有时间消耗,只能用于一些十分耗时的包才会有优化效果(谨慎使用);
2、开发环境
2.1、增量编译
使用 webpack 的 watch model 监听模式,内置的会更优化;watch model 会记录时间戳并将此信息传递给 conpilation 让缓存失效;在某些配置环境中,watch model 会回退到 poll model 轮询模式,监听文件过多会导致大量的 CPU 负载,这时可以使用 watchOptions.poll 来增加轮询的间隔时间;
2.2、在内存中编译
下面几个工具都是通过内存编译合 serve 资源资源来提高性能:
webpack-dev-server
webpack-hot-middleware
webpaclk-dev-middleware
2.3、stats.toJson 加速
webpack 4 默认使用 stats.toJson() 输出大量的数据,这个方法尽量避免使用,除非要做增量的统计;webpack-dev-server 在 3.1.3 版本修复了这一问题:最小化每一个增量构建中,从 stats 中获取数据;
2.4、devtool
不同的 devtool 配置,会导致性能上的差异:最佳选中是 eval-cheap-module-source-map ;
2.5、避免在生产环境中才用到的工具
有些 plugin、loader 、utility 是只有生产环境才有作用,所以尽量排除这些工具:TerserPlugin、miniify、mangle等;
2.6、最小化 entry chunk
确保在生成 entry chunk 时尽量减少体积来提高性能可以配置:
optimization:{
runtimeChunk:true }
2.7、避免额外的优化步骤
webpack 通过执行额外的算法任务来优化输出结果的体积合加载性能,这些只适用小型代码库,如果是大型代码库就会非常消耗性能:一般情况下要关闭掉下面这三个
optimization:{
removeAvailableModules:false, removeEmptyChunks:false, splitChunks:false }
2.8、输出结果不携带路径信息
webpack 会在输出的 bundle 中生成路径信息,然而在打包数千个模块的项目中,会导致垃圾回收性能压力,可以设置关闭:
output:{
pathinfo:false }
2.9、node 版本问题
尽量不要使用 node.js v8.9.10-v9.11.1 版本,因为它的 ES2015 Map 和 Set 实现存在性能回退,webpack使用的话会影响编译时间;
2.10、TypeScript loader
在使用 TS loader 的时候尽量加一个配置项 transpileOnly 选项,来缩短使用 ts-loader 的构建时间,这个选项会关闭类型检查;
module:{
rules:[ {
test:/\.js$/, use:[ {
loader:'ts-loader', options:{
transpileOnly:true } } ] } ] }
3、生产环境
3.1、不启用 sourceMap
source map 非常消耗资源,开发环境不要设置 source map;
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/145701.html