webpack5 javascript 代码打包、代码转化、 代码压缩、Tree Shaking、代码分割、懒加载

一、搭建 webpack 基础环境

初始化环境

npm init -y

安装环境依赖

npm install webpack webpack-cli cross-env -D

在 package.json 中修改 scripts

"scripts": {

"dev": "cross-env NODE_ENV=development webpack --config ./webpack.config.js --watch --progress --color ",

"build": "cross-env NODE_ENV=production webpack --config ./webpack.config.js --progress --color "

},

新建入口文件 src/index.js

consle.log("hello world");

创建 webpack.config.js 配置文件

var path = require("path");

module.exports = {

entry: {

app: "./src/index.js",

},

output: {

path: path.resolve(__dirname, "dist"),

filename: "[name]-budle.js",

},

mode: process.env.NODE_ENV,

module: {

rules: [],

},

};

打包测试

npm run dev

npm run build

二、javascript 代码转化

ES6+ 转化为 ES5

安装依赖

babel-loader:babel 语法转化工具

@babel/core:babel 核心工具

@babel/preset-env:babel 预设,限定 babel 语法转化的范围,指定兼容的浏览器版本

npm install babel-loader @babel/core @babel/preset-env -D

修改 webpack.config.js 配置文件

var path = require("path");

module.exports = {

entry: {

app: "./src/index.js",

},

output: {

path: path.resolve(__dirname, "dist"),

filename: "[name]-budle.js",

},

mode: process.env.NODE_ENV,

module: {

rules: [

{

test: /\.m?js$/,

exclude: /(node_modules|bower_components)/,

use: {

loader: "babel-loader",

options: {

presets: ["@babel/preset-env"],

},

},

},

],

},

};

在 package.json 中新增浏览器兼容范围 browserslist 属性

配合 @babel/preset-env 使用

"browserslist": [

"Android 4.1",

"iOS 7.1",

"ie >= 8",

"last 8 Chrome versions",

"last 5 Firefox versions",

"Safari >= 6",

"> 1%"

]

新建入口文件 src/index.js

const list = ["a", "b", "c"];

const listA = list.map((item) => item + "A");

console.log(listA);

const array = [1, 2, 3, 4, 1, 2, 3];

const set = new Set(array);

const newArray = Array.from(set);

console.log(newArray);

打包测试

npm run build

在 dist/app-budile.js 中查看结果

实际代码是压缩的,此处经过代码格式化展开查看效果,发现只是处理了变量声明和箭头函数,但是 ES6 API 未处理

!(function () {

var o = ["a", "b", "c"].map(function (o) {

return o + "A";

});

console.log(o);

var n = new Set([1, 2, 3, 4, 1, 2, 3]),

r = Array.from(n);

console.log(r);

})();

安装垫片处理 ES6 API

@babel/plugin-transform-runtime @babel/runtime-corejs3 core-js:组合垫片,转化 babel-loader 无法转化的 ES6+ API

npm install @babel/plugin-transform-runtime @babel/runtime-corejs3 core-js -D

修改 webpack.config.js 配置文件

const path = require("path");

module.exports = {

entry: {

app: "./src/index.js",

},

output: {

path: path.resolve(__dirname, "dist"),

filename: "[name]-budle.js",

},

mode: process.env.NODE_ENV,

module: {

rules: [

{

test: /\.m?js$/,

exclude: /(node_modules|bower_components)/,

use: {

loader: "babel-loader",

options: {

presets: [

[

"@babel/preset-env",

{

useBuiltIns: "usage",

corejs: "3",

},

],

],

plugins: [

[

"@babel/plugin-transform-runtime",

{

corejs: "3",

},

],

],

},

},

},

],

},

experiments: {

topLevelAwait: true, // 开启 async await 转化,默认 false

asyncWebAssembly: true,

},

};

新建入口文件 src/index.js

const list = ["a", "b", "c"];

const listA = list.map((item) => item + "A");

console.log(listA);

const array = [1, 2, 3, 4, 1, 2, 3];

const set = new Set(array);

const newArray = Array.from(set);

console.log(newArray);

async function getdata() {

return new Promise((resolve, reject) => {

resolve("hello world");

});

}

const data = await getdata();

console.log(data);

打包测试

npm run build

在 dist/app-budile.js 中查看结果,发现代码是压缩的,且 ES6+ API 已经不存在

运行 node ./dist/app-budle.js ,发现功能正常,完成代码转化

[ 'aA', 'bA', 'cA' ]

[ 1, 2, 3, 4 ]

hello world

三、javascript 代码压缩 和 Tree Shaking

webpack5 生产环境默认支持代码压缩、公共代码整合 和 Tree Shaking,通过 optimization 选项的属性来控制

安装依赖

terser-webpack-plugin:打包时不生成 LICENSE.txt 文件

npm install terser-webpack-plugin -D

