webpack

155 阅读12分钟

基础部分

1.为什么要使用打包工具?

将语法编译成浏览器识别的语法,(框架vue/react,es6语法,sass)

功能:
压缩代码
兼容性处理
提高性能

2.基本使用

注:webpack本身只能编译JS语法

> 问题的产生

// 1,创建src/js/sum.js
export default function sum(x, y) {
    return x + y
}

// 2,创建minus.js
export default function minus(x, y) {
    return x - y
}

// 3,创建src/main.js,导入sum和minus
import sum from './js/sum'
import minus from './js/minus'

console.log(sum(1, 2));
console.log(minus(1, 2));


// 4,创建public/index.html,导入main.js,结果报错
<script src="../src/main.js"></script>

// Uncaught SyntaxError: Cannot use import statement outside a module
// 报错原因:不能编译模块化语言,需要引入webpack

> 使用webpack

// 1,生成package.json包编译文件
// 注:name不能为webpack,main为main.js的文件路径
npm init -y

// 2,安装webpack
npm i webpack webpack-cli -D
注意:-D === --save-dev

// 3,运行,生成dist/main.js
npx webpack ./src/main.js --mode=development
npx webpack ./src/main.js --mode=production

// 注:
1.编译后的箭头函数\es6相关语法都没有改变
2.--mode=development\--mode=production的区别:production代码会被压缩成一行

// 4,将index.html中script标签的路径改为dist/main.js
<!-- <script src="../src/main.js"></script> -->
<script src="../dist/main.js"></script> // 3 -1
缺点:webpack只能编译js/json,不能编译css,如样式资源、图标字体图片等

3.五个核心概念

entry入口

output出口

loader加载器

plugins插件

mode模式:development/production

4.基本配置

// 1,创建webpack.config.js,必须在最外层目录,和package.json同级

// 注:所有配置在nodejs环境运行,所以用commonjs模块化

const path = require("path")

module.exports = {
    entry: "./src/main.js",
    output: {
        path: path.resolve(__dirname, "dist"), // 绝对路径,__dirname表示当前文件的目录
        filename: "main.js",
        clean: true, // 为避免每次打包前删除dist包,在生成文件之前清空output所在目录
    },
    module: { rules: [] },
    plugins: [],
    mode: "development",
}

// 2,npx webpack

5.处理css

再次:webpack不能直接编译css资源,需要style-loader和css-loader

css-loader:是将css资源编译成commonjs模块到js中
style-loader:将js中的css通过创建style标签的形式添加到html中

> 处理css实操

// 1,安装
npm i css-loader style-loader -D


// 2,配置
webpack.config.jsmodule: {
    rules: [{
        test: /\.css$/i,
        use: ["style-loader", "css-loader"], // 执行顺序:从下往上
    }]
},


// 3,创建src/css/index.css,导入到main.js中
index.css:
.box1 {
    width: 100px;
    height: 100px;
    background-color: orange;
}

main.js:
import './css/index.css'


// 4,npx webpack
在页面的head中能找到对应的style标签

6.处理less,sass/scss,stylus

> less

// 1,新建less/index.less,main.js中导入index.less
.box2 {
    width: 100px;
    height: 100px;
    background-color: pink;
}

import './less/index.less'


2,安装less,less-loader,并配置
npm i less less-loader -D

webpack.config.jsmodule: {
    rules: [{
        test: /\.css$/,
        use: ["style-loader", "css-loader"], // 执行顺序:从下往上
    },
    {
        test: /\.less$/,
        use: ["style-loader", "css-loader", 'less-loader'], 
    },
    ]
},

// 3,npx webpack

> sass/scss

1,新建scss/index.scss,main.js中导入index.scss
.box3 {
    width: 100px;
    height: 100px;
    background-color: red;
}

import './scss/index.scss'


//2,安装sass,sass-loader,并配置
npm i sass-loader sass  -D

webpack.config.js中
{
    test: /\.s[ac]ss$/,
    use: ["style-loader", "css-loader", 'sass-loader'],
},


// 3,npx webpack

> stylus

stylus和上述相同,具体参考webpack官方文档loader部分,仅供了解

7.处理图片资源

webpack4处理图片资源需要file-loader,url-loader,5已经内置

