#说明

本笔记为 观看 尚硅谷的webpack5入门到精通 教学视频整理而成

说明:

  1. 个人认为虽然这个视频名字是`从webpack5入门到精通` ,但是学下来内容应该是基于`webpack4`讲解,学完感觉刚入门webpack,只会简单使用webpack罢了,所以这部分我归类于`webpack的基础学习`. 真要精通,需要看官网以及其他学习资源,再多动手才行.所以接下去我将直接进入`webpack高级进阶阶段`学习,这部分应该是`webpack5`,介于本笔记已经内容过长,将新开高阶笔记,这里说一句,尚硅谷的视频还真的挺不错的,建议可以去看看
  2. 本知识点配置代码多,为了节约空间,后面的`配置部分`的配置代码,默认是基于上一个代码增加修改(`优化`部分基于`生产或者开发环境配置`代码进行优化),只写出新增或者修改部分(最后会在每部分最后一章写出完整配置在`代码),如有其他情况将在其代码示例或其上方注释指出

除此笔记外大家可以看我其他笔记 :全栈笔记数据结构与算法编程_前端开发学习笔记编程_后台服务端学习笔记JavaNodejsJavaScript笔记ES6及后续版本学习笔记Vue笔记整合React笔记微信小程序学习笔记Chrome开发使用及学习笔记 以及许多其他笔记就不一一例举了

#目录

一、Webpack简介

1、webpack是什么?

Webpack是一种前端资源构建工具,一个静态模块打包器(module bundler)

在 Webpack看来,前端的所有资源文件(js/json/css/img/less/…)都会作为模块处理

它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)

2、Webpack的五个核心概念

  1. Entry

入口指示 Webpack以哪个文件为入口七点开始打包,分析构建内部依赖图

  1. Output

输出指示 Webpack打包后的资源bundles输出到哪里去,以及如何命名

  1. Loader

Loader让Webpack 能够去处理那些非JavaScript文件(webpack自身只理解JavaScrpit)

  1. Plugins

插件可以用于执行范围更广的任务.插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等

  1. Mode

模式指示Webpack使用相应模式的配置

选项 描述 特点
development 会将process.env.NODE_ENV的值设置为development.
启用NamedChunksPlugin和NamedModulesPlugin
能让代码本地调试运行的环境
production 启动好多东西…. 能让代码优化上线运行的环境

3、Webpack初体验

Ⅰ、安装与使用

  1. 先安装全局webpack包(也可以不安装)
  1. npm i webpack webpack-cli -g
  1. 初始化项目npm包
  1. npm init
  1. 安装当前项目webpack包
  1. npm i webpack webpack-cli -D

Ⅱ、编译打包应用

  1. 创建相关文件夹与文件(build与src文件加与index.js)

  2. 运行指令

a)开发环境指令:webpack ./src/index.js -o ./build/built.js --mode=development

webpack就会以./src/index.js为入口文件开始打包,打包后输出到./build/built.js整体打包环境,是开发环境

b)生产环境指令:webpack ./src/index.js -o ./build/built.js --mode=production

做的都是一样的,环境是生产环境

c)当配置文件写好后,可以直接使用webpack指令进行打包



二、Webpack配置详解

1、在学习过程中这部分本是最后学习的,但本人认为可先放前面,先了解后再去使用.

  1. 建议:`二`--> `三` --> `四` --> `二` 顺序翻阅,先粗略过一遍`二`,有不懂的留着疑惑看二、三,最后再回头看这里

2、webpack配置实际是创建一个对象,所以里面的属性并没有执行顺序,即使你将entry写在output下面都可以执行,因为到时候执行的时候是webpack通过调用对象属性进行实现顺序的

Ⅰ- entry

entry:入口起点,有三种形式写法

1、string —> ‘./src/index.js’

  1. 单入口:打包形成一个chunk(模块),输出一个bundle(包)文件
  2. 此时默认的chunk名称是main

2、array —> [‘./src/index.js’,’./src/add.js’]

  1. 多入口:所有入口文件最终只会形成一个chunk,输出出去只有一个bundle文件(类似将add.js打包进index.js中)
  2. -->通常只有在`HMR功能中使用`, html热更新生效使用

3、object —> {index:’./src/index.js’,add:’./src/add.js’}

  1. 多入口:有几个入口文件就形成几个chunk,输出几个bundle文件
  2. 此时chunk名称是 key

4、特殊用法(混合使用)

  1. 通常在`dll`优化功能中使用
  1. module.exports = {
  2. entry: {
  3. // 所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。
  4. index: ['./src/index.js', './src/count.js'],
  5. // 形成一个chunk,输出一个bundle文件。
  6. add: './src/add.js'
  7. },
  8. output: {
  9. filename: '[name].js',
  10. path: resolve(__dirname, 'build')
  11. },
  12. plugins: [new HtmlWebpackPlugin()],
  13. mode: 'development'
  14. };

Ⅱ- output

使用entry为单入口为例讲解

1、filename:文件名称(指定名称+目录)

2、chunkFilename:非入口chunk的名称,如未指定这项,在入口文件中导入的js打包也会用上filename的文件名称进行命名,但是名字与入口文件冲突,就会使用0~∞数字命名,不容易区分

3、path:输出文件目录(将来所有资源输出的公共目录)

4、publicPath:所有资源引入公共路径的前缀 — ‘img/a.jpg’—>’/img/a.jpg’ 这个适合生产环境使用

  1. 这两者变化区别:前者是将`imgs`目录跟在当前目录路径后面,后者是将imgs跟在服务器地址后面

5、library:'[name]' //整个库向外暴露的变量名 实际上使用var声明

6、libraryTarget:将变量名添加到哪个对象上

  1. a) libraryTarget: 'window' 适合浏览器端
  2. b) libraryTarget: 'global' 适合node
  3. c) libraryTarget: 'commonjs' 使用commonjs方式进行模块导出
  1. const { resolve } = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. module.exports = {
  4. entry: './src/index.js',
  5. output: {
  6. // 文件名称(指定名称+目录)
  7. filename: 'js/[name].js',
  8. // 输出文件目录(将来所有资源输出的公共目录)
  9. path: resolve(__dirname, 'build'),
  10. // 所有资源引入公共路径前缀 --> 'imgs/a.jpg' --> '/imgs/a.jpg' 适合生产环境使用,
  11. // 这两者地址区别:前者是将imgs目录跟在当前目录后面 ,后者是将imgs跟在服务器地址后面
  12. publicPath: '/',
  13. chunkFilename: 'js/[name]_chunk.js', // 非入口chunk的名称
  14. // library: '[name]', // 整个库向外暴露的变量名 定义为var
  15. // libraryTarget: 'window' // 变量名添加到哪个上 browser(适用浏览器端)
  16. // libraryTarget: 'global' // 变量名添加到哪个上 node(适用node服务端)
  17. // libraryTarget: 'commonjs' //使用commonjs的方式模块导出
  18. },
  19. plugins: [new HtmlWebpackPlugin()],
  20. mode: 'development'
  21. };

Ⅲ- module

1、test:文件名匹配规则,后面参数是一个正则

2、exclude:排除匹配某个目录下的内容 —> exclude: /node_modules/ ->排除node_modules下的文件

3、include:只检查 某个目录下的文件 —> include: resolve(__dirname, ‘src’) ->只检查 src 下的js文件

4、loaderuse:单个loader使用loader,多个loader用use

5、enforce:指定该配置的执行顺序: enforce:’pre‘(优先执行) > 默认 > enforce:’post‘(延后执行)

6、options:指定这个loader的配置选项

7、oneOf: []:里面的配置只会生效一次,即里面有100个配置,当我一个文件进入这里检测,可能第10个配置匹配到了就生效,然后该文件就不会进行下面90次匹配,如果是不放oneOf里面的配置,就会完全执行100次匹配,性能优化使用

  1. 该部分知识在`优化`部分:当你使用enlintbabel两种配置进行对于js文件的匹配的情景下会使用
  1. const { resolve } = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. module.exports = {
  4. entry: './src/index.js',
  5. output: {
  6. filename: 'js/[name].js',
  7. path: resolve(__dirname, 'build')
  8. },
  9. module: {
  10. rules: [
  11. {
  12. test: /\.js$/,
  13. // 排除node_modules下的js文件
  14. exclude: /node_modules/,
  15. // 只检查 src 下的js文件
  16. include: resolve(__dirname, 'src'),
  17. // 单个loader用loader
  18. loader: 'eslint-loader',
  19. // 多个loader用use
  20. //use: ['style-loader', 'css-loader']
  21. // 优先执行
  22. enforce: 'pre',
  23. // 延后执行
  24. // enforce: 'post',
  25. //指定这个loader的配置选项
  26. options: {}
  27. },
  28. {
  29. // 以下配置只会生效一个
  30. oneOf: []
  31. }
  32. ]
  33. },
  34. plugins: [new HtmlWebpackPlugin()],
  35. mode: 'development'
  36. };

Ⅳ- resolve

该resolve并不是path的relove,而是配置中的resolve配置项:解析模块的规则

1、alias:配置解析模块路径别名: 优点简写路径 缺点路径没有提示

2、extensions:配置省略文件路径的后缀名

3、modules:告诉webpack解析模块是去哪个目录 —>如果不指定会webpack会一层一层往外找,造成不必要的性能浪费

  1. const { resolve } = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. module.exports = {
  4. entry: './src/js/index.js',
  5. output: {},
  6. module: {
  7. rules: [ ]
  8. },
  9. plugins: [new HtmlWebpackPlugin()],
  10. mode: 'development',
  11. // 解析模块的规则
  12. resolve: {
  13. // 配置解析模块路径别名: 优点简写路径 缺点路径没有提示
  14. alias: {
  15. $css: resolve(__dirname, 'src/css')
  16. },
  17. // 配置省略文件路径的后缀名
  18. extensions: ['.js', '.json', '.jsx', '.css'],
  19. // 告诉 webpack 解析模块是去找哪个目录
  20. modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
  21. }
  22. };

配置后js引用示例代码

  1. import '$css/index';

Ⅴ- devServer

这部分配置很多,只抽出我觉得比较重要的部分

1、proxy:服务器代理 —>解决开发环境跨域问题

  1. target: 一旦devserver服务器接收到`/接口名/xxx`,就会把请求转发到`target`后面的参数url服务器上
  2. pathRewrite:发送请求时,请求路径重写 --> 如:将/api/xxx ->/xxx(去掉前面的/api)

2、contentBase:指定运行代码的目录

3、hot:开启HMR模块热替换,这是优化部分功能

4、overlay:当设置为false时,如果代码错误,不要进行全屏提示

5、watchContentBase:当设置为true时,监听contentBase目录下的所有文件 一旦文件变化就会reload

6、watchOptions:内部设置监听的忽略文件,通常与5搭配使用

7、compress:是否开启gzip压缩

  1. const { resolve } = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. module.exports = {
  4. devServer: {
  5. // 运行代码的目录
  6. contentBase: resolve(__dirname, 'build'),
  7. // 监视 contentBase 目录下的所有文件,一旦文件变化就会 reload
  8. watchContentBase: true,
  9. watchOptions: {
  10. // 忽略文件
  11. ignored: /node_modules/
  12. },
  13. // 启动gzip压缩
  14. compress: true,
  15. // 端口号
  16. port: 5000,
  17. // 域名
  18. host: 'localhost',
  19. // 自动打开浏览器
  20. open: true,
  21. // 开启HMR功能
  22. hot: true,
  23. // 不要显示启动服务器日志信息
  24. clientLogLevel: 'none',
  25. // 除了一些基本启动信息以外,其他内容都不要显示
  26. quiet: true,
  27. // 如果出错了,不要全屏提示~
  28. overlay: false,
  29. // 服务器代理 --> 解决开发环境跨域问题
  30. proxy: {
  31. // 一旦devServer(5000)服务器接受到 /api/xxx 的请求,就会把请求转发到另外一个服务器(3000)
  32. '/api': {
  33. target: 'http://localhost:3000',
  34. // 发送请求时,请求路径重写:将 /api/xxx --> /xxx (去掉/api)
  35. pathRewrite: {
  36. '^/api': ''
  37. }
  38. }
  39. }
  40. }
  41. };

Ⅵ- optimization

主要用于优化部分的code split —>代码分割,这个需要先看优化部分的代码分割后再翻阅

1、splitChunks:开启代码分割

2、runtimeChunk:将当前模块的记录其他模块的hash单独打包为一个文件 runtime

  1. 解决的问题:当你修改a文件后,因为你使用的是`contenthash`作为文件名,它根据内容会生成不同的哈希值,所以你a文件文件名将会变化.而如果你在b文件中导入a文件,b文件就会因为a文件的文件名变化导致b文件自己内容也跟着变化(即使你对b文件无任何修改),导致b文件也会`重新打包,缓存失效`
  2. 解决原理:将当前模块的记录其他模块的hash单独打包为一个文件

3、terser-webpack-plugin:当你的webpack版本在4.26以上,它使用的是terser的库进行压缩,要使用的时候需要下载依赖,且如果要优化压缩速度,需要修改里面配置,否则会使用默认配置

  1. const TerserWebpackPlugin = require('terser-webpack-plugin')
  2. module.exports = {
  3. entry: './src/js/index.js',
  4. output: {
  5. filename: 'js/[name].[contenthash:10].js',
  6. path: resolve(__dirname, 'build'),
  7. chunkFilename: 'js/[name].[contenthash:10]_chunk.js'
  8. },
  9. module: {
  10. rules: [
  11. {
  12. test: /\.css$/,
  13. use: ['style-loader', 'css-loader']
  14. }
  15. ]
  16. },
  17. plugins: [new HtmlWebpackPlugin()],
  18. mode: 'production',
  19. resolve: {
  20. alias: {
  21. $css: resolve(__dirname, 'src/css')
  22. },
  23. extensions: ['.js', '.json', '.jsx', '.css'],
  24. modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
  25. },
  26. optimization: {
  27. splitChunks: {
  28. chunks: 'all'
  29. // 下面的是默认值,当你启用上面哪个`all`后,可以不写、一般也不进行修改
  30. ---------------------------------------------------------------------------
  31. minSize: 30 * 1024, // 分割的chunk最小为30kb
  32. maxSize: 0, // 最大没有限制
  33. minChunks: 1, // 要提取的chunk最少被引用1次,如果少于1次,就不提取了
  34. maxAsyncRequests: 5, // 按需加载时并行加载的文件的最大数量
  35. maxInitialRequests: 3, // 入口js文件最大并行请求数量
  36. automaticNameDelimiter: '~', // 名称连接符
  37. name: true, // 可以使用命名规则
  38. cacheGroups: {
  39. // 分割chunk的组
  40. // node_modules文件会被打包到 vendors 组的chunk中。--> vendors~xxx.js
  41. // 满足上面的公共规则,如:大小超过30kb,至少被引用一次。
  42. vendors: {
  43. test: /[\\/]node_modules[\\/]/,
  44. // 优先级
  45. priority: -10
  46. },
  47. default: {
  48. // 要提取的chunk最少被引用2次
  49. minChunks: 2,
  50. // 优先级
  51. priority: -20,
  52. // 如果当前要打包的模块,和之前已经被提取的模块是同一个,就会复用,而不是重新打包模块
  53. reuseExistingChunk: true
  54. }
  55. --------------------------------------------------------------------------------------------
  56. }
  57. },
  58. // 将当前模块的记录其他模块的hash单独打包为一个文件 runtime
  59. // 解决:修改a文件导致b文件的contenthash变化
  60. runtimeChunk: {
  61. name: entrypoint => `runtime-${entrypoint.name}`
  62. },
  63. minimizer: [
  64. // 配置生产环境的压缩方案:js和css
  65. new TerserWebpackPlugin({
  66. // 开启缓存
  67. cache: true,
  68. // 开启多进程打包
  69. parallel: true,
  70. // 启动source-map
  71. sourceMap: true
  72. })
  73. ]
  74. }
  75. };


三、Webpack.config.js基本配置学习

详细参数部分见下面内容的webpack配置详解

1、module:moudle对应loader(加载器 )的配置,主要对指定类型的文件进行操作

  1. `举例`js类型的文件和css文件需要不同的loader来处理。最常用的加载器是eslint-loaderbabel-loader

2、Plugin: plugins用于扩展webpack的功能,相比着loader更加灵活,不用指定文件类型

  1. `举例`:html-webpack-plugincommonChunkPluginExtractTextPlugin

3、Output: 指定输出编译后代码的位置。

  1. `注意`:即使指定了多个入口点(entry points),Ouput配置项也只能设置一个。

4、mode:指定模式:开发模式(development)生产模式(production)

  1. `注意`:生产模式`默认会压缩js文件`

5、loader:当引用多个loader时,使用use["loader名1","loader名2"]形式引入,单个时使用loader:'loader名'形式

注意:

1、loader的配置:rules执行顺序是从下往上,从右往左执行

2、对于rules中的loader,webpack还定义了一个属性 enforce,可取值有 pre(为pre loader)、post(为post loader),如果没有值则

为(normal loader)。所以loader在webpack中有4种:normal,inline,pre,post

1、开发环境配置部分

Ⅰ-通用配置

  1. /*
  2. webpack.config.js webpack的配置文件
  3. 作用: 指示 webpack 干哪些活(当你运行 webpack 指令时,会加载里面的配置)
  4. 所有构建工具都是基于nodejs平台运行的~模块化默认采用commonjs。
  5. */
  6. // resolve用来拼接绝对路径的方法
  7. const { resolve } = require('path');
  8. const HtmlWebpackPlugin = require('html-webpack-plugin'); //plugins需要引入这个插件
  9. module.exports = {
  10. // webpack配置
  11. // 入口起点
  12. entry: './src/index.js',
  13. // 输出
  14. output: {
  15. // 输出文件名
  16. filename: 'built.js',
  17. // 输出路径
  18. // __dirname nodejs的变量,代表当前文件的目录绝对路径
  19. path: resolve(__dirname, 'build')
  20. },
  21. // loader的配置
  22. //loader: 1. 下载 2. 使用(配置loader)
  23. module: {
  24. rules: [
  25. // 详细loader配置,如样式打包修改这里
  26. ]
  27. },
  28. // plugins的配置
  29. //plugins: 1. 下载 2. 引入 3. 使用
  30. plugins: [
  31. // 详细plugins的配置 如:html打包配置这里
  32. ],
  33. // 模式
  34. mode: 'development', // 开发模式
  35. // mode: 'production'
  36. }

Ⅱ-打包样式资源

  1. // loader的配置,样式打包改这里
  2. module: {
  3. rules: [
  4. // 详细loader配置
  5. // 不同文件必须配置不同loader处理
  6. {
  7. // 匹配哪些文件
  8. test: /\.css$/,
  9. // 使用哪些loader进行处理
  10. use: [
  11. //需要下载style-loader与css-loader
  12. // use数组中loader执行顺序:从右到左,从下到上 依次执行
  13. // 创建style标签,将js中的样式资源插入进行,添加到head中生效
  14. 'style-loader',
  15. // 将css文件变成commonjs模块加载js中,里面内容是样式字符串
  16. 'css-loader'
  17. ]
  18. },
  19. {
  20. test: /\.less$/,
  21. use: [
  22. 'style-loader',
  23. 'css-loader',
  24. // 将less文件编译成css文件
  25. // 需要下载 less-loader和less
  26. 'less-loader'
  27. ]
  28. }
  29. ]
  30. },
  1. loader的配置:rules执行顺序是从下往上,从右往左执行
  2. css需要下载依赖`npm i style-loader css-loader -D
  3. 使用less的时候需要在上面css依赖基础上再下载npm i less-loader less -D

Ⅲ-打包html

  1. const { resolve } = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin'); //需要引入这个插件
  3. plugins: [
  4. // plugins的配置
  5. // html-webpack-plugin
  6. // 功能:默认会创建一个空的HTML,自动引入打包输出的所有资源(JS/CSS)
  7. // 需求:需要有结构的HTML文件
  8. new HtmlWebpackPlugin({
  9. // 复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS)
  10. template: './src/index.html'
  11. })
  12. ],

plugins打包html需要下载依赖:npm i html-webpack-plugin -D

Ⅳ-打包图片资源

  1. module: {
  2. rules: [
  3. {
  4. test: /\.less$/,
  5. // 要使用多个loader处理用use
  6. use: ['style-loader', 'css-loader', 'less-loader']
  7. },
  8. {
  9. // 问题:默认处理不了html中img图片
  10. // 处理图片资源
  11. test: /\.(jpg|png|gif)$/,
  12. // 使用一个loader
  13. // 下载 url-loader file-loader
  14. loader: 'url-loader',
  15. options: {
  16. // 图片大小小于8kb,就会被base64处理
  17. // 优点: 减少请求数量(减轻服务器压力)
  18. // 缺点:图片体积会更大(文件请求速度更慢)
  19. limit: 8 * 1024,
  20. // 问题:因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs
  21. // 解析时会出问题:[object Module]
  22. // 解决:关闭url-loader的es6模块化,使用commonjs解析
  23. esModule: false,
  24. // 给图片进行重命名
  25. // [hash:10]取图片的hash的前10位
  26. // [ext]取文件原来扩展名
  27. name: '[hash:10].[ext]'
  28. }
  29. },
  30. {
  31. test: /\.html$/,
  32. // 处理html文件的img图片(负责引入img,从而能被url-loader进行处理)
  33. loader: 'html-loader'
  34. }
  35. ]
  36. },
  37. plugins: [
  38. new HtmlWebpackPlugin({
  39. template: './src/index.html'
  40. })
  41. ],

ps:url-loader file-loader与html-loader都需要下载依赖

  1. 问题一:只设置一个test{/\.(jpg|png|gif)$/} ,loader:'url-loader',,默认处理不了html中img图片,html中仍然是./src/img.jpg

解: 需要再引入test:{/\.html$/},才可以,但是这时候解析时会出问题:html的图片路径编程[object Module]`

  1. 问题二:html的图片路径变成[object Module]

分析: 因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs,所以解析时用的es6模块化解析,出现问题

解决:关闭options中的url-loader的es6模块化,使用commonjs解析 options:{esModule: false}

Ⅴ-打包其他资源

exclude:指定除此之外的资源

  1. module: {
  2. rules: [
  3. {
  4. test: /\.css$/,
  5. use: ['style-loader', 'css-loader']
  6. },
  7. // 打包其他资源(除了html/js/css资源以外的资源)
  8. {
  9. // 排除css/js/html资源
  10. exclude: /\.(css|js|html|less)$/,
  11. loader: 'file-loader',
  12. options: {
  13. name: '[hash:10].[ext]'
  14. }
  15. }
  16. ]
  17. },

Ⅵ-热更新 devServer

热更新需要下载依赖 : npm i webpack-dev-server -D

启动devServer指令为:npx webpack-dev-server

只会在内存中编译打包,不会有任何输出

  1. const { resolve } = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. module.exports = {
  4. entry: './src/index.js',
  5. output: {},
  6. module: {},
  7. plugins: [ ],
  8. mode: 'development',
  9. // 开发服务器 devServer:用来自动化(自动编译,自动打开浏览器,自动刷新浏览器~~)
  10. // 特点:只会在内存中编译打包,不会有任何输出
  11. // 启动devServer指令为:npx webpack-dev-server
  12. devServer: {
  13. // 项目构建后路径
  14. contentBase: resolve(__dirname, 'build'),
  15. // 启动gzip压缩
  16. compress: true,
  17. // 端口号
  18. port: 3000,
  19. // 自动打开浏览器
  20. open: true
  21. }
  22. };

Ⅶ-开发环境配置

  1. const { resolve} = require('path')
  2. const HtmlWebpackPlugin = require('html-webpack-plugin')
  3. module.exports = {
  4. entry: './src/js/index.js',
  5. output: {
  6. filename: 'js/built.js',
  7. path: resolve(__dirname, 'build')
  8. },
  9. module: {
  10. rules: [{
  11. //1.1 处理less资源
  12. test: /\.less$/,
  13. use: [ 'style-loader','css-loader', 'less-loader' ]
  14. },
  15. {
  16. //1.2 处理css资源
  17. test: /\.css$/,
  18. use: [ 'style-loader', 'css-loader' ]
  19. },
  20. {
  21. //2.1 处理图片资源
  22. test: /\.(jpg|png|gif)$/,
  23. loader: 'url-loader',
  24. options: {
  25. limit: 8 * 1024,
  26. //压缩后的文件名 压缩后保留后缀名
  27. name: '[hash:10].[ext]',
  28. //关闭es6模块化
  29. esModule: false,
  30. //在built文件中输入位置,这里是放在build中的imgs文件夹中
  31. outputPath: 'imgs'
  32. }
  33. },
  34. {
  35. //2.2 处理html中的图片资源
  36. test: /\.html$/,
  37. loader: 'html-loader',
  38. },
  39. {
  40. //处理其他资源 先排除以下几个
  41. exclude: /\.(html|js|css|less|jpg|png|gif)/,
  42. loader: 'file-loader',
  43. options: { name: '[hash:10].[ext]', outputPath: 'media' }
  44. }
  45. ]
  46. },
  47. plugins: [
  48. //插件的配置
  49. new HtmlWebpackPlugin({//打包HTML文件
  50. template: './src/index.html'
  51. })
  52. ],
  53. //指定开发模式
  54. mode: 'development',
  55. //热更新配置
  56. devServer: {
  57. contentBase: resolve(__dirname, 'build'),
  58. compress: true,
  59. port: 3000,
  60. open: true
  61. }
  62. }

2、生产环境配置部分

Ⅰ-提取css成单独文件

1、抽出成css文件: 防止闪屏现象

  1. 因为之前是压缩到js中,当页面加载完后用js进行渲染,当性能不够好时可能出现闪屏现象
  2. 抽出成css后用link引入,就不会出现这个现象

2、需要下载依赖:npm i mini-css-extract-plugin

3、使用mini-css-extract-plugin插件,然后用使用这个插件的loader取代style-loader

  1. const HtmlWebpackPlugin = require('html-webpack-plugin');
  2. const {resolve}=require('path');
  3. //下载依赖mini-css-extract-plugin
  4. //将css提取为单独插件
  5. const MiniCssExtractPlugin=require('mini-css-extract-plugin')
  6. module.exports={
  7. entry:'./src/js/index.js',
  8. output:{
  9. filename:'js/built.js',
  10. path:resolve(__dirname,'build')
  11. },
  12. module:{
  13. rules:[{
  14. test:/\.css$/,
  15. use:[
  16. //创建style标签 'style-loader',
  17. //使用这个插件的loader取代style-loader 作用:提取js中的css成单独文件
  18. MiniCssExtractPlugin.loader,
  19. //将css文件整合到js文件中
  20. 'css-loader']
  21. }]},
  22. mode:'development',
  23. plugins:[
  24. new HtmlWebpackPlugin({ template:'./src/index.html' }),
  25. //对输出的文件进行重命名
  26. new MiniCssExtractPlugin({ filename:'css/built.css'})
  27. ]
  28. }

Ⅱ-css兼容性处理

1、需要依赖postcss—>postcss-loaderpostcss-preset-env

2、需要修改package.json文件

3、需要设置nodejs的环境变量

4、此处有可能出现问题,问题记载于下方问题解决部分

  1. const HtmlWebpackPlugin = require('html-webpack-plugin')
  2. const MiniCssExtractPlugin = require('mini-css-extract-plugin')
  3. const {
  4. resolve
  5. } = require('path')
  6. //设置nodejs环境变量
  7. process.env.NODE_ENV='development'
  8. module.exports = {
  9. entry: './src/js/index.js',
  10. output: {
  11. filename: 'js/built.js',
  12. path: resolve(__dirname, 'build')
  13. },
  14. module: {
  15. rules: [{
  16. test: /\.css$/,
  17. use: [MiniCssExtractPlugin.loader, 'css-loader',
  18. {
  19. // 1、使用loader的默认配置写法 --> 'postcss-loader'
  20. // 2、修改loader的配置写法
  21. loader: 'postcss-loader',
  22. options: {
  23. ident: 'postcss',
  24. plugins: () => [
  25. //使用postcss的插件进行兼容性处理
  26. require('postcss-preset-env')()
  27. ]
  28. //如果没效果用下面的 postcssOptions: { plugins: [【'postcss-preset-env',{}】] }
  29. //如果{}中不需要再传入options,可以这样 plugins: 【'postcss-preset-env'】
  30. }
  31. }
  32. ]
  33. }]
  34. },
  35. mode: 'development',
  36. plugins: [
  37. new HtmlWebpackPlugin({
  38. template: './src/index.html'
  39. }),
  40. new MiniCssExtractPlugin({
  41. filename: 'css/built.css'
  42. })
  43. ]
  44. }

默认是生产环境,所以使用开发环境的时候需要设置nodejs环境

  1. "browserslist": {
  2. "development": [
  3. "last 1 chrome version",
  4. "last 1 firefox version",
  5. "last 1 safari version"
  6. ],
  7. "production": [
  8. ">0.2%",
  9. "not dead",
  10. "not op_mini all"
  11. ]
  12. },

Ⅲ-css压缩

1、压缩通常用的插件plugins,而兼容性处理之类的都使用loader来做

2、下载依赖:optimize-css-assets-webpack-plugin

  1. const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
  2. // 压缩css
  3. plugins: [
  4. // 压缩css
  5. new OptimizeCssAssetsWebpackPlugin()
  6. ]

Ⅳ-js代码检查 _eslint

1、语法检查依赖 eslint-loadereslint

2、airbnb规则依赖:eslint-config-airbnb-baseeslint-plugin-import

3、 注意:只检查自己写的源代码,第三方的库是不用检查的

4、设置package.json中eslintConfig

5、当某些地方代码只是测试使用,不想被eslint检测 :下一行eslint所有规则都失效(下一行不进行eslint检查)

  1. 使用注释`//eslint-disable-next-line`

webpack.config.js配置

  1. const { resolve } = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. module.exports = {
  4. entry: './src/js/index.js',
  5. output: { },
  6. module: {
  7. rules: [
  8. {
  9. test: /\.js$/,
  10. exclude: /node_modules/,
  11. loader: 'eslint-loader',
  12. options: {
  13. // 自动修复eslint的错误
  14. fix: true
  15. }
  16. }
  17. ]
  18. },
  19. plugins: [ ],
  20. mode: 'development'
  21. };

package.json配置

airbnb是相对热门的模板

  1. "eslintConfig": {
  2. "extends": "airbnb-base",
  3. "env": {
  4. "browser": true
  5. }
  6. },

当你想在文件中跳过检测时 :下一行eslint所有规则都失效(下一行不进行eslint检查)

  1. // eslint-disable-next-line
  2. console.log(add(2, 5));

Ⅴ-js兼容性处理_babel

js兼容性处理的三种方式:

1、基本js兼容性处理 —>@babel/preset-env

问题:只能转换基本语法,比如promise等高级语法不能转换

2、全部js兼容性处理 —>@babel/polyfill

使用: js代码中直接导入:import '@babel/polyfill';

问题: 我只要解决部分的兼容性问题,但是将所有兼容性代码全部引入,体积太大了

3、需要做兼容性处理的就做:按需加载:core-js ————> 最为推荐

依赖四个下载:babel-loader@babel/core@babel/polyfillcore-js

  1. const {
  2. resolve
  3. } = require('path')
  4. const HtmlWebpackPlugin = require('html-webpack-plugin')
  5. module.exports = {
  6. entry: './src/js/index.js',
  7. output: {
  8. filename: 'js/built.js',
  9. path: resolve(__dirname, 'build')
  10. },
  11. module:{
  12. rules:[
  13. {
  14. test:/\.js$/,
  15. exclude:/node_modules/,
  16. loader:'babel-loader',
  17. options:{
  18. //预设,存放的是数组,而不是对象,所以里面第一层是[]而不是{}
  19. presets:[[
  20. //预设用什么模板
  21. '@babel/preset-env',
  22. {
  23. //使用内置对象:按需加载
  24. useBuiltIns:'usage',
  25. //指定core-js版本
  26. corejs:{
  27. version:3
  28. },
  29. //指定兼容性做到哪个版本浏览器
  30. targets:{
  31. chrome:'60',
  32. firefox:'60',
  33. ie:'9',
  34. safari:'10',
  35. edge:'17'
  36. }
  37. }
  38. ]]
  39. }
  40. }
  41. ]
  42. },
  43. plugins:[
  44. new HtmlWebpackPlugin({
  45. template:'./src/index.html'
  46. })
  47. ],
  48. mode:'development'
  49. }

Ⅵ-Js与HTML压缩

1、在生产环境下,会自动进行Js代码压缩

2、HTML压缩需要在HtmlWebpackPlugin插件的配置中编写要求,如下面代码的去除空格与注释

  1. const { resolve } = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. module.exports = {
  4. entry: './src/js/index.js',
  5. output: {
  6. filename: 'js/built.js',
  7. path: resolve(__dirname, 'build')
  8. },
  9. plugins: [
  10. new HtmlWebpackPlugin({
  11. template: './src/index.html',
  12. minify:{
  13. //移除空格
  14. collapseWhitespace:true,
  15. //移除注释
  16. removeComments:true
  17. }
  18. })
  19. ],
  20. // 生产环境下会自动压缩js代码
  21. mode: 'production'
  22. };

Ⅶ-生产环境配置

完整配置文件以及相应注解

注意:

1、正常来讲,一个文件只能被一个loader处理.但当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:

  • 如:先执行eslint 再执行babel
  • —>因为babel会将es6的语法转为低阶语法如(let->var),如果先转babel的话eslint就会报错
  1. const HtmlWebpackPlugin = require('html-webpack-plugin'); //引入html打包插件
  2. const MiniCssExtractPlugin = require('mini-css-extract-plugin'); //引入css提取打包插件
  3. const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin') //引入css压缩插件
  4. const { resolve } = require('path'); //引入路径插件
  5. //复用loader配置 这里因为cssless配置大部分相同,所以抽出
  6. const commonCssLoader = [
  7. MiniCssExtractPlugin.loader, //使用这个插件的loader取代style-loader 作用:提取css为单独的文件
  8. 'css-loader',
  9. {
  10. loader: 'postcss-loader', //还需要在packahe.json中定义browserslist
  11. options: {
  12. ident: 'postcss',
  13. plugins: () => [require('postcss-preset-env')] //使用postcss插件进行兼容性处理
  14. }
  15. }
  16. ]
  17. module.exports = {
  18. entry: './src/js/index.js',
  19. output: {
  20. filename: 'js/built.js',
  21. path: resolve(__dirname, 'built')
  22. },
  23. /*一、引用 */
  24. module: {
  25. rules: [
  26. /**1cssless规则配置 提取单独文件与css兼容性处理 */
  27. {
  28. test: /\.css$/,
  29. use: [...commonCssLoader]
  30. },
  31. {
  32. test: /\.less$/,
  33. use: [...commonCssLoader, 'less-loader']
  34. },
  35. /**2js处理规则配置
  36. * 正常来讲,一个文件只能被一个loader处理.但当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
  37. * 如:先执行eslint 再执行babel
  38. * -->因为babel会将es6的语法转为低阶语法如(let->var),如果先转babel的话eslint就会报错
  39. */
  40. {
  41. test: /\.js$/,
  42. exclude: /node_modules/, //使用eslint不检查依赖库里面的js
  43. enforce: "pre", //下面的babel处理jsloader应该在eslint检查后再执行,设置优先执行当前loader
  44. loader: 'eslint-loader',
  45. options: {
  46. fix: true // 自动修复eslint的错误,比如补全分号
  47. }
  48. },
  49. {
  50. test: /\.js$/,
  51. exclude: /node_modules/, //使用babel不转换依赖库里面的js
  52. loader: 'babel-loader',
  53. options: {
  54. presets: [
  55. [ //注意 这里两个[]
  56. '@babel/preset-env', //预设指定用的什么babel模板
  57. {
  58. useBuiltIns: 'usage', //使用内置对象:按需加载
  59. corejs: { //指定core-js版本
  60. version: 3
  61. },
  62. targets: { //指定babel转换js兼容性做到哪个版本浏览器兼容
  63. chrome: '60',
  64. firefox: '60',
  65. ie: '9'
  66. }
  67. }
  68. ]
  69. ]
  70. }
  71. },
  72. /**3、图片压缩*/
  73. {
  74. //图片打包,但是默认处理不了html中的图片,需要另外配置一个loader处理
  75. test: /\.(jpg|png|gif)$/,
  76. loader: 'url-loader',
  77. options: {
  78. limit: 8 * 1024, //当图片
  79. name: '[hash:10].[ext]', //限制打包后的哈希文件名长度,保留后缀名
  80. outputPath: 'imgs', //执行打包后放在哪个位置
  81. //url-loader默认使用的是es6的模块化解析,而html-loader引入图片是commonjs
  82. //所以需要关闭`url-loader`的es6模块化解析,使用commonjs模块化解析
  83. esModule: false
  84. }
  85. },
  86. {
  87. test: /\.html$/,
  88. loader: 'html-loader' //需要使用html插件
  89. },
  90. /**4 其他资源打包 */
  91. {
  92. exclude: /\.(js|css|less|scss|html|jpg|png|gif)$/, //先排除自己配置过的文件
  93. loader: 'file-loader',
  94. options: {
  95. outputPath: 'media', //执行打包后放在哪个位置
  96. name: '[hash:10].[ext]' //限制打包后的哈希文件名长度,保留后缀名
  97. }
  98. }
  99. ]
  100. },
  101. //插件
  102. plugins: [
  103. //1html打包插件,在html打包图片,以及打包html文件时候用到
  104. new HtmlWebpackPlugin({
  105. template: './src/index.html',
  106. minify: { //指定`minify`该属性后,将会根据里面配置进行html文件压缩
  107. collapseWhitespace: true, //去除文件中的空格
  108. removeComments: true //去除文件中的注释
  109. }
  110. }),
  111. //2css提取打包插件
  112. new MiniCssExtractPlugin({
  113. filename: 'css/built.css' //指定提取打包后的css文件位置与名字
  114. }),
  115. //3css压缩插件 默认配置即可达到正常的打包要求
  116. new OptimizeCssAssetsWebpackPlugin()
  117. ],
  118. //指定模式为生产模式
  119. mode: 'production' //指定为生产模式后,js将会默认压缩
  120. }

package.json

  1. {
  2. "name": "webpack_code",
  3. "version": "1.0.0",
  4. "description": "",
  5. "main": "index.js",
  6. "scripts": {
  7. "test": "echo \"Error: no test specified\" && exit 1"
  8. },
  9. "author": "",
  10. "license": "ISC",
  11. "devDependencies": {
  12. "@babel/core": "^7.8.4",
  13. "@babel/polyfill": "^7.8.3",
  14. "@babel/preset-env": "^7.8.4",
  15. "add-asset-html-webpack-plugin": "^3.1.3",
  16. "babel": "^6.23.0",
  17. "babel-loader": "^8.0.6",
  18. "core-js": "^3.6.4",
  19. "css-loader": "^3.4.2",
  20. "eslint": "^6.8.0",
  21. "eslint-config-airbnb-base": "^14.0.0",
  22. "eslint-loader": "^3.0.3",
  23. "eslint-plugin-import": "^2.20.1",
  24. "file-loader": "^5.0.2",
  25. "html-loader": "^0.5.5",
  26. "html-webpack-plugin": "^3.2.0",
  27. "less": "^3.11.1",
  28. "less-loader": "^5.0.0",
  29. "mini-css-extract-plugin": "^0.9.0",
  30. "optimize-css-assets-webpack-plugin": "^5.0.3",
  31. "postcss-loader": "^3.0.0",
  32. "postcss-preset-env": "^6.7.0",
  33. "style-loader": "^1.1.3",
  34. "terser-webpack-plugin": "^2.3.5",
  35. "thread-loader": "^2.1.3",
  36. "url-loader": "^3.0.0",
  37. "webpack": "^4.46.0",
  38. "webpack-cli": "^3.3.12",
  39. "webpack-dev-server": "^3.10.3",
  40. "workbox-webpack-plugin": "^5.0.0"
  41. },
  42. "dependencies": {
  43. "jquery": "^3.4.1"
  44. },
  45. "browserslist": {
  46. "development": [
  47. "last 1 chrome version",
  48. "last 1 firefox version",
  49. "last 1 safari version"
  50. ],
  51. "production": [
  52. ">0.2%",
  53. "not dead",
  54. "not op_mini all"
  55. ]
  56. },
  57. "eslintConfig": {
  58. "extends": "airbnb-base",
  59. "env": {
  60. "browser": true //支持浏览器端的bian'l,比如 window
  61. }
  62. },
  63. "sideEffects": [
  64. "*.css" //这是在优化部分的树摇配置
  65. ]
  66. }


四、Webpack优化

Ⅰ- HMR优化

HMR: hot module replacement热模块替换/模块热替换

作用:一个模块发生变化,指挥重新打包这一个模块(而不是打包所有模块) 极大的提升构建速度,主要是在开发模式中使用,方便调试

1、样式文件:可以使用HMR功能:因为style-loader内部实现了

2、js文件:默认不能使用HMR功能
使用方法—>需要修改js代码,添加支持HMR功能的代码
注意:HMR功能对js的处理,只能处理非入口js文件的其他文件

3、html文件:默认不能使用HMR功能,同事会导致问题:html文件不能热更新了
解决:修改entry入口,将html文件引入(不用做HMR功能,毕竟现在流行单页面应用)

基于前面的开发环境配置部分代码进行优化

  1. const {resolve}=require('path');
  2. const HtmlWebpackPlugin=require('html-webpack-plugin');
  3. module.exports={
  4. entry:['./src/js/index.js','./src/index.html'],
  5. output:{
  6. filename:'js/built.js',
  7. path:resolve(__dirname,'build')
  8. },
  9. module:{
  10. rules:[
  11. {
  12. test:/\.less$/,
  13. //此处开发环境使用的是style-loader,因为它自己实现了HMR功能
  14. use:['style-loader','css-loader','less-loader']
  15. },
  16. {
  17. test:/\.css$/,
  18. use:['style-loader','css-loader']
  19. },
  20. {//处理图片资源
  21. test:/\.(jpg|png|gif)$/,
  22. loader:'url-loader',
  23. options:{
  24. limit:8*1024,
  25. name:'[hash:10].[ext]',
  26. //关闭es6模块
  27. esModule:false,
  28. outputPath:'imgs'
  29. }
  30. },
  31. {//处理html中的img资源
  32. test:/\.html$/,
  33. loader:'html-loader'
  34. },
  35. {//处理其他资源
  36. exclude:/\.(html|js|css|less|jpg|png|gif)$/,
  37. loader:'file-loader',
  38. options:{
  39. name:'[hash:10].[ext]',
  40. outputPath:'media'
  41. }
  42. }
  43. ]
  44. },
  45. plugins:[
  46. new HtmlWebpackPlugin({
  47. template:'./src/index.html'
  48. })
  49. ],
  50. mode:'development',
  51. devServer:{
  52. contentBase:resolve(__dirname,'build'),//项目构建后路径
  53. compress:true, //启动gzip压缩
  54. port:3000,
  55. open:true,//自动打开浏览器
  56. hot:true//开启HMR功能,注意:当修改了webpack功能,新配置想要生效,必须重新启动webpack服务
  57. }
  58. }

js进行HMR优化,在入口文件写下监听代码

注意:你要监听进行热模块替换,前提是你这个js要在入口文件中导入,然后入口文件中才能监听得到变化

  1. import print from './print';
  2. import test from './test';
  3. if (module.hot) {
  4. // 一旦 module.hot 为true,说明开启了HMR功能。 --> 让HMR功能代码生效
  5. module.hot.accept(['./print.js','./test.js'], function() {//只有一个js文件需要监听打包就直接输入url字符串,不用数组
  6. // 方法会监听 print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建。
  7. // 会执行后面的回调函数
  8. console.log("前提是你要在入口文件上导入,才能监听得到变化")
  9. });
  10. }

Ⅱ- source-map 优化

1、source-map: 一种 提供源代码到构建后代码映射 技术 (如果构建后代码出错了,通过映射可以追踪源代码错误)分为内联外部:

2、内联和外部的区别:1. 外部生成了文件,内联没有 2. 内联构建速度更快

3、不同环境选择:

开发环境:考虑 速度快,调试更友好
① 速度快(eval>inline>cheap>…): eval-cheap-souce-map > eval-source-map

  1. 调试更友好: souce-map > cheap-module-souce-map > cheap-souce-map
  2. 最优选--> `eval-source-map` > eval-cheap-module-souce-map

生产环境:考虑 源代码要不要隐藏? 调试要不要更友好

  1. 内联会让代码体积变大,所以在生产环境不用内联
  2. 考虑隐藏:nosources-source-map 全部隐藏 >hidden-source-map 只隐藏源代码,会提示构建后代码错误信息
  3. 综合考虑:source-map `or` cheap-module-souce-map
  1. module.exports = {
  2. entry: [],
  3. output: { },
  4. module: { rules: [] },
  5. plugins: [],
  6. mode: 'development',
  7. devServer: {},
  8. //选定映射模式
  9. devtool: 'eval-source-map'
  10. };

不同映射模式的区别:感觉记住结论就好了,真的需要的时候再来翻阅

[inline-|hidden-|eval-] [nosources-] [cheap-[module-]]source-map

  1. 1source-map:外部<br />
  2. 错误代码准确信息 源代码的错误位置<br />
  3. 2inline-source-map:内联<br />
  4. 只生成一个内联source-map<br />
  5. 错误代码准确信息 源代码的错误位置<br />
  6. 3hidden-source-map:外部<br />
  7. 错误代码错误原因,但是没有错误位置<br />
  8. 不能追踪源代码错误,只能提示到构建后代码的错误位置<br />
  9. 4eval-source-map:内联<br />
  10. 每一个文件都生成对应的source-map,都在eval<br />
  11. 错误代码准确信息 源代码的错误位置<br />
  12. 5nosources-source-map:外部<br />
  13. 错误代码准确信息, 但是没有任何源代码信息<br />
  14. 6cheap-source-map:外部<br />
  15. 错误代码准确信息 源代码的错误位置<br />
  16. 只能精确的行<br />
  17. 7cheap-module-source-map:外部<br />
  18. 错误代码准确信息 源代码的错误位置<br />
  19. module会将loadersource map加入

Ⅲ- oneOf

正常来说,一个文件会被所有的loader过滤处理一遍,如果我有100个loader配置,那么我一个文件就要被100个loader匹配,而使用oneOf后,而如果放在oneOf中的loader规则有一个匹配到了,oneOf中的其他规则就不会再对这文件进行匹配

注意:oneOf中不能有两个loader规则配置处理同一种文件,否则只能生效一个 例如:对于js进行eslint检测后再进行babel转换

  1. 解决:将eslint抽出到外部,然后优先执行,这样在外部检测完后`oneOf`内部配置就会再进行检测匹配
  1. module.exports = {
  2. entry: './src/js/index.js',
  3. output: {},
  4. module: {
  5. rules: [
  6. {
  7. test: /\.js$/,
  8. exclude: /node_modules/, enforce: 'pre', loader: 'eslint-loader',
  9. },
  10. {
  11. // 以下loader只会匹配一个
  12. // 注意:不能有两个配置处理同一种类型文件
  13. oneOf: [
  14. {
  15. test: /\.css$/,
  16. use: [...commonCssLoader]
  17. },
  18. {
  19. test: /\.less$/,
  20. use: [...commonCssLoader, 'less-loader']
  21. },
  22. {
  23. test: /\.js$/,
  24. exclude: /node_modules/,
  25. loader: 'babel-loader',
  26. options: {
  27. presets: [
  28. [
  29. '@babel/preset-env',
  30. {
  31. useBuiltIns: 'usage',
  32. corejs: {version: 3},
  33. targets: {
  34. chrome: '60',
  35. firefox: '50'
  36. }
  37. }
  38. ]
  39. ]
  40. }
  41. }
  42. ]
  43. }
  44. ]
  45. },
  46. plugins: [],
  47. mode: 'production'
  48. };

Ⅳ-缓存

缓存需要在server环境中才有效果

1、babel缓存:

babel的loader选项部分添加 cacheDirectory: true —>让第二次打包构建速度更快

2、文件资源缓存:

文件资源当你文件名不变时会默认读取本地缓存,所以当你修改某个文件内容后,并不能实时更新到线上项目中,所以解决方法是在每次webpack构建时生成一个唯一的hash值加在文件名中,每次修改便改动文件名,达到更新效果.而不同的hash也有不同效果,其中需要选用contenthash

① hash:每次webpack构建时会生成一个唯一的hash值

  1. 问题:因为jscss同时使用`同一个hash值`,如果重新打包,会导致所有缓存失效(即使你只改动了一个文件)

② chunkhash:根据chunk生成的hash值,如果打包来源于同一个chunk,那么hash值就一样

  1. 问题:jscsshash值还是一样, 因为css时在js中被引入的,所以属于同一个chunk

contenthash:根据文件的内容生成hash值,不同的文件hash一定不一样

  1. -->让代码上线运行缓存更好使用(当你线上项目出现紧急BUG时,可以更快的修改)
  1. const { resolve } = require('path');
  2. module.exports = {
  3. entry: './src/js/index.js',
  4. output: {
  5. //使用contenthash哈希值
  6. filename: 'js/built.[contenthash:10].js',
  7. path: resolve(__dirname, 'build')
  8. },
  9. module: {
  10. rules: [
  11. {
  12. oneOf: [
  13. {
  14. test: /\.js$/,
  15. exclude: /node_modules/,
  16. loader: 'babel-loader',
  17. options: {
  18. presets: [
  19. ['@babel/preset-env',
  20. { useBuiltIns: 'usage', corejs: { version: 3 }, targets: {chrome: '60',firefox: '50'}} ]
  21. ],
  22. // 开启babel缓存
  23. // 第二次构建时,会读取之前的缓存
  24. cacheDirectory: true
  25. }
  26. },
  27. }
  28. ]
  29. }
  30. ]
  31. },
  32. plugins: [ new MiniCssExtractPlugin({ filename: 'css/built.[contenthash:10].css' }), ],
  33. mode: 'production',
  34. devtool: 'source-map'
  35. };

