装置与启动

Webpack 5 公布曾经有一段时间了,很多小伙伴都在思考要不要降级,有没有降级的必要,不晓得降级后有哪些扭转;
明天咱们就来做个比照看看,webpack5 带来了那些全新的扭转;
没有比照就没有挫伤,为了更好地挫伤 webpack 4 , 咱们应用 webpack4 和 webpack 5 别离构建一个 React 我的项目来做比照:

mkdir webpack4 mkdir webpack5 # 别离执行 初始化命令 npm init -y 

创立文件 /src/index.js, /src/App.js, /src/index.html

React 代码示例
index.js

import React from "react" import ReactDom from "react-dom" import App from "./App" ReactDom.render(,document.getElementById('root'))

App.js

index.html

<!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

// 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 的版本号;

webpack5

// 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.js

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
配置服务器:

  //  服务器  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 之前,通常应用:

  • raw-loader 将文件导入为字符串
  • url-loader 将文件作为 data URI 内联到 bundle 中
  • file-loader 将文件发送到输入目录

资源模块类型(asset module type),通过增加 4 种新的模块类型,来替换所有这些 loader:

  • asset/resource 发送一个独自的文件并导出 URL(之前通过应用 file-loader 实现)
  • asset/inline 导出一个资源的 data UR(之前通过应用 url-loader 实现)
  • asset/source 导出资源的源代码(之前通过应用 raw-loader 实现)
  • asset 在导出一个 data URI 和发送一个独自的文件之间主动抉择(之前通过应用 url-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')  }

即便内容批改,增量编译的缓存成果也很显著

更好的 Tree Shaking

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 = 456export 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 的字段小伙伴们也要留神,

  • exposes 的裸露字段要写上 ./name
  • remotes 的字段跟裸露模块的 name 保持一致,外面别名的定义也要统一

最初,两个利用同时启动,就会发现最终你要的利用就把其余利用的模块也引入进来了