file-loader:简单的说就是把文件资源编译成webpack可以识别的资源
url-loader:是将图片优化,小于某个大小的文件转化成base64格式

> 处理图片资源

// 1,新建src/images,放入2张图片,一张png一张jpg


// 2,图片放入index.html中,npx webpack成功打包
index.css:
    .img1 {
      width: 100px;
      height: 100px;
      background-image: url("../images/img1.jpg");
      background-size: cover;
    }

    .img2 {
      width: 100px;
      height: 100px;
      background-image: url("../images/img2.png");
      background-size: cover;
    }
    
index.html:
  <div class="img1"></div>
  <div class="img2"></div>
  
// 成功显示
// 优化:将文件较小的图片转化成base64格式,减少请求次数
// 优点:减少请求次数;缺点:体积变大

// 1,配置webpack.config.js
{
    test: /\.(png|jpg|jpeg|gif|svg)$/,
    type: 'asset',
    parser: {
        dataUrlCondition: {
            maxSize: 40 * 1024 // 小于40kb会转为base64格式
            // 优点:减少请求次数;缺点:体积变大
        }
    }
}

// 2,删除之前的dist,重新打包,发现图片资源少了一个

8.修改输出文件的目录

问题:打包后的dist文件夹有点乱,分类
// 1,修改output中入口文件的路径
output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/main.js",
},


// 2,处理图片的输出目录,删除原先的dist,重新打包
    {
        test: /\.(png|jpg|jpeg|gif|svg)$/,
        type: 'asset',
        parser: {
            dataUrlCondition: {
                maxSize: 40 * 1024 // 小于40kb会转为base64格式
                // 优点:减少请求次数;缺点:体积变大
            }
        },
        generator: {
            filename: 'images/[hash:10][ext][query]'
        }
    }

// 注:
[hash:10]:打包后的文件名
    :10长度限制在10以内
[ext]:后缀名
[query]:携带参数,没有可以不写


// 3,注意:打包路径改变后,记得同时修改index.html中的路径

> 自动清空上次打包结果

output: {
    path: path.resolve(__dirname, 'dist'),
    filename: "main.js",
    clean: true,
},

9.处理icon图标和字体资源和其他资源


// 1,iconfont下好资源
// 将iconfont.css放入css文件夹
// 将iconfont.css中依赖的ttf,woff,woff2文件放入font文件夹


// 2,iconfont.css里的路径要改一下
@font-face {
  font-family: "iconfont"; /* Project id 4284619 */
  src: url("../font/iconfont.woff2?t=1697139184016") format("woff2"),
    url("../font/iconfont.woff?t=1697139184016") format("woff"),
    url("../font/iconfont.ttf?t=1697139184016") format("truetype");
}


// 3,main.js中引入css文件,重新打包,index.html中使用查看效果
main.js:
import './css/iconfont.css'

index.html:
<span class="iconfont icon-Books"></span>
<span class="iconfont icon-Email"></span>
    
    
    
// 4,优化:类似处理图片的方式处理字体\图标
{
    test: /\.(ttf|woff|woff2)$/,
    type: 'asset/resource', // asset是用来转码base64的,改为resource
    generator: {
        filename: 'media/[hash:10][ext][query]'
    }
}


// 其他资源
// 原封不动的输出即可,但是webpack又不能识别
// 1,在上述的优化中直接添加后缀即可
{
    test: /\.(ttf|woff|woff2|mp3|mp4)$/,
    type: 'asset/resource',
    generator: {
        filename: 'media/[hash:10][ext][query]'
    }
}

10.处理JS资源

webpack只能编译es模块化语法,像箭头函数、三目运算符无法编译


处理js资源:
1,babel:针对js兼容性处理
2,eslint:针对代码格式代码规范(优先)

11.eslint基本了解,使用

eslint:代码规范检查工具

配置文件,常用.eslintrc前缀文件,如.eslintrc.js
// 1,安装eslint相关插件,在webpack.config.js中引入
npm i eslint-webpack-plugin eslint -D

webpack.config.jsconst ESLintPlugin = require('eslint-webpack-plugin');

plugins: [new ESLintPlugin({
    context: path.resolve(__dirname, 'src') // 检查哪些文件,指定为src路径下的
})],


