webpack 是一个模块打包器。它的主要目标是将 JavaScript 文件打包在一起. 加载资源不再与index.html
对话, 不再通过标签引入. 而是与 entry points (入口js文件)对话, 通过import
引入资源.
webpack能直接加载的只有 js 和 json, 要想加载其他类型的资源(css, sass, ts, jpg, png 等)要使用loader, 想要更多功能(打包优化, 压缩等)需要plugin.
loader都是官方出品, 配置方法比较规范. plugin就不一定了.
一般来说,我们会将配置文件拆成三部分:
webpack.common.js
: 开发环境和生产环境通用配置webpack.dev.js
: 开发环境配置,用于 npm start
webpack.prod.js
: 生产环境配置,用于 npm run-script build
可以利用 webpack-merge
这个库,帮我们把通用配置,结合到另外两个配置里。
不同环境,使用的 loader 和 plugin 可能会不同。因为在开发环境中,我们考虑的是怎么让项目更方便的在本地跑起来,方便本地调试等。而在生产环境中,我们需要考虑代码打包后的体积,兼容性等。
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
//...
modules: {
rules:[
{
test: /\.css$/i,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.html$/i,
loader: 'html-loader',
},
],
plugins:[
new HtmlWebpackPlugin({
template: './src/index.html'
})
]
}
}
方法二: 不知道用什么loader或plugin时, 可以参考React和Vue.
注意,在一个 rule 里配置多个 loader 时,执行顺序是从后到前。例如,配置 sass 文件的 loader 时,要把 sass-loader 放到 css-loader 等之后,这样就可以先让 sass-loader 将 sass 文件转为 css,再利用 css 相关 loader 进行进一步的处理。
npm install -D xxx-loader
webpack.config.js
)
module.exports = {
...
module: {
rules: [
{
test: /(\.csv)$/ // loader要处理的文件的名字, 一般用正则表达式表示
loader: "file-loader" // loader = loader名字/loader列表/外部引入模块提供的对象/自定义对象(包含loader和option等),
}
]
}
}
import "xxx.jpg";
import "xxx.css";
...
npm install xxx-plugin -D
webpack.config.js
)
const MyPlugin = require("my-plugin"); // 有些可能引入方式略有不同, 需要解构赋值等.
module.exports = {
// ...
plugins: [
new MyPlugin(params),
]
};
3.一些 plugin 可能还提供了自己的 loader, 需要 plugin 和 loader 结合使用。 比如mini-css-extract-plugin
。对于它们提供的loader,按照普通 loader 来配置。
安装 loader
style-loader
:用于在html文档中创建一个style标签, 将样式塞进去css-loader
:将css转换为CommonJS模块, 翻译 @import
和 @url
并且解析它们。配置 webpack:
{
test: /\.css$/i,
use: [
'style-loader',
'css-loader'
]
}
在入口文件中导入样式文件:
import "index.css"
注意,不可以加载样式文件中的图片链接. 要加载需要额外的loader, 详见该部分: 打包图片资源
webpack不能解析html文件, 需要借助插件编译解析. 只有打包 html 不是用的 loader 而是plugin, 不走入口文件.
html-webpack-plugin
图片文件webpack不能解析, 需要借助loader编译解析. 前两个loader用于打包样式文件中的图片, 后一个loader用于打包html中的图片.
file-loader
url-loader
file-loader
的上层封装, 使用时需配合file-loader
使用html-loader
Url-loader vs File-loader
除了js和json及上述资源外的其他资源(如字体文件), webpack也不能解析, 需要借助loader编译解析.
file-loader
live reload, 自动刷新整个页面. 想要局部刷新, 见HMR
webpack-dev-server
package.json
中的webpack配置对象(顶级目录下)
devServer: {
open: true, // 自动打开浏览器
compress: true, // 启动gzip压缩(数据由服务器传输到浏览器时是可以压缩的)
port: 3000 // 端口号
}
url-loader
部分配置
'../build/images'
–> publicPath: 'images'
package.json
中的scripts指令
webpack-cli@3.x.x
: "start": "webpack-dev-server"
webpack-cli@4.x.x
: "start": "webpack serve"
run
, 比如start: npm run start
等价于 npm start
.npm run start
安装基本库
npm install -D typescript
在根目录下配置好 ts 的配置文件 tsconfig.json
. 可参考官网手册 以及一些开源项目的配置。尤其注意配置好 ts 生效的文件夹。
安装其他库的类型库。比如如果有使用 jest 的话,执行 npm install -D @types/jest
安装 loader
npm install -D ts-loader
配置 webpack
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
安装基本库
npm install -D node-sass # provides binding for Node.js to LibSass, a Sass compiler.
或
npm install -D sass # dark sass
安装 loader
npm install -D style-loader css-loader sass-loader
{
test: /\.(scss|sass|css)$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
import 'xxx.sass'
本节提到的plugin只安装在webpack.prod.js
, 不安装在webpack.dev.js
.
默认不装插件的情况下, import 样式表, 会直接将其转化为内联样式, 不会生成单独的css文件.
mini-css-extract-plugin
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader, // 取代style-loader
'css-loader',
'less-loader'
]
}
new MiniCssExtractPlugin({
filename: "css/[name].css", // 原来的所有css,less等类型的文件, 合并成一个名为main的css文件.
})
postcss-loader
https://webpack.js.org/loaders/postcss-loader/
很多教程只安装这个, 不装下面的一些package. 但是只安装这个对IE兼容性不太好
postcss-preset-env
: 指定运行环境postcss-flexbugs-fixes
postcss-normalize
: 加上后对老浏览器的支持比较好, react在用autoprefixer
{
loader: 'postcss-loader',
options: {
postCssOptions: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: "no-2009" // flex老语法新浏览器反而不支持
},
stage: 3, // 兼容级别
}),
require('postcss-normalize')(),
]
}
},
}
.browserslistrc
(直接在package.json中顶级目录配置browserlist
的话, 有些文件可能会失效)
last 1 version
> 1%
IE 10 # sorry
调成production模式只能压缩js, 不能压缩css. 想要压缩css, 我们需要借助其他的插件.
optimize-css-assets-webpack-plugin
const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");
new OptimizeCssAssetsPlugin({
cssProcessorPluginOptions: {
preset: ['default', { descardComments: { removeAll: true } }],
},
cssProcessorOptions: {
map: {
inline: false,
annotation: true,
}
}
})
有些浏览器不支持新特性, 需要将浏览器不能识别的新语法转换成原来识别的旧语法(es6->es5),做浏览器兼容性处理.
@babel/core
babel-loader
@babel/preset-env
@babel/polyfill
babel
只能转换部分低级语法的问题(如: let/const/解构赋值), 引入polyfill
可以转换高级语法(如: Promise)core-js
module.exports = {
...
mode: "production"
}
html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
// 压缩html代码
minify: {
removeComments: true,
removeRedundantAttributes: true, // 移除无用的标签
removeEmptyAttributes: true, // 移除空标签
removeStyleLinkTypeAttributes: true, // 移除rel="stylesheet"
/* html文档中可能有嵌入js,css等, 精简它们 */
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
collapseWhitespace: true,
useShortDoctype: true, // 使用短的文档申明
keepClosingSlash: true, // 自结束
}
})
],
}
对js基本语法错误, 或是隐患, 进行提前检查
eslint
eslint-loader
exclude: /node_modules/
, 排除node_modules文件夹enforce: pre
, 提前加载使用(在被其他loader更改前就检查完)webpack.config.js
对应rule中的options
里, 而是放在package.json的顶级目录下
{
"name": "",
"version": "",
...
"eslintConfig": {
"parserOptions": {
"ecmaVersion": 6, // 支持es6, 不加的话用es6语法会报错
"sourceType": "module" // 使用es6模块化
},
"env":{//设置环境
"browser": true, // 支持浏览器环境: 能使用window上的全局变量
"node": true // 支持服务器环境, 能使用node上global的全局变量
},
"globals": { // 声明使用的全局变量, 这样即使没有定义也不会报错了
"$": "readonly"
},
"rules": { // eslint检查的规则(覆盖默认规则). 0 忽略, 1 警告, 2 错误
"no-console": 0, // 源代码有console忽略报错
"eqeqeq": 2, //用==而不用===就报错
},
"extends": "eslint:recommended" // 其他的使用eslint推荐的默认规则 https://cn.eslint.org/docs/rules/
}
}
eslint-config-airbnb-base
(可选)
eslint-plugin-import
(可选)HMR(Hot Module Replacement, 热模替换, 模块热更新)可以实现局部刷新(对比webpack-dev-server自带的live reload, 真个页面刷新). 它允许在运行时更新所有类型的模块, 而无需完全刷新(只更新变化的模块, 不变化的模块不更新).
webpack-dev-server
webpack.config.js
中的devServer配置
devServer: {
open: true, // 自动打开浏览器
compress: true, // 启动gzip压缩
port: 3000, // 端口号
hot: true // 开启HMR
}
index.js
外, 还需要在入口中加上index.html
. 这样一来, 更新js和css等模块时就会使用HMR, 更新主页面时就会刷新整个页面.
entry: ['./src/js/index.js', './src/index.html']
我们发布到生产环境的代码,一般都有如下步骤:
这三个步骤,都使得实际运行的代码不同于开发代码,不管是 debug 还是捕获线上的报错,都会变得困难重重。
解决这个问题的方法,就是使用sourceMap。
简单说,sourceMap就是一个文件,里面储存着位置信息。仔细点说,这个文件里保存的,是转换后代码的位置,和对应的转换前的位置。有了它,出错的时候,通过断点工具可以直接显示原始代码,而不是转换后的代码。
devtool
webpack.config.js
中配置
module.exports= {
...
devtool: 'cheap-module-eval-source-map'
}
eval-cheap-module-source-map
cheap-module-source-map
在与他人协作编程时, 其他人可能会不小心直接向打包文件夹中添加文件, 这样的话该文件在下次重新打包后并不会被删除. 每次打包生成了新的文件, 都需要手动删除之前的文件, 引入插件帮助我们自动删除上一次的文件.
clean-webpack-plugin
const {CleanWebpackPlugin} = require("clean-webpack-plugin");
参考资料: