Webpack
1、Webpack简介
1.1、webpack是什么
本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
官网地址: https://webpack.js.org/
中文网址: https://www.webpackjs.com/
Github: https://github.com/webpack/webpack
1.2、安装
首先使用 npm init 初始化项目,然后安装 webpack 以及 webpack-cli 。
// 全局安装 -global简写:-g
npm i webpack webpack-cli -g
// 本地安装(推荐) --save-dev简写:-D
npm i webpack webpack-cli --D
卸载命令
# 卸载全局的webpack
npm uninstall webpack webpack-cli --global
查看webpack打包的详细日志
webpack --stats detailed
1.3、配置文件
在文件根目录下新建 webpack.config.js 配置文件
// webpack.config.js
module.exports = {
entry: './assets/js/main.js',
output: {
filename: 'app.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
]
},
plugins: [
],
mode: 'development'
}
1.4、打包命令
使用本地环境进行打包输出
# 本地打包
npx webpack
# 如果是全局的打包
webpack
2、webpack五个核心概念
01、入口(entry)
入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
可以通过在 webpack 配置中配置 entry
属性,来指定一个入口起点(或多个入口起点)。默认值为 ./src
。
接下来我们看一个 entry
配置的最简单例子:
webpack.config.js
module.exports = {
entry: './path/to/my/entry/file.js'
};
02、输出(output)
output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist
。基本上,整个应用程序结构,都会被编译到你指定的输出路径的文件夹中。你可以通过在配置中指定一个 output
字段,来配置这些处理过程:
webpack.config.js
module.exports = {
...
output: {
// 输出文件名称
filename: 'app.js',
// 输出文件路径
path: path.resolve(__dirname, 'dist'),
// 删除不需要的旧文件
clean: true
}
}
03、loader
loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。
本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。
在更高层面,在 webpack 的配置中 loader 有两个目标:
test
属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。use
属性,表示进行转换时,应该使用哪个 loader。
webpack.config.js
const path = require('path');
const config = {
output: {
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
}
};
module.exports = config;
以上配置中,对一个单独的 module 对象定义了 rules
属性,里面包含两个必须属性:test
和 use
。这告诉 webpack 编译器(compiler) 如下信息:
“嘿,webpack 编译器,当你碰到「在require()
/import
语句中被解析为 '.txt' 的路径」时,在你对它打包之前,先 使用raw-loader
转换一下。”
04、插件(Plugins)
loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。
想要使用一个插件,你只需要 require()
它,然后把它添加到 plugins
数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new
操作符来创建它的一个实例。
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件
const config = {
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
module.exports = config;
05、模式(mode)
通过选择 development
或 production
之中的一个,来设置 mode
参数,你可以启用相应模式下的 webpack 内置的优化
module.exports = {
mode: 'production'
};
或者从 CLI 参数中传递:
webpack --mode=production
webpack相对应模式的配置!
选项 | 描述 | 特点 |
---|---|---|
development | 会将 process.env.NODE_ENV 的值设为 development 。启用 NamedChunksPlugin 和 NamedModulesPlugin 。 |
能让代码本地调试运行的环境 |
production | 会将 process.env.NODE_ENV 的值设为 production 。启用 FlagDependencyUsagePlugin , FlagIncludedChunksPlugin , ModuleConcatenationPlugin , NoEmitOnErrorsPlugin , OccurrenceOrderPlugin , SideEffectsFlagPlugin 和 UglifyJsPlugin . |
能让代码优化上线运行的环境 |
none |
3、devServer
在开发环境中,用于自动编译并自动刷新页面,方便开发过程中的调试。注:该功能只会在内存中编译打包,不会有任何文件输出,如需更新到生产环境中,还需重新打包代码。
下载
npm i webpack-dev-server -D
配置
在 webpack.config.js 文件中进行配置
const path = require('path')
module.exports = {
...
devServer: {
// 环境目录
static: path.resolve(__dirname,'./dist'),
// 设置 gzip 压缩,提高传输效率
compress: true,
// 设置服务器主机
host: '0.0.0.0',
// 设置端口号
port: 3000,
// 添加响应头,也可以写成函数
headers:{
'X-Access-Token': '125hythujgdtr'
},
// 这个配置会让你在重新启动服务后,自动更新代码!
devMiddleware: {
writeToDisk: true, // 写入到硬盘里!
},
// 设置路由
historyApiFallback: true,
// 自动打开页面
open: true,
// 更改后自动更新
watchFiles: {
paths: [
'./*'
],
options: {
usePolling: false
}
},
// 启用热加载功能
liveReload: true,
// 启用热模块功能
hot: true
}
开启代理
我们打包的 js bundle
里面时会含有一些对特定接口的网络请求(ajax/fetch),要注意,此时客户端地址是在 http://localhost:3000/
下,假设我们接口来自http://localhost:5000/
,那么毫无疑问,此时控制台里会报错并提示你跨域。如何解决这个问题?在开发环境中,我们可以使用devServer自带的proxy功能:
module.exports = {
...
devServer: [
proxy: {
'/api': {
target: 'http://localhost:5000',
// 如果不希望传递api,重写路径
pathRewrite:{'^/api':''},
// 如果想让我们本地的http服务变成https服务,设置为true,但是这个是不安全的,需要证书
// https: true,
https: {
cacert: './server.pem',
pfx: './server.pfx',
key: './server.key',
cert: './servercrt',
passphare: 'webpack-dev-server',
requestCert: true,
}
}
}
],
};
- http2
配置http2,与https不同的是http2自带https证书,也可以通过https配置自己的证书!
module.exports = {
...
devServer: [
http2: true
],
};
historyApiFallback
如果我的应用是个SPA(单页面应用),当路由到 /index 时(可以直接在地址栏输入),会发现此时页面刷新后,控制台会报错!
GET http://localhost:3000/home 404 (Not Found)
此时打开network,刷新并查看,就会发现问题所在– – 浏览器把这个路由当成了静态的资源地址去请求,然后我们并没有打包出/home这样的资源,所以访问无疑是404的,如何解决它?这种时候,我们可以通过配置来提供页面代替任何404的静态资源响应:
module.exports = {
// ...
devServer: [
historyApiFallback: true
],
};
此时重启服务器刷新后发现请求变成了index.html。当然,在很多业务场景下,我们需要根据不同的访问路径定制替代的页面,这种情况下,我们可以通过rewrites这个配置项。类似这样:
module.exports = {
// ...
devServer: [
historyApiFallback: {
rewrites: [
{ from: /^\/$/, to: '/view/lading.html'},
{ from: /^\/subpage/, to: '/views/subpage.html'},
{...},
]
}
],
};
启动
npx webpack-dev-server
4、资源模块 Asset Modules
官方说明:https://webpack.docschina.org/guides/asset-modules
该方法需将资源在 JS 中通过 import 进行导入或css中进行导入
// js 文件导入
import 命名 from '资源路径'
// css 文件引用
.box {
background-image: url('资源路径');
}
资源模块类型
- asset/resource:发送一个单独的文件并导出 URL
- asset/inline:导出一个资源的 Data URI ( 64位图 )
- asset/source:导出资源的源代码
- asset:在导出一个资源的 Data URI 和发送一个单独的文件之间自动进行选择
resource
module.exports = {
...
module: {
output: [
// 生成资源名称 contenthash自动生成文件名, ext自己定义的拓展类型
assetModuleFilename: 'images/[contenthash].[ext]'
],
rules: [
{
// 监听资源文件
test: /\.png$/,
// 设置资源类型
type: 'asset/resource',
generator: {
// 生成资源名称
filename: 'assets/images/[name][ext]'
}
}
]
}
}
- 资源名称可以使用 contenthash 将资源名称生成为 hash 值命名
inline
module.exports = {
...
module: {
output: [
// 生成资源名称 contenthash自动生成文件名, ext自己定义的拓展类型
assetModuleFilename: 'images/[contenthash].[ext]'
],
rules: [
{
// 监听资源文件
test: /\.svg$/,
// 设置资源类型
type: 'asset/inline'
}
]
}
}
source
module.exports = {
...
module: {
output: [
// 生成资源名称 contenthash自动生成文件名, ext自己定义的拓展类型
assetModuleFilename: 'images/[contenthash].[ext]'
],
rules: [
{
// 监听资源文件
test: /\.txt$/,
// 设置资源类型
type: 'asset/source'
}
]
}
}
asset
module.exports = {
...
module: {
output: [
// 生成资源名称 contenthash自动生成文件名, ext自己定义的拓展类型
assetModuleFilename: 'images/[contenthash].[ext]'
],
rules: [
{
// 监听资源文件
test: /\.jpg$/,
// 设置资源类型
type: 'asset',
// 小于设置的大小则转为 64 位图,否则转 URL
parser: {
dataUrlCondition: {
maxSize: 4 * 1024 // 4kb
}
},
generator: {
// 生成资源名称
filename: 'assets/images/[contenthash].[ext]'
}
}
]
}
}
5、资源处理
5.1、HTML 资源
打包 HTML
1、下载 html-webpack-plugin 插件
npm i html-webpack-plugin - D
2、在 webpack.config.js 文件中引入插件并调用
// 引用插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
// 指定 HTML 模版文件
template: './index.html',
filename: 'app.html',
// 指定 Script 标签位置
inject: 'body',
}),
],
}
- Webpack 会在输出目录中新创建一个 HTML 文件,在原始的 HTML 文件中无需引入 JS 文件,通过 Webpack 编译后的 HTML 文件会自动引入。
官方说明:https://webpack.docschina.org/plugins/html-webpack-plugin/
配置选项:https://github.com/jantimon/html-webpack-plugin#options
5.2、样式资源
打包 CSS 资源
下载样式处理解析器 css-loader 与 style-loader
npm i css-loader style-loader -D
在配置文件中添加解析器
module.exports = {
...
module: {
rules: [
{
test: /\.css$/,
use: [
// 在 head 中创建 style 标签
'style-loader',
// 将 css 文件整合到 js 文件中
'css-loader'
]
}
]
}
}
在 JS 文件中导入 CSS 文件
import '../css/main.css'
打包 SCSS / LESS 资源
下载样式处理解析器
npm i sass-loader sass -D
在配置文件中添加解析器
module.exports = {
...
module: {
rules: [
{
test: /\.(css|sass)$/,
use: [
// 在 head 中创建 style 标签
'style-loader',
// 将 css 文件整合到 js 文件中
'css-loader',
// 编译 sass 文件为 css 文件
'sass-loader'
]
}
]
}
}
这里实例为sass,如果打包less就将对应配置改为less!
在 JS 文件中导入 SCSS 文件
import '../css/main.scss'
抽离 CSS 代码为独立文件
下载插件 mini-css-extract-plugin
npm i mini-css-extract-plugin -D
引用插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
...
module: {
rules: [
{
test: /\.(css|sass)$/,
use: [
// 抽离 css 为独立文件
MiniCssExtractPlugin.loader,
// 将 css 文件整合到 js 文件中
'css-loader',
// 编译 sass 文件为 css 文件
'sass-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
// 对输出结果重命名
filename: 'assets/css/[name].css'
})
]
}
- 如果是生成模式,将自动压缩css文件,无需额外配置。
官方文档:https://webpack.docschina.org/plugins/mini-css-extract-plugin
CSS 代码压缩(生产模式)
安装插件 css-minimizer-webpack-plugin
npm i css-minimizer-webpack-plugin -D
在配置文件中进行配置
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin")
module.exports = {
...
optimization: {
minimizer: [
// 使用插件优化 css 代码
new CssMinimizerPlugin()
],
},
// 模式
mode: 'production'
}
- 压缩 CSS 代码,仅在生产模式下有效
官方文档:https://webpack.docschina.org/plugins/css-minimizer-webpack-plugin/
CSS 兼容性处理
下载 postcss-loader, postcss, postcss-preset-env 模块
npm i postcss-loader postcss postcss-nested postcss-preset-env -D
# 安装插件,自动管理浏览器前缀解析CSS文件并且添加浏览器前缀到CSS内容里
npm i autoprefixer -D
在根目录下创建 postcss.config.js 文件并进行配置
module.exports = {
...
plugins: [
require('autoprefixer'), // 解析css文件添加到浏览器中
require('postcss-nested'), // 可以实现嵌套css
[
'postcss-preset-env',
{
// 其他选项
},
],
],
};
引用模块
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
...
module: {
rules: [
{
test: /\.(css|sass)$/,
use: [
// 抽离 css 为独立文件
MiniCssExtractPlugin.loader,
// 将 css 文件整合到 js 文件中
{
loader: 'css-loader',
options: {
modules: true, // 如果采用这种方式引入,就需要import模块化进行处理!
}
},
// css 兼容处理
'postcss-loader',
// 编译 sass 文件为 css 文件
'sass-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
// 对输出结果重命名
filename: 'assets/css/[name].css'
})
]
}
import style from './color.css'
const div = document.createElement('div')
div.textContent = 'hello postcss'
div.classList.add(style.xxx) // 这里xxx就是你定义的样式!
document.body.appendChild(div)
postcss-preset-env 帮助 postcss 找到 package.json 中 browserslist 里的配置,通过配置加载指定的 css 兼容性
// 在 package.json 中添加浏览器列表
{
...
"browserslist": {
"development": [
"> 1%", // 这个插件要在全球浏览器使用率大于1%
"last 1 chrome version",
"last 1 firfoxe version",
"last 1 safari version"
],
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
}
}
CSS模块模式
*.global.css 普通模式
*.css css module模式
这里统一用global关键字进行识别,用正则表达式匹配文件:
// css module
module.exports = {
module: {
rules: [
{
test: new RegExp(`^(?!.*\\.global).*\\.css`),
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[hash:base64:6]',
},
},
{
loader: 'postcss-loader',
},
],
exclude: [path.resolve(__dirname, '...', 'node_modules')],
},
],
},
};
// 普通模式
// css module
module.exports = {
module: {
rules: [
{
test: new RegExp(`^(.*\\.global).*\\.css`),
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
},
{
loader: 'postcss-loader',
},
],
exclude: [path.resolve(__dirname, '..', 'node_modules')],
},
],
},
};
图片资源 *
下载图片处理解析器
npm i url-loader file-loader html-loader -D
.…..
字体资源
通过 CSS 引入字体资源
@font-face {
font-family: 'PujiSansExpandedHeavy';
src: url('../fonts/PujiSans-ExpandedHeavy.eot'); /* IE9 Compat Modes */
src: url('../fonts/PujiSans-ExpandedHeavy.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('../fonts/PujiSans-ExpandedHeavy.woff2') format('woff2'), /* Modern Browsers */
url('../fonts/PujiSans-ExpandedHeavy.woff') format('woff'), /* Modern Browsers */
url('../fonts/PujiSans-ExpandedHeavy.ttf') format('truetype'); /* Safari, Android, iOS */
font-style: normal;
font-weight: normal;
text-rendering: optimizeLegibility;
}
在 webpack.config.js 文件中进行配置
module.exports = {
...
module: {
rules: [
{
// 监听资源文件
test: /\.(woff|woff2|eot|ttf|otf)$/,
// 设置资源类型
type: 'asset/resource',
generator: {
// 生成资源名称
filename: 'assets/fonts/[name][ext]'
},
}
]
}
}
数据资源
如需导入 CSV, TSV, XML 等数据格式文件,需使用相关的数据 loader 进行加载
下载csv-loader、xml-loader
npm i xml-loader csv-loader -D
在 webpack.config.js 文件中进行配置
module.exports = {
...
module: {
rules: [
{
test: /\.(csv|tsv)/,
use: 'csv-loader',
},
{
test: /\.xml$/,
use: 'xml-loader',
},
]
}
}
结论:
- data.xml会转换成一个js对象
- data.csv会转换成一个数组!
自定义 JSON 资源
安装依赖
npm install toml yaml json5 -D
在webpack.config.js中进行配置
const toml = require('toml');
const yaml = require('yaml');
const json5 = require('json5');
module.exports = {
...
module: {
rules: [
{
test: /\.toml$/,
type: 'json',
parser: {
parser: toml.parser,
},
},
{
test: /\.yaml$/,
type: 'json',
parser: {
parser: yaml.parser,
},
},
{
test: /\.json5$/,
type: 'json',
parser: {
parser: json5.parser,
},
},
],
},
};
JS 资源
ESLint
使用 eslint 扫描我们所写的代码是否符合规范,严格意义上来说,eslint 配置跟 webpack 无关,但在工程化开发环境中,他往往是不可或缺的。
安装
yarn add eslint -D
创建配置文件,根据提示选择需要的类型。配置完成后,将在 node_modules 文件夹中生成一个 .eslintrc(或者.eslintrc.json, .js等) 文件,将文件复制到根目录下。
npx eslint --init
我们可以看到控制台里的展示:
并生成一个配置文件(.eslint.json),这样我们就完成了eslint的基本配置规则。eslint配置文件里的配置选项含义如下:
{
// 环境定义了预定义的全局变量。
"env": {
//环境定义了预定义的全局变量。更多在官网查看
"browser":true,
"node":true,
"commonjs":true,
"amd":true,
"es6":true,
"mocha":true
},
// JavaScript 语言选项
"parserOptions": {
// ECMAScript 版本
"ecmaVersion":6,
"sourceType":"script",//module
// 想使用的额外的语言特性:
"ecmaFeatures": {
// 允许在全局作用域下使用 return 语句
"globalReturn":true,
// impliedStric
"impliedStrict":true,
// 启用 JSX
"jsx":true
}
},
/**
* "off" 或 0 - 关闭规则
* "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出),
* "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
*/
"rules": {
// 可能的错误 //
// 禁止条件表达式中出现赋值操作符
"no-cond-assign":2,
// 禁用 console
"no-console":0,
// 禁止在条件中使用常量表达式
// if (false) {
// doSomethingUnfinished();
// } //cuowu
"no-constant-condition":2,
// 禁止在正则表达式中使用控制字符 :new RegExp("\x1f")
"no-control-regex":2,
// 数组和对象键值对最后一个逗号, never参数:不能带末尾的逗号, always参数:必须带末尾的逗号,
// always-multiline:多行模式必须带逗号,单行模式不能带逗号
"comma-dangle": [1,"always-multiline"],
// 禁用 debugger
"no-debugger":2,
// 禁止 function 定义中出现重名参数
"no-dupe-args":2,
// 禁止对象字面量中出现重复的 key
"no-dupe-keys":2,
// 禁止重复的 case 标签
"no-duplicate-case":2,
// 禁止空语句块
"no-empty":2,
// 禁止在正则表达式中使用空字符集 (/^abc[]/)
"no-empty-character-class":2,
// 禁止对 catch 子句的参数重新赋值
"no-ex-assign":2,
// 禁止不必要的布尔转换
"no-extra-boolean-cast":2,
// 禁止不必要的括号 //(a * b) + c;//报错
"no-extra-parens":0,
// 禁止不必要的分号
"no-extra-semi":2,
// 禁止对 function 声明重新赋值
"no-func-assign":2,
// 禁止在嵌套的块中出现 function 或 var 声明
"no-inner-declarations": [2,"functions"],
// 禁止 RegExp 构造函数中无效的正则表达式字符串
"no-invalid-regexp":2,
// 禁止在字符串和注释之外不规则的空白
"no-irregular-whitespace":2,
// 禁止在 in 表达式中出现否定的左操作数
"no-negated-in-lhs":2,
// 禁止把全局对象 (Math 和 JSON) 作为函数调用 错误:var math = Math();
"no-obj-calls":2,
// 禁止直接使用 Object.prototypes 的内置属性
"no-prototype-builtins":0,
// 禁止正则表达式字面量中出现多个空格
"no-regex-spaces":2,
// 禁用稀疏数组
"no-sparse-arrays":2,
// 禁止出现令人困惑的多行表达式
"no-unexpected-multiline":2,
// 禁止在return、throw、continue 和 break语句之后出现不可达代码
/*
function foo() {
return true;
console.log("done");
}//错误
*/
"no-unreachable":2,
// 要求使用 isNaN() 检查 NaN
"use-isnan":2,
// 强制使用有效的 JSDoc 注释
"valid-jsdoc":1,
// 强制 typeof 表达式与有效的字符串进行比较
// typeof foo === "undefimed" 错误
"valid-typeof":2,
//
// 最佳实践 //
//
// 定义对象的set存取器属性时,强制定义get
"accessor-pairs":2,
// 强制数组方法的回调函数中有 return 语句
"array-callback-return":0,
// 强制把变量的使用限制在其定义的作用域范围内
"block-scoped-var":0,
// 限制圈复杂度,也就是类似if else能连续接多少个
"complexity": [2,9],
// 要求 return 语句要么总是指定返回的值,要么不指定
"consistent-return":0,
// 强制所有控制语句使用一致的括号风格
"curly": [2,"all"],
// switch 语句强制 default 分支,也可添加 // no default 注释取消此次警告
"default-case":2,
// 强制object.key 中 . 的位置,参数:
// property,'.'号应与属性在同一行
// object, '.' 号应与对象名在同一行
"dot-location": [2,"property"],
// 强制使用.号取属性
// 参数: allowKeywords:true 使用保留字做属性名时,只能使用.方式取属性
// false 使用保留字做属性名时, 只能使用[]方式取属性 e.g [2, {"allowKeywords": false}]
// allowPattern: 当属性名匹配提供的正则表达式时,允许使用[]方式取值,否则只能用.号取值 e.g [2, {"allowPattern": "^[a-z]+(_[a-z]+)+$"}]
"dot-notation": [2, {"allowKeywords":false}],
// 使用 === 替代 == allow-null允许null和undefined==
"eqeqeq": [2,"allow-null"],
// 要求 for-in 循环中有一个 if 语句
"guard-for-in":2,
// 禁用 alert、confirm 和 prompt
"no-alert":0,
// 禁用 arguments.caller 或 arguments.callee
"no-caller":2,
// 不允许在 case 子句中使用词法声明
"no-case-declarations":2,
// 禁止除法操作符显式的出现在正则表达式开始的位置
"no-div-regex":2,
// 禁止 if 语句中有 return 之后有 else
"no-else-return":0,
// 禁止出现空函数.如果一个函数包含了一条注释,它将不会被认为有问题。
"no-empty-function":2,
// 禁止使用空解构模式no-empty-pattern
"no-empty-pattern":2,
// 禁止在没有类型检查操作符的情况下与 null 进行比较
"no-eq-null":1,
// 禁用 eval()
"no-eval":2,
// 禁止扩展原生类型
"no-extend-native":2,
// 禁止不必要的 .bind() 调用
"no-extra-bind":2,
// 禁用不必要的标签
"no-extra-label:":0,
// 禁止 case 语句落空
"no-fallthrough":2,
// 禁止数字字面量中使用前导和末尾小数点
"no-floating-decimal":2,
// 禁止使用短符号进行类型转换(!!fOO)
"no-implicit-coercion":0,
// 禁止在全局范围内使用 var 和命名的 function 声明
"no-implicit-globals":1,
// 禁止使用类似 eval() 的方法
"no-implied-eval":2,
// 禁止 this 关键字出现在类和类对象之外
"no-invalid-this":0,
// 禁用 __iterator__ 属性
"no-iterator":2,
// 禁用标签语句
"no-labels":2,
// 禁用不必要的嵌套块
"no-lone-blocks":2,
// 禁止在循环中出现 function 声明和表达式
"no-loop-func":1,
// 禁用魔术数字(3.14什么的用常量代替)
"no-magic-numbers":[1,{"ignore": [0,-1,1] }],
// 禁止使用多个空格
"no-multi-spaces":2,
// 禁止使用多行字符串,在 JavaScript 中,可以在新行之前使用斜线创建多行字符串
"no-multi-str":2,
// 禁止对原生对象赋值
"no-native-reassign":2,
// 禁止在非赋值或条件语句中使用 new 操作符
"no-new":2,
// 禁止对 Function 对象使用 new 操作符
"no-new-func":0,
// 禁止对 String,Number 和 Boolean 使用 new 操作符
"no-new-wrappers":2,
// 禁用八进制字面量
"no-octal":2,
// 禁止在字符串中使用八进制转义序列
"no-octal-escape":2,
// 不允许对 function 的参数进行重新赋值
"no-param-reassign":0,
// 禁用 __proto__ 属性
"no-proto":2,
// 禁止使用 var 多次声明同一变量
"no-redeclare":2,
// 禁用指定的通过 require 加载的模块
"no-return-assign":0,
// 禁止使用 javascript: url
"no-script-url":0,
// 禁止自我赋值
"no-self-assign":2,
// 禁止自身比较
"no-self-compare":2,
// 禁用逗号操作符
"no-sequences":2,
// 禁止抛出非异常字面量
"no-throw-literal":2,
// 禁用一成不变的循环条件
"no-unmodified-loop-condition":2,
// 禁止出现未使用过的表达式
"no-unused-expressions":0,
// 禁用未使用过的标签
"no-unused-labels":2,
// 禁止不必要的 .call() 和 .apply()
"no-useless-call":2,
// 禁止不必要的字符串字面量或模板字面量的连接
"no-useless-concat":2,
// 禁用不必要的转义字符
"no-useless-escape":0,
// 禁用 void 操作符
"no-void":0,
// 禁止在注释中使用特定的警告术语
"no-warning-comments":0,
// 禁用 with 语句
"no-with":2,
// 强制在parseInt()使用基数参数
"radix":2,
// 要求所有的 var 声明出现在它们所在的作用域顶部
"vars-on-top":0,
// 要求 IIFE 使用括号括起来
"wrap-iife": [2,"any"],
// 要求或禁止 “Yoda” 条件
"yoda": [2,"never"],
// 要求或禁止使用严格模式指令
"strict":0,
//
// 变量声明 //
//
// 要求或禁止 var 声明中的初始化(初值)
"init-declarations":0,
// 不允许 catch 子句的参数与外层作用域中的变量同名
"no-catch-shadow":0,
// 禁止删除变量
"no-delete-var":2,
// 不允许标签与变量同名
"no-label-var":2,
// 禁用特定的全局变量
"no-restricted-globals":0,
// 禁止 var 声明 与外层作用域的变量同名
"no-shadow":0,
// 禁止覆盖受限制的标识符
"no-shadow-restricted-names":2,
// 禁用未声明的变量,除非它们在 /*global */ 注释中被提到
"no-undef":2,
// 禁止将变量初始化为 undefined
"no-undef-init":2,
// 禁止将 undefined 作为标识符
"no-undefined":0,
// 禁止出现未使用过的变量
"no-unused-vars": [2, {"vars":"all","args":"none"}],
// 不允许在变量定义之前使用它们
"no-use-before-define":0,
//
// Node.js and CommonJS //
//
// require return statements after callbacks
"callback-return":0,
// 要求 require() 出现在顶层模块作用域中
"global-require":1,
// 要求回调函数中有容错处理
"handle-callback-err": [2,"^(err|error)$"],
// 禁止混合常规 var 声明和 require 调用
"no-mixed-requires":0,
// 禁止调用 require 时使用 new 操作符
"no-new-require":2,
// 禁止对 __dirname 和 __filename进行字符串连接
"no-path-concat":0,
// 禁用 process.env
"no-process-env":0,
// 禁用 process.exit()
"no-process-exit":0,
// 禁用同步方法
"no-sync":0,
//
// 风格指南 //
//
// 指定数组的元素之间要以空格隔开(, 后面), never参数:[ 之前和 ] 之后不能带空格,always参数:[ 之前和 ] 之后必须带空格
"array-bracket-spacing": [2,"never"],
// 禁止或强制在单行代码块中使用空格(禁用)
"block-spacing":[1,"never"],
//强制使用一致的缩进 第二个参数为 "tab" 时,会使用tab,
// if while function 后面的{必须与if在同一行,java风格。
"brace-style": [2,"1tbs", {"allowSingleLine":true}],
// 双峰驼命名格式
"camelcase":2,
// 控制逗号前后的空格
"comma-spacing": [2, {"before":false,"after":true}],
// 控制逗号在行尾出现还是在行首出现 (默认行尾)
// http://eslint.org/docs/rules/comma-style
"comma-style": [2,"last"],
//"SwitchCase" (默认:0) 强制 switch 语句中的 case 子句的缩进水平
// 以方括号取对象属性时,[ 后面和 ] 前面是否需要空格, 可选参数 never, always
"computed-property-spacing": [2,"never"],
// 用于指统一在回调函数中指向this的变量名,箭头函数中的this已经可以指向外层调用者,应该没卵用了
// e.g [0,"that"] 指定只能 var that = this. that不能指向其他任何值,this也不能赋值给that以外的其他值
"consistent-this": [1,"that"],
// 强制使用命名的 function 表达式
"func-names":0,
// 文件末尾强制换行
"eol-last":2,
"indent": [2,4, {"SwitchCase":1}],
// 强制在对象字面量的属性中键和值之间使用一致的间距
"key-spacing": [2, {"beforeColon":false,"afterColon":true}],
// 强制使用一致的换行风格
"linebreak-style": [1,"unix"],
// 要求在注释周围有空行 ( 要求在块级注释之前有一空行)
"lines-around-comment": [1,{"beforeBlockComment":true}],
// 强制一致地使用函数声明或函数表达式,方法定义风格,参数:
// declaration: 强制使用方法声明的方式,function f(){} e.g [2, "declaration"]
// expression:强制使用方法表达式的方式,var f = function() {} e.g [2, "expression"]
// allowArrowFunctions: declaration风格中允许箭头函数。 e.g [2, "declaration", { "allowArrowFunctions": true }]
"func-style":0,
// 强制回调函数最大嵌套深度 5层
"max-nested-callbacks": [1,5],
// 禁止使用指定的标识符
"id-blacklist":0,
// 强制标识符的最新和最大长度
"id-length":0,
// 要求标识符匹配一个指定的正则表达式
"id-match":0,
// 强制在 JSX 属性中一致地使用双引号或单引号
"jsx-quotes":0,
// 强制在关键字前后使用一致的空格 (前后腰需要)
"keyword-spacing":2,
// 强制一行的最大长度
"max-len":[1,200],
// 强制最大行数
"max-lines":0,
// 强制 function 定义中最多允许的参数数量
"max-params":[1,7],
// 强制 function 块最多允许的的语句数量
"max-statements":[1,200],
// 强制每一行中所允许的最大语句数量
"max-statements-per-line":0,
// 要求构造函数首字母大写 (要求调用 new 操作符时有首字母大小的函数,允许调用首字母大写的函数时没有 new 操作符。)
"new-cap": [2, {"newIsCap":true,"capIsNew":false}],
// 要求调用无参构造函数时有圆括号
"new-parens":2,
// 要求或禁止 var 声明语句后有一行空行
"newline-after-var":0,
// 禁止使用 Array 构造函数
"no-array-constructor":2,
// 禁用按位运算符
"no-bitwise":0,
// 要求 return 语句之前有一空行
"newline-before-return":0,
// 要求方法链中每个调用都有一个换行符
"newline-per-chained-call":1,
// 禁用 continue 语句
"no-continue":0,
// 禁止在代码行后使用内联注释
"no-inline-comments":0,
// 禁止 if 作为唯一的语句出现在 else 语句中
"no-lonely-if":0,
// 禁止混合使用不同的操作符
"no-mixed-operators":0,
// 不允许空格和 tab 混合缩进
"no-mixed-spaces-and-tabs":2,
// 不允许多个空行
"no-multiple-empty-lines": [2, {"max":2}],
// 不允许否定的表达式
"no-negated-condition":0,
// 不允许使用嵌套的三元表达式
"no-nested-ternary":0,
// 禁止使用 Object 的构造函数
"no-new-object":2,
// 禁止使用一元操作符 ++ 和 --
"no-plusplus":0,
// 禁止使用特定的语法
"no-restricted-syntax":0,
// 禁止 function 标识符和括号之间出现空格
"no-spaced-func":2,
// 不允许使用三元操作符
"no-ternary":0,
// 禁用行尾空格
"no-trailing-spaces":2,
// 禁止标识符中有悬空下划线_bar
"no-underscore-dangle":0,
// 禁止可以在有更简单的可替代的表达式时使用三元操作符
"no-unneeded-ternary":2,
// 禁止属性前有空白
"no-whitespace-before-property":0,
// 强制花括号内换行符的一致性
"object-curly-newline":0,
// 强制在花括号中使用一致的空格
"object-curly-spacing":0,
// 强制将对象的属性放在不同的行上
"object-property-newline":0,
// 强制函数中的变量要么一起声明要么分开声明
"one-var": [2, {"initialized":"never"}],
// 要求或禁止在 var 声明周围换行
"one-var-declaration-per-line":0,
// 要求或禁止在可能的情况下要求使用简化的赋值操作符
"operator-assignment":0,
// 强制操作符使用一致的换行符
"operator-linebreak": [2,"after", {"overrides": {"?":"before",":":"before"} }],
// 要求或禁止块内填充
"padded-blocks":0,
// 要求对象字面量属性名称用引号括起来
"quote-props":0,
// 强制使用一致的反勾号、双引号或单引号
"quotes": [2,"single","avoid-escape"],
// 要求使用 JSDoc 注释
"require-jsdoc":1,
// 要求或禁止使用分号而不是 ASI(这个才是控制行尾部分号的,)
"semi": [2,"always"],
// 强制分号之前和之后使用一致的空格
"semi-spacing":0,
// 要求同一个声明块中的变量按顺序排列
"sort-vars":0,
// 强制在块之前使用一致的空格
"space-before-blocks": [2,"always"],
// 强制在 function的左括号之前使用一致的空格
"space-before-function-paren": [2,"always"],
// 强制在圆括号内使用一致的空格
"space-in-parens": [2,"never"],
// 要求操作符周围有空格
"space-infix-ops":2,
// 强制在一元操作符前后使用一致的空格
"space-unary-ops": [2, {"words":true,"nonwords":false}],
// 强制在注释中 // 或 /* 使用一致的空格
"spaced-comment": [2,"always", {"markers": ["global","globals","eslint","eslint-disable","*package","!"] }],
// 要求或禁止 Unicode BOM
"unicode-bom":0,
// 要求正则表达式被括号括起来
"wrap-regex":0,
//
// ES6.相关 //
//
// 要求箭头函数体使用大括号
"arrow-body-style":2,
// 要求箭头函数的参数使用圆括号
"arrow-parens":2,
"arrow-spacing":[2,{"before":true,"after":true}],
// 强制在子类构造函数中用super()调用父类构造函数,TypeScrip的编译器也会提示
"constructor-super":0,
// 强制 generator 函数中 * 号周围使用一致的空格
"generator-star-spacing": [2, {"before":true,"after":true}],
// 禁止修改类声明的变量
"no-class-assign":2,
// 不允许箭头功能,在那里他们可以混淆的比较
"no-confusing-arrow":0,
// 禁止修改 const 声明的变量
"no-const-assign":2,
// 禁止类成员中出现重复的名称
"no-dupe-class-members":2,
// 不允许复制模块的进口
"no-duplicate-imports":0,
// 禁止 Symbol 的构造函数
"no-new-symbol":2,
// 允许指定模块加载时的进口
"no-restricted-imports":0,
// 禁止在构造函数中,在调用 super() 之前使用 this 或 super
"no-this-before-super":2,
// 禁止不必要的计算性能键对象的文字
"no-useless-computed-key":0,
// 要求使用 let 或 const 而不是 var
"no-var":0,
// 要求或禁止对象字面量中方法和属性使用简写语法
"object-shorthand":0,
// 要求使用箭头函数作为回调
"prefer-arrow-callback":0,
// 要求使用 const 声明那些声明后不再被修改的变量
"prefer-const":0,
// 要求在合适的地方使用 Reflect 方法
"prefer-reflect":0,
// 要求使用扩展运算符而非 .apply()
"prefer-spread":0,
// 要求使用模板字面量而非字符串连接
"prefer-template":0,
// Suggest using the rest parameters instead of arguments
"prefer-rest-params":0,
// 要求generator 函数内有 yield
"require-yield":0,
// enforce spacing between rest and spread operators and their expressions
"rest-spread-spacing":0,
// 强制模块内的 import 排序
"sort-imports":0,
// 要求或禁止模板字符串中的嵌入表达式周围空格的使用
"template-curly-spacing":1,
// 强制在 yield* 表达式中 * 周围使用空格
"yield-star-spacing":2
}
}
在 VSCode 中安装扩展 Eslint ,重启软件后将自动生效。
结合webpack使用
我们期望eslint能够实时提示错误而不必等待执行命令。这个功能可以通过自己的IDE(代码编辑器)安装对应的eslint插件来实现。然而,不是每个IDE都有插件,如果不想安装插件,又想实时提示报错,那么我们就需要webpack 的打包编译功能来实现。
安装
# eslint-loader已经被弃用,可以安装eslint-webpack-plugin代替
yarn add webpack webpack-cli webpack-dev-server html-webpack-plugin babel-loader eslint-loader @babel/core -D
这个采用了eslint-loader,配置如下:
// ...
{
test:/\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader','eslint-loader']
}
// ...
因为我们使用了devServer,因此需要在DevServer下添加一个对应的配置参数:
module.exports = {
// ...
devServer: {
client: {
overlay: false, // 设置这个参数就只会在控制台报错,网页不会显示!
},
liveReload: false, // 默认为true,即开启热更新功能!
}
}
现在我们就可以实时看到代码里的不规范报错了。
git-hooks 与 husky
husky
为了保证团队里的开发人员提交的代码符合规范,我们可以在开发者论坛上传代码的时候进行校验。我们常使用 husky 和 lint-staged 来进行代码提交时的eslint 校验:
// package.json
// 先安装:yarn add husky lint-staged -D
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
}
为了保证团队里的开发人员提交的代码符合规范,我们可以在开发者上传代码时进行校验。我们常用 husky来协助进行代码提交时的eslint校验。在使用husky之前,我们先来研究一下 git-hooks
。
我们回到项目的根目录下。运行ls -a命令----“ a" 可以显示隐藏目录(目录名的第一-位是)。我们可以看到,存在一个".git" 名称的文件夹。
事实上,在我们项目中根目录下运行git命令时,git会根据它来工作。
接来下我们进入到这个文件夹,进-步查看它内部的内容。
cd .git
ls -a
我们发现它内部还挺有料!不慌,我们这节课仅仅只讲到其中的一个内容---- hooks,可以看到,当前目录下存在一个hooks文件夹,顾名思义,这个文件夹提供了git命令相关的钩子。
继续往里看。
cd hooks
ls -a
ok,那我们可以看到有很多git命令相关的文件名。比如"pre- commit.sample pre push.sample".
回到正题一-我们期望在gi提交(commit)前, 对我们的代码进行检测,如果不能通过检测,就无法提交我们的代码。
自然而然的,这个动作的时机应该是? ---- "pre -commit,也就是commit之前。
# cat 命令擦汗一个文件的内容
cat pre-commit.sample
ok,他返回了这样的内容,是一串shell注释,大概意思就是,这是个示例钩子。
husky官网地址:https://typicode.github.io/husky/#/
Github地址:https://github.com/typicode/husky
JS 兼容处理
将 ES6 代码转换为低版本 ES 代码
安装模块
- babel-loader: 在 webpack 里应用 babel 解析 ES6 的桥梁
- @babel/core: babel 核心模块
- @babel/preset-env: babel 预设,一组 babel 插件的集合
npm i babel-loader @babel/core @babel/preset-env -D
在 webpack.config.js 中配置
module.exports = {
...
module: {
rules: [
{
test: /\.js$/,
// 排除 node_modules 中安装的库
exclude: /(node_modules|bower_components)/,
use: {
// 加载 loader
loader: 'babel-loader',
options: {
// 配置预设
presets: ['@babel/preset-env']
}
}
}
]
}
}
regeneratorRuntime
regeneratorRuntime 是 webpack 打包生成的全局辅助函数,由 babel 生成,用于兼容 async/await 的语法。
安装
npm i @babel/runtime @babel/plugin-transform-runtime -D
在 webpack.config.js 中配置:
module.exports = {
...
module: {
rules: [
{
test: /\.js$/,
// 排除 node_modules 中安装的库
exclude: /(node_modules|bower_components)/,
use: {
// 加载 loader
loader: 'babel-loader',
options: {
// 配置预设
presets: ['@babel/preset-env']
plugins: [
[
'@babel/plugin-transform-runtime'
]
]
}
}
}
]
}
}
JS 压缩
安装插件 terser-webpack-plugin
npm i terser-webpack-plugin -D
配置
const TerserWebpackPlugin = require("terser-webpack-plugin")
module.exports = {
...
optimization: {
minimizer: [
// 使用插件压缩 js 代码 (生产模式)
new TerserWebpackPlugin()
]
}
}
6、优化
八个通用构建优化
01、更新到最新版本
02、将loader应用于最少数量的必要模块
const path = require('path')
module.exports = {
// ...
module: {
rules: [
{
test: /\.js$/,
include: path.resolve(__dirname,'src'), // 只解析src提高速度!
loader: 'babel-loader'
}
]
}
}
03、引导(bootstrap)每个额外的 loader/plugin 都有其启动的时间。尽量减少使用工具。
以下步骤可以提高解析速度:
- 减少 resolve.modules,resolve.extensions,resolve.mainFiles,resolve.descriptionFiles中条目数量,因为他们会增加文件系统调用的次数。
- 如果你不使用 symlinks(例如npm link 或者 yarn link ),你可以设置resolve.symlinks:false。
- 如果你使用自定义 resolve plugin规则,并且没有指定 content上下文,可以设置 resolve.cacheWithContent:false。
04、小即是快(smaller = faster)
减少编译结果整体大小,以提高构建性能。尽量保持 chunk 提价小。
- 使用数量更少/体积更小的e library。
- 在多页面应用程序中使用 SplitChunksPlugin。
- 在多页面应用程序中使用 SplitChunksPlugin,并开启 async 模式。
- 移除未引用代码。
- 只编译你当前正在开发的哪些代码。
05、持久化缓存
在webpack配置中使用cache选项。使用 package.json中的“postinstall”清除缓存目录。
将cache类型设置为内存或者文件系统。memory 选项很简单,他告诉webpack在内存中存储缓存,不允许额外的配置:
module.exports = {
// ...
cache: {
type: 'memory'
}
}
06、自定义 plugin/loader,对他们进行概要分析,以免在此处引入性能问题。
07、progress plugin 将ProgressPlugin 从 webpack中删除,可以缩短构建时间。请注意,ProgressPlugin 可能不会为快速构建提供太多价值,因此,注意使用!
08、对于不同环境的配置不同设置优化!
公共路径 publicPath
publicPath 配置公共路径,所有文件的引用将自动添加公共路径的绝对地址。
module.exports = {
...
output: {
...
publicPath: 'https://localhost:3000/'
}
}
环境变量 Environment variable
环境变量可以消除 webpack.config.js 在开发环境和生产环境之间的差异
module.exports = ( env ) => {
return {
...
mode: env.production ? 'production' : 'development'
}
}
打包命令时如果使用生产模式,则在命令后增加:
npx webpack --env production
配置文件优化
分别对 development 和 production 两种模式优化。完整配置文件可查看本页下方 “完整配置”。
1、首先新建 webpack-config 文件夹,在文件夹中添加三个文件,分别为通用的配置文件、开发模式的配置文件以及生产模式的配置文件。
2、使用 webpack-merge 将文件进行合并。安装 webpack-merge
npm i webpack-merge -D
3、添加一个合并文件 webpack.config.js
const { merge } = require('webpack-merge')
const commonConfig = require('./webpack.config.common')
const developmentConfig = require('./webpack.config.dev')
const productionConfig = require('./webpack.config.prod')
module.exports = (env) => {
switch(true) {
case env.development:
return merge(commonConfig, developmentConfig)
case env.production:
return merge(commonConfig, productionConfig)
default:
return new Error('No matching configuration was found.')
}
}
4、修改 package.json 文件
// 将自定义的命令分别指向相应的文件以及添加 env 环境变量的参数
{
"scripts": {
"start": "webpack serve -c ./webpack-config/webpack.config.js --env development",
"build": "webpack -c ./webpack-config/webpack.config.js --env production"
},
}
5、使用命令运行
npm run start
npm run build
文件大小问题
打开webpack.config.js中定义,去除文件太大提示:
module.exports = {
...
performance: {
...
// 关闭文件太大提示
hints: false
}
}
HMR ( 开发环境 )
Hot module replacement 热模块替换,可使一个模块发生变化,只重新打包这一个模块,而非全部重新打包,可以更快速的构建代码打包速度。
module.exports = {
...
devServer: {
...
// 开启 HMR 功能
hot: true
}
}
热加载
热加载(文件更新时,自动刷新我们的服务和页面)新版的webpack-dev-server默认开启了热加载的功能。它对应的参数是devServer.liveReload,默认为true。注意,如果想要关闭它,要将liveReload设置为false的同时,也要关闭hot
module.exports = {
...
devServer: {
...
// 开启 HMR 功能
liveReload: false, // 默认为true,即开启热更新功能。
}
}
Source Map
一种提供源代码到构建后代码映射的技术,如果构建后代码出错了,通过映射关系可以追踪源代码的错误。在 webpack.config.js 文件中配置
module.exports = {
...
devtool: 'source-map'
}
常用的几种 source-map 类型
- source-map:生成外部文件,错误代码的准确信息和源代码的错误位置
- inline-source-map:内联,错误代码的准确信息和源代码的错误位置。在代码底部生成,构建速度比外部文件更快
- hidden-source-map:生成外部文件,错误代码的原因,没有错误位置,无法追踪源代码错误。
- eval-source-map:内联,错误代码的准确信息和源代码的错误位置。每一个文件都生成对应的 source-map
- nosources-source-map:生成外部文件,
- cheap-source-map:生成外部文件,错误代码的准确信息和源代码的错误位置。只精确到行
- cheap-module-source-map:同 cheap-source-map,会将 loader 的 source map 加入
开发环境建议
- eval-source-map
- eval-cheap-module-source-map
生产环境建议
- source-map
- nosources-source-map
- hidden-source-map
要注意的是,生产环境我们一般不会开启source-map功能,主要有以后两种原因:
- 通过bundle和sourcemap文件,可以反编译出源码- - - 也就是说,线上产物有sourcemap文件的话,就意味着有暴露源码的风险。
- 我们可以观察到,sourcemap文件的体积相对比较巨大,这跟我们生产环境的追求不同(生产环境追求更小更轻量的bundle)。
移到思考题:有时候我们期望能第一时间通知线上的错误信息,来追踪到源码的位置,从而快速解决掉bug以及损失,但不希望sourcemap文件暴露在生产环境中,有什么比较好的方案吗?
Oneof ( 生产模式 )
每个loader只会匹配一个,不能有两个配置处理一个类型的文件
module.exports = {
module: {
rules: [
{
oneOf:[
{
// 处理 css 资源
test: /\.css$/,
use: [...CommonCssLoader]
},
{
// 处理 scss 资源
test: /\.sass$/,
use: [...CommonCssLoader, 'sass-loader']
},
{
// 处理图片资源
test: /\.(jpg|jpeg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:12].[ext]',
esModule: false,
outputPath: 'images'
}
},
{
// 处理 html 中的图片资源
test: /\.html$/,
loader: 'html-loader'
}
]
}
]
}
}
Tree shaking ( 生产模式 )
去除应用程序中没有使用的代码,可更大程度的优化代码。必须使用 ES6 模块化,并开启 production 模式。
import { module } from './filename.js'
如果不需要某些文件被 webpack 清除,可以在 package.json 中配置 sideEffects 属性
{
"sideEffects": ["*.css" ,"*.scss", "*.global.js"...]
}
Code split ( 生产模式 )
代码分离是 webpack 中最引人瞩目的特性之一,可将代码分离到不同的文件中,然后将这些文件按需加载或并行加载,同时还可以获取代码加载的优先级。
方法1: 入口起点( 不推荐 )
使用 entry 配置手动分离代码,如果多个入口共享的文件,会分别在每个包里重复打包。
module.exports = {
entry: {
main: './src/index.js',
other: './src/another-module.js',
},
output: {
filename: 'scripts/[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
},
}
方法2: 防止重复
使用 Entry dependencies 或者 SplitChunksPlugin 去重和分离代码
module.exports = {
entry: {
main: {
import: './assets/js/main.js',
dependOn: 'shared'
},
other: {
import: './assets/js/add.js',
dependOn: 'shared'
},
shared: 'jQuery'
},
output: {
filename: 'js/[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
}
sliptChunks 插件
另外,还可使用 sliptChunks 插件来实现
module.exports = {
...
entry: {
main: './assets/js/main.js',
other: './assets/js/add.js'
},
output: {
filename: 'js/[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
sliptChunks: {
chunks: 'all'
}
}
}
可以通过 import 方法对文件名进行自定义
import(/* webpackChunkName: '自定义文件名' */'文件路径')
方法3: 动态导入
通过模块的内联函数调用分离代码:需要自己创建一个js文件
function getComponent() {
return import('lodash').then(({ default: _ }) => {
const element = document.createElement('div');
element.innerHTML = _.join(['hello', 'word'], ' ');
return element;
});
}
getComponent().then(element => {
document.body.appendChild(element);
});
懒加载
指的是 JS 文件的懒加载,当事件触发或条件满足后才进行加载。是很好的优化网页或应用的方法。这种方法实际上是先把代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用一些新的代码块。这样加快了应用的初始加载速度,减轻总体体积,因为某些代码块可能永远不会被加载。
document.querySelector('button').addEventListener('click', () => {
import(/* webpackChunkName: 'filename' */'./filename').then(({ module }) => {
...
})
})
预加载
webpack4.6.0增加了对预获取和预加载的支持。
在生命import时,使用下面这些内置命令,可以让webpack输出 “resource hint(资源提示)”,来告诉浏览器:
- prefetch(预获取):将来某些导航下可能需要的资源
- perload(预加载):当前导航下可能需要资源
等其他资源加载完毕后再进行加载,当事件触发或条件满足后,才会执行。兼容性较差,只能在pc端高版本浏览器中使用,手机端浏览器兼容较差。
添加第二句魔法注释:webpackPrefetch:true
、或者是webpackPreload:true
告诉webpack执行预获取,这回生成并加载到页面头部,指示着浏览器在空闲时间获取 math.js
文件。
document.querySelector('button').addEventListener('click', () => {
import(/* webpackChunkName: 'filename', webpackPrefetch: true */'./filename').then(({ module }) => {
...
})
})
缓存 ( 生产模式 )
使用 hash 值为文件命名。
module.exports = {
...
output: {
filename: 'js/[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
},
}
缓存第三方库
将第三方库(library)提取到单独的 vendor chunk 文件中。利用 client 的长效缓存机制,命中缓存来消除请求,并减少向 server 获取资源,同时还能保证 client 代码和 server 代码版本一致。我们在optimization.splitChunks
添加如下 cacheGroups
参数构建:
module.exports = {
...
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
PWA
非离线环境下运行
下载
npm i http-server -D
配置package.json
{
"script": {
"start": "http-server dist"
}
}
Workbox
渐进式网络应用开发程序,可实现网页离线访问,兼容性较差
这样我们就算服务挂了,也可以访问,不会影响用户体验!
下载插件
npm i workbox-webpack-plugin -D
webpack.config.js配置文件:
// 引入插件
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
// 配置
module.exports = {
...
plugins: [
new WorkboxWebpackPlugin.GenerateSW({
// 帮组 serviceworkder 快速启动
clientsClaim: true,
// 删除旧的 serviceworker
skipWaiting: true
})
]
}
// js 文件中注册
if( 'serviceWorker' in navigator ) {
window.addEventListener('load', () => {
navigator.serviceworker.register('/service-worker.js')
.then(registration => {
console.log('serviceWorker 注册成功:',registration)
})
.catch( registrationError => {
console.log('serviceWorker 注册失败:',registrationError)
})
})
}
也可以手动取消服务,输入网址:
chrome://serviceworker-internals 单击:Unregister进行取消!
- 会和eslint 产生冲突,需要修改 package.json 中 eslintConfig 配置
- 必须运行在服务器上
多进程打包
通常给 babel 使用,只有工作消耗时间较长时才建议使用。
npm i babel-loader @babel/core @babel/preset-env -D
worker 池(worker pool)
thread-loader 可以将非常消耗资源的loader分流给一个 worker pool。通用环境提升性能。用来代替happy pack
安装
npm i thread-loader -D
配置文件
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.js',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-ent'],
},
},
{
loader: 'thread-loader',
options: {
workers: 2,
},
},
],
},
],
},
};
不要使用太多的 worker,因为nodejs的runtime 和loader都有启动开销。最小化 worker 和main process(主进程)之间的模块传输。进程间通讯(IPC、inter process communication)是非常耗资源的。
Externals
为了减小打包后的文件体积,从而把一些第三方库用 cdn 的形式引入进来,如 jQuery。Externals 就是用来防止将某些文件打包到最终生成的文件包中。
定义外部第三方包
module.exports = {
...
// 定义标签类型
externalsType: 'script',
// 定义第三方包
externals: {
// 这里的文件名jquery就是key必须跟你引入的文件名一致!
// jquery: 'jQuery', // 如果使用这个,但是需要在html页面导入cdn
jquery: [
'https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js',
'jQuery', // "$"也可以!
]
}
}
在 JS 文件中使用 import 方式引入,这里 from 后的名称需和定义时的名称进行对应。
import $ from 'jQuery'
模块解析(resolve)
const path = require('path');
module.exports = {
mode: 'development',
entry: './app.js',
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
extensions: ['.json', '.js','.vue'], // 此配置可以让你在相同文件名选择优先加载哪个后缀开始的!
},
};
导入方法:
const math = require('@/math.js')
Shimming(预置依赖)
让我们开始第一个shimming全局变量的用例。还记得我们之前用过的lodash吗?出于演示目的,例如把这个应用程序中的模块依赖,改为- 个全局变量依赖。要实现这些,我们需要使用ProvidePlugin插件。
使用ProvidePlugin后,能够在webpack编译的每个模块中,通过访问一个变量来获取一个package.如果webpack看到模块中用到这个变量,它将在最终bundle中引入给定的package.让我们先移除lodash 的import语句,改为通过插件提供它:
- src/index.js
console.log(_.join(['hello','word'],' '))
- webpack.config.js
const http = require('http')
const webpack = require('webpack')
module.exports = {
mode: 'development',
entry: './src/index.ts',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname,'./dist')
}
plugins: [new webpack.ProvidePlugin(
_: 'lodash' // 这样在业务代码就不需要引入了
)],
};
细粒度 Shimming
一些遗留模块依赖的 this 指向的是 window 对象。在接下来的用例中,调整我们的index.js:
this.alert('hello,webpack');
当模块运行在CommonJS 上下文中,这将会成为一个问题,也就是说此时的 this 指向的是module.exports。在这种情况下,你可以通过使用 imports-loader 覆盖 this 指向:
安装
npm i imports-loader -D
配置文件
const http = require('http')
const webpack = require('webpack')
module.exports = {
mode: 'development',
entry: './src/index.ts',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname,'./dist')
}
plugins: [new webpack.ProvidePlugin(
_: 'lodash'
)],
module: {
rules: [
{
test: require.resolve('./src/index.js'),
use: 'imports-loader?wrapper=window'
}
]
}
};
Export
让我们假设,某个library创建出一个全局变量,它期望 consumer(使用者)使用这个变量。为此,我们可以在项目配置中,添加一个小模块来演示说明:
安装
npm i exports-loader -D
定义要导出的js文件
- src/globals.js
const file = 'example.txt';
const helpers = {};
test: function () {
console.log("test something");
}
parse: function() {
console.log('parse something');
}
配置文件
- webpack.config.js
const http = require('http')
const webpack = require('webpack')
module.exports = {
mode: 'development',
entry: './src/index.ts',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname,'./dist')
}
plugins: [new webpack.ProvidePlugin(
_: 'lodash'
)],
module: {
rules: [
{
test: require.resolve('./src/index.js'),
use: 'imports-loader?wrapper=window'
},
{
test: require.resolve('./src/global.js'),
use: 'exports-loader?type=commonjs&exports=file,multiple|helpers.parse|parse'
}
]
}
};
Polyfills
目前为止,我们讨论的所有内容都是处理那些遗留的package,让我们进入到第二个话题: polyfill。
有很多方法来加载polyfill。例如,想要引入 @babel/polyfill
我们只需如下操作:
npm install --save @babel/polyfill
然后,使用import 将其引入到我们的主bundle文件
import '@babel/polyfill'
console.log(Array.from([1,2,3], x => x + x))
注意,这种方式优先考虑正确性,而不考虑bundle体积大小。为了安全和可靠,polyfill/shim 必须运行于所有其他代码之前,而且需要同步加载,或者说,需要在所有polyfill/shim 加载之后,再去加载所有应用程序代码。社区中存在许多误解,即现代浏览器“不需要polyfill,或者polyfill/shim 仅用于添加缺失功能-实际上, 它们通常用于修复损坏实现(repair broken implementation),即使是在最现代的浏览器中,也会出现这种情况。因此,最佳实践仍然是,不加选择地和同步地加载所有polyfill/shim,尽管这会导致额外的bundle体积成本。
进一步 优化Polyfills
不建议使用 import @babel/polyfill
。因为这样做的缺点是会全局引入整个polyfill包,比如 Array.from 会全局引入,不但包的体积大,而且还会污染全局环境。
babel-preset-env
package 通过 browserslist
来转义那些你浏览器不支持的特性。这个preset使用 useBuiltIns
选项,默认值是 false
, 这种方式可以将全局 babel-polyfill
导入,改进为更细粒度的import格式:
安装@babel/preset env及相关的包
npm i babel-loader @babel/core @babel/preset-env -D
webpack.config.js配置
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer');
module.exports = {
mode: 'production',
entry: './src/index.js',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
targets: ['last 1 versions', '> 1%'],
useBuiltIns: 'usage',
corejs: 3, // 这里的3就是corejs的版本根据自己的定义
},
],
],
},
},
},
],
},
};
但是这样配置的话,我们就还需要安装一个依赖:
npm i core-js -D
然后在进行配置,如上!以及不需要手动引入 @babel/polyfill
Dll
动态连接库,dll会对某些库(第三方)进行单独打包。
使用DllPlugin为更改不频繁的代码生成单独的编译结果。这可以提升应用程序的编译速度,尽管它增加了构建过程的复杂度。
1、下载好第三方库后,使用 import 语法在 JS 文件中引入文件
import { gsap } from 'gsap';
2、在根目录中创建 webpack.dll.config.js
const path = require('path');
const Webpack = require('webpack');
module.exports = {
entry: {
// 需要单独打包的库
gsap: ['gsap'],
},
output: {
// 输出文件名称
filename: '[name].js',
// 输出文件路径
path: path.resolve(__dirname, '../dll'),
// 导出库名称
library: '[name]_[hash]',
},
plugins: [
// 引入插件
new Webpack.DllPlugin({
// 对应导出的库名称
name: '[name]_[hash]',
// 生成 manifest 文件
path: path.resolve(__dirname, '../dll/manifest.json'),
}),
],
mode: 'production',
}
3、在 package.json 中编辑
"scripts": {
"dll": "webpack --config ./webpack.dll.config.js"
}
4、执行指令
npm run dll
5、然后配置 webpack.config.js 文件
const path = require('path')
const Webpack = require('webpack')
module.exports = {
...
plugins: [
// 告诉 webpack 哪些库布参与打包,以及使用的名称
new Webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, 'dll/manifest.json')
})
]
}
6、如需在页面中自动引用,需安装一个插件 add-asset-html-webpack-plugin,再在 webpack.config.js 文件中进行配置
下载
npm i add-asset-html-webpack-plugin -D
配置文件
const path = require('path')
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
module.exports = {
...
plugins: [
// 在html中自动引入
new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, 'dll/gsap.js'),
publicPath: './'
})
]
}
扩展
肯定遇到过如果我们在页面中进行导入,但是虽然没有使用这个方法,但是暴露了出来,可以进行如下设置:
webpack.config.js配置
module.exports = {
optimization: {
usedExports: true, // 这样就不会暴露我们没有使用的方法!
}
}
模块联邦
参考博客:https://zhuanlan.zhihu.com/p/485148715
参考博客:https://blog.csdn.net/qq_41887214/article/details/122084965
依赖图 Dependency graph
每当一个文件依赖另一个文件时,webpack 会直接将文件视为存在依赖关系。这使 webpack 可以获取非代码资源,如 images 或 web 字体等。并会把他们作为依赖提供给应用程序。当 webpack 开始工作时,它会根据我们写好的配置,从入口 (Entry) 开始,webpack 会递归的构建一个依赖关系图,这个依赖图包含着应用程序中所需的每个模块,然后将所有模块打包为输出文件。
bundle 分析工具
- webpack-chart:webpack stats 可交互饼图;
- webpack-visualizer:可视化并分析你的bundle,检查哪些模块占用空间,哪些可能使重复使用的;
- webpack-bundle-analyzer:一个 plugin 和 CLI 工具,它将 bundle 内容展示为一个便捷的、交互式、可缩放的树状图形式;
- webpack bundle optimize helper:分析你的bundle并提供可操作的改进措施,减少 bundle 的大小;
- bundle-stats:生成一个 bundle 报告 ( bundle大小、资源、模块 ),并比较不同构建之间的结果。
我们来使用 webpack-bundle-analyzer
实现。
# 首先安装这个插件作为依赖
# npm安装
npm install --save-dev webpack-bundle-analyzer
# yarn安装
yarn add -D webpack-bundle-analyzer
然后我们配置它:
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer');
module.exports = {
// ...
plugins: [new BundleAnalyzerPlugin()],
};
TypeScript
安装
npm i typescript ts-loader -D
# or
yarn add typescript ts-loader -D
配置文件如下:
const http = require('http')
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/index.ts',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname,'./dist')
}
devtool: 'inline-source-map',
module: {
rules: [
test: /\.ts$/,
use: 'ts-loader',
exclude: /node_modules/
],
},
resolve: {
extensions: ['.ts','.js']
},
plugins: [new HtmlWebpackPlugin()],
};
前提:已经创建了tsconfig.js文件!
如果想要整合一些其他第三方模块,请参考以下网站进行寻找:
- https://www.typescriptlang.org/dt/search?search=
- 官网:https://www.typescriptlang.org/
- 中文网:https://www.tslang.cn/
- Github:https://github.com/Microsoft/TypeScript
7、多页面应用
entry 配置
module.exports = {
...
entry: {
main: {
// 将多个文件打包合成一个文件
import: ['app1.js', 'app2.js'],
dependOn: 'jquery',
filename: '[name].js'
},
main2: {
import: 'app3.js',
dependOn: 'jquery',
filename: 'page/[name].js'
},
// 第三方库依赖
jquery: 'jQuery',
finename: '[name].js'
}
}
页面配置
// 引用插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
title: '多页面应用', // 这个配置如果使用的话,需要在html页面配置取值!
template: './index.html',
inject: 'body',
chunks: [
'main',
'jquery',
],
filename: 'index.html'
}),
new HtmlWebpackPlugin({
template: './page.html',
inject: 'body',
chunks: [
'main2',
'jquery',
],
filename: 'page/page.html'
})
]
}
html页面模板取值
<title> <%= htmlWebpackPlugin.options.title %> </title>
8、创建 Library
module.exports = {
...
entry: './mylib.js',
output: {
path: path.resolve(__dirname. 'dist'),
filename: 'mylib.js',
// 防止文件被 tree shaking
library: {
// name: 'mylib', // 设置了type就不能设置name
type: 'module' // 就是以下的几种,如果配置这个就需要添加下面的这个配置!
},
experiments: {
outputModule: true,
}
}
}
然而它只能通过 script 标签引用而发挥作用,它不能运行在 CommonJS、AMD、Nodejs等环境中。
作为一个库作者,我们希望它能够兼容不同的环境,也就是说,用户应该可以通过以下方式使用打包后的库:
- CommonJS module require:
const webpackNumbers = require('webpack-numbers');
// ...
webpackNumbers.wordToNum('Two');
- AMD module require:
require(['webpackNumbers'],function (webpackNumbers) {
// ...
webpackNumbers.wordToNum('Two')
});
- script tag:
<body>
<script src="http://example.org/webpack-numbers.js"></script>
<script>
// ...
// Global variable
webpackNumbers.wordToNum('Five');
// Property in the window object
window.webpackNumbers.wordToNum('Five')
// ...
</script>
</body>
我们更新 output.library
配置项,将其 type
设置为 umd
:
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
// experiments: {
// outputModule: true,
// },
output: {
library: {
name: 'mylib',
type: 'umd',
},
globalObject: 'globalThis', // 如果使用其他模块就要设置这个,例commonjs..
},
externals: {
lodash: {
commonjs: 'lodash',
amd: 'lodash',
root: '_'
}
}
};
9、完整配置 *
开发环境与生产环境优化建议
一、增量编译
使用webpack的watch mode(监听模式)。而不使用其他工具来watch文件和调用webpack。内置的watch mode会记录时间戳并将此信息传递给compilation以使缓存失效。
在某些配置环境中,watch mode会回退到poll mode(轮询模式)。监听许多文件会导致CPU大量负载。在这些情况下,可以使用watchOptions. poll来增加轮询的间隔时间。
二、在内存中编译
下面几个工具通过在内存中(而不是写入磁盘)编译和serve资源来提高性能:
- webpack-dev-server
- webpack- hot-middleware
- webpack -dev- middleware
三、stats.toJson 加速
webpack 4默认使用stats.toJson()输出大量数据。除非在增量步骤中做必要的统计,否则请避免获取stats对象的部分内容。
webpack-dev-server在v3.1.3以后的版本,包含-个重 要的性能修复,即最小化每个增量构建步骤中,从stats 对象获取的数据量。
四、Devtool
需要注意的是不同的devtool 设置,会导致性能差异。
- "eval"具有最好的性能,但并不能帮助你转译代码。
- 如果你能接受稍差一些的 map质量,可以使用cheap-source-map变体配置来提高性能
- 使用eval-source -map变体配置进行增量编译。
在大多数情况下,最佳选择是eval-cheap-module-source-map。
五、避免在生产环境才用到的工具
某些utility, plugin和loader都只用于生六坏境。例如,在开发环境下使用TerserPlugin来minify(圧縮)和mangle(混淆破坏)代码是没有意义的。通常在开发环境下,立垓排除以下这些工具:
- TerserPlugin
- [fullhash]/[chunkhash]/[contenthash]
- AggressiveSplittingPlugin
- AggressiveMergingPlugin
- ModuleConcatenationPlugin
六、最小化 entry chunk
Webpack只会在文件系统中输出已经更新的chunk。某些配置选项( HMR,
output.chunkFilename的[name]/chunkhash, [fullhash]) 来说,除了对已经更新的chunk无效之外,对于entry chunk也不会生效。
确保在生成entry chunk时,尽量减少其体积以提高性能。下面的配置为运行时代码创建了一个额外的chunk,所以它的生成代价较低:
module.exports = {
// ...
optimization: {
runtimeChunk: true,
}
}
七、避免额外的优化步骤
webpack 通过执行额外的算法任务,来优化输出结果的体积和加载性能。这些游湖适合于小型代码库,但是在大型代码库中却非常耗费性能:
module.exports = {
// ...
optimization: {
removeAvailableModules: false,
removeEmptyChunks: false,
splitChunks: false
}
}
八、输出结果不携带路径信息
Webpack会在输出的bundle中生成路径信息。然而,在打包数千个模块的项目中,这会导致造成垃圾回收性能压力。在
options.output.pathinfo设置中关闭:
module.exports = {
// ...
output: {
pathinfo: false,
}
}
九、Node.js版本
注意更新到最新!
十、TypeScript loader
你可以为loader传入transpileOnly选项,以缩短使用ts-loader时的构建时间。使用此选项,会关闭类型检查。如果要再次开启类型检查,请使用ForkTsCheckerWebpackPlugin。使用此插件会将检查过程移至单独的进程,可以加快TypeScript的类型检查和ESLint插入的速度。
module.exports = {
// ...
test: /\.tsx?$/,
use: [
{
loader: 'ts-loader',
options: {
transpileOnly: true
}
}
]
}
生产环境提升构建性能
不启用 SourceMap
source map相当消耗资源,开发环境下不要设置source map
开发环境
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CommonCssLoader = [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: { importLoaders: 1 }
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'postcss-preset-env'
]
]
}
}
}
]
process.env.NODE_ENV = 'development'
module.exports = {
entry: './assets/js/main.js',
output: {
filename: 'js/app.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
oneOf:[
{
// 处理 css 资源
test: /\.css$/i,
use: [...CommonCssLoader]
},
{
// 处理 scss 资源
test: /\.s[ac]ss$/i,
use: [...CommonCssLoader, 'sass-loader']
},
{
// 处理图片资源
test: /\.(jpg|jpeg|png|gif)$/i,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:12].[ext]',
esModule: false,
outputPath: 'images'
}
},
{
// 处理 html 中的图片资源
test: /\.html$/i,
loader: 'html-loader'
}
]
}
]
},
plugins: [
// 处理 html 资源
new HtmlWebpackPlugin({
template: './index.html',
minify: {
collapseWhitespace: true,
removeComments: true
}
}),
new MiniCssExtractPlugin({
// 对输出结果重命名
filename: 'css/app.css'
})
],
mode: 'development',
devServer: {
static: {
directory: path.join(__dirname, 'build'),
},
compress: true,
port: 3000,
liveReload: true,
watchFiles: {
paths: [
'./assets/*/*',
'./*.html'
],
options: {
usePolling: false
}
},
open: true,
hot: true
},
devtool: 'source-map'
}
生产环境
// 生产环境
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CommonCssLoader = [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: { importLoaders: 1 }
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'postcss-preset-env'
]
]
}
}
}
]
// process.env.NODE_ENV = 'development'
module.exports = {
entry: './assets/js/main.js',
output: {
filename: 'js/app.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
// 处理 css 资源
test: /\.css$/i,
use: [...CommonCssLoader]
},
{
// 处理 scss 资源
test: /\.s[ac]ss$/i,
use: [...CommonCssLoader, 'sass-loader']
},
{
// 处理图片资源
test: /\.(jpg|jpeg|png|gif)$/i,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:12].[ext]',
esModule: false,
outputPath: 'images'
}
},
{
// 处理 html 中的图片资源
test: /\.html$/i,
loader: 'html-loader'
}
]
},
plugins: [
// 处理 html 资源
new HtmlWebpackPlugin({
template: './index.html',
minify: {
collapseWhitespace: true,
removeComments: true
}
}),
new MiniCssExtractPlugin({
// 对输出结果重命名
filename: 'css/app.css'
})
],
mode: 'production',
}
10、详细配置 *
11、插件 *
官方列举了可在 Webpack 5 中所有可使用的插件。
https://webpack.docschina.org/plugins/
12、使用技巧 *
// 忽略警告(包含注释)
// eslint-disable-next-line
// 输出 ES6 版本代码
output.ecmaVersion: 2015
// 删除全局 webpack
npm uninstall webpack webpack-cli --global
13、参考链接
官方文档(中文)
https://webpack.docschina.org/concepts/
https://stackoverflow.com/questions/49348365/webpack-4-size-exceeds-the-recommended-limit-244-kib
1、运行指令:
webpack4版本处理:
开发环境:webpack ./sr c/index.js -o ./build/built.js --mode=development
生产环境:webpack ./src/index.js -o ./build/built.js --mode=production
webpack5版本处理:(如果你是webpack5,推荐你使用这个命令!)
开发环境:webpack --entry ./src/index.js --output-filename bundle.js --mode=development
生产环境:webpack --entry ./src/index.js --output-filename bundle.js --mode=production