// 2,配置.eslintrc.js
module.exports = {
    // 解析选项
    parserOptions: {
        ecmaVersion: 6, // es语法版本
        sourceType: 'module', // es模块化
        ecmaFeatures: { // 其他特性
            jsx: true // 如react项目开启jsx
        }
    },
    // 具体规则
    // 0,off——关闭规则
    // 1,warn——开启规则,警告级别
    // 2,error——开启规则,错误级别
    rules: {
        'no-var': 2 // 禁用var
    },
    // extends: ['eslint-recommended'], // 继承现有规则,8.56版本已无该配置
    env: {
        node: true, // 启用node中全局变量
        browser: true, // 启用浏览器中全局变量
    }
}


// 3,打包,此时如果项目中用var则会报错,用Eslint插件会标红,prettier类插件会自动修改为var


// 4,忽略dist下的文件
创建.eslintignore
dist

12.babel的了解,使用

> 了解

- 1.作用:
js编译器,将es6语法编译为向后兼容的js语法,能够在旧版本浏览器\环境运行


- 2.配置文件
babel.config.*
    可以是babel.config.js/json,放在根目录下
    
.babelrc.*
    可以是.babelrc.js/json,放在根目录下
    
package.json里的babel配置



- 3.具体配置
module.exports = {
    presets: ['@babel/preset-env'] // 预设
}

@babel/preset-env:智能预设,能够编译es6文件
@babel/preset-react:编译jsx文件
@babel/preset-typescript:编译ts语法

> 使用


- 1.安装
npm install -D babel-loader @babel/core @babel/preset-env


- 2.配置
webpack.config.js中
{
    test: /\.js$/,
    exclude: /node_modules/,
    use: {
        loader: 'babel-loader',
        options: {
            presets: ['@babel/preset-env']
        }
    }
}
    
或者在webpack.config.js中
{
    test: /\.js$/,
    exclude: /node_modules/,
    use: {
        loader: 'babel-loader',
    }
}
根目录下新建babel.config.js
module.exports = {
    presets: ['@babel/preset-env']
}
    

- 3.npx webpack观察dist下的js文件变化,箭头函数\es6语法都被修改了

13.处理html资源

> 问题

public下的js文件当前是手动引入的,如果打包的名称改变或者打包出多个js文件,会造成麻烦和问题∴要改成自动引入
将<script src="../dist/js/main.js"></script>注释掉

> 使用

- 1.安装插件plugin
npm i -D html-webpack-plugin

- 2.配置webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
    new HtmlWebpackPlugin()
],

- 3.打包
此时dist下会多出一个index.html文件,但是结构是空的和之前的Index.html不同,∴需要配置模板

- 4.重新配置+打包即可
 new HtmlWebpackPlugin({
    template: path.resolve(__dirname, "public/index.html"),
})

14.自动化,开发服务器

> 问题

改变项目中的数据时,dist/index.html页面不会即时更新,每次改变数据时需要重新打包,需要引入开发服务器的概念

> 使用

- 1.安装
npm i webpack-dev-server -D


- 2.配置webpack.config.js
devServer: {
    host: 'localhost',
    port: '3000',
    open: true
},


- 3.启动服务
npx webpack server(serve也行)
// 注:开发服务器是没有输出的,在内存中编译打包,打包不会生成dist下的文件

15.总结开发配置

entry:入口
output:出口
mode:模式
module(loader):用来处理webpack处理不了的资源,css相关\图片字体\音频视频\babel代码兼容性处理
plugins:插件,如eslint规范代码\处理html资源\开发服务器

16.生产环境与开发环境

> 开发环境

- 1.新建config/webpack.dev.js

- 2.修改相关路径
相对路径不用改(可以理解成在根目录下打包)
绝对路径使用到path需要往上一层
开发环境下是没有输出的,path直接写undefined

entry: "./src/main", // 不变
output: {
    path: undefined,
    filename: "main.js",
    clean: true,
},
plugins: [
    new ESLintPlugin({
        context: path.resolve(__dirname, '../src')
    }),
    new HtmlWebpackPlugin({
        template: path.resolve(__dirname, "../public/index.html"),
    })
],

- 3.npx webpack serve --config ./config/webpack.dev.js

> 生产环境

- 1.新建config/webpack.prod.js