新建入口文件 src/index.js

const list = ["a", "b", "c"];

const listA = list.map((item) => item + "A");

console.log(listA);

const array = [1, 2, 3, 4, 1, 2, 3];

const set = new Set(array);

const newArray = Array.from(set);

console.log(newArray);

async function getdata() {

return new Promise((resolve, reject) => {

resolve("hello world");

});

}

const data = await getdata();

console.log(data);

const aaaaa = 123456;

修改 webpack.config.js 配置文件

const path = require("path");

const TerserPlugin = require("terser-webpack-plugin");

module.exports = {

entry: {

app: "./src/index.js",

},

output: {

path: path.resolve(__dirname, "dist"),

filename: "[name]-budle.js",

},

mode: process.env.NODE_ENV,

module: {

rules: [

{

test: /\.m?js$/,

exclude: /(node_modules|bower_components)/,

use: {

loader: "babel-loader",

options: {

presets: [

[

"@babel/preset-env",

{

useBuiltIns: "usage",

corejs: "3",

},

],

],

plugins: [

[

"@babel/plugin-transform-runtime",

{

corejs: "3",

},

],

],

},

},

},

],

},

experiments: {

topLevelAwait: true, // 开启 async await 转化,默认 false

asyncWebAssembly: true,

},

optimization: {

usedExports: true, // 配合实现 Tree Shaking,,默认 true 开启

concatenateModules: true, // 公共代码整合,生产环境下被启用

minimize: true, // 在生成环境是否开启js代码压缩和Tree Shaking,默认 true 开启

minimizer: [

new TerserPlugin({

extractComments: false, // 默认:true,当 minimize为true 打包时会生成注释文件,参数为 false 不生成注释文件

}),

],

},

};

打包测试

npm run build

在 dist/app-budile.js 中查看结果

发现代码是压缩的,无法找到 123456、说明 js 代码压缩和 Tree Shaking 生效,且不生成 app-budle.js.LICENSE.txt 文件

四、javascript 代码分割 和 懒加载

webpack 执行异步懒加载的代码,在打包时会进行代码分割

webpack 懒加载的方式有两种,一种是通过 CommonJS 规范的 require.ensure(),一种是 ESModel 规范的 import()

webpack.config.js 配置文件不变

const path = require("path");

const TerserPlugin = require("terser-webpack-plugin");

module.exports = {

entry: {

app: "./src/index.js",

},

output: {

path: path.resolve(__dirname, "dist"),

filename: "[name]-budle.js",

},

mode: process.env.NODE_ENV,

module: {

rules: [

{

test: /\.m?js$/,

exclude: /(node_modules|bower_components)/,

use: {

loader: "babel-loader",

options: {

presets: [

[

"@babel/preset-env",

{

useBuiltIns: "usage",

corejs: "3",

},

],

],

plugins: [

[

"@babel/plugin-transform-runtime",

{

corejs: "3",

},

],

],

},

},

},

],

},

experiments: {

topLevelAwait: true, // 开启 async await 转化,默认 false

asyncWebAssembly: true,

},

optimization: {

usedExports: true, // 配合实现 Tree Shaking,,默认 true 开启

concatenateModules: true, // 公共代码整合,生产环境下被启用

minimize: true, // 在生成环境是否开启js代码压缩和Tree Shaking,默认 true 开启

minimizer: [

new TerserPlugin({

extractComments: false, // 当 minimize为true 打包时会生成注释文件,此参数为 false 不生成注释文件,默认:true

}),

],

},

};

新建入口文件 src/index.js

// 同步执行,打包后不生成新 budle

import listA from "./map.js";

console.log(listA);

// require.ensure() 异步执行,打包后生成新 budle

require.ensure(

[],

function () {

const set = require("./set.js");

console.log(set.default);

},

"set"

);

// ESModel import() 异步执行,打包后生成新 budle

import(/* webpackChunkName: "async" */ "./async.js").then((data) =>

console.log(data.default)

);

const aaaaa = 123456;

console.log(aaaaa);

新建测试文件 src/async.js、src/map.js、src/set.js 代码如下

// src/async.js

async function getdata() {

return new Promise((resolve, reject) => {

resolve("async await");

});

}

const data = await getdata();

export default data;

// src/map.js

const list = ["m", "a", "p"];

const listA = list.map((item) => item + "A");

export default listA;

// src/set.js

const array = [1, 2, 3, 4, 1, 2, 3];

const set = new Set(array);

const newArray = Array.from(set);

export default newArray;

新建 src/index.html 模板文件

Document

打包

npm run build

在浏览器打开 src/index.html 查看结果

通过 Network 查看代码分割情况,通过 Console 控制台查看同步异步执行效果

['mA', 'aA', 'pA'] // 同步引入执行

123456 // 同步执行

[1, 2, 3, 4] // require.ensure 异步执行

async await // import 异步执行