server.js代码

  1. /*
  2. 服务器代码
  3. 启动服务器指令:npm i nodemon -g
  4. `` nodemon server.js` or `node server.js`
  5. 访问服务器地址:
  6. http://localhost:3000
  7. */
  8. const express = require('express');
  9. const app = express();
  10. // express.static向外暴露静态资源
  11. // maxAge 资源缓存的最大时间,单位ms
  12. app.use(express.static('build', { maxAge: 1000 * 3600 }));
  13. app.listen(3000);

Ⅴ-tree shaking 树摇

1、tree shaking:去除无用代码

前提:1. 必须使用ES6模块化 2. 开启production环境 3.webpack4中,对于嵌套的代码,无法去除

作用: 减少代码体积

2、在package.json中配置

“sideEffects”: false 所有代码都没有副作用(都可以进行tree shaking)

  1. 问题:可能会把css / @babel/polyfill (副作用)文件干掉

解决:”sideEffects”: [“.css”, “.less”]

  1. "sideEffects": [
  2. "*.css"
  3. ]

Ⅵ-code split 代码分割

1、多入口与单入口文件打包 (通常不使用这个方法,一般使用2、3的方法)

① 多入口:有一个入口,最终输出就有一个bundle

[name]:取文件名

  1. const { resolve } = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. module.exports = {
  4. // 单入口
  5. // entry: './src/js/index.js',
  6. entry: {
  7. // 多入口:有一个入口,最终输出就有一个bundle
  8. index: './src/js/index.js',
  9. test: './src/js/test.js'
  10. },
  11. output: {
  12. // [name]:取文件名
  13. filename: 'js/[name].[contenthash:10].js',
  14. path: resolve(__dirname, 'build')
  15. },
  16. plugins: [
  17. new HtmlWebpackPlugin({
  18. template: './src/index.html',
  19. minify: {
  20. collapseWhitespace: true,
  21. removeComments: true
  22. }
  23. })
  24. ],
  25. mode: 'production'
  26. };