- 2.修改相关路径
相对路径不用改(可以理解成在根目录下打包)
绝对路径使用到path需要往上一层

entry: "./src/main", // 不变
output: {
    path: path.resolve(__dirname, '../dist'),
    filename: "main.js",
    clean: true,
},
plugins: [
    new ESLintPlugin({
        context: path.resolve(__dirname, '../src')
    }),
    new HtmlWebpackPlugin({
        template: path.resolve(__dirname, "../public/index.html"),
    })
],
mode: "production"
// devServer删除

- 3.npx webpack --config ./config/webpack.prod.js

> 别名

npx webpack serve --config ./config/webpack.dev.js
npx webpack --config ./config/webpack.prod.js
启动指令太长,简化:

"scripts": {
    "start": "npm run dev",
    "dev": "webpack serve --config ./config/webpack.dev.js",
    "build": "webpack --config ./config/webpack.prod.js"
},

17.提取css成为单独的文件

> 问题

在第五章中,处理css的方法是将css资源编译成commonjs模块到js中,通过创建style标签的形式添加到html中

当使用慢速网络时,会出现白屏情况

> 操作

- 1.安装插件
npm i mini-css-extract-plugin -D

- 2.配置
webpack.prod.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
plugins: [
    new MiniCssExtractPlugin()
],
// 将之前所有的style-loader替换为MiniCssExtractPlugin.loader,如下
 rules: 
 [{ test: /\.css$/i,
 use: [MiniCssExtractPlugin.loader, "css-loader"],
}, { test: /\.less$/,
use: [MiniCssExtractPlugin.loader, "css-loader", 'less-loader'],
}, { test: /\.s[ac]ss$/,
use: [MiniCssExtractPlugin.loader, "css-loader", 'sass-loader'],
} ]

- 3.打包,此时dist下已生成main.css

- 4.改变main.css的路径,重新打包即可
new MiniCssExtractPlugin({
    filename: "css/main.css",
})

18.样式的兼容性处理

- 1.安装插件
npm  i postcss-loader postcss postcss-preset-env -D

- 2.配置
// 注意位置:要在css-loader后面,sass/scss/less-loader前面
  rules: [{
            test: /\.css$/i,
            use: [MiniCssExtractPlugin.loader, "css-loader",
            {
                loader: "postcss-loader",
                options: {
                    postcssOptions: {
                        plugins: [
                            'postcss-preset-env' // 预设
                        ]
                    }
                }
            }
            ],
        }, {
            test: /\.less$/,
            use: [MiniCssExtractPlugin.loader, "css-loader",
            {
                loader: "postcss-loader",
                options: {
                    postcssOptions: {
                        plugins: [
                            'postcss-preset-env'
                        ]
                    }
                }
            }, 'less-loader'
            ],
        }, {
            test: /\.s[ac]ss$/,
            use: [MiniCssExtractPlugin.loader, "css-loader", {
                loader: "postcss-loader",
                options: {
                    postcssOptions: {
                        plugins: [
                            'postcss-preset-env'
                        ]
                    }
                }
            }, 'sass-loader'],
        },  ]


- 3.配置package.json
// 在package.json最下面加上常用的浏览器配置
// 最后两个版本,兼容99%,未停更
"browserslist": [
    "last 2 version",
    "> 1%",
    "not dead"
]

19.封装loader函数,优化18

> 问题

18中,loader有很多重复的代码,封装函数优化

> 操作

const getStyleLoader = (params) => {
    return [
        MiniCssExtractPlugin.loader,
        "css-loader",
        {
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: ["postcss-preset-env"],
                },
            },
        },
        params,
    ].filter(Boolean)
}

module: {
    rules: [{
        test: /\.css$/i,
        use: getStyleLoader(),
    },
    {
        test: /\.less$/,
        use: getStyleLoader("less-loader"),
    },
    {
        test: /\.s[ac]ss$/,
        use: getStyleLoader("sass-loader"),
    }],
},

20.js\html\css压缩

> 问题

生产模式中,js\html默认压缩
现在处理css压缩

> 操作

- 1.安装插件
npm i css-minimizer-webpack-plugin -D

- 2.配置
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin")

// 写法一
optimization: {
    minimizer: [
        new CssMinimizerPlugin()
    ]
},

