近期看了webpack4.0的教程,以下是视频学习整理,供大家参考
视频地址:https://www.bilibili.com/video/av41546218
视频中的笔记见:https://malun666.github.io/aicoder_vip_doc/#/pages/vip_2webpack
webpack中文文档:https://www.webpackjs.com/
我根据视频教程的学习,截止到第15个视频,实践的代码及配置详见:
https://github.com/Niccce/webpackLearning/blob/master/webpackdemo/webpack4.x%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8.md
文章目录
- 前言
- 1.Webpack快速入门demo
- 2.webpack处理css模块
- 3.webpack的模块和sass
- 4.webpack的sass添加c3前缀和sourcemap处理
- 5.webpack的css提取成单独的文件
- 6.webpack的css和js压缩插件使用
- 7.解决文件名带哈希值的问题
- 8.清理dist目录插件clean-webpack-plugin应用
- 9.图片处理及优化
- 10.图片base64优化和字体处理
- 11.webpack的配置合并、提前公共配置
- 12.webpack的启动监控自动编译和启用js的sourcemap
- 13.webpack的启动热更新和代理配置
- 14.webpack的代理服务器设置和babel转换及优化
- 15.webpack的eslint校验配置
前言
webpack是什么
本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
loader
loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。
模块(modules)
对比 Node.js 模块,webpack 模块能够以各种方式表达它们的依赖关系,几个例子如下:
- ES2015
import
语句 - CommonJS
require()
语句 - AMD
define
和require
语句 - css/sass/less 文件中的
@import
语句。 -
- 样式(
url(...)
)或 HTML 文件(<img src=...>
)中的图片链接(image url)
- 样式(
1.Webpack快速入门demo
webpack最好安装在项目中,若全局安装,就都是全局安装的版本,但是有一些老项目可能是用老版本的webpack开发的。
webpack官方文档写到:不推荐全局安装 webpack。这会将你项目中的 webpack 锁定到指定版本,并且在使用不同的 webpack 版本的项目中,可能会导致构建失败。
- 4.0之前的版本安装webpack就可以了,但是4.0之后的版本需要再安装webpack-cli
创建demo
- 新建一个空的文件夹wpdemo
- 打开终端,输入
npm init -y
,初始化package.json文件 - 在终端输入
npm i -D webpack
本地安装webpack - 在终端输入
npm i -D webpack-cli
安装webpack-cli - 新建dist文件夹及其中的index.html;新建src文件夹机器中的index.js
- 在终端输入
npm i lodash -P
安装lodash - 编写html.js文件
import _ from 'lodash'; function createDomELement() { let dom = document.createElement('div'); //使用了loadsh 的 join() 方法,将 字符串 用 空格进行 拼接 dom.innerHTML = _.join(['你好', 'webpack'], ''); return dom; } document.body.appendChild(createDomELement());
- 在根目录下新建文件webpack.config.js
- 编写webpack.config.js
const path = require('path'); //引用path模块 //对外导出一个对象 module.exports = { entry: './src/index.js', //入口 mode: 'development', //代表我们现在是开发阶段 output: { //输出 filename: 'main.js', //最终打包后的文件名 path: path.resolve(__dirname,'dist')//最终main.js在哪个文件夹下 } }
- 执行
npx webpack
进行打包,dist中出现了main.js文件 - 编写dist中的index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <script src="./main.js"></script> </body> </html>
- 注:这个demo也可以看webpack的中文文档实例
- https://www.webpackjs.com/guides/installation/
2.webpack处理css模块
- webpack默认处理不了css文件,需要对应的loader去解析
- css-loader可以让webpack解析css模块
- style-loader可以帮我们把依赖的的css文件转换成style标签,加入到html文件中去
-
在src中新建style文件夹及其中的index.css文件
.box{ color: yellow; }
-
将样式给指定元素,修改index.js
import _ from 'lodash'; import './style/index.css'; function createDomELement() { let dom = document.createElement('div'); //使用了loadsh 的 join() 方法,将 字符串 用 空格进行 拼接 dom.innerHTML = _.join(['你好', 'webpack'], ''); // dom.className = 'box'; dom.classList.add('box'); return dom; } document.body.appendChild(createDomELement());
-
安装:
npm i -D style-loader css-loader
-
修改webpack.config.js
const path = require('path'); //引用path模块 //对外导出一个对象 module.exports = { entry: './src/index.js', //入口 mode: 'development', //代表我们现在是开发阶段 output: { //输出 filename: 'main.js', //最终打包后的文件名 path: path.resolve(__dirname,'dist')//最终main.js在哪个文件夹下 }, module: { rules: [ //模块处理规则 { test: /\.css$/,//当引入的模块的后缀满足这个,就使用use的loader处理 use: ['style-loader', 'css-loader'] //处理顺序从右向左 } ] } }
-
执行
npx webpack
查看效果
- 拓展:修改package.json文件,可以用
npm run build
代替npx webpack
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "npx webpack" }
3.webpack的模块和sass
https://www.webpackjs.com/configuration/module/#rule-test
- 加载sass需要sass-loader
- sass-loader需要node-sass
-
安装
npm i -D sass-loader node-sass
此处可能会有遇到node-sass安装失败的问题,详见:https://blog.csdn.net/qq_41445033/article/details/100032529 -
修改webpack.config.js
const path = require('path'); //引用path模块 //对外导出一个对象 module.exports = { entry: './src/index.js', //入口 mode: 'development', //代表我们现在是开发阶段 output: { //输出 filename: 'main.js', //最终打包后的文件名 path: path.resolve(__dirname,'dist')//最终main.js在哪个文件夹下 }, module: { rules: [ //模块处理规则 { test: /\.(sc|c|sa)ss$/, //当引入的模块的后缀满足这个,就使用use的loader处理 use: ['style-loader', 'css-loader','sass-loader'] //处理顺序从右向左 } ] } }
- 普通的css文件也可以经过sass-loader处理
- 假设在style文件夹中添加一个a.scss文件
$bgcolor:red; .box{ background-color: $bgcolor; font-size: 30px; }
- 在index.js,导入a.scss
import './style/a.scss'
- 执行
npx webpack
查看效果
4.webpack的sass添加c3前缀和sourcemap处理
Source Map处理
- Source Map可以帮助我们在使用浏览器的开发者工具查看元素的样式时,追踪到样式的来源
- 如果没有开启source map,查看样式来源的时候只能看到来自
<style></style>
方法:
修改webpack.config.js文件
module: {
rules: [//模块处理规则
{
test: /\.(sc|c|sa)ss$/,
//当引入的模块的后缀满足这个,就使用use的loader处理
use: [//处理顺序从后向前
'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}]
}
]
}
为样式添加前缀(-webkit-等等)
-
PostCSS是一个用 JavaScript 工具和插件转换 CSS 代码的工具,是一个css的预处理工具
- 可以帮助我们
- 给CSS3的属性添加前缀
- 样式格式校验(stylelint)
- 实现CSS的模块化,防止CSS样式冲突等
- 可以帮助我们
-
比较常用的就是使用 PostCSS 进行添加前缀,以此为例:
-
安装postcss-loader
npm i -D postcss-loader
-
接下来,我们需要引用什么插件,就要把什么插件装上,我们需要添加前缀
npm install autoprefixer --save-dev
npm i -D postcss-loader npm install autoprefixer --save-dev # 以下可以不用安装 # cssnext可以让你写CSS4的语言,并能配合autoprefixer进行浏览器兼容的不全,而且还支持嵌套语法 $ npm install postcss-cssnext --save-dev # 类似scss的语法,实际上如果只是想用嵌套的话有cssnext就够了 $ npm install precss --save-dev # 在@import css文件的时候让webpack监听并编译 $ npm install postcss-import --save-dev
-
修改webpack.config.js文件
module: { rules: [ //模块处理规则 { test: /\.(sc|c|sa)ss$/, //当引入的模块的后缀满足这个,就使用use的loader处理 use: [//处理顺序从后向前 'style-loader', { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { ident: 'postcss', sourceMap: true, plugins: loader => [ require('autoprefixer')({ browsers: ['> 0.15% in CN'] }) // 添加前缀 ] } }, { loader: 'sass-loader', options: { sourceMap: true } }] } ] }
-
修改a.sass
$bgcolor:red; .box{ background-color: $bgcolor; font-size: 30px; display: flex; }
-
运行查看效果,F12看样式,可以看到
.box { background-color: red; font-size: 30px; display: -webkit-box; display: flex; }
-
5.webpack的css提取成单独的文件
- 抽取了样式,就不能再用 style-loader 注入到 html 中了。
- webpack4 开始使用: mini-css-extract-plugin插件, 1-3 的版本可以用:extract-text-webpack-plugin
- webpack.config.js中的
mode
必须设置为production
,插件才有用;在开发阶段的话就用style-loader就可以了
- 安装
npm install --save-dev mini-css-extract-plugin
- 在根目录下新建配置文件
webpack.product.config.js
,即开发阶段和生成最终dist版本用两个不同的配置文件- 将webpack.config.js的内容粘贴进去
- 引入mini-css-extract-plugin
- 替换style-loader
- 更改mode
const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { entry: './src/index.js', //入口 mode: 'production', output: { //输出 filename: 'main.js', //最终打包后的文件名 path: path.resolve(__dirname, 'dist')//最终main.js在哪个文件夹下 }, module: { rules: [ //模块处理规则 { test: /\.(sc|c|sa)ss$/, //当引入的模块的后缀满足这个,就使用use的loader处理 use: [//处理顺序从后向前 MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { ident: 'postcss', sourceMap: true, plugins: loader => [ require('autoprefixer')({ browsers: ['> 0.15% in CN'] }) // 添加前缀 ] } }, { loader: 'sass-loader', options: { sourceMap: true } }] } ] }, plugins: [ new MiniCssExtractPlugin({ // filename: devMode ? '[name].css' : '[name].[hash].css', //判断了是什么模式 // chunkFilename: devMode ? '[id].css' : '[id].[hash].css' filename: '[name].css', // 设置最终输出的文件名,与output中filename一样 chunkFilename:'[id].css' }) ] }
npx webpack -h
可以看webpack的帮助,可看到--config
webpack默认配置文件- 终端执行
npx webpack --config webpack.product.config.js
(敲到webpack.p,按Tab可自动补全名称) - 考虑到4的指令可能需要执行多次,可在package.json中添加,如果是npm run build,默认使用的配置文件仍然是webpack.config.js,npm run dist时才使用webpack.product.config.js配置文件
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "npx webpack", "dist":"npx webpack --config webpack.product.config.js" },
- 执行4后,可见dist中出现了一个main.css文件
- 如果plugins中,输出文件制定了哈希值,
filename: '[name].[hash].css'
,生成的main.css会变成mainxxxxxxxxxx.css,xxxxxxxxxx是这次打包的哈希值,是唯一标识符 - 想引用新生成的main.css,要在index.html文件中引用一下
6.webpack的css和js压缩插件使用
压缩CSS
- webpack4压缩CSS需要安装插件
- 安装css插件optimize-css-assets-webpack-plugin
npm i -D optimize-css-assets-webpack-plugin
- 修改webpack.product.config.js配置文件
- 引入mini-css-extract-plugin
- 添加optimization
const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); module.exports = { entry: './src/index.js', //入口 mode: 'production', output: { //输出 filename: 'main.js', //最终打包后的文件名 path: path.resolve(__dirname, 'dist')//最终main.js在哪个文件夹下 }, module: { rules: [ //模块处理规则 { test: /\.(sc|c|sa)ss$/, //当引入的模块的后缀满足这个,就使用use的loader处理 use: [//处理顺序从后向前 MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { ident: 'postcss', sourceMap: true, plugins: loader => [ require('autoprefixer')({ browsers: ['> 0.15% in CN'] }) // 添加前缀 ] } }, { loader: 'sass-loader', options: { sourceMap: true } }] } ] }, plugins: [ new MiniCssExtractPlugin({ // filename: devMode ? '[name].css' : '[name].[hash].css', //判断了是什么模式 // chunkFilename: devMode ? '[id].css' : '[id].[hash].css' filename: '[name].css', // 设置最终输出的文件名 chunkFilename:'[id].css' }) ], optimization: { minimizer: [new OptimizeCSSAssetsPlugin({})] } }
npm run dist
,查看main.css,css代码都变成了一行,压缩成功
压缩JS
- 压缩js需要一个插件:uglifyjs-webpack-plugin, 此插件需要一个前提就是:mode: ‘production’
- 安装
npm i -D uglifyjs-webpack-plugin
- 修改webpack.product.config.js配置文件
- 引入uglifyjs-webpack-plugin
- 在optimization的minimizer中引用插件
optimization: { minimizer: [ new OptimizeCSSAssetsPlugin({}),//压缩CSS new UglifyJsPlugin({ //压缩JS cache: true, parallel: true, sourceMap: true // set to true if you want JS source maps })] }
npm run dist
,此处出现错误
是因为es6需要转成es5在给webpack打包,快速解决方法是把index.js中的let改成var,再次执行,查看main.js,代码被压缩了ERROR in main.js from UglifyJs Unexpected token: name «dom», expected: punc «;» [./src/index.js:6,0][main.js:17215,8]
7.解决文件名带哈希值的问题
为什么要让文件带上哈希值?
- 因为有时候在生产的时候会遇到缓存问题。如果是相同的css/js文件,虽然文件内容是更新了,但是由于浏览器的缓存问题,可能会出现问题,所以一般都会给css/js静态文件打上版本
方法
修改webpack.product.config.js文件
- js文件输出:
filename: 'main.[hash].js', //最终打包后的文件名
- css文件输出:
filename: '[name][hash].css', // 设置最终输出的文件名 chunkFilename: '[id][hash].css'
优化
- 由于每次打包生成的文件名都不一样,所以将js/css文件引入html中的文件也不一样
HtmlWebpackPlugin
插件,可以把打包后的 CSS 或者 JS 文件引用直接注入到 HTML 模板中,这样就不用每次手动修改文件引用了。
-
安装
npm install --save-dev html-webpack-plugin
-
修改webpack.product.config.js配置文件中的引入及plugins,从而引用插件
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); module.exports = { entry: './src/index.js', //入口 mode: 'production', output: { //输出 filename: 'main.[hash].js', //最终打包后的文件名 path: path.resolve(__dirname, 'dist')//最终main.js在哪个文件夹下 }, module: { rules: [ //模块处理规则 { test: /\.(sc|c|sa)ss$/, //当引入的模块的后缀满足这个,就使用use的loader处理 use: [//处理顺序从后向前 MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { ident: 'postcss', sourceMap: true, plugins: loader => [ require('autoprefixer')({ browsers: ['> 0.15% in CN'] }) // 添加前缀 ] } }, { loader: 'sass-loader', options: { sourceMap: true } }] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name][hash].css', // 设置最终输出的文件名 chunkFilename: '[id][hash].css' }), new HtmlWebpackPlugin({ title: '学习webpack4.0 demo', // 默认值:Webpack App filename: 'main.html', // 最终生成的dist中的文件的名字,默认值: 'index.html'文件名 template: path.resolve(__dirname, 'src/main.html'),//这里可以给它加模版文件,可以是index.html,也可以指定具体文件 minify: { collapseWhitespace: true, //是否去掉空格 removeComments: true, //是否移除注释 removeAttributeQuotes: true // 移除属性的引号 } }) ], optimization: { minimizer: [ new OptimizeCSSAssetsPlugin({}),//压缩CSS new UglifyJsPlugin({ //压缩JS cache: true, parallel: true, sourceMap: true // set to true if you want JS source maps }) ] } }
- 设置模版文件:
- 假设在src目录下新建一个main.html
- 修改main.html内容
<!DOCTYPE html> <html lang="en"> <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>Document</title> </head> <body> <div id="demo">我是模版文件自带的内容</div> </body> </html>
- 设置模版文件:
-
执行
npm run dist
,查看效果,可见dist中生成了main.html文件,且引入了css和js文件
8.清理dist目录插件clean-webpack-plugin应用
- 每次构建,我们的 /dist 文件夹都会保存生成的文件,然后就会非常杂乱。
- 通常,在每次构建前清理 /dist 文件夹,是比较推荐的做法
clean-webpack-plugin
是一个比较普及的管理插件。
- 安装
npm install clean-webpack-plugin --save-dev
- 修改webpack.config.js/webpack.product.config.js
const path = require('path'); + const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { app: './src/index.js', print: './src/print.js' }, plugins: [ + new CleanWebpackPlugin(['dist']) //想清理其他目录也可以修改名字 ], output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } };
- 执行
npm run dist
,查看效果。- 此处发现产生报错,因为新版本的CleanWebpackPlugin用法变了,详见https://blog.csdn.net/qq_36242361/article/details/90709258
9.图片处理及优化
- 在src文件夹下新建assets文件夹,assets内新建img文件夹,img内放置几张照片
- 在a.scss中添加background一行
$bgcolor:red; .box{ background-color: $bgcolor; font-size: 30px; display: flex; background: url('../assets/img/01.jpg') }
- 打包时解析sass模块的时候,会认为background这里引入的是一个新的模块,这个模块的文件名是jpg,默认处理不了,会报错
ERROR in ./src/assets/img/01.jpg 1:0 Module parse failed: Unexpected character '�' (1:0) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
- 打包时解析sass模块的时候,会认为background这里引入的是一个新的模块,这个模块的文件名是jpg,默认处理不了,会报错
file-loader处理文件的导入
- 安装
npm install --save-dev file-loader
- 修改配置文件webpack.product.config.js,注意添加的位置
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); module.exports = { entry: './src/index.js', //入口 mode: 'production', output: { //输出 filename: 'main.[hash].js', //最终打包后的文件名 path: path.resolve(__dirname, 'dist')//最终main.js在哪个文件夹下 }, module: { rules: [ //模块处理规则 { test: /\.(png|svg|jpg|gif|jpeg|ico)$/, use: [ 'file-loader' ] }, { test: /\.(sc|c|sa)ss$/, //当引入的模块的后缀满足这个,就使用use的loader处理 use: [//处理顺序从后向前 MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { ident: 'postcss', sourceMap: true, plugins: loader => [ require('autoprefixer')({ browsers: ['> 0.15% in CN'] }) // 添加前缀 ] } }, { loader: 'sass-loader', options: { sourceMap: true } } ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name][hash].css', // 设置最终输出的文件名 chunkFilename: '[id][hash].css' }), new HtmlWebpackPlugin({ title: '学习webpack4.0 demo', // 默认值:Webpack App filename: 'main.html', // 最终生成的dist中的文件的名字,默认值: 'index.html'文件名 template: path.resolve(__dirname, 'src/main.html'),//这里可以给它加模版文件,可以是index.html,也可以指定具体文件 minify: { collapseWhitespace: true, //是否去掉空格 removeComments: true, //是否移除注释 removeAttributeQuotes: true // 移除属性的引号 } }), new CleanWebpackPlugin() ], optimization: { minimizer: [ new OptimizeCSSAssetsPlugin({}),//压缩CSS new UglifyJsPlugin({ //压缩JS cache: true, parallel: true, sourceMap: true // set to true if you want JS source maps }) ] } }
- 修改一下src下的main.js,添加一个使用.box的div
- 执行
npm run dist
,可见dist中生成了一个图片文件
优化图片,使用image-webpack-loader可以帮助我们对图片进行压缩和优化。
- 安装
npm install image-webpack-loader --save-dev
- 修改webpack.product.config.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); module.exports = { entry: './src/index.js', //入口 mode: 'production', output: { //输出 filename: 'main.[hash].js', //最终打包后的文件名 path: path.resolve(__dirname, 'dist')//最终main.js在哪个文件夹下 }, module: { rules: [ //模块处理规则 { test: /\.(sc|c|sa)ss$/, //当引入的模块的后缀满足这个,就使用use的loader处理 use: [//处理顺序从后向前 MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { ident: 'postcss', sourceMap: true, plugins: loader => [ require('autoprefixer')({ browsers: ['> 0.15% in CN'] }) // 添加前缀 ] } }, { loader: 'sass-loader', options: { sourceMap: true } } ] }, { test: /\.(png|svg|jpg|gif|jpeg|ico)$/, use: [ 'file-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 } } }, ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name][hash].css', // 设置最终输出的文件名 chunkFilename: '[id][hash].css' }), new HtmlWebpackPlugin({ title: '学习webpack4.0 demo', // 默认值:Webpack App filename: 'main.html', // 最终生成的dist中的文件的名字,默认值: 'index.html'文件名 template: path.resolve(__dirname, 'src/main.html'),//这里可以给它加模版文件,可以是index.html,也可以指定具体文件 minify: { collapseWhitespace: true, //是否去掉空格 removeComments: true, //是否移除注释 removeAttributeQuotes: true // 移除属性的引号 } }), new CleanWebpackPlugin() ], optimization: { minimizer: [ new OptimizeCSSAssetsPlugin({}),//压缩CSS new UglifyJsPlugin({ //压缩JS cache: true, parallel: true, sourceMap: true // set to true if you want JS source maps }) ] } }
npm run dist
10.图片base64优化和字体处理
url-loader
功能类似于 file-loader,可以把 url 地址对应的文件,打包成 base64 的 DataURL,减少图片请求,提高访问的效率。- 处理的图片不能太大
- 安装
npm install --save-dev url-loader
- 修改配置文件,安装成功后,就不需要用file-loader了,改用url-loader,将use里的
'file-loader'
用一个对象取代const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); module.exports = { entry: './src/index.js', //入口 mode: 'production', output: { //输出 filename: 'main.[hash].js', //最终打包后的文件名 path: path.resolve(__dirname, 'dist')//最终main.js在哪个文件夹下 }, module: { rules: [ //模块处理规则 { test: /\.(png|svg|jpg|gif|jpeg|ico)$/, use: [ { loader: 'url-loader', options: { limit:90000 //设置大小多少以内的图片,引用是要转化为base64的链接 } } ] }, { test: /\.(sc|c|sa)ss$/, //当引入的模块的后缀满足这个,就使用use的loader处理 use: [//处理顺序从后向前 MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { ident: 'postcss', sourceMap: true, plugins: loader => [ require('autoprefixer')({ browsers: ['> 0.15% in CN'] }) // 添加前缀 ] } }, { loader: 'sass-loader', options: { sourceMap: true } } ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name][hash].css', // 设置最终输出的文件名 chunkFilename: '[id][hash].css' }), new HtmlWebpackPlugin({ title: '学习webpack4.0 demo', // 默认值:Webpack App filename: 'main.html', // 最终生成的dist中的文件的名字,默认值: 'index.html'文件名 template: path.resolve(__dirname, 'src/main.html'),//这里可以给它加模版文件,可以是index.html,也可以指定具体文件 minify: { collapseWhitespace: true, //是否去掉空格 removeComments: true, //是否移除注释 removeAttributeQuotes: true // 移除属性的引号 } }), new CleanWebpackPlugin() ], optimization: { minimizer: [ new OptimizeCSSAssetsPlugin({}),//压缩CSS new UglifyJsPlugin({ //压缩JS cache: true, parallel: true, sourceMap: true // set to true if you want JS source maps }) ] } }
- 这里本来应该加上
'image-webpack-loader'
的配置的,但是因为频繁报错,就先删掉了。 - 排查过发现可能是
'image-webpack-loader'
原来的配置和url-loader
一起用后有冲突,在学第11的时候基本把问题解决了,详见下一节的第六步
- 这里本来应该加上
11.webpack的配置合并、提前公共配置
- 开发阶段与生产阶段所需配置不完全相同,如果想把公共配置提到一起,需要工具帮忙。
webpack-merge
的工具可以实现两个配置文件的合并,这样我们可以把开发环境和生产环境的公共配置抽取到一个公共的配置文件中。
-
安装
npm install --save-dev webpack-merge
-
改造配置文件
- webpack.dev.js
- webpack.product.js/webpack.prod.js
- webpack.common.js
-
将product的配置文件都拷贝到common里,修改webpack.common.js,留下开发阶段和生产阶段的公共部分
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); module.exports = { entry: './src/index.js', module: { rules: [ { test: /\.(png|svg|jpg|gif|jpeg|ico)$/, use: [ { loader: 'url-loader', options: { limit: 90000 //设置大小多少以内的图片,引用是要转化为base64的链接 } }, { 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 } } } ] }, ] }, plugins: [ new HtmlWebpackPlugin({ title: '学习webpack4.0 demo', // 默认值:Webpack App filename: 'main.html', // 最终生成的dist中的文件的名字,默认值: 'index.html'文件名 template: path.resolve(__dirname, 'src/main.html'),//这里可以给它加模版文件,可以是index.html,也可以指定具体文件 minify: { collapseWhitespace: true, //是否去掉空格 removeComments: true, //是否移除注释 removeAttributeQuotes: true // 移除属性的引号 } }), new CleanWebpackPlugin() ] }
-
修改webpack.dev.js,留下独有部分,适当改写
const path = require('path'); //引用path模块 const merge = require('webpack-merge'); const common=require('./webpack.common'); //对外导出一个对象 let devConfig = { mode: 'development', //代表我们现在是开发阶段 output: { //输出 filename: 'main.js', //最终打包后的文件名 path: path.resolve(__dirname, 'dist')//最终main.js在哪个文件夹下 }, module: { rules: [ //模块处理规则 { test: /\.(sc|c|sa)ss$/, //当引入的模块的后缀满足这个,就使用use的loader处理 use: [//处理顺序从后向前 'style-loader', { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { ident: 'postcss', sourceMap: true, plugins: loader => [ require('autoprefixer')({ browsers: ['> 0.15% in CN'] }) // 添加前缀 ] } }, { loader: 'sass-loader', options: { sourceMap: true } } ] } ] } } module.exports = merge(common,devConfig);
-
修改package.json中的script,修改默认执行的配置文件
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "npx webpack --config webpack.dev.js", "dist": "npx webpack --config webpack.prod.js" },
-
npm run build
,有报错,后来不断尝试发现,是'image-webpack-loader'
原来的配置中的webp未能正常把图片进行转换- 报错内容
ERROR in ./src/assets/img/01.jpg Module build failed (from ./node_modules/image-webpack-loader/index.js): Error: CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL, CLSCTX_INPROC_SERVER, MAKE_REFGUID(IID_IWICImagingFactory), (LPVOID*)&factory) failed 80040154 Couldn't access Windows Imaging Component (are you running Windows XP SP3 or newer?). Most formats not available. Use -s for the available YUV input. Decoding of input data failed. Status: 3(BITSTREAM_ERROR) Error! Could not process file C:\Users\ADMINI~1\AppData\Local\Temp\89c3cc73-dbc6-49f7-8b9d-c0d0b66c8aad Error! Cannot read input picture file 'C:\Users\ADMINI~1\AppData\Local\Temp\89c3cc73-dbc6-49f7-8b9d-c0d0b66c8aad'
- 解决方法:修改webpack.common.js,将
'image-webpack-loader'
原来的配置中的webp删除 - 参考:https://www.npmjs.com/package/image-webpack-loader
- 若仍想将图片转换成webp格式,可以试试https://stackoverflow.com/questions/53483962/webpack-encore-convert-images-to-webp-using-image-webpack-loader
- 报错内容
-
修改webpack.prod.js,留下独有部分,适当改写
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const merge = require('webpack-merge'); const common = require('./webpack.common'); let prodConfig = { mode: 'production', output: { //输出 filename: 'main.[hash].js', //最终打包后的文件名 path: path.resolve(__dirname, 'dist')//最终main.js在哪个文件夹下 }, module: { rules: [ //模块处理规则 { test: /\.(sc|c|sa)ss$/, //当引入的模块的后缀满足这个,就使用use的loader处理 use: [//处理顺序从后向前 MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { ident: 'postcss', sourceMap: true, plugins: loader => [ require('autoprefixer')({ browsers: ['> 0.15% in CN'] }) // 添加前缀 ] } }, { loader: 'sass-loader', options: { sourceMap: true } } ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name][hash].css', // 设置最终输出的文件名 chunkFilename: '[id][hash].css' }) ], optimization: { minimizer: [ new OptimizeCSSAssetsPlugin({}),//压缩CSS new UglifyJsPlugin({ //压缩JS cache: true, parallel: true, sourceMap: true // set to true if you want JS source maps }) ] } } module.exports = merge(common, prodConfig);
-
npm run dist
12.webpack的启动监控自动编译和启用js的sourcemap
启用js的sourcemap
- 当 webpack 打包源代码时,可能会很难追踪到错误和警告在源代码中的原始位置。例如,如果将三个源文件(a.js, b.js 和 c.js)打包到一个 bundle(bundle.js)中,而其中一个源文件包含一个错误,那么堆栈跟踪就会简单地指向到 bundle.js。
- webpack4可以直接使用
inline-source-map
选项,这有助于解释说明 js 原始出错的位置。(不要用于生产环境)
- webpack.dev.js中加上一行
const path = require('path'); //引用path模块 const merge = require('webpack-merge'); const common = require('./webpack.common'); //对外导出一个对象 let devConfig = { mode: 'development', //代表我们现在是开发阶段 output: { //输出 filename: 'main.js', //最终打包后的文件名 path: path.resolve(__dirname, 'dist')//最终main.js在哪个文件夹下 }, + devtool: 'inline-source-map', module: { rules: [ //模块处理规则 { test: /\.(sc|c|sa)ss$/, //当引入的模块的后缀满足这个,就使用use的loader处理 use: [//处理顺序从后向前 'style-loader', { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { ident: 'postcss', sourceMap: true, plugins: loader => [ require('autoprefixer')({ browsers: ['> 0.15% in CN'] }) // 添加前缀 ] } }, { loader: 'sass-loader', options: { sourceMap: true } } ] } ] } } module.exports = merge(common,devConfig);
- 在index.js中随便输出一点东西
console.log(123);
npm run build
,查看控制台- 在未使用inline-source-map时,输出的123指向的是index.js:21(打包后index.js头部增加了一些代码)
- 使用inline-source-map后,输出的123指向的是index.js:14
监控文件变化,自动编译,使用观察模式
- 每次修改完毕后,都手动编译非常麻烦,最简单解决的办法就是启动watch。
npx webpack --watch
- 可修改package.json
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "npx webpack --config webpack.dev.js", "watch":"npx webpack --watch --config webpack.dev.js", "dist": "npx webpack --config webpack.prod.js" },
npm run watch
- ctrl+c可以停止
- 但是有个bug,就是每次我们修改 js 或者 css 文件后,要看到修改后的 html 的变化,可能需要我自己重新刷新页面。
- 如何能不刷新页面,自动更新变化呢?,详见下一节
13.webpack的启动热更新和代理配置
webpack-dev-server
为你提供了一个简单的 web 服务器,并且能够实时重新加载(live reloading)。webpack-dev-server
编译速度快,编译到内存里,不写在dist中。
- 安装
npm install --save-dev webpack-dev-server
- 修改开发阶段的配置文件,简单配置
const path = require('path'); //引用path模块 const merge = require('webpack-merge'); const common = require('./webpack.common'); const webpack = require('webpack'); //对外导出一个对象 let devConfig = { mode: 'development', //代表我们现在是开发阶段 output: { //输出 filename: 'main.js', //最终打包后的文件名 path: path.resolve(__dirname, 'dist')//最终main.js在哪个文件夹下 }, devtool: 'inline-source-map', devServer: { contentBase: './dist', hot: true, open: true }, module: { rules: [ //模块处理规则 { test: /\.(sc|c|sa)ss$/, //当引入的模块的后缀满足这个,就使用use的loader处理 use: [//处理顺序从后向前 'style-loader', { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { ident: 'postcss', sourceMap: true, plugins: loader => [ require('autoprefixer')({ browsers: ['> 0.15% in CN'] }) // 添加前缀 ] } }, { loader: 'sass-loader', options: { sourceMap: true } } ] } ] }, plugins: [ new webpack.NamedModulesPlugin(), // 更容易查看(patch)的依赖 new webpack.HotModuleReplacementPlugin() // 替换插件 ] } module.exports = merge(common, devConfig);
- 启动此webserver可用
webpack-dev-server --open
- 官网其他配置,也可以用这些配置
devServer: { clientLogLevel: 'warning', // 可能的值有 none, error, warning 或者 info(默认值) hot: true, // 启用 webpack 的模块热替换特性, 这个需要配合: webpack.HotModuleReplacementPlugin插件 contentBase: path.join(__dirname, "dist"), // 告诉服务器从哪里提供内容, 默认情况下,将使用当前工作目录作为提供内容的目录 compress: true, // 一切服务都启用gzip 压缩 host: '0.0.0.0', // 指定使用一个 host。默认是 localhost。如果你希望服务器外部可访问 0.0.0.0 port: 8080, // 端口 open: true, // 是否打开浏览器 overlay: { // 出现错误或者警告的时候,是否覆盖页面线上错误消息。 warnings: true, errors: true }, publicPath: '/', // 此路径下的打包文件可在浏览器中访问。 proxy: { // 设置代理 "/api": { // 访问api开头的请求,会跳转到 下面的target配置 target: "http://192.168.0.102:8080", pathRewrite: {"^/api" : "/mockjsdata/5/api"} } }, quiet: true, // necessary for FriendlyErrorsPlugin. 启用 quiet 后,除了初始启动信息之外的任何内容都不会被打印到控制台。这也意味着来自 webpack 的错误或警告在控制台不可见。 watchOptions: { // 监视文件相关的控制选项 poll: true, // webpack 使用文件系统(file system)获取文件改动的通知。在某些情况下,不会正常工作。例如,当使用 Network File System (NFS) 时。Vagrant 也有很多问题。在这些情况下,请使用轮询. poll: true。当然 poll也可以设置成毫秒数,比如: poll: 1000 ignored: /node_modules/, // 忽略监控的文件夹,正则 aggregateTimeout: 300 // 默认值,当第一个文件更改,会在重新构建前增加延迟 } }
- 此处,用
npx webpack-dev-server --config webpack.dev.js
,然后在路由上添加/main.html
- 然后不管修改js还是其他,都会自动更新,然后会发现dist文件夹里没有文件,所以说明它编译是在内存里面生成的。
- 将命令加到package.json中
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "npx webpack --config webpack.dev.js", "watch": "npx webpack --watch --config webpack.dev.js", "start": "npx webpack-dev-server --config webpack.dev.js", "dist": "npx webpack --config webpack.prod.js" },
- 使用代理部分此处省略,具体请看第14个视频。会用到axios发起Ajax请求。
14.webpack的代理服务器设置和babel转换及优化
babel转码
- 虽然现代的浏览器已经兼容了96%以上的ES6的语法了,但是为了兼容老式的浏览器(IE8、9)我们需要把最新的ES6的语法转成ES5的,那么babel的loader就出场了。
- 安装
npm i -D babel-loader babel-core babel-preset-env
- 在webpack的配置文件中,添加js的处理模块。(开发阶段和生成阶段都需要,因此加到common里)
module: { rules: [ { test: /\.js$/, exclude: /(node_modules)/, // 加快编译速度,不包含node_modules文件夹内容 use: { loader: 'babel-loader' } } ] }
- 在项目根目录下,添加babel的配置文件
.babelrc
,其内容为{ "presets": ["env"] }
- 将原本的var改成let压缩JS,此时打包不会再报错;或者在index.js里添加一些ES6语法的内容
class Temp { show() { console.log('this.Age :', this.Age); } get Age() { return this._age; } set Age(val) { this._age = val + 1; } } let t = new Temp(); t.Age = 19; t.show();
npm run build
- 查看生成的main.js,发现原来的let已被替换成var
- 此处可能会产生报错,报错原因是找不到@babel/core,解决方法详见https://www.cnblogs.com/soyxiaobi/p/9554565.html
babel优化
-
babel-loader可以配置如下几个options:
- cacheDirectory:默认值为 false。当有设置时,指定的目录将用来缓存 loader 的执行结果。之后的 webpack 构建,将会尝试读取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 重新编译过程(recompilation process)。如果设置了一个空值 (loader: ‘babel-loader?cacheDirectory’) 或者 true (loader: babel-loader?cacheDirectory=true),loader 将使用默认的缓存目录 node_modules/.cache/babel-loader,如果在任何根目录下都没有找到 node_modules 目录,将会降级回退到操作系统默认的临时文件目录。(使用跟这个可以提升编译速度)
- cacheIdentifier:默认是一个由 babel-core 版本号,babel-loader 版本号,.babelrc 文件内容(存在的情况下),环境变量 BABEL_ENV 的值(没有时降级到 NODE_ENV)组成的字符串。可以设置为一个自定义的值,在 identifier 改变后,强制缓存失效。
- forceEnv:默认将解析 BABEL_ENV 然后是 NODE_ENV。允许你在 loader 级别上覆盖 BABEL_ENV/NODE_ENV。对有不同 babel 配置的,客户端和服务端同构应用非常有用。
-
注意:sourceMap 选项是被忽略的。当 webpack 配置了 sourceMap 时(通过 devtool 配置选项),将会自动生成 sourceMap。
-
babel 在每个文件都插入了辅助代码,使代码体积过大.babel 对一些公共方法使用了非常小的辅助代码,比如 _extend。 默认情况下会被添加到每一个需要它的文件中。你可以引入 babel runtime 作为一个独立模块,来避免重复引入。
- 安装
npm install babel-plugin-transform-runtime --save-dev npm install babel-runtime --save
- 配置
rules: [ // 'transform-runtime' 插件告诉 babel 要引用 runtime 来代替注入。 { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', } } ]
- 修改.babelrc
{ "presets": ["env"], "plugins": [ ["transform-runtime", { "helpers": true, "polyfill": true, "regenerator": true, "moduleName": "babel-runtime" }] ] }
- 此时,webpack打包的时候,会自动优化重复引入公共方法的问题。
- 安装
15.webpack的eslint校验配置
- ESLint校验代码格式规范
- 安装
npm install eslint --save-dev npm install eslint-loader --save-dev # 以下是用到的额外的需要安装的eslint的解释器、校验规则等 npm i -D babel-eslint standard
- 在webpack.common.js中配置,把ESLint的配置放到处理js文件的最后
rules: [ { test: /\.js$/, exclude: /(node_modules)/, // 加快编译速度,不包含node_modules文件夹内容 use: [{ loader: 'babel-loader', options: { cacheDirectory: true } }, { loader: "eslint-loader", options: { // eslint options (if necessary) fix: true } }] },
- eslint配置可以直接放到webpack的配置文件中,也可以直接放到项目根目录的 .eslintrc中(文档)。
- 也可以将eslint配置文件创建为
.eslintrc.js
// .eslintrc.js // https://eslint.org/docs/user-guide/configuring module.exports = { root: true, parserOptions: { parser: 'babel-eslint' }, env: { browser: true }, extends: [ // https://github.com/standard/standard/blob/master/docs/RULES-en.md 'standard' ], globals: { NODE_ENV: false }, rules: { // allow async-await 'generator-star-spacing': 'off', // allow debugger during development 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', // 添加,分号必须 semi: ['error', 'always'], 'no-unexpected-multiline': 'off', 'space-before-function-paren': ['error', 'never'], // 'quotes': ["error", "double", { "avoidEscape": true }] quotes: [ 'error', 'single', { avoidEscape: true } ] } };
- 最好在根目录下再加一个
.eslintignore
文件,去忽略一些文件/dist/ /node_modules/ /*.js
- 这样vscode的eslint插件就不会报错了
- 然后在src的index.js文件中多添加一些分号,
npm run build
之后查看生成的main.js文件,发现自动修复了
16.webpack的模块解析后缀和别名配置详解
- 创建 import 或 require 的别名,来确保模块引入变得更简单。
- 自动解析确定的扩展。
17.webpack的模块的外部依赖配置
- 把一个模块做成外部依赖,最终不会打包到js文件中去
18.webpack的打包分析报表插件与优化总结
以上三课本文不作详细阐释,有兴趣可查看视频或查看于文章开头的链接查看相关笔记