2、optimization: {splitChunks: {chunks: 'all'}}配置

① 可以将node_modules中代码单独打包一个chunk最终输出
② 自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个chunk(多入口文件)

  1. const { resolve } = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. module.exports = {
  4. // 单入口
  5. // entry: './src/js/index.js',
  6. entry: {
  7. index: './src/js/index.js',
  8. test: './src/js/test.js'
  9. },
  10. output: {
  11. // [name]:取文件名
  12. filename: 'js/[name].[contenthash:10].js',
  13. path: resolve(__dirname, 'build')
  14. },
  15. plugins: [
  16. new HtmlWebpackPlugin({
  17. template: './src/index.html',
  18. minify: {
  19. collapseWhitespace: true,
  20. removeComments: true
  21. }
  22. })
  23. ],
  24. /*
  25. 1. 可以将node_modules中代码单独打包一个chunk最终输出
  26. 2. 自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个chunk
  27. */
  28. optimization: {
  29. splitChunks: {
  30. chunks: 'all'
  31. },
  32. // 将当前模块的记录其他模块的hash单独打包为一个文件 runtime
  33. // 解决:修改a文件导致b文件的contenthash变化
  34. runtimeChunk: {
  35. name: entrypoint => `runtime-${entrypoint.name}`
  36. },
  37. },
  38. mode: 'production'
  39. };