// 写法二
plugins: [
    new CssMinimizerPlugin()
],

进阶部分

// 提高开发体验
sourceMap

// 提升打包速度
hmr
oneOf
include/exclude
cache
thread多进程

// 减少代码体积
tree shaking
@babel/plugin-transform-runtime
image minimizer

// 优化代码运行性能
code split
preload\prefetch
newtork cache
corejs
PWA

1.sourceMap

> 引入

提高开发体验:当开发中碰到错误时,定位到的往往是编译后的文件,sourceMap是源码映射,会生成源码和构建后代码的映射关系

> 操作

// 开发环境常用:
// devtool: "cheap-module-source-map"
// devtool: "eval-cheap-module-source-map"
// 优点:只有行映射,快
// 缺点:没有列映射

- 1.故意写错add.js中的代码

- 2.webpack.dev.jsdevtool: "cheap-module-source-map" 

- 3.npm start观察结果


// 生产环境可以不用,或用source-map
// 优点:有行列映射
// 缺点:编译速度慢

- 1.webpack.prod.jsdevtool: "source-map" 

2.HMR

- 1.用处
// Hot Module Replace,热模块替换(热更新)
// 用来提升构建速度


- 2.先关闭
// 默认开启,关闭查看效果
devServer: {
    host: 'localhost',
    port: '3000',
    open: true,
    hot: false,
},


- 3.启动项目,重新观察
// 此时改变宽高等样式,页面会刷新


// 注:JS代码的修改还是会引发全局更新
// 判断是否有hot热更新功能,有则使用,此时修改add函数中的内容会触发局部更新
main.js中:
if (module.hot) {
    module.hot.accept(add);
}

3.oneOf/include,exclude

oneOf
// 作用:提升打包构建速度
// 打包时,每个资源都会经过所有loader处理,比较慢
// oneOf顾名思义就是只要匹配上一个loader剩下的就不匹配了


include,exclude
// 包含排除

> 操作

// webpack.dev.js/webpack.prod.js中
rules: [{
    oneOf: [{
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
    }, {
        test: /\.less$/,
        use: ["style-loader", "css-loader", 'less-loader'],
    }, {
        test: /\.s[ac]ss$/,
        use: ["style-loader", "css-loader", 'sass-loader'],
    }, {
        test: /\.(png|jpg|jpeg|gif|svg)$/,
        type: 'asset',
        parser: {
            dataUrlCondition: {
                maxSize: 20 * 1024
            }
        },
        generator: {
            filename: 'images/[hash:10][ext][query]'
        }
    },
    {
        test: /\.(ttf|woff|woff2|mp3|mp4)$/,
        type: 'asset/resource',
        generator: {
            filename: 'media/[hash:10][ext][query]'
        }
    },
    {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
            loader: 'babel-loader',
            options: {
                presets: ['@babel/preset-env']
            }
        }
    }
    ]
}]

4.缓存cache

> babel缓存

- 1.配置webpack.dev.js/webpack.prod.js
{
    test: /\.js$/,
    exclude: /node_modules/,
    use: {
        loader: 'babel-loader',
        options: {
            presets: ['@babel/preset-env'],
            cacheDirectory: true, // 开启babel缓存
            cacheCompression: false // 关闭缓存文件压缩
        }
    }
}

- 2.打包后,node_modules/.cache/babel-loader出现缓存

> eslint缓存

// eslint目前自带缓存,如果没有

- 1.配置webpack.dev.js/webpack.prod.js
new ESLintPlugin({
    context: path.resolve(__dirname, '../src'),
    cache: true,
    cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintCache')
}),

5.多进程打包threads

> 说明

针对性的提高babel\eslint\terser的运行速度

> 操作

- 1.安装thread-loader
npm i thread-loader -D


- 2.配置webpack.dev.js/webpack.prod.js
const os = require("os")
const thread = os.cpus().length // cpu核数
const TerserWebpackPlugin = require("terser-webpack-plugin") // terser是内置的

// babel
{
    test: /\.js$/,
    exclude: /node_modules/,
    use: [{
        loader: 'thread-loader',
        options: {
            workers: threads // 启动
        }
    }, {
        loader: 'babel-loader',
        options: {
            presets: ['@babel/preset-env'],
            cacheDirectory: true,
            cacheCompression: false
        }
    }]
}

