# webpack_learn
**Repository Path**: mus-z/webpack_learn
## Basic Information
- **Project Name**: webpack_learn
- **Description**: 老陈的视频和尚硅谷的视频
代码+笔记
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 5
- **Forks**: 2
- **Created**: 2020-07-31
- **Last Updated**: 2022-05-27
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# webpack_learn
[老陈:https://www.bilibili.com/video/BV1gA411B7M2?from=search&seid=11762398641701819399](https://www.bilibili.com/video/BV1gA411B7M2?from=search&seid=11762398641701819399)
[尚硅谷:https://www.bilibili.com/video/BV1e7411j7T5?p=39](https://www.bilibili.com/video/BV1e7411j7T5?p=39)
[TOC]
# webpack学习-老陈
官方文档https://www.webpackjs.com/concepts/

webpack是我们必须了解的打包工具
四个**核心概念**:
- 入口(entry)
- 输出(output)
- loader
- 插件(plugins)
### 初始化
先全局安装webpack和webpack-cli
```
npm install webpack webpack-cli -g
```
进入demo01目录
然后,初始化生成package.json
```
npm init -y
```

```
tyarn add webpack webpack-cli
```
然后安装到项目依赖

新建src/index.js的测试代码并引用data.json的数据

执行下面命令的时候路径不对劲(发现全局安装用yarn是不太行 之后改回城npm安装全局)
开发环境的打包命令
```
webpack ./src/index.js -o ./dist/bundle_dev.js --mode=development
```

生产环境的打包命令
```
webpack ./src/index.js -o ./dist/bundle_pro.js --mode=production
```

生产环境没有注释并且压缩代码了,会小很多
而且webpack默认是可以处理js和json文件的,不需要loader和plugin
### 使用webpack.config.js
新建demo02 安装webpack和webpack-cli的依赖之后
新建webpack.config.js
```js
let path=require('path')
module.exports={
entry:"./src/index.js",//入口文件
output:{
filename:'bundle.js',//输出文件名
path:path.resolve(__dirname,'dist')//输出路径,用绝对路径
},
mode:'development',
}
```
__dirname在node的环境下可以拿到webpack.config.js的运行目录
直接运行`webpack`

### 使用loader加载样式打包
loader可以识别css等并一起打包
新建demo03,基本上可以使用demo02的内容,在根目录下放一个index.html
```html
demo03
```
然后src下面搞一个index.css
```css
body{
background-color: blanchedalmond;
}
```
在index.js中引入
```js
import './index.css'
```
因为webpack默认不支持css引入,所以要使用loader
```js
let path=require('path')
module.exports={
entry:"./src/index.js",//入口文件
output:{
filename:'bundle.js',//输出文件名
path:path.resolve(__dirname,'dist')//输出路径,用绝对路径
},
mode:'development',//生产模式
module:{
//对某种格式的文件进行转换处理
rules:[
{
test:/\.css$/,//使用正则匹配
use:[
//顺序从下到上
'style-loader',//把css-js引入到style
'css-loader',//把css转换成js
]
}
]
}
}
```
要安装两个loader依赖

之前的颜色生效了


### 使用plugin插件
先可以实现自动整合编译,需要用到的插件为`html-webpack-plugin`要安装依赖
/index.html
```js
demo04
```
```js
let path=require('path')
let HtmlWebpackPlugin=require('html-webpack-plugin')
module.exports={
entry:"./src/index.js",//入口文件
output:{
filename:'bundle.js',//输出文件名
path:path.resolve(__dirname,'dist')//输出路径,用绝对路径
},
mode:'development',//生产模式
module:{
//对某种格式的文件进行转换处理
rules:[
{
test:/\.css$/,//使用正则匹配
use:[
//顺序从下到上
'style-loader',//把css-js引入到style
'css-loader',//把css转换成js
]
}
]
},
//配置插件
plugins:[
new HtmlWebpackPlugin({
template:'./index.html'
})
]
}
```
运行webpack之后发现dist目录下有了新的index.html

### 打包图片资源
新建demo05

在入口index.js中引入图片
```html
demo04
```
这时候肯定要加关于png的依赖
需要两个loader: url-loader html-loader file-loader
```js
let path=require('path')
let HtmlWebpackPlugin=require('html-webpack-plugin')
module.exports={
entry:"./src/index.js",//入口文件
output:{
filename:'bundle.js',//输出文件名
path:path.resolve(__dirname,'dist')//输出路径,用绝对路径
},
mode:'development',//生产模式
module:{
//对某种格式的文件进行转换处理
rules:[
{
test:/\.css$/,//使用正则匹配
use:[
//顺序从下到上
'style-loader',//把css-js引入到style
'css-loader',//把css转换成js
]
},{
test:/\.(jpg|png|gif)$/,
loader:'url-loader',
options:{
limit:8*1024,//图片小于8kb,用base64处理,减少请求
esModule:false,//把url-loader的es6模块化解析取消,不加的话与html-loader冲突,src会变成object module
name:'[hash:10].[ext]',//取图片hash的前十位与扩展名来命名
}
},{
test:/\.html$/,
loader:'html-loader',//用来解析html
}
]
},
//配置插件
plugins:[
new HtmlWebpackPlugin({
template:'./index.html'
})
]
}
```

打包结果可以看见

### 热更新
使用插件
```
tyarn add webpack-dev-server -g
```
```js
let path=require('path')
let HtmlWebpackPlugin=require('html-webpack-plugin')
module.exports={
entry:"./src/index.js",//入口文件
output:{
filename:'bundle.js',//输出文件名
path:path.resolve(__dirname,'dist'),//输出路径,用绝对路径
},
mode:'development',//生产模式
module:{
//对某种格式的文件进行转换处理
rules:[
{
test:/\.css$/,//使用正则匹配
use:[
//顺序从下到上
'style-loader',//把css-js引入到style
'css-loader',//把css转换成js
]
},{
test:/\.(jpg|png|gif)$/,
loader:'url-loader',
options:{
limit:8*1024,//图片小于8kb,用base64处理,减少请求
esModule:false,//把url-loader的es6模块化解析取消,不加的话与html-loader冲突,src会变成object module
name:'[hash:10].[ext]',//取图片hash的前十位与扩展名来命名
}
},{
test:/\.html$/,
loader:'html-loader',//用来解析html
}
]
},
//配置插件
plugins:[
new HtmlWebpackPlugin({
template:'./index.html'
})
],
//开发服务环境
devServer:{
contentBase:path.resolve(__dirname,'dist'),//开发环境路径,用绝对路径
compress:true,//开启gzip压缩
port:3001,//端口号
open:true,//浏览器自动打开
}
}
```

可以热更新了
# webpack-尚硅谷
### 概述
webpack是前端模块打包器,把资源文件打包生成对应的bundle
比如.less需要先转化成浏览器识别的css才能解析
又比如js中使用了import的语法浏览器是识别不了的


会有依赖树状结构
### 五个核心概念
entry:入口,指示打包的起点文件
output:输出,指示打包资源bundles输出路径和命名
loader:处理非js文件(webpack自身只理解js、json),相当于翻译官
plugins:功能插件,可以做到优化、压缩、定义环境变量等
mode:模式氛围development和production模式

### 看演示~

上面是没有配置webpack.config.js的时候的webpack命令 ,生产环境会压缩代码,打包后的文件是处理成浏览器能识别的情况
一个干净的`webpack.config.js`

##### 如何打包样式:

如果使用其他的比如less文件要继续配置loader

##### 如何打包html:

使用`html-webpack-plugin`默认创建一个空html,自动引入built.js
如果要有结构

##### 打包图片:
老师在less中引入了图片

打包的时候,图片会报错,所以要使用处理图片的loader

但是这种情况下处理不了html引用的img图片,所以要用到`html-loader`
但是视频中发现会和url-loader的es6模式冲突,要关闭url-loader的esModule

不过经过自己**测试**这个已经解决了,**不需要考虑这个冲突问题**,下面是我用到的版本
> "url-loader": "^4.1.0",
>
> "webpack": "^4.44.0",
>
> "webpack-cli": "^3.3.12",
>
> "html-loader": "^1.1.0",
##### 打包其他资源:
视频中以字体文件为例(用的阿里的图标库)


```js
module:{
//对某种格式的文件进行转换处理
rules:[
{
test:/\.css$/,//使用正则匹配
use:[
//顺序从下到上
'style-loader',//把css-js引入到style
'css-loader',//把css转换成js
]
},{
//排除css、js、html、json
exclude:/\.(css|js|html|json)$/,
loader:'file-loader'
}
]
},
```

这样子大概是可以用的(不过特殊的需要处理的比如less、图片之类的在静态模板中是没有引用,可能还是要加loader,另外资源使用相对路径好像是不可以的,如果是import就可以)
##### 热加载devServer
修改代码需要重新打包,这时候就需要devserver了,保证我们一些小的修改不需要重新打包
可以自动编译、自动打开浏览器、自动刷新项目
临时的打包文件是保存在内存中,不会更新到我们的文件夹
启动命令是
```
webpack-dev-server
```
需要全局安装
### 开发环境搭建
```js
// 开发环境配置
const path =require('path')
const HtmlWebpackPlugin=require('html-webpack-plugin')
module.exports={
entry:'./src/index.js',
output:{
filename:'built.js',
path:path.resolve(__dirname,'build')
},
module:{
rules:[
{
test:/\.less$/,
use:['style-loader','css-loader','less-loader']
},
{
test:/\.css$/,
use:['style-loader','css-loader']
},
{
test:/\.(png|jpg|gif)$/,
loader:'url-loader',
options:{
limit:8*1024,
name:'[hash:10].[ext]'
}
},
{
test:/\.html$/,
loader:'html-loader',
},
{
exclude:/\.(js|css|less|html|json|png|jpg|gif)$/,
loader:'file-loader',
options:{
limit:8*1024,
name:'[hash:10].[ext]'
}
}
]
},
plugins:[
new HtmlWebpackPlugin({
template:'./src/index.html'
})
],
mode:'development',
devServer:{
open:true,
compress:true,
port:3001,
contentBase:path.resolve(__dirname,'build')
}
}
```

讲道理我用了css、less、png、以及其他文件 确实是没问题的
src路径的话我就不动了,视频里面老师带着整理了下,其实就是相对路径转化的问题

后面主要是一个output路径的配置,像这样子就比较神奇了

能够制定output下的子目录
```js
// 开发环境配置
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
filename: "built.js",
path: path.resolve(__dirname, "build"),
},
module: {
rules: [
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"],
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(png|jpg|gif)$/,
loader: "url-loader",
options: {
limit: 8 * 1024,
name: "[hash:10].[ext]",
outputPath: "imgs",
},
},
{
test: /\.html$/,
loader: "html-loader",
},
{
exclude: /\.(js|css|less|html|json|png|jpg|gif)$/,
loader: "file-loader",
options: {
limit: 8 * 1024,
name: "[hash:10].[ext]",
outputPath: "default",
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
],
mode: "development",
devServer: {
open: true,
compress: true,
port: 3001,
contentBase: path.resolve(__dirname, "build"),
},
};
```
### 生产环境的痛点
生产中痛点:css从js中提取、代码压缩、兼容性问题
##### css

之前也说过css是被js引入才导入的,这样会导致js很大,想要拆出来
需要使用插件`mini-css-extract-plugin`
```js
// 开发环境配置
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: "./src/js/index.js",
output: {
filename: "js/built.js",
path: path.resolve(__dirname, "build"),
},
module: {
rules: [
{
test: /\.less$/,
use: [
// "style-loader",
MiniCssExtractPlugin.loader,//代替style-loader不使用标签插入
"css-loader",
"less-loader",
],
},
{
test: /\.css$/,
use: [
// "style-loader",
MiniCssExtractPlugin.loader,
"css-loader",
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
new MiniCssExtractPlugin(),
],
mode: "development",
devServer: {
open: true,
compress: true,
port: 3001,
contentBase: path.resolve(__dirname, "build"),
},
};
```

之前定义的css文件被整合到main.css了
还可以通过传参的方式制定output的目录和文件名
```js
new MiniCssExtractPlugin({
filename:'css/built.css'
}),
```
然后考虑css的兼容处理,一个loader一个插件
css兼容性处理:postcss-loader postcss-preset-env
还要配置browserslist,设置环境
```js
// 生产环境配置
// process.env.NODE_ENV='development';//设置node为开发环境
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");//单独提取css的插件
module.exports = {
entry: "./src/js/index.js",
output: {
filename: "js/built.js",
path: path.resolve(__dirname, "build"),
},
module: {
rules: [
{
test: /\.less$/,
use: [
// "style-loader",
MiniCssExtractPlugin.loader,//代替style-loader不使用标签插入
"css-loader",
"less-loader",
],
},
{
test: /\.css$/,
use: [
// "style-loader",//代替style-loader不使用标签插入
MiniCssExtractPlugin.loader,
//css兼容性处理:postcss-loader postcss-preset-env
"css-loader",
// 'postcss-loader',//不能使用默认配置,帮postcss找到package.json中的browserslist(自己配置)
// 而且browserslist默认是开发环境,要设置node环境变量,process.env.NODE_ENV='development'
// "browserslist":{
// "development":[
// "last 1 chrome version",
// "last 1 firefox version",
// "last 1 safari version"
// ],
// "production":[
// ">0.01%",
// "not dead",
// "not op_mini all"
// ]
// }
{
loader:'postcss-loader',
options:{
ident:'postcss',
plugins:()=>[
require("postcss-preset-env")()
]
}
}
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
new MiniCssExtractPlugin({
filename:'css/built.css'
}),
],
mode: "development",
devServer: {
open: true,
compress: true,
port: 3001,
contentBase: path.resolve(__dirname, "build"),
},
};
```
package.json加上
```json
"browserslist":{
"development":[
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production":[
">0.01%",
"not dead",
"not op_mini all"
]
}
```

能够看见兼容效果啦
至于压缩css也需要使用一个插件`optimize-css-assets-webpack-plugin`
安装好直接调用就行了
```js
// 生产环境配置
// process.env.NODE_ENV='development';//设置node为开发环境
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");//单独提取css的插件
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');//压缩css文件
module.exports = {
entry: "./src/js/index.js",
output: {
filename: "js/built.js",
path: path.resolve(__dirname, "build"),
},
module: {
rules: [
{
test: /\.less$/,
use: [
// "style-loader",
MiniCssExtractPlugin.loader,//代替style-loader不使用标签插入
"css-loader",
"less-loader",
],
},
{
test: /\.css$/,
use: [
// "style-loader",//代替style-loader不使用标签插入
MiniCssExtractPlugin.loader,
//css兼容性处理:postcss-loader postcss-preset-env
"css-loader",
// 'postcss-loader',//不能使用默认配置,帮postcss找到package.json中的browserslist(自己配置)
{
loader:'postcss-loader',
options:{
ident:'postcss',
plugins:()=>[
require("postcss-preset-env")()
]
}
}
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
new MiniCssExtractPlugin({
filename:'css/built.css'
}),
new OptimizeCssAssetsWebpackPlugin(),
],
mode: "development",
devServer: {
open: true,
compress: true,
port: 3001,
contentBase: path.resolve(__dirname, "build"),
},
};
```
##### js
下面学一下eslint语法检查
语法检查:对js进行规范,检查语法错误,eslint-loader eslint
设置检查规则 package.json中的eslintConfig中设置(airbnb)eslint-config-airbnb(-base)
不带base的有react相关eslint
```
tyarn add eslint-loader eslint eslint-plugin-import eslint-config-airbnb-base
```
```js
const path =require('path')
const HtmlWebpackPlugin =require('html-webpack-plugin')
module.exports={
mode:'development',
entry:'./src/js/index.js',
output:{
filename:'js/built.js',
path:path.resolve(__dirname,'build')
},
module:{
rules:[
{
//语法检查:对js进行规范,检查语法错误,eslint-loader eslint
//只检查源代码,不检查第三方
//设置检查规则 package.json中的eslintConfig中设置(airbnb)
// "eslintConfig":{
// "extends":"airbnb-base"
// }
//eslint-config-airbnb-base->eslint eslint-plugin-import
test:/\.js$/,
exclude:/node_modules/,
loader:'eslint-loader',
options:{},
}
]
},
plugins:[
new HtmlWebpackPlugin({
template:'./src/index.html'
})
]
}
```
听说eslint很可怕 我看看这么几行会咋样

运行webpack,2333

```js
options:{
fix:true,//自动修复
},
```
开启自动修复,我们的src下的源代码还真的被格式化了呢
```js
function add(x, y) {
return x + y;
}
// eslint-disable-next-line的注释会让eslint忽略其下一行的检查
// eslint-disable-next-line
console.log(add(2, 3));
```
之后关注一下js的兼容性
默认情况下,eval()中执行的字符串不会对我们的js兼容性做解释
现在比较新的浏览器还好,但是怼到ie上边就很头疼了
因此babel-loader就比较重要了,帮我们把es6转化成es5
```
tyarn add babel-loader @babel/preset-env @babel/core
```
```
const path =require('path')
const HtmlWebpackPlugin =require('html-webpack-plugin')
module.exports={
mode:'development',
entry:'./src/js/index.js',
output:{
filename:'js/built.js',
path:path.resolve(__dirname,'build')
},
module:{
rules:[
{
//下载babel-loader和@babel/preset-env @babel/core
loader:'babel-loader',
test:/\.js$/,
exclude:/node_modules/,
options:{
//预设:指示babel做什么样的兼容性处理
presets:['@babel/preset-env']
}
}
]
},
plugins:[
new HtmlWebpackPlugin({
template:'./src/index.html'
})
]
}
```
注:暂时只能转换基本语法,比如箭头函数是可以的,但是promise他又不认识了
因此要进行进一步兼容
使用@babel/polyfill引入所有的兼容语法

但是体积过大
所以推荐按需加载的方式core-js,删除之前引入的polyfill
```js
const path =require('path')
const HtmlWebpackPlugin =require('html-webpack-plugin')
module.exports={
mode:'development',
entry:'./src/js/index.js',
output:{
filename:'js/built.js',
path:path.resolve(__dirname,'build')
},
module:{
rules:[
{
//下载babel-loader和@babel/preset-env @babel/core
//暂时只能转换一部分简单语法
//使用@babel/polyfill,但是特别大
//最终选择按需加载的方式 core-js
loader:'babel-loader',
test:/\.js$/,
exclude:/node_modules/,
options:{
//预设:指示babel做什么样的兼容性处理
//presets:['@babel/preset-env']
presets:[
[
'@babel/preset-env',{
useBuiltIns:'usage',//按需加载
corejs:{
version:3,//制定core-js版本
},
targets:{
chrome:"60",
firefox:'60',
ie:'9',
safari:'10',
edge:'17',
}
}
]
]
}
}
]
},
plugins:[
new HtmlWebpackPlugin({
template:'./src/index.html'
})
]
}
```
最终只需要babel-loader @babel/preset-env @babel/core core-js 这些
然后考虑下js和html的代码压缩
js很简单 只需要用`mode:'production'`
```js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "production",//生产环境自动压缩js
entry: "./src/js/index.js",
output: {
filename: "js/built.js",
path: path.resolve(__dirname, "build"),
},
module: {
rules: [
{
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
],
};
```
##### html
html为什么要压缩呢,反正是可以缩成一行并且去掉注释
```js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "production",//生产环境自动压缩js
entry: "./src/js/index.js",
output: {
filename: "js/built.js",
path: path.resolve(__dirname, "build"),
},
module: {
rules: [
{
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
minify:{
//移除空格
collapseWhitespace:true,
//移除注释
removeComments:true,
}
}),
],
};
```
### 生产环境配置
主要就是css的提取、兼容、压缩、js的兼容、压缩、html的压缩
css依赖:postcss-loader、postcss-preset-env、less-loader、optimize-css-assets-webpack-plugin、less
js依赖:babel-loader、@babel/preset-env、@babel/core、core-js
html依赖:html-webpack-plugin、html-loader、file-loader、url-loader
```js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin"); //html模板
const MiNiCssExtractPlugin = require("mini-css-extract-plugin"); //提取出css
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin"); //压缩css
process.env.NODE_ENV = "production";
//复用css的loader
let commonCssLoader = [
{
loader:MiNiCssExtractPlugin.loader,
options:{
publicPath:'../',//避免css中的路径引入错误
}
}, //代替style标签而是引入css的形式
"css-loader",
{
//把css压缩
loader: "postcss-loader",
options: {
//配置package.json的"browserslist"
ident: "postcss",
plugins: () => [require("postcss-preset-env")()],
},
},
];
module.exports = {
mode: "production", //生产模式
entry: "./src/js/index.js",
output: {
filename: "built.js",
path: path.resolve(__dirname, "build"),
},
module: {
rules: [
//css\less
{
test: /\.css$/,
use: [...commonCssLoader],
},
{
test: /\.less$/,
use: [...commonCssLoader, "less-loader"],
},
//js先eslint再babel
//eslint检查
{
//package.json配置"eslintConfig"
test: /\.js$/,
exclude: /node_modules/,
enforce:'pre',//js优先执行eslint
loader: "eslint-loader",
options: {
fix: true, //规范自动修复
},
},
//js兼容处理
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
presets: [
[
"@babel/preset-env",
{
useBuiltIns: "usage", //按需加载
corejs: {
version: 3, //制定core-js版本
},
targets: {//制定兼容的目标
chrome: "60",
firefox: "60",
ie: "9",
safari: "10",
edge: "17",
},
},
],
],
},
},
{
test:/\.(jpg|png|gif)$/,
loader:'url-loader',
options:{
limit:8*1024,
name:'[hash:10].[ext]',
outputPath:'imgs',
esMoudle:false,
}
},
{
test:/\.html$/,
loader:'html-loader',
},
{
exclude:/\.(js|html|css|less|json|jpg|png|gif)$/,
loader:'file-loader',
options:{
outputPath:'media'
}
}
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html", //引用html模板
minify:{//压缩html
collapseInlineTagWhitespace:true,
removeComments:true,
}
}),
new MiNiCssExtractPlugin({
filename: "css/built.css", //设置css的输出
}),
new OptimizeCssAssetsWebpackPlugin(), //压缩css的插件
],
};
```
package.json中
```json
"browserslist": {
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production": [
">0.01%",
"not dead",
"not op_mini all"
]
},
"eslintConfig": {
"extends": "airbnb-base"
}
```
### webpack优化配置
性能优化:开发环境性能优化、生产环境性能优化
#### 开发环境性能优化:
##### 优化打包构建速度-HRM
因为修改部分文件的时候其他文件也会重新打包,肯定会影响打包速度因此就需要
**HRM热模块替换**:只会打包某一个模块而不是所有都重新打包
- 样式文件在使用style-loader的情况下可以使用HRM
- js文件默认不使用HRM,需要自动刷新:需要修改js文件实现热加载
- html不能随之热加载,也不会自动刷新(修改entry入口,改成数组引入html),spa不做HRM

```js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); // html模板
const MiNiCssExtractPlugin = require('mini-css-extract-plugin'); // 提取出css
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
// 压缩css
process.env.NODE_ENV = 'development';
const webpack = require('webpack');
// 复用css的loader
const commonCssLoader = [
// {
// loader:MiNiCssExtractPlugin.loader,
// options:{
// publicPath:'../',//避免css中的路径引入错误
// }
// }, //代替style标签而是引入css的形式
'style-loader',
'css-loader',
// {
// //把css压缩
// loader: "postcss-loader",
// options: {
// //配置package.json的"browserslist"
// ident: "postcss",
// plugins: () => [require("postcss-preset-env")()],
// },
// },
];
module.exports = {
mode: 'development', // 生产模式
entry: ['./src/js/index.js', './src/index.html'],
output: {
filename: 'built.js',
path: path.resolve(__dirname, 'build'),
},
module: {
rules: [
// css\less
{
test: /\.css$/,
use: [...commonCssLoader],
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader'],
},
// js先eslint再babel
// eslint检查
// {
// // package.json配置"eslintConfig"
// test: /\.js$/,
// exclude: /node_modules/,
// enforce: 'pre', // js优先执行eslint
// loader: 'eslint-loader',
// options: {
// fix: true, // 规范自动修复
// },
// },
// js兼容处理
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage', // 按需加载
corejs: {
version: 3, // 制定core-js版本
},
targets: {
// 制定兼容的目标
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17',
},
},
],
],
},
},
{
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
outputPath: 'imgs',
esMoudle: false,
},
},
{
test: /\.html$/,
loader: 'html-loader',
},
{
exclude: /\.(js|html|css|less|json|jpg|png|gif)$/,
loader: 'file-loader',
options: {
outputPath: 'media',
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html', // 引用html模板
// minify:{//压缩html
// collapseInlineTagWhitespace:true,
// removeComments:true,
// }
}),
new MiNiCssExtractPlugin({
filename: 'css/built.css', // 设置css的输出
}),
// new OptimizeCssAssetsWebpackPlugin(), //压缩css的文件
// new webpack.HotModuleReplacementPlugin(),
// new webpack.NamedModulesPlugin(),
],
devServer: {
contentBase: path.resolve(__dirname, 'build'),
compress: true,
port: 3001,
// 开启HRM热模块替换
hot: true,
},
};
```
```js
//index.js
//.......
//.......
if (module.hot) {
//可以监听print.js的代码变化启动HRM,但是对入口文件index.js不能起到作用
module.hot.accept('./print.js', () => {
console.log('热加载print.');
//console.log(module.hot);
print();
});
}
```
对于非入口js文件和css可以启动HRM了,html对于pwa单页面应用则不宜启动HRM,随之刷新就好了(index.js也是如此)
##### 优化代码**调试**-source-map
source-map是一种提供源代码到构建后代码的映射技术
如果构建后代码出错,会通过映射关系追踪源代码错误
在webpack.config.js中加入
```
devtool:'source-map',
```
重要的是source-map的构建参数
`[inlie-|hidden-|eval-][nosources-][cheap-[module-]]source-map`
前面有三个可选的参数位置

> source-map:会生成built.js.map的一个映射文件
>
> inline-source-map:内联,映射文件的代码会内嵌在构建后的built.js的末尾
>
> hidden-source-map:会生成外部的built.js.map,但是在浏览器的source中会隐藏webpack://,不隐藏构建后代码
>
> eval-source-map:也是内联的source-map,但是都会追加在built.js的每个模块引入的eval中
>
> nosources-source-map:生成在外部,但是在浏览器的source中会隐藏我们的js源代码(有路径),构建后的js代码也看不见
>
> cheap-source-map:生成在外部,和source-map类似但是错误提示到整行而不是整句,会受到babel的影响
>
> cheap-module-source-map:生成在外部,和source-map类似但是错误提示到整行而不是整句,不会收到babel的影响
内联和外部的区别:外部会多生成文件,内联的构建速度会快
1. 开了source-map之后,如果print.js出现某种错误会告诉你出错在**源代码**的哪里可以精确到文件的行和列


看视频里弹幕的老哥说不加source-map也会提示我特意关了去试试看,(好像真的一样
但是,有些情况下,点击后面的跳转是不一样的,因为这些非语法错误,而是执行错误的部分,在非source-map的情况下跳转到的是一个eval的执行内容,而使用了source-map虽然提示一样但是跳转的确实是源代码的样子


2. inline-source-map和source-map的执行效果是完全一致的只是映射的代码文件一个在.map文件中一个是内嵌在构建后的built.js中
3. hidden-source-map执行到错误后隐藏了错误源代码的位置,指挥提示原因和构建后代码的报错位置,相较于我们的default状态,他更不会暴露我的源代码

4. eval-source-map相较于inlinie-source-map在控制台的报错信息末尾会多出一个hash值,对于我们跳转去观察源代码没有影响,跳转之后观察下图中的路径


5. 使用nosources-source-map,会在控制台提示报错的位置和原因,但是不暴露位置sources中的webpack://下的任何代码(即我们的源代码),和hidden-source-map模式不同的是hidden模式压根就不会出现webpack://的source路径和任何js文件代码,但是nosources-source-map只是引用不到源代码的内容,路径是构建好的


6. cheap-source-map和正常的source-map相比只能精确到行,而正常的可以精确到列(不包括hidden和nosources的模式),cheap-source-map在使用babel-loader时会自动转译(转译后的源代码会独立格式化分行


7. cheap-module-source-map也一样不会精确到列,好像是一样的,但是这个不会被babel-loader影响,而cheap-source-map在使用babel-loader时会自动转译(转译后的源代码会独立格式化分行),因为module模式会把loader的sourcemap也加进来。

8. 根据环境适配
**开发环境**:速度快,调试友好
(eval>inline>cheap>...)
> 速度快排序:
>
> eval-cheap-source-map
>
> eval-source-map
> 调试友好排序:
>
> source-map
>
> cheap-module-source-map
>
> cheap-source-map
综合一下,折中推荐eval-source-map,想要更快加cheap,想要更友好加cheap-module
即eval-source-map或eval-cheap-module-source-map,前者更友好,后者更快
老师提到脚手架默认使用的是eval-source-map的方式来追溯错误源代码的
**生产环境**:源代码隐藏?调试友好?
内联会让代码体积变大,所以不使用内联,即排除inline和eval的模式
> 源代码隐藏:
>
> nosources-source-map 源代码和构建后js代码全部隐藏
>
> hidden-source-map 只隐藏源代码,会提示构建后代码错误的错误位置
> 调试友好:
>
> source-map
>
> cheap-module-source-map
生产环境推荐source-map,如果你想隐藏代码可以考虑加上隐藏的模式


```js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); // html模板
const MiNiCssExtractPlugin = require('mini-css-extract-plugin'); // 提取出css
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
// 压缩css
process.env.NODE_ENV = 'development';
const webpack = require('webpack');
// 复用css的loader
const commonCssLoader = [
// {
// loader:MiNiCssExtractPlugin.loader,
// options:{
// publicPath:'../',//避免css中的路径引入错误
// }
// }, //代替style标签而是引入css的形式
'style-loader',
'css-loader',
// {
// //把css压缩
// loader: "postcss-loader",
// options: {
// //配置package.json的"browserslist"
// ident: "postcss",
// plugins: () => [require("postcss-preset-env")()],
// },
// },
];
module.exports = {
mode: 'development', // 生产模式
entry: ['./src/js/index.js', './src/index.html'],
output: {
filename: 'built.js',
path: path.resolve(__dirname, 'build'),
},
module: {
rules: [
// css\less
{
test: /\.css$/,
use: [...commonCssLoader],
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader'],
},
// js先eslint再babel
// eslint检查
// {
// // package.json配置"eslintConfig"
// test: /\.js$/,
// exclude: /node_modules/,
// enforce: 'pre', // js优先执行eslint
// loader: 'eslint-loader',
// options: {
// fix: true, // 规范自动修复
// },
// },
// // js兼容处理
// {
// test: /\.js$/,
// exclude: /node_modules/,
// loader: 'babel-loader',
// options: {
// presets: [
// [
// '@babel/preset-env',
// {
// useBuiltIns: 'usage', // 按需加载
// corejs: {
// version: 3, // 制定core-js版本
// },
// targets: {
// // 制定兼容的目标
// chrome: '60',
// firefox: '60',
// ie: '9',
// safari: '10',
// edge: '17',
// },
// },
// ],
// ],
// },
// },
{
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
outputPath: 'imgs',
esMoudle: false,
},
},
{
test: /\.html$/,
loader: 'html-loader',
},
{
exclude: /\.(js|html|css|less|json|jpg|png|gif)$/,
loader: 'file-loader',
options: {
outputPath: 'media',
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html', // 引用html模板
// minify:{//压缩html
// collapseInlineTagWhitespace:true,
// removeComments:true,
// }
}),
// new MiNiCssExtractPlugin({
// filename: 'css/built.css', // 设置css的输出
// }),
// new OptimizeCssAssetsWebpackPlugin(), //压缩css的文件
// new webpack.HotModuleReplacementPlugin(),
// new webpack.NamedModulesPlugin(),
],
devServer: {
open:true,
contentBase: path.resolve(__dirname, 'build'),
compress: true,
port: 3001,
// 开启HRM热模块替换
hot: true,
},
devtool:'eval-source-map',
};
```
#### 生产环境性能优化
##### 优化打包构建速度-oneOf
在加载loader时候,默认每个文件都会被所有的rules检查,但我们很多文件都只需要一条rules就可以解析完成,这无疑减慢了打包效率,因此可以考虑使用rules的oneOf规则,可以保证文件从oneOf里面的规则从上到下检查,满足一个rule之后就可以不进行下面的loader(需要做好顺序,而且不能同一个文件需要两个loader规则同时处理
所以如果有两个文件都需要不同的rule处理的时候把其中一个loader提取到oneOf外面,就可以了
比如对于js文件的eslint-loader和babelloader,我们考虑把先执行的eslint-loader放到oneOf的上方就可以了。
```js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin"); //html模板
const MiNiCssExtractPlugin = require("mini-css-extract-plugin"); //提取出css
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin"); //压缩css
process.env.NODE_ENV = "production";
//复用css的loader
let commonCssLoader = [
{
loader: MiNiCssExtractPlugin.loader,
options: {
publicPath: "../", //避免css中的路径引入错误
},
}, //代替style标签而是引入css的形式
"css-loader",
{
//把css压缩
loader: "postcss-loader",
options: {
//配置package.json的"browserslist"
ident: "postcss",
plugins: () => [require("postcss-preset-env")()],
},
},
];
module.exports = {
mode: "production", //生产模式
entry: "./src/js/index.js",
output: {
filename: "built.js",
path: path.resolve(__dirname, "build"),
},
module: {
rules: [
//eslint检查
{
//package.json配置"eslintConfig"
test: /\.js$/,
exclude: /node_modules/,
enforce: "pre", //js优先执行eslint
loader: "eslint-loader",
options: {
fix: true, //规范自动修复
},
},
{
oneOf: [
//css\less
{
test: /\.css$/,
use: [...commonCssLoader],
},
{
test: /\.less$/,
use: [...commonCssLoader, "less-loader"],
},
//js先eslint再babel
//js兼容处理
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
presets: [
[
"@babel/preset-env",
{
useBuiltIns: "usage", //按需加载
corejs: {
version: 3, //制定core-js版本
},
targets: {
//制定兼容的目标
chrome: "60",
firefox: "60",
ie: "9",
safari: "10",
edge: "17",
},
},
],
],
},
},
{
test: /\.(jpg|png|gif)$/,
loader: "url-loader",
options: {
limit: 8 * 1024,
name: "[hash:10].[ext]",
outputPath: "imgs",
esMoudle: false,
},
},
{
test: /\.html$/,
loader: "html-loader",
},
{
exclude: /\.(js|html|css|less|json|jpg|png|gif)$/,
loader: "file-loader",
options: {
outputPath: "media",
},
},
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html", //引用html模板
minify: {
//压缩html
collapseInlineTagWhitespace: true,
removeComments: true,
},
}),
new MiNiCssExtractPlugin({
filename: "css/built.css", //设置css的输出
}),
new OptimizeCssAssetsWebpackPlugin(), //压缩css的插件
],
};
```
##### 优化代码运行的**性能-**缓存

**对babel缓存、对资源缓存**
对于js代码的编译处理,我们每次构建之后,只想变动我们修改的文件模块,这时候不能依赖于HMR的dev-server,所以要对babel进行缓存操作
只需要在babel-loader中的options中开启`cacheDirectory:true`

想要看出区别要在根目录下新家server.js配置服务器代码
```js
const express = require("express");
const app = express();
app.use(express.static("build", { maxAge: 1000 * 3600 }));
app.listen(3001);
//启动服务器 nodemon server.js
//或 node server.js
```
由于nodemon需要全局安装一下,就没有使用
任何把服务端跑起来之后

发现第一次执行webpack和我修改之后没有发生变化(证明是被缓存了

原因是开启了强制缓存,cache-contral,我们设置了会缓存1小时

所以考虑给打包的资源加hash值


之后重新打包一次
发现浏览器的引入network中js和css是有hash值后缀的

我尝试改一下js代码重新打包都重新生成了新的hash值后缀的静态文件

但是这个还是有问题,因为这个hash是根据webpack打包来的,如果这样的话,我们无论是修改css还是js都会刷新缓存状态,他们公用了一个hash值

因此要引入chunkhash和contenthash
chunkhash: 根据chunk生成的hash值. 如果打包来源同一个chunk, 那么hash值就一样
contenthash: 根据文件的内容生成hash值, 不同文件hash值一定不一样
很明显contenthash可以解决我们的需求,chunkhash是因为我们的css是由index.js引入的所以同属一个chunk
使用contenthash可以保证只要文件没有修改,都会是同一个hash值不会额外引入
第一次构建

稍微修改一下css之后再构建一次,貌似是可以了

试一下只改js,只有js重新构建了

##### Tree shaking 树摇(去除没有使用的代码
基本上生产环境下使用es6模块引用会自动使用
Tree shaking是为了去除无用的js或者css代码,减少代码体积
js:必须使用es6模块化,使用production环境

在index.js中只引入部分

打包后可以发现构建后的代码中没有我们写的减法minus
如果在package.json中配置了
```json
"sideEffects":false
```
表示构建所有代码都使用树摇,则css或者其他文件会被默认摇掉
这样就可以了:
```json
"sideEffects":["*.css","*.less"]
```
当然,我试过了,这句话不加我的css是不会被摇掉的,所以还是不取消副作用了,生产模式会默认
"sideEffects"为true
##### 代码分割-code split
针对js把入口chunk分割成多个chunk文件,以减小体积,实现代码分割
step1:实现entry多入口
```js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin"); //html模板
module.exports = {
mode: "production", //生产模式
// entry: "./src/js/index.js",//单入口
entry:{
//多入口,每个入口一个bundle
main:'./src/js/index.js',
test:'./src/js/test.js'
},
output: {
filename: "js/[name].[contenthash:10].js",
path: path.resolve(__dirname, "build"),
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html", //引用html模板
minify:{//压缩html
collapseInlineTagWhitespace:true,
removeComments:true,
}
}),
],
devtool:'source-map',
};
```

这个主要是应用于多页面应用
但是这种编写方式不灵活
step2:使用splitChunks
```js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin"); //html模板
module.exports = {
mode: "production", //生产模式
entry: "./src/js/index.js",//单入口
output: {
filename: "js/[name].[contenthash:10].js",
path: path.resolve(__dirname, "build"),
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html", //引用html模板
minify:{//压缩html
collapseInlineTagWhitespace:true,
removeComments:true,
}
}),
],
optimization:{
//可以将node_modules中的模块单独打包成一个chunk
splitChunks:{
chunks:'all'
}
},
devtool:'source-map',
};
```
比如我在index.js中引入了jquery
打包之后

```js
optimization:{
//可以将node_modules中的模块单独打包成一个chunk
splitChunks:{
chunks:'all'
}
},
```
可以把第三方的内容单独打包,现在我们可以改成多入口试试
```js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin"); //html模板
module.exports = {
mode: "production", //生产模式
// entry: "./src/js/index.js",//单入口
entry:{
//多入口,每个入口一个bundle
main:'./src/js/index.js',
test:'./src/js/test.js'
},
output: {
filename: "js/[name].[contenthash:10].js",
path: path.resolve(__dirname, "build"),
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html", //引用html模板
minify:{//压缩html
collapseInlineTagWhitespace:true,
removeComments:true,
}
}),
],
optimization:{
//可以将node_modules中的模块单独打包成一个chunk
splitChunks:{
chunks:'all'
}
},
devtool:'source-map',
};
```

现在有三个chunk了,如果我们的test.js中也引入了jquery呢(公用的第三方)
就会发现出现不使用splitChunks的话两个chunk都会单独包括第三方都会很大
但是打开的话就都分开单独引用,体积就会变小


step3:主要针对单页面应用,js中import语句引入
如果在单入口的时候我引入的是自己的源代码,但是想把他单独打包
语法是使用`import('./test.js').then()`
```js
//index.js
// import { mul } from './test';
const add = (x, y) => x + y;
new Promise((resolve) => {
console.log('promise');
setTimeout(() => {
console.log('timeout');
resolve();
}, 0);
}).then(() => {
console.log('p_end');
});
console.log(add(2, 3));
import('./test')//返回promisr对象
.then((res)=>{
console.log('test加载成功',res)
console.log(res.mul(3, 3));
})
.catch((e)=>{
console.log(e)
})
```

test.js就会被打包到1.js中
看一下输出

这时候有个问题我们想让那个命名1变友好,自定义命名为test的话,需要写注释
```js
import(/* webpackChunkName:'test' */'./test')//返回promisr对象
.then((res)=>{
console.log('test加载成功',res)
console.log(res.mul(3, 3));
})
.catch((e)=>{
console.log(e)
})
```
然后就见证奇迹了~

个人感觉第二种方法即使用splitChunks加上第三种方法中偶尔引用个人源代码进行打包是比较智能的
顺便查了一下多入口应用的配置多出口和相应html

##### 懒加载layzloading和预加载
这里的webpack配置很简单,需要单入口形式的
```js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin"); //html模板
module.exports = {
mode: "production", //生产模式
entry: "./src/js/index.js",//单入口
output: {
filename: "js/[name].[contenthash:10].js",
path: path.resolve(__dirname, "build"),
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html", //引用html模板
minify:{//压缩html
collapseInlineTagWhitespace:true,
removeComments:true,
}
}),
],
optimization:{
//可以将node_modules中的模块单独打包成一个chunk
splitChunks:{
chunks:'all'
}
},
devtool:'source-map',
};
```
此懒加载是js文件的懒加载,不是图片资源的懒加载


很明显test被提前加载了
现在假如有一个需求,当我点击按钮,才想加载test.js

这时候import动态加载就来啦

初始情况为

当我点击按钮


这就实现了es6动态引入的懒加载,因为其实现了代码分割,并不需要担心会重复加载
然后,还有一个东西叫预加载webpackPrefetch
```js
console.log('index.js被加载了')
document.getElementById('btn').onclick=async function(){
let {mul}=await import(/*webpackChunkName:'test',webpackPrefetch:true*/'./test')
console.log(mul(4,5))
}
```
刚开始进入也是

但是再network显示责怪文件以及在初始被引入了,但是没有被调用

当我点击按钮,会追加一个很快的引入,其实是读取了之前的缓存

> 区别:
>
> 1. 预加载会在使用之前提前请求js文件,但是不会调取;懒加载是当文件被需要才会请求
>
> 2. 正常加载被认为是并行加载(同一时间加载多个文件,但是可能没人用,所以提倡懒加载)
>
> 3. 懒加载中的预加载是会等其他文件加载完毕,在浏览器空闲的时候才会进行预加载,以备打算使用该模块的人等待时间过长
##### PWA渐进式web应用
pwa模拟webapp的功能,在缓存、通知、后台功能等方面表现更好
```js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin"); //html模板
const MiNiCssExtractPlugin = require("mini-css-extract-plugin"); //提取出css
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin"); //压缩css
const workboxWebpackPlugin=require('workbox-webpack-plugin');//使用pwa
process.env.NODE_ENV = "production";
//复用css的loader
let commonCssLoader = [
{
loader:MiNiCssExtractPlugin.loader,
options:{
publicPath:'../',//避免css中的路径引入错误
}
}, //代替style标签而是引入css的形式
"css-loader",
{
//把css压缩
loader: "postcss-loader",
options: {
//配置package.json的"browserslist"
ident: "postcss",
plugins: () => [require("postcss-preset-env")()],
},
},
];
//pwa渐进式web应用(主要是离线可以访问
//workbox->workbox-webpack-plugin
module.exports = {
mode: "production", //生产模式
entry: "./src/js/index.js",
output: {
filename: "built.[contenthash:10].js",
path: path.resolve(__dirname, "build"),
},
module: {
rules: [
//css\less
{
test: /\.css$/,
use: [...commonCssLoader],
},
{
test: /\.less$/,
use: [...commonCssLoader, "less-loader"],
},
// //js先eslint再babel
// //eslint检查
// {
// //package.json配置"eslintConfig"
// test: /\.js$/,
// exclude: /node_modules/,
// enforce:'pre',//js优先执行eslint
// loader: "eslint-loader",
// options: {
// fix: true, //规范自动修复
// },
// },
//js兼容处理
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
presets: [
[
"@babel/preset-env",
{
useBuiltIns: "usage", //按需加载
corejs: {
version: 3, //制定core-js版本
},
targets: {//制定兼容的目标
chrome: "60",
firefox: "60",
ie: "9",
safari: "10",
edge: "17",
},
},
],
],
cacheDirectory:true,//开启babel缓存
},
},
{
test:/\.(jpg|png|gif)$/,
loader:'url-loader',
options:{
limit:8*1024,
name:'[contenthash:10].[ext]',
outputPath:'imgs',
esMoudle:false,
}
},
{
test:/\.html$/,
loader:'html-loader',
},
{
exclude:/\.(js|html|css|less|json|jpg|png|gif)$/,
loader:'file-loader',
options:{
outputPath:'media'
}
}
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html", //引用html模板
minify:{//压缩html
collapseInlineTagWhitespace:true,
removeComments:true,
}
}),
new MiNiCssExtractPlugin({
filename: "css/built.[contenthash:10].css", //设置css的输出
}),
new OptimizeCssAssetsWebpackPlugin(), //压缩css的插件
new workboxWebpackPlugin.GenerateSW({
//帮助替换旧的serviceWorke文件,帮助快速启动
//去入口js中做配置
clientsClaim:true,
skipWaiting:true,
}),
],
};
```
```js
//index.js
import '../css/c.less';
import '../css/a.css';
import '../css/b.css';
import '../css/iconfont.css';
import '../css/index.css';
import { mul } from './test';
const add = (x, y) => x + y;
new Promise((resolve) => {
console.log('promise');
setTimeout(() => {
console.log('timeout');
resolve();
}, 0);
}).then(() => {
console.log('p_end');
});
console.log(add(2, 3));
console.log(mul(3, 3));
// 注册serverceWorker
//eslint不认识浏览器的配置,要去package修改eslintConfig的env
if ('serviceWorker' in navigator) {
// 兼容判断
window.addEventListener('load', () => {
//由webpack生成
navigator.serviceWorker.register('/service-worker.js')
.then((res) => {
console.log('注册service-worker成功', res);
})
.catch((e) => {
console.log(e);
});
});
}
```

我之前是把eslint注释掉了
但是可以不注释,然后在package.json中修改eslintConfig,让eslint支持浏览器的全局变量





现在改成offline

离线环境下加载成功,size提示来自serviceWorker

##### 多进程打包
js是单进程,想通过多进程打包优化速度
下载thread-loader
一般给babel-loader用

```js
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
//开启多进程打包
//进程启动大概为600ms,进程通信也有开销
//工作消耗长才需要多进程打包,在代码少的时候反而不应该使用
loader: "thread-loader",
options: {
workers: 2, //进程数
},
},
{
loader: "babel-loader",
options: {
presets: [
[
"@babel/preset-env",
{
useBuiltIns: "usage", //按需加载
corejs: {
version: 3, //制定core-js版本
},
targets: {
//制定兼容的目标
chrome: "60",
firefox: "60",
ie: "9",
safari: "10",
edge: "17",
},
},
],
],
cacheDirectory: true, //开启babel缓存
},
},
],
},
```
再次强调,应该适用于大项目,使用babel转化比较多的时候
##### Externals禁止一些打包
比如CDN
通过externals忽略了jquery
```js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin"); //html模板
module.exports = {
mode: "production", //生产模式
entry: "./src/js/index.js",
output: {
filename: "built.[contenthash:10].js",
path: path.resolve(__dirname, "build"),
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html", //引用html模板
}),
],
externals:{
//忽略库名 -- npm包名
jquery:'jQuery',
}
};
```

很明显没有打包jquery,不然起码80k而不是1k左右
这时候如果想用jquery就是要依靠cdn,所以要在模板html中手动引入,之后jquery就能用了

##### DLL动态链接库
也指示哪些webpack库不参加打包,但是是从node_modules中单独取出来第三方打包
dll会单独打包成一个chunk,可以把第三方库分开打包
需要新建一个文件`webpack.dll.js`(叫啥都行)
```js
//使用dll技术对第三方库进行单独打包jquery\react\vue等
//需要运行webpack.dll.js
// webpack --config webpack.dll.js
const {resolve} =require('path')
const webpack =require('webpack')
module.exports={
entry:{
jquery:['jquery']
//也可以写其他的,数组中也可以加
},
output:{
filename:'[name].js',
path:resolve(__dirname,'dll'),
library:'[name]_[hash]',//打包的库向外暴露的内容叫什么名字
},
plugins:[
//打包生成manifest.json-》提供和jquery的映射
new webpack.DllPlugin({
name:'[name]_[hash]',//映射库的暴露的内容名称
path:resolve(__dirname,'dll/manifest.json')//输出的配置文件
})
]
}
```
运行
```
webpack --config webpack.dll.js
```

关于jquery打包成功
然后要改一下webpack.config.js的配置
使用manifest.json告诉webpack.DllReferencePlugin配置信息
然后还要使用add-asset-html-webpack-plugin自动引入文件
```js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin"); //html模板
const webpack = require('webpack')
const AddAssetHtmlWebpackPlugin=require('add-asset-html-webpack-plugin')
module.exports = {
mode: "production", //生产模式
entry: "./src/js/index.js",
output: {
filename: "built.[contenthash:10].js",
path: path.resolve(__dirname, "build"),
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html", //引用html模板
}),
new webpack.DllReferencePlugin({
//让manifest告诉webpack哪些库不参与打包
manifest:path.resolve(__dirname,'dll/manifest.json')
}),
new AddAssetHtmlWebpackPlugin({
//将某个文件打包输出去,并在html中自动引入
filepath:path.resolve(__dirname,'dll/jquery.js')
})
],
};
```

看起来没有打包jquery,但是浏览器也能拿到

> 1. 定义webpack.dll.js,把第三方单独打包,并配置产生manifest.json文件
> 2. 运行webpack --config webpack.dll.js 得到dll动态链接库和manifest.json文件
> 3. 在webpack.config.json中使用webpack.DllReferencePlugin插件引入manifest配置信息和第三方的不打包的信息
> 4. 在webpack.config.json中使用AddAssetHtmlWebpackPlugin插件引入dll/jquery.js,可以自动引入到html中
> 5. 只要自定义的dll不变,永远都不需要再次打包dll,直接引用即可
#### !!!优化配置总结大纲
1. 开发环境优化
* 优化打包构建速度
* HRM
* 优化代码调试
* source-map
2. 生产环境性能优化
* 优化打包构建速度
* oneOf
* 对babel缓存
* 多进程打包
* externals使用cdn
* dll使用独立打包
* 优化代码运行的性能
* 资源缓存(contenthash)
* tree shaking
* code split:分为单入口和多入口
* js懒加载和预加载
* PWA离线访问
### webpack详细配置
官方文档:[https://v4.webpack.docschina.org/concepts/](https://v4.webpack.docschina.org/concepts/)
#### entry
```js
const {resolve}=require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
/**
* entry:入口起点
* 1.string-》'./src/index.js' 单入口
* 打包形成一个chunk,输出一个bundle文件
* chunk的默认名称是main
* 2.array
* 多入口
* 所有入口文件最终形成一个chunk,只有一个bundle文件
* 用于HMR功能让html热更新生效
* 3.object
* 多入口
* 有几个入口文件就有几个chunk,会有多个bundle文件
* 此时chunk的名称是key
* 也可以给value使用数组形式,单独打包
*/
//1.string
module.exports = {
mode:'development',
entry:'./src/index.js',
output:{
filename:'[name].js',
path:resolve(__dirname,'build')
},
plugins:[
new HtmlWebpackPlugin()
]
};
// //2.array
// module.exports = {
// mode:'development',
// entry:['./src/index.js','./src/add.js'],
// output:{
// filename:'[name].js',
// path:resolve(__dirname,'build')
// },
// plugins:[
// new HtmlWebpackPlugin()
// ]
// };
// //3.object
// module.exports = {
// mode:'development',
// entry:{
// index:["./src/index.js","./src/count.js"],
// add:"./src/add.js"
// },
// output:{
// filename:'[name].js',
// path:resolve(__dirname,'build')
// },
// plugins:[
// new HtmlWebpackPlugin()
// ]
// };
```
#### output
```js
const {resolve}=require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode:'development',
entry:'./src/index.js',
output:{
filename:'js/[name].js',//指定目录加名称
path:resolve(__dirname,'build'),//将来所有资源输出的公共目录
publicPath:'/',//所有引入资源公共路径前缀 ,'imgs/a.jpg'->'/imgs/a.jpg'
chunkFilename:'js/[name]_chunk.js',//非入口chunk的名称:import动态引入或者optimazition使用splitchunk都可以
//library一般用于dll,源代码构建不用
library:'[name]',//把构建后的js暴露出去
library:'window',//变量名添加到browser上,也可以使用global改用到node,或者commonjs、amd等
},
plugins:[
new HtmlWebpackPlugin()
]
};
```
#### module
```js
const {resolve}=require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode:'development',
entry:'./src/index.js',
output:{
filename:'js/[name].js',
path:resolve(__dirname,'build'),
},
module:{
rules:[
{
test:/\.css$/,//test使用正则匹配
//use使用多个loader
use:['style-loader','css-loader']
},
{
test:/\.js$/,
exclude:/node_modules/,
include:resolve(__dirname,'src'),
enforce:'pre',//可以用post表示延后执行
loader:'eslint-loader',
options:{},//对应loader的配置项
},
{
//oneOf中只会生效一个
oneOf:[
]
}
]
},
plugins:[
new HtmlWebpackPlugin()
]
};
```
#### resolve
```js
const {resolve}=require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode:'development',
entry:'./src/js/index.js',
output:{
filename:'js/[name].js',//指定目录加名称
path:resolve(__dirname,'build'),//将来所有资源输出的公共目录
},
plugins:[
new HtmlWebpackPlugin()
],
module:{
rules:[
{
test:/\.css$/,//test使用正则匹配
//use使用多个loader
use:['style-loader','css-loader']
},
]
},
resolve:{
//解析模块的规则
alias:{
//配置路径别名,简写路径,但是就没有提示了
$css:resolve(__dirname,'src/css')
},
//配置省略的后缀名,默认为.js
extensions:['.js','.json','.css','.jsx'],
//告诉webpack解析模块的目录,默认为当前目录的'node_modules'
modules:[resolve(__dirname,'../node_modules'),'node_modules']
}
};
```
#### devServer
```js
const {resolve}=require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode:'development',
entry:'./src/js/index.js',
output:{
filename:'js/[name].js',//指定目录加名称
path:resolve(__dirname,'build'),//将来所有资源输出的公共目录
},
plugins:[
new HtmlWebpackPlugin()
],
module:{
rules:[
{
test:/\.css$/,//test使用正则匹配
//use使用多个loader
use:['style-loader','css-loader']
},
]
},
devServer:{
contentBase:resolve(__dirname,'build'),//运行代码的目录
watchContentBase:true,//监视contentBase目录的所有文件,如果变化就reload
watchOptions:{
ignored:/node_modules/,//忽略文件
},
compress:true,//启动gzip压缩
port:3002,//设定端口号
host:'localhost',//设置域名
open:true,//自动打开浏览器
hot:true,//开启HRM
clintLogLevel:'none',//不打印启动服务器日志信息
quiet:true,//除了基本的启动信息意外,其他内容都不打印
overlay:false,//如果出错,不要全屏提示
proxy:{
//服务器代理,解决开发环境下的跨域问题
'api':{
//一旦devServer服务器3002接收到/api/xxx的请求,就会通过proxy转发到本地的服务器8080
target:'http://localhost:8080',
pathRewrite:{
'^/api':'',//发送请求时,请求路径重写,/api/xxx->/xxx
}
},
//下面跨域新增代理
}
}
};
```
#### optimization
```js
const {resolve}=require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const TerserWebpackPlugin = require('terser-webpack-plugin')
module.exports = {
mode:'production',
entry:'./src/js/index.js',
output:{
filename:'js/[name].[contentHash:10].js',//指定目录加名称
path:resolve(__dirname,'build'),//将来所有资源输出的公共目录
},
plugins:[
new HtmlWebpackPlugin()
],
module:{
rules:[
{
test:/\.css$/,//test使用正则匹配
//use使用多个loader
use:['style-loader','css-loader']
},
]
},
optimization:{
splitChunks:{
chunks:'all',
// //下面都是默认值,可以不写
// minSize:30*1024,//参与分割的chunk最小为30kb,
// maxSize:0,//最大没有限制
// minChunks:1,//要提取的chunk最少被引用1次
// maxAsyncRequests:5,//按需加载的时候并行加载的文件最大数量为5
// maxInitialRequests:3,//入口js文件最大并行请求数量
// automaticNameDelimiter:'~',//名称连接符
// name:true,//可以使用命名规则
// cacheGroups:{
// //分割chunk的组
// vendors:{
// //node_modules文件会被打包到vendors组中的chunk中-->vendors~xxx.js
// test:/[\\/]node_modules[\\/]/,
// priority:-10,//打包的优先级
// },
// default:{
// minChunks:2,//要提取的chunk最少被引用两次
// priority:-20,//打包的优先级
// reuseExistingChunk:true,//启动打包复用模块,再用不重新打包
// }
// }
},
runtimeChunk:{
//将模块记录其他模块的hash单独打包为一个文件 runtime
//解决修改其他文件导致入口文件中保存的hash值变化引起的入口文件缓存失效
name:entrypoint=>`runtime-${entrypoint.name}`
},
minimizer:[
//配置生产环境的压缩方案:js和css
//4.26以上版本的webpack默认使用terser方案
new TerserWebpackPlugin({
//优化terser方案
cache:true,//开启缓存
parallel:true,//开启多进程打包
sourceMap:true,//启动source-map
})
]
}
};
```
## webpack5

1. 精简配置
2. 通过持久缓存提高构建性能
3. 通过更好的算法和默认值来改善长期缓存
4. 通过更好的树摇和代码生成来改善捆绑包的大小
5. 清除处于怪异状态的内部结构,同时在v4中实现功能而不引入任何重大更改
6. 通过引入重大更改来为将来的功能做准备,以便我们尽可能长时间地使用v5
#### webpack4
先配置好webpack4,目前webpack默认是下载webpack4版本
```
npm init -y
tyarn add webpack webpack-cli -D
```

```js
console.log('a.js')
export const name='jack';
export const age=18;
```
```js
import * as a from './a'
console.log('b.js')
export {a};
```
```js
import * as b from './b';
console.log(b.a.name)
console.log('index.js')
```
```js
const {resolve}=require('path')
//最精简的基本仅能打包js代码的webpack4
module.exports={
mode:'development',
entry:'./src/index.js',
output:{
filename:'[name].js',
path:resolve(__dirname,'dist'),
},
}
```
#### webpack5
```
npm init -y
tyarn add webpack@next webpack-cli -D
```

```js
module.exports={
mode:'development',
//指定mode之后直接就可以测试了省略了enty和output
}
```

#### 生产模式下


webpack5小太多了
而且webpack4这种上级引入的形式没有办法使用treeshaking
而webpack5则是把所有的代码都整合到main.js中了
```js
(()=>{"use strict";console.log("a.js");console.log("b.js"),console.log("jack"),console.log("index.js")})();
//webpack5 的dist/main.js
```
#### 一些改进
[https://github.com/webpack/changelog-v5](https://github.com/webpack/changelog-v5)