3、单入口文件,且想打包特定js文件为单独文件,在2的配置基础上再写js代码(入口改为单入口)

通过js代码,让某个文件被单独打包成一个chunk,该代码写在入口js文件中
import动态导入语法:能将某个文件单独打包
通过注释,可以让js生成的打包文件带上这个名字,

webpack5中的开发模式中可以不用注释加名字,内部有 chunk 命名规则,不再是以 id(0, 1, 2)命名了,当然生产模式还是有必要的

  1. //通过注释,可以让js生成的打包文件带上这个名字
  2. import(/* webpackChunkName: 'test' */'./test')
  3. .then(({ mul, count }) => {
  4. // 文件加载成功~
  5. // eslint-disable-next-line
  6. console.log(mul(2, 5));
  7. })
  8. .catch(() => {
  9. // eslint-disable-next-line
  10. console.log('文件加载失败~');
  11. });
  12. // eslint-disable-next-line
  13. console.log(sum(1, 2, 3, 4));

Ⅶ- 懒加载 (lazy loading) 和预加载

应用场景:当我们模块很多时,导入的js太多,或者说有的js只有使用的时候才有用,而我一开始便加载,就可能造成一些不必要的性能浪费

1、懒加载:当文件需要使用时才加载

  1. `可能的问题`:当用户第一次使用时,如果js文件过大,可能造成加载时间过长(有延迟),但是第二次就不会了,因为懒加载第二次是从缓存中读取文件

