Webpack 5 发布已经有一段时间了,不少小伙伴都在考虑要不要升级,有没有升级的必要,不知道升级后有哪些改变;
今天咱们就来作个对比看看,webpack5 带来了那些全新的改变;
没有对比就没有伤害,为了更好地伤害 webpack 4 , 咱们使用 webpack4 和 webpack 5 分别构建一个 React 项目来作对比:css
mkdir webpack4 mkdir webpack5 # 分别执行 初始化命令 npm init -y
建立文件 /src/index.js, /src/App.js, /src/index.html html
React 代码示例
index.js前端
import React from "react" import ReactDom from "react-dom" import App from "./App" ReactDom.render(,document.getElementById('root'))
App.jsnode
index.htmlreact
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!-- 加一行注释 --> <div id="root"></div> </body> </html>
webpack4 webpack
// webpack4 npm install webpack@4 webpack-cli@3 html-webpack-plugin css-loader style-loader babel-loader @babel/core @babel/preset-env @babel/preset-react -D npm install react react-dom
由于仓库中目前默认就已是 webpack5 了,因此,想要安装 webpack4, 咱们须要加上 @4 的版本号;git
webpack5 es6
// webpack5 npm install webpack webpack-cli html-webpack-plugin css-loader style-loader babel-loader @babel/core @babel/preset-env @babel/preset-react -D npm install react react-dom
基础配置 webpack.config.jsgithub
const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { // entry 入口,output出口,module模块,plugins 插件 mode工做模式,devServer开发服务器 // mode 工做模式 mode: 'development', // production 、 development、none // 入口 entry:'./src/index.js', // 出口 output:{ filename:'./bundle.js', path:path.resolve(__dirname,'dist') }, // 模块 module:{ rules:[ { test:/\.js$/, exclude:/node_modules/, use:[ { loader:'babel-loader', options:{ presets:[ '@babel/preset-env', '@babel/preset-react' ] } } ] }, ] }, // 插件 plugins:[ new HtmlWebpackPlugin({ template:'./src/index.html' }) ] }
启动命令的区别
先安装 npm install webpack-dev-server -D
配置服务器:web
// 服务器 devServer:{ port:3004, open:true },
webpack 4 : webpack4/package.json
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack", "start": "webpack-dev-server" },
webpack 5 : webpack5/package.json
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build":"webpack", "start":"webpack serve" },
https://webpack.docschina.org/guides/asset-modules/#source-assets
资源模块(asset module)是一种模块类型,它容许使用资源文件(字体,图标等)而无需配置额外 loader。
在 webpack 5 以前,一般使用:
资源模块类型(asset module type),经过添加 4 种新的模块类型,来替换全部这些 loader:
webpack4 :
// 模块 module:{ rules:[ { test:/\.js$/, exclude:/node_modules/, use:[ ………… ] }, { test:/\.(png|jpg|gif)$/, // 安装 url-loader file-loader loader:'url-loader', options:{ // 小于 8KB 转 base64 limit:8*1024 } } ] },
webpack5 :
// 模块 module:{ rules:[ { test:/\.js$/, exclude:/node_modules/, …………………… }, { test:/\.(png|jpg|gif)$/, // 通用资源类型 type:'asset', // 如今,webpack 将按照默认条件,自动地在 resource 和 inline 之间进行选择: // 小于 8kb 的文件,将会视为 inline 模块类型,不然会被视为 resource 模块类型。 // 自定义设置 parser:{ dataUrlCondition:{ maxSize:8*1024 } } } ] },
https://webpack.docschina.org/configuration/other-options/#cache
缓存生成的 webpack 模块和 chunk,可以改善构建速度。
cache 会在 开发模式 下被设置成 type: 'memory' 并且在 生产模式 中被禁用。
cache: true 与 cache: { type: 'memory' } 配置做用一致。
cache.type
cache.type 将 cache 类型设置成内存或者文件系统。 'memory' | 'filesystem'
memory 选项很简单,它会告诉 webpack 将内容存放在内存中而且不容许额外的配置;
filesystem 选项,使用文件缓存系统;
cacheDirectory
cacheDirectory 定义缓存目录, 默认为 node_modules/.cache/webpack。
cache.cacheDirectory 选项仅当 cache.type 被设置成 filesystem 才可用。
webpack.config.js
// mode 工做模式 mode:'development', cache:{ type:'filesystem', // 默认缓存到 node_modules/.cache/webpack 中 // 也能够自定义缓存目录 // cacheDirectory:path.resolve(__dirname,'node_modules/.cac/webpack') }
即便内容修改,增量编译的缓存效果也很明显
https://webpack.docschina.org/guides/tree-shaking/
Tree Shaking 技术,也被称为 “树摇” ,没错,翻译的就是这么直接,意思也很简单,未使用的导出内容不会被打包生成;它依赖于 ES2015 模块语法的 静态结构 特性,例如 import 和 export。这个术语和概念其实是由 ES2015 模块打包工具 rollup 普及起来的。
为了更好说明这个原理,我作了一个动画,全网首发的动画效果,简单解释一下,有两个模块四个方法,在模块 x 中,使用了 B 方法和从模块Y中导入的 C 方法,而 X 模块中本身的 A 和模块 Y 中的 D 方法,并无使用,虽然定义了,由于没有在任何地方使用过,所以,在 “摇树” 过程当中,就会被 “摇掉”;
在 webpack 中如何使用呢?其实很简单,只要将 mode 工做模式改成 production 就会自动开启;
而若是想要感觉这个树摇带来的震动酥麻酸爽的过程,咱们也可使用手动配置的方式来自行选择,首先须要将 mode 工做模式改成 none,意思就是不作任何优化,所有使用配置的方式,如何配置呢?添加 optimization.usedExports 和 optimization.minimize 选项,意思就是开启树摇及压缩
// mode 工做模式 mode: 'none', // production、development、none // production 生产环境,默认优化打包 // none 不作任何操做 // usedExports:true 开启优化(树摇但保留代码) // minimize:true 开启压缩 (删除未使用代码) optimization:{ usedExports:true, minimize:true // innerGraph: true, }
接下来,咱们再使用简单代码作对比:
index.js
import * as m1 from "./m1"; console.log(m1.m2.nu1)
m1.js
import * as m2 from './m2' export function fun1(){ console.log('1--11',m2.c); } export function fun2(){ console.log('1--22') } export {m2}
m2.js
export function fun3(){ console.log('2--33'); } export function fun4(){ console.log('2--44') } export const nu1 = 456 export const nu2 = 789
相同的代码,在webpack 4 的打包结果中,咱们能看到不只代码量大,并且还有 i=789 这个多余的代码,反观 webpack 5 的打包结果,简洁到难以置信;
多个独立的构建能够组成一个应用程序,这些独立的构建之间不该该存在依赖关系,所以能够单独开发和部署它们。
这一般被称做微前端
为了更好地说明这个原理,我作了一个动画,全球首发的动画效果
导出模块
const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') // const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin") const ModuleFederationPlugin = require("webpack").container.ModuleFederationPlugin; ………… // 插件 plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }), new ModuleFederationPlugin({ // 模块名字 name: 'remote', //导入时使用名称标注 // 编译后的模块文件名,导入时使用 filename: 'remoteEntry.js', // 导出模块 关键字与模块名 exposes: { // "key导入时使用的关键字" : "对应模块文件" "./Us": './src/User.js' } }), ],
导入模块
const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const ModuleFederationPlugin = require("webpack").container.ModuleFederationPlugin; ………… // 插件 plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }), new ModuleFederationPlugin({ name:'user:55', // 导入外部模块 remotes:{ // 导入别名:关键字@地址/导出文件名 remoteHost:"remote@http://127.0.0.1:3055/remoteEntry.js" } }) ],
在 ModuleFederationPlugin 实例化的时候传入参数 options 的字段说明:
// 模块名字 name: 'remote', //导入时使用名称标注 // 编译后的模块文件名,导入时使用 filename: 'remoteEntry.js', // 导出模块 关键字与模块名 exposes: { // "key导入时使用的关键字" : "对应模块文件" "./Us": './src/User.js' } // 导入外部模块 remotes:{ // 导入别名:关键字@地址/导出文件名 remoteHost:"remote@http://127.0.0.1:3055/remoteEntry.js" }
还有就是 exposes 和 remotes 的字段小伙伴们也要注意,
最后,两个应用同时启动,就会发现最终你要的应用就把其余应用的模块也引入进来了