// eslint
new ESLintPlugin({
    context: path.resolve(__dirname, "../src"),
    threads // 启动
}),

// terser:对JS的压缩,可以写在plugins里,也可以写在optimization下minimizer里
optimization: {
    minimizer: [
        new CssMinimizerPlugin(),
        new TerserWebpackPlugin({
            parallel: threads // 启动
        })
    ]
},

6.tree shaking

//默认开启

// 作用:
移除js中没有使用的代码
// 注:依赖ES Module

7.针对babel优化

// babel为编译的每个文件都插入了辅助代码,使得体积变大
// 为避免重复引用,引入@babel/plugin-transform-runtime
- 1.安装插件
npm i @babel/plugin-transform-runtime -D

- 2.配置
{
    test: /\.js$/,
    exclude: /node_modules/,
    use: [{
        loader: 'thread-loader',
        options: {
            workers: threads
        }

    }, {
        loader: 'babel-loader',
        options: {
            presets: ['@babel/preset-env'],
            cacheDirectory: true, // 开启babel缓存
            cacheCompression: false, // 关闭缓存文件压缩
            plugins: ['@babel/plugin-transform-runtime']
        }
    }]
}

8.image minimizer

// 存在多个文件下载不下来的情况,不做演示
// 移步p41章

9.code split

// 作用:代码分割,只渲染首页就只加载首页的js文件

9-1.多入口

- 1.新建webpack2/demo

- 2.安装配置
 npm init -y
 npm i webpack webpack-cli -D
 npm i -D html-webpack-plugin
 
 - 3.新建src/app.js,main.js,新建public/index.html
 
 - 4.新建webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    entry: { // 多入口写成对象形式
        app: "./src/app.js",
        main: "./src/main.js",
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: "[name].js", // 名字根据entry的属性名决定
        clean: true,
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "public/index.html"),
        })
    ],
    mode: 'production'
}

9-2.多入口提取公共模块

> 问题引入

- 1.新建webpack/demo2,复用demo1的功能

- 2.新建math.js,在app.js/main.js中复用
export const minus = (x, y) => {
    console.log(x - y);
    return x - y
}

- 3.npx webpack,在打包后的app.js\main.js中都有重复的代码
(()=>{"use strict";console.log(2),console.log("app")})();
(()=>{"use strict";console.log(1),console.log("main")})();

> 解决

- 1.配置webpack.config.js
optimization: {
    splitChunks: {
        chunks: 'all', // 对所有模块都进行分割
        cacheGroups: {
            default: {
                minSize: 0,
                minChunks: 2,
                priority: -20,
                reuseExistingChunk: true,
            }
        }
    }
},

- 2.重新打包,观察
// 此时dist下会多生成一个24.js文件,用于main.js\app.js引入

9-3.按需加载

> 问题引入

// 页面上按钮绑定的函数,希望点击的时候再加载

> 操作

- 1.新建webpack/demo3,复用demo2的功能,npm i

- 2.新建add.js,在main.js中引用
export const add = (x, y) => {
    console.log(x + y);
    return x + y
}

- 3.在index.html上添加按钮
<button class="btn">点击</button>

- 4.main.jsconst oBtn = document.querySelector('.btn')
oBtn.onclick = function () {
    import('./add').then(res => {
        console.log('模块加载成功!', res);
    }).catch(err => {
        console.log("模块加载失败!", eerr);
    })
}

- 4.打包,此时dist下会多生成一个780.js文件,打开页面点击按钮,会加载780文件

9-4.单入口

在单入口实现code split和按需加载,和多入口操作相同
// code split
- 1.回到webpack,配置webpack.prod.js
optimization: {
    splitChunks: {
        chunks: 'all',
    }
},

- 2.index.html中添加按钮,main.js修改代码
const Obtn = document.querySelector('.btn')
Obtn.onclick = function () {
    import('./js/add').then((res) => {
        console.log('res:', res.default(1, 2));
    }).catch(err => {
        console.log('err:' + err);
    })
}

- 3.打包

9-5.打包模块命名

> 问题

打包后的模块文件命名都是数字

> 处理

