1. webpack介绍
- 本质上,webpack 是一个用于现代 JavaScript 应用程序的静态模块打包工具。当 webpack 处理应用程序时,它会在内部构建一个 依赖图(dependency graph),此依赖图对应映射到项目所需的每个模块,并生成一个或多个 bundle
1.1 安装
npm install webpack webpack-cli --save-dev
1.2 入口(entry)
- 入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的
- 默认值是
./src/index.js
,但你可以通过在webpack configuration
中配置entry
属性,来指定一个(或多个)不同的入口起点
1.2.1 src\index.js
let title = require('./title.txt');
document.write(title.default);
1.2.2 webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
};
1.3 输出(output)
-
output
属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件 - 主要输出文件的默认值是
./dist/main.js
,其他生成文件默认放置在./dist
文件夹中。
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
+ output: {
+ path: path.resolve(__dirname, 'dist'),
+ filename: 'main.js'
+ }
};
1.4 loader
- webpack 只能理解
JavaScript
和JSON
文件 - loader 让
webpack
能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中
webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
devtool:false,
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
},
+ module: {
+ rules: [
+ { test: /\.txt$/, use: 'raw-loader' }
+ ]
+ }
};
1.5 插件(plugin)
- loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量
1.5.1 src\index.html
src\index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webpack5</title>
</head>
<body>
</body>
</html>
1.5.2 webpack.config.js
const path = require('path');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
devtool:false,
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
},
+ plugins: [
+ new HtmlWebpackPlugin({template: './src/index.html'})
+ ]
};
1.6 模式(mode)
- 日常的前端开发工作中,一般都会有两套构建环境
- 一套开发时使用,构建结果用于本地开发调试,不进行代码压缩,打印 debug 信息,包含 sourcemap 文件
- 一套构建后的结果是直接应用于线上的,即代码都是压缩后,运行时不打印 debug 信息,静态文件不包括 sourcemap
- webpack 4.x 版本引入了 mode 的概念
- 当你指定使用 production mode 时,默认会启用各种性能优化的功能,包括构建结果优化以及 webpack 运行性能优化
- 而如果是 development mode 的话,则会开启 debug 工具,运行时打印详细的错误信息,以及更加快速的增量编译构建
选项 | 描述 |
---|---|
development | 会将 process.env.NODE_ENV 的值设为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin |
production | 会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin |
1.6.1 环境差异
- 开发环境
- 需要生成 sourcemap 文件
- 需要打印 debug 信息
- 需要 live reload 或者 hot reload 的功能
- 生产环境
- 可能需要分离 CSS 成单独的文件,以便多个页面共享同一个 CSS 文件
- 需要压缩 HTML/CSS/JS 代码
- 需要压缩图片
- 其默认值为 production
1.6.2 区分环境
-
--mode
用来设置模块内的process.env.NODE_ENV
-
--env
用来设置webpack配置文件的函数参数 -
cross-env
用来设置node环境的process.env.NODE_ENV
-
DefinePlugin
用来设置模块内的全局变量
1.6.2.1 命令行配置1
- webpack的mode默认为
production
-
webpack serve
的mode默认为development
- 可以在模块内通过
process.env.NODE_ENV
获取当前的环境变量,无法在webpack配置文件中获取此变量
"scripts": {
"build": "webpack",
"start": "webpack serve"
},
index.js
console.log(process.env.NODE_ENV);// development | production
webpack.config.js
console.log('NODE_ENV',process.env.NODE_ENV);// undefined
1.6.2.2 命令行配置2
-
同配置1
"scripts": { "build": "webpack --mode=production", "start": "webpack --mode=development serve" },
1.6.2.3 命令行配置
- 无法在模块内通过
process.env.NODE_ENV
访问 - 可以通过
webpack 配置文件中
中通过函数获取当前环境变量
"scripts": {
"dev": "webpack serve --env=development",
"build": "webpack --env=production",
}
index.js
console.log(process.env.NODE_ENV);// undefined
webpack.config.js
console.log('NODE_ENV',process.env.NODE_ENV);// undefined
module.exports = (env,argv) => {
console.log('env',env);// development | production
};
1.6.2.4 mode配置
-
和命令行配置2一样
module.exports = { mode: 'development' }
1.6.2.5 DefinePlugin
- 设置全局变量(不是
window
),所有模块都能读取到该变量的值 - 可以在任意模块内通过
process.env.NODE_ENV
获取当前的环境变量 - 但无法在
node环境
(webpack 配置文件中)下获取当前的环境变量
plugins:[
new webpack.DefinePlugin({
'process.env.NODE_ENV':JSON.stringify('development'),
'NODE_ENV':JSON.stringify('production'),
})
]
index.js
console.log(NODE_ENV);// production
webpack.config.js
console.log('process.env.NODE_ENV',process.env.NODE_ENV);// undefined
console.log('NODE_ENV',NODE_ENV);// error !!!
1.6.2.6 cross-env
- 只能设置
node环境
下的变量NODE_ENV
package.json
"scripts": {
"build": "cross-env NODE_ENV=development webpack"
}
webpack.config.js
console.log('process.env.NODE_ENV',process.env.NODE_ENV);// development
2. 开发环境配置
2.1 开发服务器
2.1.1 安装服务器
npm install webpack-dev-server --save-dev
2.1.2 webpack.config.js
类别 | 配置名称 | 描述 |
---|---|---|
output | path | 指定输出到硬盘上的目录 |
output | publicPath | 表示的是打包生成的index.html文件里面引用资源的前缀 |
devServer | publicPath | 表示的是打包生成的静态文件所在的位置(若是devServer里面的publicPath没有设置,则会认为是output里面设置的publicPath的值) |
devServer | contentBase | 用于配置提供额外静态文件内容的目录 |
2.1.3 webpack.config.js
module.exports = {
devServer: {
contentBase: path.resolve(__dirname, 'dist'),
compress: true,
port: 8080,
open: true
},
}
2.1.4 package.json
"scripts": {
"build": "webpack",
+ "start": "webpack serve"
}
2.2. 支持CSS
- css-loader用来翻译处理@import和url()
- style-loader可以把CSS插入DOM中
2.2.1 安装模块
cnpm i style-loader css-loader -D
{
test:/\.css$/,
//最后一个loader,就上面最左边的loader一定要返回一个JS脚本
use:['style-loader',{
loader:'css-loader',
options:{importLoaders:1}
},'postcss-loader']
},
#less-container{
transform:rotate(7deg);
}
2.2.2 webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
devtool:false,
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' },
+ { test: /\.css$/, use: ['style-loader','css-loader'] }
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
2.2.3 src\bg.css
src\bg.css
body{
background-color: green;
}
2.2.4 src\index.css
src\index.css
@import "./bg.css";
body{
color:red;
}
2.2.5 src\index.js
src\index.js
+import './index.css';
let title = require('./title.txt');
document.write(title.default);
2.3. 支持less和sass
2.3.1 安装
npm i less less-loader -D
npm i node-sass sass-loader -D
2.3.2 webpack.config.js
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
devtool: false,
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' },
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
+ { test: /\.less$/, use: ['style-loader','css-loader', 'less-loader'] },
+ { test: /\.scss$/, use: ['style-loader','css-loader', 'sass-loader'] }
]
},
plugins: [
new HtmlWebpackPlugin({ template: './src/index.html' })
]
};
2.3.3 src\index.html
src\index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webpack5</title>
</head>
<body>
+ <div id="less-container">less-container</div>
+ <div id="sass-container">sass-container</div>
</body>
</html>
2.3.4 src\index.js
src\index.js
import './index.css';
+import './less.less';
+import './sass.scss';
let title = require('./title.txt');
document.write(title.default);
2.3.5 src\less.less
src\less.less
@color:blue;
#less-container{
color:@color;
}
2.3.6 src\sass.scss
src\sass.scss
$color:orange;
#sass-container{
color:$color;
}
2.4 CSS兼容性
- 为了浏览器的兼容性,有时候我们必须加入-webkit,-ms,-o,-moz这些前缀
- Trident内核:主要代表为IE浏览器, 前缀为-ms
- Gecko内核:主要代表为Firefox, 前缀为-moz
- Presto内核:主要代表为Opera, 前缀为-o
- Webkit内核:产要代表为Chrome和Safari, 前缀为-webkit
- 伪元素
::placeholder
可以选择一个表单元素的占位文本,它允许开发者和设计师自定义占位文本的样式。
2.4.1 安装
- https://caniuse.com/
- postcss-loader可以使用PostCSS处理CSS
- postcss-preset-env把现代的CSS转换成大多数浏览器能理解的
- PostCSS Preset Env已经包含了
autoprefixer
和browsers
选项
npm i postcss-loader postcss-preset-env -D
2.4.2 postcss.config.js
postcss.config.js
let postcssPresetEnv = require('postcss-preset-env');
module.exports={
plugins:[postcssPresetEnv({
browsers: 'last 5 version'
})]
}
2.4.3 webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
devtool: false,
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' },
+ { test: /\.css$/, use: ['style-loader', 'css-loader','postcss-loader'] },
+ { test: /\.less$/, use: ['style-loader','css-loader','postcss-loader','less-loader'] },
+ { test: /\.scss$/, use: ['style-loader','css-loader','postcss-loader','sass-loader'] }
]
},
plugins: [
new HtmlWebpackPlugin({ template: './src/index.html' })
]
};
2.4.4 src\index.css
src\index.css
@import "./bg.css";
body{
color:red;
}
#logo{
width:540px;
height:258px;
background-image: url(./assets/logo.png);
background-size: cover;
}
+::placeholder {
+ color: red;
+}
2.4.5 src\index.html
src\index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webpack5</title>
</head>
<body>
<div id="less-container">less-container</div>
<div id="sass-container">sass-container</div>
<div id="logo"></div>
+ <input placeholder="请输入"/>
</body>
</html>
2.4.6 package.json
- browserslist
- browserslist-example
- .browserslistrc
{
+ "browserslist": {
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ],
+ "production": [
+ ">0.2%"
+ ]
+ }
+}
2.5 支持图片
2.5.1 安装
- file-loader 解决CSS等文件中的引入图片路径问题
-
url-loader 当图片小于
limit
的时候会把图片BASE64
编码,大于limit
参数的时候还是使用file-loader
进行拷贝
cnpm i file-loader url-loader html-loader -D
2.5.2 webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
devtool: false,
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' },
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
{ test: /\.less$/, use: ['style-loader','css-loader', 'less-loader'] },
{ test: /\.scss$/, use: ['style-loader','css-loader', 'sass-loader'] },
+ { test: /\.(jpg|png|bmp|gif|svg)$/,
+ use: [{
+ loader: 'url-loader',
+ options: {
+ esModule: false,
+ name: '[hash:10].[ext]',
+ limit: 8*1024,
+ }
+ }]
+ }
]
},
plugins: [
new HtmlWebpackPlugin({ template: './src/index.html' })
]
};
2.5.3 src\index.html
src\index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webpack5</title>
</head>
<body>
<div id="less-container">less-container</div>
<div id="sass-container">sass-container</div>
+ <div id="logo"></div>
</body>
</html>
2.5.4 src\index.js
src\index.js
import './index.css';
import './less.less';
import './sass.scss';
let title = require('./title.txt');
document.write(title.default);
+let logo=require('./assets/logo.png');
+let img=new Image();
+img.src=logo.default;
+document.body.appendChild(img);
2.6 JS兼容性处理
- Babel其实是一个编译JavaScript的平台,可以把ES6/ES7,React的JSX转义为ES5
2.6.1 @babel/preset-env
- Babel默认只转换新的最新ES语法,比如箭头函数
2.6.1.1 安装依赖
- babel-loader使用Babel和webpack转译JavaScript文件
- @babel/@babel/coreBabel编译的核心包
- babel-preset-env
- @babel/@babel/preset-reactReact插件的Babel预设
- @babel/plugin-proposal-decorators把类和对象装饰器编译成ES5
- @babel/plugin-proposal-class-properties转换静态类属性以及使用属性初始值化语法声明的属性
cnpm i babel-loader @babel/core @babel/preset-env @babel/preset-react -D
cnpm i @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D
2.6.1.2 webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'development',
devtool: false,
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
publicPath: '/'
},
module: {
rules: [
+ {
+ test: /\.jsx?$/,
+ use: {
+ loader: 'babel-loader',
+ options: {
+ presets: [["@babel/preset-env",{
+ targets: "> 0.25%, not dead",
+ }], '@babel/preset-react'],
+ plugins: [
+ ['@babel/plugin-proposal-decorators', { legacy: true }],
+ ['@babel/plugin-proposal-class-properties', { loose: true }],
+ ],
+ },
+ },
+ },
{ test: /\.txt$/, use: 'raw-loader' },
{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] },
{ test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'] },
{ test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] },
{
test: /\.(jpg|png|bmp|gif|svg)$/, use: [{
loader: 'url-loader', options: {
limit: 10,
outputPath: 'images',
publicPath: '/images'
}
}]
}
]
},
plugins: [
new HtmlWebpackPlugin({ template: './src/index.html' }),
new MiniCssExtractPlugin({
filename: 'css/[name].css'
}),
]
};
2.6.1.3 src\index.js
src\index.js
+function readonly(target,key,descriptor) {
+ descriptor.writable=false;
+}
+
+class Person{
+ @readonly PI=3.14;
+}
+let p1=new Person();
+p1.PI=3.15;
+console.log(p1)
2.6.1.4 jsconfig.json
jsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true
}
}
2.7 ESLint代码校验
2.7.1 安装
cnpm install eslint eslint-loader babel-eslint --D
2.7.2 webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'development',
devtool: false,
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
publicPath: '/'
},
module: {
rules: [
+ {
+ test: /\.jsx?$/,
+ loader: 'eslint-loader',
+ enforce: 'pre',
+ options: { fix: true },
+ exclude: /node_modules/,
+ },
{
test: /\.jsx?$/,
use: {
loader: 'babel-loader',
options: {
"presets": ["@babel/preset-env"],
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }]
]
}
},
include: path.join(__dirname, 'src'),
exclude: /node_modules/
},
{ test: /\.txt$/, use: 'raw-loader' },
{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] },
{ test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'] },
{ test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] },
{
test: /\.(jpg|png|bmp|gif|svg)$/, use: [{
loader: 'url-loader', options: {
limit: 10,
outputPath: 'images',
publicPath: '/images'
}
}]
}
]
},
plugins: [
new HtmlWebpackPlugin({ template: './src/index.html' }),
new MiniCssExtractPlugin({
filename: 'css/[name].css'
}),
]
};
2.7.3 src\index.html
src\index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webpack5</title>
</head>
<body>
+ <div id="root"></div>
</body>
</html>
2.7.4 src\index.js
src\index.js
+import React from "react";
+import ReactDOM from "react-dom";
+ReactDOM.render("hello",document.getElementById("root"));
+
+function readonly(target,key,descriptor) {
+ descriptor.writable=false;
+}
+
+class Person{
+ @readonly PI=3.14;
+}
+let p1=new Person();
+p1.PI=3.15;
2.7.5 .eslintrc.js
.eslintrc.js
module.exports = {
root: true,
parser:"babel-eslint",
//指定解析器选项
parserOptions: {
sourceType: "module",
ecmaVersion: 2015
},
//指定脚本的运行环境
env: {
browser: true,
},
// 启用的规则及其各自的错误级别
rules: {
"indent": "off",//缩进风格
"quotes": "off",//引号类型
"no-console": "error",//禁止使用console
}
}
2.7.6 airbnb
cnpm i eslint-config-airbnb eslint-loader eslint eslint-plugin-import eslint-plugin-react eslint-plugin-react-hooks and eslint-plugin-jsx-a11y -D
eslintrc.js
module.exports = {
"parser":"babel-eslint",
"extends":"airbnb",
"rules":{
"semi":"error",
"no-console":"off",
"linebreak-style":"off",
"eol-last":"off"
//"indent":["error",2]
},
"env":{
"browser":true,
"node":true
}
}
2.7.7 自动修复
- 安装vscode的eslint插件
- 配置自动修复参数
.vscode\settings.json
{
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}
2.8 sourcemap
-
sourcemap是为了解决开发代码与实际运行代码不一致时帮助我们
debug
到原始开发代码的技术 -
webpack
通过配置可以自动给我们source maps
文件,map
文件是一种对应编译文件和源文件的方法 - whyeval可以单独缓存map,重建性能更高
- source-map
2.8.1 配置项
类型 | 含义 |
---|---|
source-map | 原始代码 最好的sourcemap质量有完整的结果,但是会很慢 |
eval-source-map | 原始代码 同样道理,但是最高的质量和最低的性能 |
cheap-module-eval-source-map | 原始代码(只有行内) 同样道理,但是更高的质量和更低的性能 |
cheap-eval-source-map | 转换代码(行内) 每个模块被eval执行,并且sourcemap作为eval的一个dataurl |
eval | 生成代码 每个模块都被eval执行,并且存在@sourceURL,带eval的构建模式能cache SourceMap |
cheap-source-map | 转换代码(行内) 生成的sourcemap没有列映射,从loaders生成的sourcemap没有被使用 |
cheap-module-source-map | 原始代码(只有行内) 与上面一样除了每行特点的从loader中进行映射 |
2.8.2 关键字
- 看似配置项很多, 其实只是五个关键字eval、source-map、cheap、module和inline的任意组合
- 关键字可以任意组合,但是有顺序要求
关键字 | 含义 |
---|---|
eval | 使用eval包裹模块代码 |
source-map | 产生.map文件 |
cheap | 不包含列信息(关于列信息的解释下面会有详细介绍)也不包含loader的sourcemap |
module | 包含loader的sourcemap(比如jsx to js ,babel的sourcemap),否则无法定义源文件 |
inline | 将.map作为DataURI嵌入,不单独生成.map文件 |
2.8.3 webpack.config.js
module.exports = {
devtool: 'source-map',
devtool: 'eval-source-map',
devtool: 'cheap-module-eval-source-map',
devtool: 'cheap-eval-source-map',
devtool: 'eval',
devtool: 'cheap-source-map',
devtool: 'cheap-module-source-map',
}
2.8.4 组合规则
- [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
- source-map 单独在外部生成完整的sourcemap文件,并且在目标文件里建立关联,能提示错误代码的准确原始位置
- inline-source-map 以base64格式内联在打包后的文件中,内联构建速度更快,也能提示错误代码的准确原始位置
- hidden-source-map 会在外部生成sourcemap文件,但是在目标文件里没有建立关联,不能提示错误代码的准确原始位置
- eval-source-map 会为每一个模块生成一个单独的sourcemap文件进行内联,并使用
eval
执行 - nosources-source-map 也会在外部生成sourcemap文件,能找到源始代码位置,但源代码内容为空
- cheap-source-map 外部生成sourcemap文件,不包含列和loader的map
- cheap-module-source-map 外部生成sourcemap文件,不包含列的信息但包含loader的map
2.8.5 最佳实践
2.8.5.1 开发环境
- 我们在开发环境对sourceMap的要求是:速度快,调试更友好
- 要想速度快 推荐
eval-cheap-source-map
- 如果想调试更友好
cheap-module-source-map
- 折中的选择就是
eval-source-map
2.8.5.2 生产环境
- 首先排除内联,因为一方面我们了隐藏源代码,另一方面要减少文件体积
- 要想调试友好
sourcemap>cheap-source-map/cheap-module-source-map>hidden-source-map/nosources-sourcemap
- 要想速度快 优先选择
cheap
- 折中的选择就是
hidden-source-map
2.8.6 调试代码
2.8.6.1 测试环境调试
-
source-map-dev-tool-plugin实现了对 source map 生成,进行更细粒度的控制
- filename (string):定义生成的 source map 的名称(如果没有值将会变成 inlined)。
- append (string):在原始资源后追加给定值。通常是 #sourceMappingURL 注释。[url] 被替换成 source map 文件的 URL
- 市面上流行两种形式的文件指定,分别是以
@
和#
符号开头的,@
开头的已经被废弃
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
+const FileManagerPlugin = require('filemanager-webpack-plugin');
+const webpack = require('webpack');
module.exports = {
mode: 'none',
devtool: false,
entry: './src/index.js',
optimization: {
minimize: true,
minimizer: [
new TerserPlugin(),
],
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
publicPath: '/',
},
devServer: {
contentBase: path.resolve(__dirname, 'dist'),
compress: true,
port: 8080,
open: true,
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'eslint-loader',
enforce: 'pre',
options: { fix: true },
exclude: /node_modules/,
},
{
test: /\.jsx?$/,
use: {
loader: 'babel-loader',
options: {
presets: [[
'@babel/preset-env',
{
useBuiltIns: 'usage', // 按需要加载polyfill
corejs: {
version: 3, // 指定core-js版本
},
targets: { // 指定要兼容到哪些版本的浏览器
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17',
},
},
], '@babel/preset-react'],
plugins: [
['@babel/plugin-proposal-decorators', { legacy: true }],
['@babel/plugin-proposal-class-properties', { loose: true }],
],
},
},
include: path.join(__dirname, 'src'),
exclude: /node_modules/,
},
{ test: /\.txt$/, use: 'raw-loader' },
{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] },
{ test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader'] },
{ test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'] },
{
test: /\.(jpg|png|bmp|gif|svg)$/,
use: [{
loader: 'url-loader',
options: {
esModule: false,
name: '[hash:10].[ext]',
limit: 8 * 1024,
outputPath: 'images',
publicPath: '/images',
},
}],
},
{
test: /\.html$/,
loader: 'html-loader',
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseWhitespace: true,
removeComments: true,
},
}),
new MiniCssExtractPlugin({
filename: 'css/[name].css',
}),
new OptimizeCssAssetsWebpackPlugin(),
+ new webpack.SourceMapDevToolPlugin({
+ append: '\n//# sourceMappingURL=http://127.0.0.1:8081/[url]',
+ filename: '[file].map',
+ }),
+ new FileManagerPlugin({
+ events: {
+ onEnd: {
+ copy: [{
+ source: './dist/*.map',
+ destination: 'C:/aprepare/zhufengwebpack2021/1.basic/sourcemap',
+ }],
+ delete: ['./dist/*.map'],
+ },
+ },
+ }),
],
};
2.8.6.2 生产环境调试
- webpack打包仍然生成sourceMap,但是将map文件挑出放到本地服务器,将不含有map文件的部署到服务器
2.9 打包第三方类库
2.9.1 直接引入
import _ from 'lodash';
alert(_.join(['a','b','c'],'@'));
2.9.2 插件引入
- webpack配置ProvidePlugin后,在使用时将不再需要import和require进行引入,直接使用即可
- _ 函数会自动添加到当前模块的上下文,无需显示声明
+ new webpack.ProvidePlugin({
+ _:'lodash'
+ })
没有全局的
$
函数,所以导入依赖全局变量的插件依旧会失败
2.9.3 expose-loader
- expose-loader可以把模块添加到全局对象上,在调试的时候比较有用
- The expose loader adds modules to the global object. This is useful for debugging
- 不需要任何其他的插件配合,只要将下面的代码添加到所有的
loader
之前
module: {
rules: [
+ {
+ test: require.resolve('lodash'),
+ loader: 'expose-loader',
+ options: {
+ exposes: {
+ globalName: '_',
+ override: true,
+ },
+ },
+ }
]
}
2.9.4 externals
如果我们想引用一个库,但是又不想让webpack打包,并且又不影响我们在程序中以CMD、AMD或者window/global全局等方式进行使用,那就可以通过配置externals
const jQuery = require("jquery");
import jQuery from 'jquery';
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
+externals: {
+ lodash: '_',
+},
module: {
2.10 watch
-
当代码发生修改后可以自动重新编译
module.exports = { //默认false,也就是不开启 watch:true, //只有开启监听模式时,watchOptions才有意义 watchOptions:{ //默认为空,不监听的文件或者文件夹,支持正则匹配 ignored:/node_modules/, //监听到变化发生后会等300ms再去执行,默认300ms aggregateTimeout:300, //判断文件是否发生变化是通过不停的询问文件系统指定议是有变化实现的,默认每秒问1000次 poll:1000 } }
webpack定时获取文件的更新时间,并跟上次保存的时间进行比对,不一致就表示发生了变化,poll就用来配置每秒问多少次
当检测文件不再发生变化,会先缓存起来,等待一段时间后之后再通知监听者,这个等待时间通过
aggregateTimeout
配置webpack只会监听entry依赖的文件
我们需要尽可能减少需要监听的文件数量和检查频率,当然频率的降低会导致灵敏度下降
2.11 添加商标
+ new webpack.BannerPlugin('珠峰架构'),
2.12 拷贝静态文件
- copy-webpack-plugin可以拷贝源文件到目标目录
npm i copy-webpack-plugin -D
+const CopyWebpackPlugin = require('copy-webpack-plugin');
+new CopyWebpackPlugin({
+ patterns: [{
+ from: path.resolve(__dirname,'src/static'),//静态资源目录源地址
+ to: path.resolve(__dirname,'dist/static'), //目标地址,相对于output的path目录
+ }],
+}),
2.13 clean-webpack-plugin
- clean-webpack-plugin可以打包前先清空输出目录
npm i clean-webpack-plugin -D
+ const {CleanWebpackPlugin} = require('clean-webpack-plugin');
plugins:[
+ new CleanWebpackPlugin({cleanOnceBeforeBuildPatterns: ['**/*'],})
]
2.14 服务器代理
如果你有单独的后端开发服务器 API,并且希望在同域名下发送 API 请求 ,那么代理某些 URL 会很有用。
2.14.1 不修改路径
- 请求到 /api/users 现在会被代理到请求 http://localhost:3000/api/users。
devServer: {
proxy: {
"/api": 'http://localhost:3000'
}
}
2.14.2 修改路径
devServer: {
proxy: {
"/api": {
target: 'http://localhost:3000',
pathRewrite:{"^/api":""}
}
}
}
2.14.3 before after
before 在 webpack-dev-server 静态资源中间件处理之前,可以用于拦截部分请求返回特定内容,或者实现简单的数据 mock。
devServer: {
before(app){
app.get('/api/users', function(req, res) {
res.json([{id:1,name:'zhufeng'}])
})
}
}
2.14.4 webpack-dev-middleware
webpack-dev-middleware就是在 Express 中提供 webpack-dev-server
静态服务能力的一个中间件
npm install webpack-dev-middleware --save-dev
const express = require('express');
const app = express();
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackOptions = require('./webpack.config');
webpackOptions.mode = 'development';
const compiler = webpack(webpackOptions);
app.use(webpackDevMiddleware(compiler, {}));
app.listen(3000);
- webpack-dev-server 的好处是相对简单,直接安装依赖后执行命令即可
- 而使用
webpack-dev-middleware
的好处是可以在既有的 Express 代码基础上快速添加 webpack-dev-server 的功能,同时利用 Express 来根据需要添加更多的功能,如 mock 服务、代理 API 请求等
3.生产环境
3.1 提取CSS
- 因为CSS的下载和JS可以并行,当一个HTML文件很大的时候,我们可以把CSS单独提取出来加载
3.1.1 安装
cnpm install --save-dev mini-css-extract-plugin
3.1.2 webpack.config.js
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'development',
devtool: false,
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
+ publicPath: '/'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' },
+ { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] },
+ { test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'] },
+ { test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] },
{ test: /\.(jpg|png|bmp|gif|svg)$/,
use: [{
loader: 'url-loader',
options: {
esModule: false,
name: '[hash:10].[ext]',
limit: 8*1024
}
}]
}
]
},
plugins: [
new HtmlWebpackPlugin({ template: './src/index.html' }),
+ new MiniCssExtractPlugin({
+ filename: '[name].css'
+ })
]
};
3.2 指定图片和CSS目录
3.2.1 webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'development',
devtool: false,
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
publicPath: '/'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' },
{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] },
{ test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'] },
{ test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] },
{ test: /\.(jpg|png|bmp|gif|svg)$/,
use: [{
loader: 'url-loader',
options: {
esModule: false,
name: '[hash:10].[ext]',
limit: 8*1024,
+ outputPath: 'images',
+ publicPath: '/images'
}
}]
}
]
},
plugins: [
new HtmlWebpackPlugin({ template: './src/index.html' }),
new MiniCssExtractPlugin({
+ filename: 'css/[name].css'
}),
]
};
3.3 hash、chunkhash和contenthash
-
文件指纹
是指打包后输出的文件名和后缀 - hash一般是结合CDN缓存来使用,通过webpack构建之后,生成对应文件名自动带上对应的MD5值。如果文件内容改变的话,那么对应文件哈希值也会改变,对应的HTML引用的URL地址也会改变,触发CDN服务器从源服务器上拉取对应数据,进而更新本地缓存。
指纹占位符
占位符名称 | 含义 |
---|---|
ext | 资源后缀名 |
name | 文件名称 |
path | 文件的相对路径 |
folder | 文件所在的文件夹 |
hash | 每次webpack构建时生成一个唯一的hash值 |
chunkhash | 根据chunk生成hash值,来源于同一个chunk,则hash值就一样 |
contenthash | 根据内容生成hash值,文件内容相同hash值就相同 |
3.3.1 hash
function createHash(){
return require('crypto').createHash('md5');
}
let entry = {
entry1:'entry1',
entry2:'entry2'
}
let entry1 = 'require depModule1';//模块entry1
let entry2 = 'require depModule2';//模块entry2
let depModule1 = 'depModule1';//模块depModule1
let depModule2 = 'depModule2';//模块depModule2
//如果都使用hash的话,因为这是工程级别的,即每次修改任何一个文件,所有文件名的hash至都将改变。所以一旦修改了任何一个文件,整个项目的文件缓存都将失效
let hash = createHash()
.update(entry1)
.update(entry2)
.update(depModule1)
.update(depModule2)
.digest('hex');
console.log('hash',hash)
//chunkhash根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。
//在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,接着我们采用chunkhash的方式生成哈希值,那么只要我们不改动公共库的代码,就可以保证其哈希值不会受影响
let entry1ChunkHash = createHash()
.update(entry1)
.update(depModule1).digest('hex');;
console.log('entry1ChunkHash',entry1ChunkHash);
let entry2ChunkHash = createHash()
.update(entry2)
.update(depModule2).digest('hex');;
console.log('entry2ChunkHash',entry2ChunkHash);
let entry1File = entry1+depModule1;
let entry1ContentHash = createHash()
.update(entry1File).digest('hex');;
console.log('entry1ContentHash',entry1ContentHash);
let entry2File = entry2+depModule2;
let entry2ContentHash = createHash()
.update(entry2File).digest('hex');;
console.log('entry2ContentHash',entry2ContentHash);
3.3.2 hash
- Hash 是整个项目的hash值,其根据每次编译内容计算得到,每次编译之后都会生成新的hash,即修改任何文件都会导致所有文件的hash发生改变
module.exports = {
+ entry: {
+ main: './src/index.js',
+ vender:['lodash']
+ },
output:{
path:path.resolve(__dirname,'dist'),
+ filename:'[name].[hash].js'
},
plugins: [
new MiniCssExtractPlugin({
+ filename: "css/[name].[hash].css"
})
]
};
3.3.2 chunkhash
- chunkhash 采用hash计算的话,每一次构建后生成的哈希值都不一样,即使文件内容压根没有改变。这样子是没办法实现缓存效果,我们需要换另一种哈希值计算方式,即chunkhash
- chunkhash和hash不一样,它根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。我们在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,接着我们采用chunkhash的方式生成哈希值,那么只要我们不改动公共库的代码,就可以保证其哈希值不会受影响
module.exports = {
entry: {
main: './src/index.js',
vender:['lodash']
},
output:{
path:path.resolve(__dirname,'dist'),
+ filename:'[name].[chunkhash].js'
},
plugins: [
new MiniCssExtractPlugin({
+ filename: "css/[name].[chunkhash].css"
})
]
};
3.3.3 contenthash
- 使用chunkhash存在一个问题,就是当在一个JS文件中引入CSS文件,编译后它们的hash是相同的,而且只要js文件发生改变 ,关联的css文件hash也会改变,这个时候可以使用
mini-css-extract-plugin
里的contenthash
值,保证即使css文件所处的模块里就算其他文件内容改变,只要css文件内容不变,那么不会重复构建
module.exports = {
plugins: [
new MiniCssExtractPlugin({
+ filename: "css/[name].[contenthash].css"
})
],
};
3.4 压缩JS、CSS和HTML
- optimize-css-assets-webpack-plugin是一个优化和压缩CSS资源的插件
- terser-webpack-plugin是一个优化和压缩JS资源的插件
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
+const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
+ mode: 'none',
devtool: false,
entry: './src/index.js',
+ optimization: {
+ minimize: true,
+ minimizer: [
+ new TerserPlugin(),
+ ],
+ },
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
publicPath: '/',
},
devServer: {
contentBase: path.resolve(__dirname, 'dist'),
compress: true,
port: 8080,
open: true,
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'eslint-loader',
enforce: 'pre',
options: { fix: true },
exclude: /node_modules/,
},
{
test: /\.jsx?$/,
use: {
loader: 'babel-loader',
options: {
presets: [[
'@babel/preset-env',
{
useBuiltIns: 'usage'
corejs: {
version: 3
},
targets: {
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17',
},
},
], '@babel/preset-react'],
plugins: [
['@babel/plugin-proposal-decorators', { legacy: true }],
['@babel/plugin-proposal-class-properties', { loose: true }],
],
},
},
include: path.join(__dirname, 'src'),
exclude: /node_modules/,
},
{ test: /\.txt$/, use: 'raw-loader' },
{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] },
{ test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader'] },
{ test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'] },
{
test: /\.(jpg|png|bmp|gif|svg)$/,
use: [{
loader: 'url-loader',
options: {
esModule: false,
name: '[hash:10].[ext]',
limit: 8 * 1024,
outputPath: 'images',
publicPath: '/images',
},
}],
},
{
test: /\.html$/,
loader: 'html-loader',
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
+ minify: {
+ collapseWhitespace: true,
+ removeComments: true
}
}),
new MiniCssExtractPlugin({
filename: 'css/[name].css',
}),
+ new OptimizeCssAssetsWebpackPlugin(),
],
};
3.5 图片压缩
- image-webpack-loader可以帮助我们对图片进行压缩和优化
npm install image-webpack-loader --save-dev
{
test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
use: [
'url-loader',
+ {
+ loader: 'image-webpack-loader',
+ options: {
+ mozjpeg: {
+ progressive: true,
+ quality: 65
+ },
+ optipng: {
+ enabled: false,
+ },
+ pngquant: {
+ quality: '65-90',
+ speed: 4
+ },
+ gifsicle: {
+ interlaced: false,
+ },
+ webp: {
+ quality: 75
+ }
+ }
+ }
]
}
3.6 px 自动转成rem
- lib-flexible + rem,实现移动端自适应
- px2rem-loader自动将px转换为rem
- px2rem
- 页面渲染时计算根元素的
font-size
值
3.6.1 安装
cnpm i px2rem-loader lib-flexible -D
3.6.2 index.html
index.html
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>主页</title>
<script>
let docEle = document.documentElement;
function setRemUnit () {
//750/10=75 375/10=37.5
docEle.style.fontSize = docEle.clientWidth / 10 + 'px';
}
setRemUnit();
window.addEventListener('resize', setRemUnit);
</script>
</head>
<body>
<div id="root"></div>
</body>
3.6.3 reset.css
src/reset.css
*{
padding: 0;
margin: 0;
}
#root{
width:750px;
height:750px;
border:1px solid red;
box-sizing: border-box;
}
3.6.4 webpack.config.js
{
test:/\.css$/,
use:[{
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
{
+ loader:'px2rem-loader',
+ options:{
+ remUnit:75,
+ remPrecesion:8
+ }
+ }]
+ },
4. polyfill
@babel/preset-env
会根据预设的浏览器兼容列表从stage-4选取必须的plugin,也就是说,不引入别的stage-x,@babel/preset-env将只支持到stage-4-
三个概念
最新ES 语法:比如,箭头函数
最新ES API:,比如,Promise
-
最新ES 实例方法:比如,String.prototype.includes
4.1 babel-polyfill
Babel默认只转换新的javascript语法,而不转换新的API,比如 Iterator, Generator, Set, Maps, Proxy, Reflect,Symbol,Promise 等全局对象。以及一些在全局对象上的方法(比如 Object.assign)都不会转码。
比如说,ES6在Array对象上新增了
Array.form
方法,Babel就不会转码这个方法,如果想让这个方法运行,必须使用babel-polyfill
来转换等babel-polyfill
它是通过向全局对象和内置对象的prototype上添加方法来实现的。比如运行环境中不支持Array.prototype.find
方法,引入polyfill
, 我们就可以使用es6
方法来编写了,但是缺点就是会造成全局空间污染@babel/@babel/preset-env为每一个环境的预设
@babel/preset-env默认支持语法转化,需要开启
useBuiltIns
配置才能转化API和实例方法useBuiltIns
可选值包括:"usage" | "entry" | false, 默认为 false,表示不对 polyfills 处理,这个配置是引入 polyfills 的关键
4.1.1 安装
npm i @babel/polyfill
4.1.2 "useBuiltIns": false
- "useBuiltIns": false 此时不对 polyfill 做操作。如果引入
@babel/polyfill
,则无视配置的浏览器兼容,引入所有的polyfill
86.4 KiB
import '@babel/polyfill';
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [["@babel/preset-env", {
+ useBuiltIns: false,
}], "@babel/preset-react"],
plugins: [
["@babel/plugin-proposal-decorators", { legacy: true }],
["@babel/plugin-proposal-class-properties", { loose: true }]
]
}
}
},
4.1.3 "useBuiltIns": "entry"
- 在项目入口引入一次(多次引入会报错)
- "useBuiltIns": "entry" 根据配置的浏览器兼容,引入浏览器不兼容的 polyfill。需要在入口文件手动添加
import '@babel/polyfill'
,会自动根据 browserslist 替换成浏览器不兼容的所有 polyfill - 这里需要指定 core-js 的版本, 如果 "corejs": 3, 则 import '@babel/polyfill' 需要改成
import 'core-js/stable';import 'regenerator-runtime/runtime';
-
corejs
默认是2,配置2的话需要单独安装core-js@3
-
80.6 KiB
10.7 KiB
import '@babel/polyfill';
npm i core-js@3
import 'core-js/stable';
import 'regenerator-runtime/runtime';
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [["@babel/preset-env", {
+ useBuiltIns: 'entry',
+ corejs: { version: 2 }
}], "@babel/preset-react"],
plugins: [
["@babel/plugin-proposal-decorators", { legacy: true }],
["@babel/plugin-proposal-class-properties", { loose: true }]
]
}
}
},
{
"browserslist": {
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production": [
">1%"
]
},
}
4.1.4 "useBuiltIns": "usage"
- "useBuiltIns": "usage"
usage
会根据配置的浏览器兼容,以及你代码中用到的 API 来进行 polyfill,实现了按需添加 - 当设置为usage时,polyfills会自动按需添加,不再需要手工引入
@babel/polyfill
- usage 的行为类似
babel-transform-runtime
,不会造成全局污染,因此也会不会对类似Array.prototype.includes()
进行 polyfill 0 bytes
8.98 KiB
import '@babel/polyfill';
console.log(Array.from([]));
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [["@babel/preset-env", {
+ useBuiltIns: 'usage',
+ corejs: { version: 3 }
}], "@babel/preset-react"],
plugins: [
["@babel/plugin-proposal-decorators", { legacy: true }],
["@babel/plugin-proposal-class-properties", { loose: true }]
]
}
}
},
4.2 babel-runtime
- Babel为了解决全局空间污染的问题,提供了单独的包babel-runtime用以提供编译模块的工具函数
- 简单说
babel-runtime
更像是一种按需加载的实现,比如你哪里需要使用 Promise,只要在这个文件头部import Promise from 'babel-runtime/core-js/promise'
就行了
npm i babel-runtime -D
import Promise from 'babel-runtime/core-js/promise';
const p = new Promise(()=> {
});
console.log(p);
4.3 babel-plugin-transform-runtime
- @babel/plugin-transform-runtime插件是为了解决
- 多个文件重复引用相同helpers(帮助函数)-> 提取运行时
- 新API方法全局污染 -> 局部引入
- 启用插件
babel-plugin-transform-runtime
后,Babel就会使用babel-runtime
下的工具函数 -
babel-plugin-transform-runtime
插件能够将这些工具函数的代码转换成require
语句,指向为对babel-runtime
的引用 -
babel-plugin-transform-runtime
就是可以在我们使用新 API 时自动import babel-runtime
里面的polyfill
- 当我们使用
async/await
时,自动引入babel-runtime/regenerator
- 当我们使用 ES6 的静态事件或内置对象时,自动引入
babel-runtime/core-js
- 移除内联
babel helpers
并替换使用babel-runtime/helpers
来替换
- 当我们使用
-
corejs
默认是3,配置2的话需要单独安装@babel/runtime-corejs2
npm i @babel/runtime-corejs2 -D
{
test: /\.jsx?$/,
use: {
loader: 'babel-loader',
options: {
presets: ["@babel/preset-env",'@babel/preset-react'],
plugins: [
+ [
+ "@babel/plugin-transform-runtime",
+ {
+ corejs: 2,//当我们使用 ES6 的静态事件或内置对象时自动引入 babel-runtime/core-js
+ helpers: true,//移除内联babel helpers并替换使用babel-runtime/helpers 来替换
+ regenerator: true,//是否开启generator函数转换成使用regenerator runtime来避免污染全局域
+ },
+ ],
['@babel/plugin-proposal-decorators', { legacy: true }],
['@babel/plugin-proposal-class-properties', { loose: true }],
],
},
},
},
corejs: 2 corejs 2=>false 131 KiB => 224 bytes
const p = new Promise(()=> {});
console.log(p);
helpers true=>false 160 KiB=>150 KiB
class A {
}
class B extends A {
}
console.log(new B());
regenerator false=>true B490 bytes->28.6 Ki
function* gen() {
}
console.log(gen());
4.4 最佳实践
-
babel-runtime
适合在组件和类库项目中使用,而babel-polyfill
适合在业务项目中使用。
4.5 polyfill-service
- polyfill.io自动化的 JavaScript Polyfill 服务
- polyfill.io通过分析请求头信息中的 UserAgent 实现自动加载浏览器所需的 polyfills
<script src="https://polyfill.io/v3/polyfill.min.js"></script>