2、预加载 prefetch:等其他资源加载完毕,浏览器空闲了,再偷偷加载

  1. 正常加载可以认为时并行加载(同一时间加载多个文件,但是同一时间有上限)
  2. 就例如下面例子,有预加载的代码运行效果,是页面刷新后,但是还未进行使用时,该文件其实已经加载好了

注意:预加载虽然性能很不错,但是需要浏览器版本较高,兼容性较差,慎用预加载

  1. console.log('index.js文件被加载了~');
  2. // import { mul } from './test';
  3. //懒加载
  4. document.getElementById('btn').onclick = function() {
  5. //懒加载其实也是需要前面Ⅵ代码分割功能,将我的需要加载的文件打包成单独文件
  6. import(/* webpackChunkName: 'test'*/'./test').then(({ mul }) => {
  7. console.log(mul(4, 5));
  8. });
  9. };
  10. //预加载
  11. //在注释参数上添加 webpackPrefetch: true
  12. import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
  13. console.log(mul(4, 5));
  14. });

Ⅷ- PWA (离线访问)

PWA: 渐进式网络开发应用程序(离线可访问) workbox —>下载依赖: workbox-webpack-plugin

1、在配置中使用该插件 :① 帮助serviceworker快速启动 ② 删除旧的 serviceworker