- 1.魔术命名
const Obtn = document.querySelector('.btn')
Obtn.onclick = function () {
    import( /* webpackChunkName:"add" */ './js/add').then((res) => {
        console.log('res:', res.default(1, 2));
    }).catch(err => {
        console.log('err:' + err);
    })
}


- 2.配置chunkFilename
output: {
    path: path.resolve(__dirname, "../dist"),
    chunkFilename: "[name].js",
    filename: "main.js",
    clean: true,
},

9-6.统一命名

> 问题

目前存在的命名:
主文件命名,chunk文件命名,图片字体资源命名,css命名等,统一规范

> 解决

 output: {
    path: path.resolve(__dirname, "../dist"),
    filename: "[name].js", // 主文件改为[name]
    chunkFilename: "js/[name].chunk.js",// chunk文件加上后缀
    assetModuleFilename:"images/[hash:10][ext][query]"
    clean: true,
},

// 图片字体的命名重复部分删掉,统一成assetModuleFilename
// generator: {
//     filename: 'images/[hash:10][ext][query]'
// }


// css资源
new MiniCssExtractPlugin({
    filename: "css/[name].css",
    chunkFilename: "js/[name].chunk.js",
}),

10.preload/prefetch

> 说明

// 说明:
// 之前已经做过code split和懒加载,但是如果懒加载的资源体积很大,会卡顿


// 相同点\不同点
// preload:立即加载资源
// prefetch:空闲时加载资源

// 共同点:都会加载资源,不执行,都有缓存

// 不同点:
// preload:优先级高,只能加载当前页面的资源
// prefetch:优先级低,可以加载当前页面资源,也可以加载下一页面资源

// 缺点:兼容性差

> 操作

- 1.安装,配置
npm i --save-dev preload-webpack-plugin

webpack.prod.js:
const PreloadWebpackPlugin = require('@vue/preload-webpack-plugin');

plugins: [
    new PreloadWebpackPlugin({
        rel: 'preload',
        as: 'script'
    })
],


- 2.打包后观察index.html
<link href="js/add.chunk.js" rel="preload" as="script" />

11.network cache,

> 问题

// 问题:
当add.js发生变化,打包后hash值改变,引入add.js的main.js也随之发生改变

// 解决:
// 通过一个runtime文件

> 操作

- 0.在开始之前,将output的文件加上contenthash
output: {
    path: path.resolve(__dirname, "../dist"),
    filename: "[name].[contenthash:8].js",
    chunkFilename: "js/[name].chunk.[contenthash:8].js",
    assetModuleFilename: "images/[hash:10][ext][query]",
    clean: true,
},

- 1.配置webpack.prod.dev
optimization: {
    runtimeChunk: {
        name: entrypoint => `runtime~${entrypoint.name}.js`
    }
},

- 2.打包
// 打包会生成一个runtime文件用来管理,此时修改add不会改变main

12.corejs

babel预设可以解决箭头函数\解构符等语法兼容性,但promsie\async等解决不了,需要corejs
corejs用来做es6及以上的api的polyfill

> 使用

npm i core-js

// 全局
import 'core-js'
打包
// 打包后生成的文件很大,改为按需引入

// 按需引入,如下
import 'core-js/es/promise'


// 继续优化
- 1.新建babel.config.js
module.exports = {
    presets: [
        ['@babel/preset-env', {
            useBuiltIns: "usage",
            // corejs: 3,
        }]
    ],
}

- 2.打包
// 打包之后没有反应,是因为browserlist中的浏览器都能兼容promise语法
// 如果换成ie浏览器就会生成文件

13.PWA

// 渐进式网络应用程序progressive web app

// 提升离线体验

// 缺点:兼容性差

> 操作

- 1.安装
npm i workbox-plugin -D


- 2.配置
webpack.prod.js中:
const PreloadWebpackPlugin = require('@vue/preload-webpack-plugin');

new WorkboxPlugin.GenerateSW({
    clientsClaim: true,
    skipWaiting: true,
})

main.js中:
if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
        navigator.serviceWorker.register('/service-worker.js').then(registration => {
            console.log('SW-register', registration);
        }).catch(err => {
            console.log('SW-failed', err);
        })
    })
}

- 3.打包,观察
// SW加载失败,因路径问题,页面是在/dist文件夹下


- 4.npm i serve -g

- 5.serve dist打开开发服务器localhost:3000即可

14.