webpack学习笔记
一、 学习webpack
1. webpack基本环境
npm init -y
cnpm i webpack webpack-cli -D
-
package.json
下增加打包脚本
"scripts": {
"build": "webpack"
},
-
npm run build
执行打包, webpack默认是执行src/index.js
,然后生成到dist
目录, 相当于如下的配置文件webpack.config.js
const path = require('path')
module.exports = {
entry: {
main: './src/index.js'
},
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
}
}
-
webpack
默认会读取根目录下的webpack.config.js
,默认的模式是production
会启用压缩,增加脚本
"scripts": {
"dev": "webpack --mode=development",
"build": "webpack"
},
2. webpack打包样式
index.css
* { margin: 0; padding: 0;}
body {
background-color: red;
}
index.js
import './index.css'
console.log('webpack learning')
- 要打包样式需要用到
style-loader
和css-loader
cnpm i style-loader css-loader -D
- 配置
webpack
的module
节点
const path = require('path')
module.exports = {
entry: {
main: './src/index.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{ test: /\.css$/, use: ['style-loader','css-loader']}
]
}
}
3. html-webpack-plugin
插件
cnpm i html-webpack-plugin -D
- 配置
webpack
const HtmlWebpackPlugin = require('html-webpack-plugin')
plugins: [
new HtmlWebpackPlugin({
title: 'webpack + react学习',
template: 'index.html'
})
]
-
index.html
模板文件
<%= htmlWebpackPlugin.options.title %>
4. 样式文件打包到单独的文件中
cnpm i mini-css-extract-plugin -D
- 配置
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module: {
rules: [
{ test: /\.css$/, use: [{
loader: MiniCssExtractPlugin.loader
},'css-loader']}
]
},
plugins: [
new HtmlWebpackPlugin({
title: 'webpack + react学习',
template: 'index.html'
}),
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css', // 这里的id就是 entry 对象的 main
ignoreOrder: false, // Enable to remove warnings about conflicting order
})
]
5. 区别开发环境和生产环境
cnpm i cross-env -D
- 改写
package.json
的执行脚本
"scripts": {
"dev": "cross-env NODE_ENV=development webpack --mode=development",
"build": "cross-env NODE_ENV=production webpack"
},
- 改写
mini-css-extract-plugin
的配置在开发环境下启用hmr(hot module replacement)
const devMode = process.env.NODE_ENV !== 'production'
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module: {
rules: [
{ test: /\.css$/, use: [{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: process.env.NODE_ENV === 'development'
}
},'css-loader']}
]
},
plugins: [
new HtmlWebpackPlugin({
title: 'webpack + react学习',
template: 'index.html'
}),
new MiniCssExtractPlugin({
filename: devMode ? '[name].css' : '[name].[hash].css',
chunkFilename: devMode ? '[id].css' : '[id].[hash].css', // 这里的id就是 entry 对象的 main,此选项决定了非入口(non-entry) chunk 文件的名称。
ignoreOrder: false, // Enable to remove warnings about conflicting order
})
]
6. webpack-dev-server
cnpm i webpack-dev-server -D
-
webpack.config.js
配置文件增加devServer
配置节
devServer: {
contentBase: './dist',
// host: '0.0.0.0',
port: 3000,
open: true
},
- 修改
package.json
中的启动脚本
"scripts": {
"dev": "cross-env NODE_ENV=development webpack-dev-server --mode=development",
"build": "cross-env NODE_ENV=production webpack"
},
7. 热模块替换
- 首先在
devServer
配置节启用热模块替换
const webpack = require('webpack')
devServer: {
contentBase: './dist',
// host: '0.0.0.0',
port: 3000,
hot: true, // 启用热模块替换
open: true
},
plugins: [
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
]
- 将
css
的打包修改成开发时使用style-loader
, 生产环境使用mini-css-extract-plugin
module: {
rules: [
{ test: /\.css$/, use: [
devMode ? 'style-loader' :
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: process.env.NODE_ENV === 'development'
}
},'css-loader']}
]
},
8. webpack
的devtool
可以参考: https://segmentfault.com/a/1190000008315937
- 开发环境下一般配置成:
devtool: 'cheap-module-source-map',
- 启用
css-loader
的sourceMap
module: {
rules: [
{ test: /\.css$/, use: [
devMode ? 'style-loader' :
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: process.env.NODE_ENV === 'development'
}
}, {
loader: 'css-loader',
options: { sourceMap: true }
}]}
]
},
- 几个关键字意义
- eval: 使用eval包裹模块代码
- source-map: 产生.map文件
- cheap: 不包含列信息(关于列信息的解释下面会有详细介绍)也不包含loader的sourcemap
- module: 包含loader的sourcemap(比如jsx to js ,babel的sourcemap)
- inline: 将.map作为DataURI嵌入,不单独生成.map文件(这个配置项比较少见)
9. 给样式加浏览器厂商前缀
- 安装
cnpm i postcss-loader autoprefixer -D
- 配置
module: {
rules: [
{ test: /\.css$/, use: [
devMode ? 'style-loader' :
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: process.env.NODE_ENV === 'development'
}
}, {
loader: 'css-loader',
options: { sourceMap: true, importLoaders: 1 }
}, "postcss-loader"]}
]
},
注意上面的css-loader
增加了importLoaders: 1
选项,该选项表示的是在交给css-loader
处理前的 处理loader
数量,默认值是0,上面配置了postcss-loader
,所以改成1
- 添加 postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')
]
}
10. 清理dist目录
cnpm i clean-webpack-plugin -D
- 使用
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
plugins: [
new CleanWebpackPlugin(),
...
]
11. 处理图片资源
cnpm i file-loader url-loader -D
- 配置
webconfig.js
module: {
rules: [
{ test: /\.css$/, use: [
devMode ? 'style-loader' :
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: process.env.NODE_ENV === 'development',
publicPath: '../'
}
}, {
loader: 'css-loader',
options: { sourceMap: true, importLoaders: 1 }
}, "postcss-loader"]
},
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 1024 * 500, // 500KB以下会被转成base64
name: 'images/[name].[hash:8].[ext]',
},
},
],
},
]
},
plugins: [
new MiniCssExtractPlugin({
filename: devMode ? 'css/[name].css' : 'css/[name].[hash].css',
chunkFilename: devMode ? 'css/[id].css' : 'css/[id].[hash].css', // 这里的id就是 entry 对象的 main,此选项决定了非入口(non-entry) chunk 文件的名称。
ignoreOrder: false, // Enable to remove warnings about conflicting order
})
]
上面有个路径问题需要特别注意
-
MiniCssExtractPlugin
的filename
增加了css/
,也就是将提取的css都放到 css目录 - 图片资源放置到
images
目录,所以url-loader
也给name
增加了images/
- 因为样式都被放到了
css
目录,要相应的给MiniCssExtractPlugin.loader
增加publicPath: '../'
12. 字体文件
cnpm i file-loader -D
- 配置
module: {
rules: [
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
use: [
{
loader: 'file-loader',
options: {
name: 'fonts/[name].[hash:8].[ext]'
}
}
]
}
]
}
13. 使用babel
cnpm install -D babel-loader @babel/core @babel/preset-env
- 根目录下增加
babel
的配置文件.babelrc
{
"presets": ["@babel/preset-env"]
}
- 修改
webpack.config.js
启用babel-loader
output: {
filename: 'js/bundle.js', // 将js都保存到js目录
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{ test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" }
]
}
14. 完整的webpack.config.js
webpack.config.js
const path = require('path')
const devMode = process.env.NODE_ENV !== 'production'
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
entry: {
main: './src/index.js'
},
output: {
filename: 'js/bundle.js', // 将js都保存到js目录
path: path.resolve(__dirname, 'dist')
},
devtool: 'cheap-module-source-map',
devServer: {
contentBase: './dist',
// host: '0.0.0.0',
port: 3000,
hot: true, // 启用热模块替换
open: true
},
module: {
rules: [
{ test: /\.css$/, use: [
devMode ? 'style-loader' :
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: process.env.NODE_ENV === 'development',
publicPath: '../'
}
}, {
loader: 'css-loader',
options: { sourceMap: true, importLoaders: 1 }
}, "postcss-loader"]
},
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 1024 * 500, // 500KB以下会被转成base64
name: 'images/[name].[hash:8].[ext]',
},
},
],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
use: [
{
loader: 'file-loader',
options: {
name: 'fonts/[name].[hash:8].[ext]'
}
}
]
},
{ test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" }
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'webpack + react学习',
template: 'index.html'
}),
new MiniCssExtractPlugin({
filename: devMode ? 'css/[name].css' : 'css/[name].[hash].css',
chunkFilename: devMode ? 'css/[id].css' : 'css/[id].[hash].css', // 这里的id就是 entry 对象的 main,此选项决定了非入口(non-entry) chunk 文件的名称。
ignoreOrder: false, // Enable to remove warnings about conflicting order
}),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
]
}
-
package.json
中的脚本
"scripts": {
"dev": "cross-env NODE_ENV=development webpack-dev-server --mode=development",
"devTest": "cross-env NODE_ENV=development webpack --mode=development",
"build": "cross-env NODE_ENV=production webpack"
},
15. 将开发和生产环境的公共配置抽离出来
因为开发和生产的配置有些是共用的配置,有些是不同的配置, 如: 开发时使用style-loader
方便hmr
,不需要mini-css-extract-plugin
,有devServer
配置
而生产环境是不需要热模块替换的(hot module replacement
)
- 建立
build
文件夹 cnpm i webpack-merge -D
build/webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
entry: {
main: path.resolve(__dirname, '../src/index.js')
},
output: {
filename: 'js/bundle.js', // 将js都保存到js目录
path: path.resolve(__dirname, '../dist')
},
module: {
rules: [
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 1024 * 500, // 500KB以下会被转成base64
name: 'images/[name].[hash:8].[ext]',
},
},
],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
use: [
{
loader: 'file-loader',
options: {
name: 'fonts/[name].[hash:8].[ext]'
}
}
]
},
{ test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" }
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'webpack + react学习',
template: 'index.html'
})
]
}
build/webpack.dev.js
const merge = require('webpack-merge')
const webpack = require('webpack')
const common = require('./webpack.common')
module.exports = merge(common, {
mode: 'development',
devtool: 'cheap-module-source-map',
devServer: {
contentBase: '../dist',
// host: '0.0.0.0',
port: 3000,
hot: true, // 启用热模块替换
open: true
},
module: {
rules: [
{ test: /\.css$/, use: [
'style-loader',
{
loader: 'css-loader',
options: { sourceMap: true, importLoaders: 1 }
},
'postcss-loader']
},
]
},
plugins: [
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
]
})
build/webpack.prod.js
const merge = require('webpack-merge')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const common = require('./webpack.common')
module.exports = merge(common, {
mode: 'production',
devtool: 'source-map',
module: {
rules: [
{ test: /\.css$/, use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../'
}
},
{
loader: 'css-loader',
options: { sourceMap: true, importLoaders: 1 }
},
'postcss-loader']
},
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].[hash].css',
chunkFilename: 'css/[id].[hash].css', // 这里的id就是 entry 对象的 main,此选项决定了非入口(non-entry) chunk 文件的名称。
ignoreOrder: false, // Enable to remove warnings about conflicting order
})
]
})
- 最后修改
package.json
中的脚本
"scripts": {
"dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.dev.js",
"devTest": "cross-env NODE_ENV=development webpack --config build/webpack.dev.js",
"build": "cross-env NODE_ENV=production webpack --config build/webpack.prod.js"
},
遗留配置:
- 使用 scss、stylus等css预处理器
- treeshaking 配置
- 公共模块的打包配置 (多页面需要重点考虑,使用默认也可以)