2、在入口文件js中添加代码

3、eslint不认识 window、navigator全局变量

解决:需要修改package.json中eslintConfig配置

4、代码必须运行在服务器上才有效果

① node.js

npm i serve -g —>serve -s build 启动服务器,将build目录下所有资源作为静态资源暴露出去

webpack.config.js新增配置

  1. const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
  2. plugins: [
  3. new WorkboxWebpackPlugin.GenerateSW({
  4. /*生成一个 serviceworker 配置文件~*/
  5. //1. 帮助serviceworker快速启动
  6. clientsClaim: true,
  7. //2. 删除旧的 serviceworker
  8. skipWaiting: true
  9. })
  10. ],

入口文件js —>index.js

  1. // 注册serviceWorker
  2. // 处理兼容性问题
  3. if ('serviceWorker' in navigator) {
  4. window.addEventListener('load', () => {
  5. navigator.serviceWorker
  6. .register('/service-worker.js')
  7. .then(() => {
  8. console.log('sw注册成功了~');
  9. })
  10. .catch(() => {
  11. console.log('sw注册失败了~');
  12. });
  13. });
  14. }

package.json新增配置

  1. "eslintConfig": {
  2. "extends": "airbnb-base",
  3. "env": {
  4. "browser": true //开启为eslint支持浏览器端的bian'l,比如 window
  5. }
  6. },

