• webpack5学习进阶:多页面应用、Tree Shaking、PWA、Shimming


    一、多页面应用

    在实际的项目开发中,一个完整的系统不会将所有的功能都放在一个网页里,这是因为会导致网页的性能不佳;实际的做法是,按照功能模块来划分为多个单页面应用,每一个单页面应用又生成一个 HTML 文件,随着业务的发展,更多的多页应用可以被逐渐的加到这个项目里;

    1、entry 配置

    1.1、将两个本地文件打包在一起
    entry:['./src/app1.js', './src/app2.js']
    //或者
    entry:{
    	main:{
    		import:['./src/app1.js', './src/app2.js']
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    打包过后,app1.js 和 app2.js 都会被打包进 dist/main.js 文件中,按照数组里面的前后顺序,webpack 会将前面的文件先打包,并且放在 main.js 的上面;

    1.2、还可以打包第三方库文件
    entry:['./src/app1.js', './src/app2.js', 'lodash']
    
    • 1
    1.3、多页面打包
    entry:{
    	app1:'./src/app1.js',
    	app2:'./src/app2.js',
    }
    
    • 1
    • 2
    • 3
    • 4

    这种配置 webpack 会打包出两个文件,dist/app1.js 和 dist/app2.js ;

    2、index.html 模板配置

    新建一个 html 页面,动态获取 html-webpack-plugin 插件配置的 title,将多个 js 文件分别打包到对应的 html 文件中;

    <%= htmlWebpackPlugin.options.title %>
    
    • 1

    在配置文件中

    plugins:[
    		new HtmlWebpackPlugin({
    			title:'nihao xxs', 
    			template:'./index.html',
    			inject:'body',
    			chunks:['app1'],
    			filename:'http:www.a.com/'
    		})
    	]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    title:html 的 title 名称
    template:打包 html 页面的模板
    inject:js 在打包后的 html 中引入的位置
    chunks:默认情况下,多入口文件打包出来的 js 文件都会放到打包后的 html 页面中,如果想载入指定的 js,可以在 chunks 数组中配置对应的 js 名称;
    filename:配置包的前缀,一般比较大的项目会用到;

    3、多页面环境搭建

    添加多个 HtmlWebpackPlugin 插件配置:

    plugins:[
    		new HtmlWebpackPlugin({
    			filename:'index1.html',
    			title:'nihao xxs', 
    			template:'./index.html',
    			inject:'body',
    			chunks:['app1']
    		}),
    		new HtmlWebpackPlugin({
    			filename:'index2.html',
    			title:'nihao xxs', 
    			template:'./index.html',
    			inject:'body',
    			chunks:['app2']
    		})
    	]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    不设置 chunks 的话会默认两个打包后的 html 文件都引入所有的 js,设置 chunks 之后,只会引入设置的 js;

    我们还可以设置其他的一些东西:
    1、打包后的 html 放到指定文件夹下,只需要修改 filename 路径,在路径上加一个文件夹名称,打包就会自动生成一个文件夹并把 html 放进去;

    plugins:[
    		new HtmlWebpackPlugin({
    			filename:'chunk/index1.html',
    			template:'./index.html',
    		})
    	]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2、将 js 页放到对应 html 文件夹下,也是修改入口文件的 filename 路径;

    entry:{
    		app1:{
    			import:'./src/app.js',
    			filename:'chunk/[name].js'
    		}
    	},
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    二、Tree Shaking(摇树优化)

    Tree Shaking 是 webpack 内置的一个优化,主要功能就是去除没有用到代码。因为 JavaScript 大多数是要通过加载的,加载的文件越小,性能越好,所以 Tree-shaking 对于优化 JavaScript 很有意义。

    Tree-shaking 主要依赖于 ES6 的模块化 import 和 export,在 production 环境默认开启;

    注意:
    1、Tree Shaking 只支持 ESMAScript 的引入方式,不支持 Common JS 的引入方式
    2、在引入模块时就应该避免将全部引入,应该引入局部才可以触发 tree shaking 机制

    1、配置 Tree Shaking

    //test.js
    import {add,mun} from './app1'
    console.log(add(1,2))
    
    //app.js
    export const add = function(a,b){
    	return a+b
    }
    export const mun = function(a,b){
    	return a
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    webpack中开启 Tree Sharking:

    optimization:{
    	usedExports:true
    }
    
    • 1
    • 2
    • 3

    开发环境打包结果:
    在这里插入图片描述
    这里只暴露了 add 方法,并且提示 mun 未被导出;哪怕引入了,但是未被使用,依然会被认为是死代码,webpack5 在打包的时候会自动过滤掉死代码;

    2、设置 sideEffects

    如果在一个纯粹的 ESMScript 模块项目中,很容易识别出哪些文件有副作用;然而,我们实际开发的项目却无法达到这种纯度,所以,此时就必要提示 webpack compiler 哪些代码是“纯粹部分”。 通过 package.json 文件中的 sideEffects 属性;

    1、sideEffects 默认为 true, 告诉 Webpack ,所有文件都有副作用,他们不能被 Tree Shaking。
    2、sideEffects 为 false 时,告诉 Webpack ,没有文件是有副作用的,他们都可以 Tree Shaking。
    3、sideEffects 为一个数组时,告诉 Webpack ,数组中那些文件不要进行 Tree Shaking,其他的可以 Tree Shaking。

    在 package.json 文件中:

    {
    	"description": "",
        "sideEffects":true,
    }
    
    • 1
    • 2
    • 3
    • 4

    3、对全局 css 的影响

    正常不设置 sideEffects 的话,我们通过 import 引入一个全局的样式文件,webpack 默认是可以正常打包使用的;但是如果设置 sideEffects 为 false 时,这时候所有的文件都会被 Tree Shaking;我们需要单独设置 css 文件文件不要 Tree Shaking;

    {
    	"description": "",
        "sideEffects":["*.css"],
    }
    
    • 1
    • 2
    • 3
    • 4

    所以我的理解是:开启 Tree Sharking 后 sideEffects 默认值为 true,这个时候 Tree Sharking 只处理没有关联关系的 js 代码,不处理引入的文件;如果设置 sideEffects 为 false,那么 import 引入的文件也放入到 Tree Sharking 中进行修剪处理;sideEffects 是控制 Tree Sharking 处理文件直接依赖关系的开关;

    三、PWA (开启离线服务)

    渐进式网络应用程序:简称 PWA,它可以提供类似于 native(原生)应用程序体验的 web app(web 应用程序),换句话就说说我们可以在浏览器端能够实现类似于原生应用程序的体验;

    PWA 可以做的事情很多,其中最重要的就是在离线的情况下,应用程序能够继续运行的功能,它是通过 service worker 的 web 技术实现的;

    1、非离线环境下运行

    通常情况下,用户通过网络访问 web app ,浏览器会与一个提供所需资源的 server 通讯;这里我们来看看非离线环境下项目的运行效果;
    1、安装 http-server

    npm i http-server -D
    
    • 1

    2、在 package.json 定义自己的 npm 脚本

    "scripts":{
    	"start":"http-server dist"
    }
    
    • 1
    • 2
    • 3

    用 http-server 来编译运行 dist 下的内容
    3、启动服务

    npm start
    
    • 1

    webpack-dev-server 和 http-server 都是在线的服务;在线服务在服务停止后,浏览器是无法正常访问到网页的,同时在线服务会把启动编译的代码放在内存里,也就是说当我们修改代码之后重新启动服务,这个时候并不会把修改同步打包到 dist 文件夹下面,因为它放在内存里面了;
    4、如果启动服务想同步打包更新 dist 文件

    devServer:{
    	devMiddleware:{
    		writeToDisk:true
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这个配置没什么太大意义,只是介绍一下有这个功能;

    2、添加 Workbox 实现 PWA

    1、安装 workbox-webpack-plugin 插件

    npm i workbox-webpack-plugin -D
    
    • 1

    2、安装成功之后修改配置文件

    const WorkboxPlugin = require("workbox-webpack-plugin")
    module.exports={
    	plugins:[
    		new WorkboxPlugin.GenerateSW({
    			clientsClaim:true, //帮助快速启用 service-worker
    			skipWaiting:true //跳出等待,不允许遗留任何旧的 service-worker
    		})
    	]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3、打包编译
    这个时候会发现多了两个文件:workbox.js 、service-worker.js;添加成功之后还需要进行下一步:注册 service-worker;

    3、注册 Service-Worker

    注册 service-worker 实现离线浏览页面的功能;

    //在入口文件中 业务代码中 app.js
    //判断浏览器是否支持service-worker
    if("serviceWorker" in navigator) {
    	//页面资源加载完成之后执行下面函数
    	window.addEventListener("load",()=>{
    		navigator.serviceWorker.register("/service-worker.js").then(reg=>{
    			console.log('注册成功')
    		}).catch(err=>{
    			console.log("注册失败”)
    		})
    	})
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    然后执行 npx webpack 打包,执行 npm start 启动服务;这里我们是通过 npm start 来启动服务的,所以必须重新打包;这样我们的离线服务就启动成功了;

    为了实验效果,可以在控制台将服务停止,然后继续在浏览器刷新页面访问网页,是能正常访问的;

    实现这个效果主要原理是:浏览器把我们的页面做了缓存,我们可以把这个缓存清理掉:新开个页面输入"chrome://serviceworker-internals" 然后点击 Unregister 按钮,这样缓存就被清理了,页面就不能正常访问了;

    四、Shimming (预置依赖)

    shimming 又称垫片,本身不是一种具体的使用方法,而是一种使用思路。比如 @babel/polyfill,他所解决的就是打包代码运行在低版本浏览器上时有些 api 不兼容的问题,这些行为就是 shimming;

    1、预置全局变量

    比如我们想将 lodash 暴露成一个全局变量,需要借助 webpack 提供的 providePlugin 来实现,无需安装,因为是 webpack 内部提供的;

    const webpack = require('webpack')
    module.exports={
    	plugins:[
    		new webpack.providePlugin({
    			_:lodash
    		})
    	]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这样,lodash 就是一个全局的变量了;

    2、改变 this 指向

    如果一些模块依赖的 this 指向的是 window,当它运行在 CommonJS 上下文中的时候,这就会出现问题,因为此时的 this 指向的是 module.exports ;这个时候我们需要通过 imports-loader 来改变 this;
    安装

    npm i imports-loader -D
    
    • 1

    配置

    module:{
    	rules:[
    		{
    			test:require.resolve('./src/index.js'),
    			use:"imports-loader?wrapper=window"
    		}
    	]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这里的 test 需要加载某一个文件,所以借助 require.resolve 方法;然后 imports-loader 后面问号拼接一个 wrapper=window ,这是将包里面的 this 指向 window;

    3、全局导出一个模块(exports)

    将某些不支持模块化规范的模块所声明的全局变量、局部变量作为模块内容导出;
    安装

    npm i exports-loader -D
    
    • 1

    配置

    module:{
    	rules:[
    		{
    			test:require.resolve('./src/index.js'),
    			loader:'exports-loader',
    			options:{
    				type:'commonjs',
    				exports:["hello", "multiple help.parse parse"]
    			}
    		}
    	]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    需要导出的模块

    const hello = "hello webpack"
    const help = {
    	parse:function(){
    		console.log("i need help")
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    type:模块导出的类型,我们指定的是 CommonJS
    exports:导出的变量,我们导出的是 hello,multiple 表示导出局部变量
    参数设置:参考 exports-loader

    4、Polyfils

    polyfill(polyfiller),指的是一个代码块;这个代码块向开发者提供了一种技术, 这种技术可以让浏览器提供原生支持,抹平不同浏览器对 API 兼容性的差异;你只需要把需要的Polyfill引入到你的程序里,就可以正常使用了;

    4.1、加载 polyfiles

    安装

    npm i @babel/polyfile -D
    
    • 1

    引入

    import "@babel/polyfile"
    
    • 1
    4.2、优化 polyfiles

    如果在全局引入整个 polyfiles 包,转化之后打包的体积会比较大,而且还会污染全局环境;所以我们可以通过 babel-preset-env 包通过 browserslist 来指定那些浏览器、那些浏览器的版本进行转译;
    我们要使用 babel 所以要先安装 babel 相关的 loader:

    npm i babel-loader @babel/core @babel/preset-env core-js@3 -D
    
    • 1

    配置

    module:{
    	rules:[
    		test:/\.js$/,
    		exclude:/node_modules/,
    		use:{
    			loader:'babel-loader',
    			options:{
    				presets:[
    					[
    						"@babel/preset-env",
    						targets:[
    							"last 1 version",
    							"> 1%"
    						],
    						useBuiltIns:'usage',
    						corejs:3
    					]
    				]
    			}
    		}
    	]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    配置完成之后,我们就不需要在手动引入 @babel/polyfile 包了;同时如果已经引入了,需要删除掉;

  • 相关阅读:
    消息队列—RabbitMQ如何保证消息可靠性?
    目录自动清洗
    利用pybind11在python中使用C++
    数据结构——图的遍历
    Chrome Thems 介绍
    Verilog 避免 Latch
    java的面向对象基础(6)——高级泛型
    Helm安装Kafka集群(保姆级教程)
    【C语言】【牛客刷题】【BC69】 空心正方形图案
    网络安全:个人信息保护,企业信息安全,国家网络安全的重要性
  • 原文地址:https://blog.csdn.net/weixin_43299180/article/details/126008912