Ⅸ-多线程打包

1、下载thread-loader依赖

2、使用loader: 'thread-loader'开启多线程打包

注意点:进程启动大约为600ms,进程通信也有开销,只有工作消耗时间较长,才需要多进程打包 比如:babel转换可以使用多线程

  1. const { resolve } = require('path');
  2. module.exports = {
  3. module: {
  4. rules: [
  5. oneOf: [
  6. {
  7. test: /\.js$/,
  8. exclude: /node_modules/,
  9. use: [
  10. /*
  11. 开启多进程打包。
  12. 进程启动大概为600ms,进程通信也有开销。
  13. 只有工作消耗时间比较长,才需要多进程打包
  14. */
  15. {
  16. loader: 'thread-loader',
  17. options: {
  18. workers: 2 //设置 进程2个
  19. }
  20. },
  21. {
  22. loader: 'babel-loader',
  23. options: {
  24. presets: [
  25. [
  26. '@babel/preset-env',
  27. {
  28. useBuiltIns: 'usage',
  29. corejs: { version: 3 },
  30. targets: {
  31. chrome: '60',
  32. firefox: '50'
  33. }
  34. }
  35. ]
  36. ],
  37. // 开启babel缓存
  38. // 第二次构建时,会读取之前的缓存
  39. cacheDirectory: true
  40. }
  41. }
  42. ]
  43. }
  44. ]
  45. }
  46. ]
  47. }
  48. };

Ⅹ-externals

当你使用外部引入代码时:如CDN引入,不想他将我引入的模块也打包,就需要添加这个配置

即:声明哪些库是不进行打包的

—>externals: {}

  1. const { resolve } = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. module.exports = {
  4. entry: './src/js/index.js',
  5. output: {
  6. filename: 'js/built.js',
  7. path: resolve(__dirname, 'build')
  8. },
  9. plugins: [
  10. new HtmlWebpackPlugin({
  11. template: './src/index.html'
  12. })
  13. ],
  14. mode: 'production',
  15. externals: {
  16. // 拒绝jQuery被打包进来
  17. jquery: 'jQuery'
  18. }
  19. };

Ⅺ-dll

使用dll技术,对某些库(第三方库:jquery、react、vue…)进行单独打包

作用:如果不是cdn引入,而是使用第三方库,想要打包后暴露出去,使用该方法

1、首先你需要写一个新的配置文件,因为使用dll技术,所以命名为webpack.dll.js

当你运行 webpack 时,默认查找 webpack.config.js 配置文件 需求:需要先运行 webpack.dll.js 文件

—>webpack --config webpack.dll.js 在这个文件中进行对某些库的单独打包

2、在webpack.config.js中,需要告诉webpack哪些库不需要再次打包(即在dll.js中打包后生成的文件)

3、这里需要使用到add-asset-html-webpack-pluginwebpack插件

4、运行webpack.dll.js对第三方库进行单独打包后,除非你要加新的库,不然不用再重新打包这个,直接webpack打包其他的即可

webpack.dll.js配置文件

  1. const { resolve } = require('path');
  2. const webpack = require('webpack');
  3. module.exports = {
  4. entry: {
  5. // 最终打包生成的[name] --> jquery
  6. // ['jquery'] --> 要打包的库是jquery
  7. jquery: ['jquery'],
  8. // react:['react','react-dom' ]
  9. },
  10. output: {
  11. filename: '[name].js',
  12. path: resolve(__dirname, 'dll'),
  13. library: '[name]_[hash]' // 打包的库里面向外暴露出去的内容叫什么名字
  14. },
  15. plugins: [
  16. // 打包生成一个 manifest.json --> 提供和jquery映射
  17. new webpack.DllPlugin({
  18. name: '[name]_[hash]', // 映射库的暴露的内容名称
  19. path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径
  20. })
  21. ],
  22. mode: 'production'
  23. };

webpack.config.js配置文件

  1. const { resolve } = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. const webpack = require('webpack');
  4. const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
  5. module.exports = {
  6. entry: './src/index.js',
  7. output: {
  8. filename: 'built.js',
  9. path: resolve(__dirname, 'build')
  10. },
  11. plugins: [
  12. new HtmlWebpackPlugin({
  13. template: './src/index.html'
  14. }),
  15. // 告诉webpack哪些库不参与打包,同时使用时的名称也得变~
  16. new webpack.DllReferencePlugin({
  17. manifest: resolve(__dirname, 'dll/manifest.json')
  18. }),
  19. // 将某个文件打包输出去,并在html中自动引入该资源
  20. new AddAssetHtmlWebpackPlugin({
  21. filepath: resolve(__dirname, 'dll/jquery.js')
  22. })
  23. ],
  24. mode: 'production'
  25. };

XII-性能优化总结

1、# webpack性能优化

  1. 开发环境性能优化
  2. 生产环境性能优化

2、# 开发环境性能优化

  1. 优化打包构建速度:HMR
  2. 优化代码调试:source-map

3、# 生产环境性能优化

  1. 优化打包构建速度
  2. Ⅰ- oneOf
  1. Ⅱ- babel缓存
  1. Ⅲ- 多进程打包

``` Ⅳ- externals

Ⅴ- dll 这个技术加上代码拆分code split可以做出更加细度化拆分

  1. > 优化代码运行的性能
  2. > Ⅰ- 缓存(hash-chunkhash-contenthash)
  3. > Ⅱ- tree shaking
  4. > Ⅲ-code split
  5. > Ⅳ- 懒加载/预加载
  6. > Ⅴ- pwa
  7. ---
  8. ---
  9. <a name="1df913c2"></a>
  10. # 五、Webpack5的介绍与预学习
  11. > 此版本重点关注以下内容:
  12. > - 通过持久缓存提高构建性能.
  13. > - 使用更好的算法和默认值来改善长期缓存.
  14. > - 通过更好的树摇和代码生成来改善捆绑包大小.
  15. > - 清除处于怪异状态的内部结构,同时在 v4 中实现功能而不引入任何重大更改.
  16. > - 通过引入重大更改来为将来的功能做准备,以使我们能够尽可能长时间地使用 v5.
  17. <a name="19ef4b7e"></a>
  18. ### Ⅰ-下载
  19. > `npm i webpack@next webpack-cli -D`
  20. <a name="a866987b"></a>
  21. ### Ⅱ-自动删除 Node.js Polyfills
  22. > 早期,webpack 的目标是允许在浏览器中运行大多数 node.js 模块,但是模块格局发生了变化,许多模块用途现在主要是为前端目的而编写的。webpack <= 4 附带了许多 node.js 核心模块的 polyfill,一旦模块使用任何核心模块(即 crypto 模块),这些模块就会自动应用。
  23. > 尽管这使使用为 node.js 编写的模块变得容易,但它会将这些巨大的 polyfill 添加到包中。在许多情况下,这些 polyfill 是不必要的。
  24. > webpack 5 会自动停止填充这些核心模块,并专注于与前端兼容的模块。
  25. > 迁移:
  26. > - 尽可能尝试使用与前端兼容的模块。
  27. > - 可以为 node.js 核心模块手动添加一个 polyfill。错误消息将提示如何实现该目标。
  28. <a name="cfe80d4a"></a>
  29. ### Ⅲ-Chunk 和模块 ID
  30. > 添加了用于长期缓存的新算法。在生产模式下默认情况下启用这些功能。

chunkIds: “deterministic”, moduleIds: “deterministic”

  1. <a name="3fd089dd"></a>
  2. ### Ⅳ-Chunk ID
  3. > 你可以不用使用 `import(/* webpackChunkName: "name" */ "module")` 在开发环境来为 chunk 命名,生产环境还是有必要的
  4. > webpack 内部有 chunk 命名规则,不再是以 id(0, 1, 2)命名了
  5. > 这部分在`import动态导入语法`中会用到,如代码分割与懒加载
  6. <a name="ffdab8d2"></a>
  7. ### Ⅴ-Tree Shaking 树摇优化
  8. > 1、webpack 现在`能够处理对嵌套模块`的 tree shaking
  9. > 在生产环境中, inner 模块暴露的 `b` 会被删除,原本的webpack4无法判断中间这个被引用的内容是否有所被引用,无法删除
  10. ```javascript
  11. // inner.js
  12. export const a = 1;
  13. export const b = 2;
  14. // module.js
  15. import * as inner from './inner';
  16. export { inner };
  17. // user.js
  18. import * as module from './module';
  19. console.log(module.inner.a);

2、webpack 现在能够判断多个模块之前的关系

  1. import { something } from './something';
  2. function usingSomething() {
  3. return something;
  4. }
  5. export function test() {
  6. return usingSomething();
  7. }

当设置了"sideEffects": false时,一旦发现test方法没有使用,不但删除test,还会删除"./something"

3、webpack 现在能处理对 Commonjs 的 tree shaking

Ⅵ-Output

webpack 4 默认只能输出 ES5 代码

webpack 5 开始新增一个属性 output.ecmaVersion, 可以生成 ES5 和 ES6 / ES2015 代码.

如:output.ecmaVersion: 2015

Ⅶ-SplitChunk

之前webpack4只能指定最小文件大小,现在能对不同文件进行最小大小判断

此属性在代码分割的optimization配置中

  1. // webpack4
  2. minSize: 30000;
  3. // webpack5
  4. minSize: {
  5. javascript: 30000,
  6. style: 50000,
  7. }

Ⅷ-Caching

  1. // 配置缓存
  2. cache: {
  3. // 磁盘存储
  4. type: "filesystem",
  5. buildDependencies: {
  6. // 当配置修改时,缓存失效
  7. config: [__filename]
  8. }
  9. }

缓存将存储到 node_modules/.cache/webpack

Ⅸ-监视输出文件

之前 webpack 总是在第一次构建时输出全部文件,但是监视重新构建时会只更新修改的文件。

此次更新在第一次构建时会找到输出文件看是否有变化,从而决定要不要输出全部文件。

Ⅹ-默认值

webpack5将部分配置赋予默认值,当你使用如下配置,在webpack5中可以不进行代码编写

  • entry: "./src/index.js
  • output.path: path.resolve(__dirname, "dist")
  • output.filename: "[name].js"

更多内容



###、出现的问题与解决

Ⅰ-如果开启了 eslint 再进行懒加载会报错 无法再非顶层使用import

解决:
1、新建 .eslintrc 文件

2、配置

  1. {
  2. "parser": "babel-eslint",
  3. "parserOptions": {
  4. "sourceType": "module",
  5. "allowImportExportEverywhere": true
  6. }
  7. }

Ⅱ-css兼容性配置没效果

可能是版本问题,换个写法试试

问题代码

  1. use: [MiniCssExtractPlugin.loader, 'css-loader',
  2. {
  3. loader: 'postcss-loader',
  4. options: {
  5. ident: 'postcss',
  6. plugins: () => [
  7. require('postcss-preset-env')()
  8. ]
  9. }
  10. }
  11. ]

解决代码

如果{}不需要继续传入options,可以直接这样plugins: ['postcss-preset-env'] 效果同上

  1. {
  2. loader: 'postcss-loader',
  3. options: {
  4. postcssOptions: {
  5. plugins: [['postcss-preset-env', {}]]
  6. }
  7. }
  8. }