Webpack

目录

  • [1]为什么需要打包工具
  • [2]webpack
    • webpack五大核心概念
      • [1]处理js模块化语法
      • [2]处理样式资源
        • (1)处理css资源
          • css-loader+style-loader
          • less-loader
          • sass\scss
        • (2)打包时样式资源的更多操作
          • [1]将css单独处理成一个文件-MiniCssExtractPlugin
          • [2]处理css的兼容性问题
          • [3]css打包压缩
      • [3]处理图片资源
        • 配置
      • 修改文件的目录
        • 修改图片文件的目录
        • 修改字体图标文件打包后的位置
        • 每次打包前先清空path目录
      • [4]处理js资源
        • (1)eslint
          • 在webpack中使用eslint
          • 编写配置文件
            • 打包构建优化-eslint缓存(*)
            • 多进程打包
        • (2)babel
          • 在webpack中使用
            • 打包构建优化-babel缓存(*)
            • 打包体积优化-(*)
        • (3)js多进行打包-thread-loader(*)
        • (4)js多进程压缩(*)
      • [5]处理html资源
      • 渐进式网络应用程序PWA(*)
        • 使用
      • 总结
        • (1)entry
          • 作用
          • 语法-单入口
          • 语法-多入口
        • (2)output
          • 作用
          • 语法
            • filename属性
        • (3)module
          • 作用
          • 语法
            • 语法-loader配置
            • 语法-其他资源进行配置
            • 打包构建速度优化-oneOf(*)
            • 打包构建速度优化-include/exclude(*)
          • 常用的loader
        • (4)plugins
          • 作用
          • 语法
            • 打包构建速度优化-include/exclude(*)
          • 常用的插件
        • (5)mode
          • 作用
          • 语法
        • 举例-使用
          • 下载
          • 配置
            • 创建多个webpack配置文件
          • 项目的目录结构
    • webpack的其他概念
      • [1]搭建开发服务器
        • 配置
        • 启动
        • 开发体验优化-模块热替换(*)
          • 前提
          • 作用
          • 配置
      • [2]开发体验优化-调试工具(*)
        • 本质
        • 作用
        • 配置
        • 运行
      • [3]代码分割(*)
        • 举例说明
        • 文件按需导入
        • 预加载
    • 总结-优化
      • [1]提升开发体验
      • [2]打包构建速度优化
      • [3]减少打包后的体积
        • (1)Tree Shaking
      • [4]代码拆分

[1]为什么需要打包工具

在项目开发时可能会使用到 Less/Sass等css 预处理器、ES6 模块化语法, React/Vue等框架,这些语言浏览器是不识别的。使用以上技术的项目想要在浏览器运行必须经过编译,编译成浏览器能够识别的Html、Css 、JS等语法才能运行, 打包工具可以帮我们做完这些事。

除此之外,打包工具还能压缩代码、做兼容性处理、提升代码性能等。

[2]webpack

webpack官网
Webpack 是一个静态资源打包工具。它会以一个或多个文件作为打包的入口,将整个项目所有文件编译组合成一个或多个文件输出出去。输出的文件就是编译好的文件( 将浏览器不能识别的代码编译成浏览器能识别的 Css 、JS等语法),就可以在浏览器端运行了,另外webpack还能压缩代码、做兼容性处理、提升代码性能等。

Webpack 本身功能是有限的,没有进行任何配置的webpack仅能做以下处理
----开发模式:仅能编译 JS 模块化语法;
----生产模式:不仅能编译 JS 模块化语法,还能压缩 JS 代码

但是像样式资源、字体图标、图片资源、html 资源等,webpack 默认都不能处理,若是需要处理上述资源需要对webpack进行一定的配置。

webpack五大核心概念

1.entry(入口):指示 Webpack 从哪个文件开始打包
2.output(输出):指示 Webpack 打包完的文件输出到哪里去,如何命名等
3.module(加载器):webpack 本身只能处理 js、json 等资源,其他资源需要借助 loader,Webpack 才能解析
4.plugins(插件):扩展 Webpack 的功能
5.mode(模式):开发模式:development;生产模式:production;

由于webpack是基于nodejs的,因此导入导出与nodejs相同

// 导入
const xxx = require("xxx")

// 导出
module.exports = {

}

[1]处理js模块化语法

在这里插入图片描述

  • b.js

    const b = 100
    export default b
    
  • index.js

    import b from './b'
    console.log('b', b)
    
  • 在index.html引入

    <script src="./index.js"></script>
    
  • error:运行发现报错
    在这里插入图片描述
    不允许在模块以外使用import

  • 原因:其实原因很简单:浏览器不能识别模块化语法

  • 解决:可以通过webpack将代码进行编译,编译之后再在浏览器运行。

    • [1] 对项目进行初始化

      npm init -y
      

      会在当前目录下生成一个package.json文件,该文件用于管理项目、配置项目

    • [2] 下载webpack包

      npm i -D webpack webpack-cli
      

      webpack: webpack核心包
      webpack-cli:webpack的命令行工具

    • [3] 使用webpack进行打包

      tips: 因为此处只是为了打包模块化语法,因此webpack不需要做其他配置,所以此处没有创建webpack配置文件。

      // 若是没有配置文件,在编译时需要声明编译文件以及开发模式
       npx webpack ./index.js --mode=development
      
    • [4] 编译成功

      在编译成功之后会在当前目录下生成一个dist文件夹(webpack默认打包出口-可通过配置文件修改)。

      将index.html里引入dist文件夹下的main.js,发现可以正常运行并在控制台输出了 b 100

[2]处理样式资源

(1)处理css资源

Webpack_第1张图片
在[1]的基础上在当前项目添加了一个css文件引入到index.js之后再打包,却报了如下错误:
在这里插入图片描述
原因是浏览器虽然能够识别css,但是webpack是不能够识别样式资源的,所以需要借助 Loader 来帮助 Webpack 解析样式资源。
loader为webpack的加载器

css-loader+style-loader

官网->loader->css-loader
Webpack_第2张图片
步骤

  • [1]下载 css-loaderstyle-loader

      npm i  css-loader style-loader
    
  • [2]在项目根目录下创建webpack.config.js配置文件

    module.exports = {
      entry: './index.js',
      module:{
        rules: [
          {
            test: /\.css$/, // 检查所有以css结尾的文件
            use: [
              'style-loader', // 动态创建style标签,将js中的css通过style标签引入到html文件中
              'css-loader' // 将css资源编译成commonjs模块到js中
            ] // use的执行顺序是从右到左
          }
        ]
      },
      mode: 'development'
    }
    
  • [3]执行npx webpack 命令将代码通过webpack进行编译,发现成功了并且样式成功显示

less-loader

官网->loader->less-loader
Webpack_第3张图片

  • [1]下载lessless-loader
      npm i less less-loader
    
  • [2]配置
    module.exports = {
      entry: './index.js', 
      module:{
        rules: [
          {
            test: /\.less$/, // 检查所有以less结尾的文件
            use: [
              'style-loader',
              'css-loader',
              'less-loader' // 将less文件编译为css文件
            ]
          }
        ]
      },
      mode: 'development'
    }
    
  • [3] 编译
sass\scss

官网->loader->sass-loader

  • [1]下载sasssass-loader
     npm i sass sass-loader
    
  • [2]配置
    module.exports = {
      entry: './index.js', 
      module:{
        rules: [
          {
            test: /\.sass$/, // 检查所有以sass结尾的文件
            use: [
              'style-loader',
              'css-loader',
              'sass-loader' // 将sass文件编译为css文件
            ]
          }
        ]
      },
      mode: 'development'
    }
    
  • [3]编译
(2)打包时样式资源的更多操作
[1]将css单独处理成一个文件-MiniCssExtractPlugin

Css 文件目前被打包到 js 文件中,当 js 文件加载时,会创建一个 style 标签来生成样式。

也就是说在项目加载过程中会先加载js,js加载完毕之后处理css在style标签中进行显示。

这样对于网站来说,会出现闪屏现象,用户体验不好(刚开始没有任何样式,图片样式全部出现-体验不好)。

因此我们应该将css提取为一个单独的文件,然后通过link引入。

官网->plugin->MiniCssExtractPlugin插件
MiniCssExtractPlugin插件的作用会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持异步加载

配置步骤

  • [1]下载插件

    npm install --save-dev mini-css-extract-plugin
    
  • [2]在配置文件中引入插件

    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    
  • [3]调用方法

    plugins:[
      new MiniCssExtractPlugin({
        filename: "static/css/main.css",// 定义输出文件名和目录
        chunkFilename:'static/css/[name].chunk.css', //若是需要按需引入的文件名
      }),
    ]
    
  • [4]结合css-loader一起使用
    style-loader是动态创建style标签,然后将css通过style标签引入。
    现在要通过MiniCssExtractPlugin插件将css单独放在一个文件中,所以要将所有使用到style-loader的地方替换为MiniCssExtractPlugin.loader

    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    module.exports = {
      entry: './index.js', 
      module:{
        rules: [
          {
            test: /\.less$/,
            use: [
              MiniCssExtractPlugin.loader,
              'css-loader',
              'less-loader'
            ]
          }
        ]
      },
      plugins:[
        new MiniCssExtractPlugin({
          filename: "static/css/main.css",// 定义输出文件名和目录
          chunkFilename:'static/css/[name].chunk.css', //若是需要按需引入的文件名
        }),
      ],
      mode: 'development'
    }
    

    tips: MiniCssExtractPlugin.loader会将css提取到一个单独的文件中,此时这个文件需要单独引入到index.html中

[2]处理css的兼容性问题

某些语法,在很多浏览器会有细微的差异或是有些语法不能被某些低版本浏览器识别(eg:display:flex;),因此需要进行一定的编译处理。
官网-loader-postcss-loader

{
  loader: "postcss-loader",
  options: {
    postcssOptions: {
      plugins: [
        "postcss-preset-env", // 能解决大多数样式兼容性问题
      ],
    },
},

在module中对css后缀的文件添加了此loader后,发现没有作用因为我们需要进行一个配置告诉浏览器我们需要做一个怎样的兼容
在package.json中,存在一个browserslist属性

{
  // 其他省略
  "browserslist": ["ie >= 8"]
}

如上述代码,告诉我们需要兼容到ie8版本

{
  // 其他省略
  "browserslist": [
      "last 2 version", // 除每个浏览器的后两个版本其余兼容
       "> 1%", // 兼容99%的浏览器
       "not dead" // 不兼容下架的浏览器
   ]
}

我们常用的是上述的兼容版本

[3]css打包压缩

在打包时将css进行压缩使得其体积变小;
注:在进行打包时,webpack默认会将html+js进行压缩,不需要额外配置。
官网->plugin->CssMinimizerWebpackPlugin
[1]下载

npm i css-minimizer-webpack-plugin -D

[2]在webpack配置文件中引入

const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

[3]使用

plugins:[new CssMinimizerPlugin()]

[3]处理图片资源

过去在 Webpack4 时,我们处理图片资源通过 file-loader 和 url-loader 进行处理,现在 Webpack5 已经将两个 Loader 功能内置到 Webpack 里了,因此当在webpack5里面使用图片不需要经过任何处理就可以将图片直接显示在页面上了。
但是我们可以针对图片做一定的优化,比如如果是小图片转化为base64(data URL)格式.

  • 将小图片转化为 base64 格式 这样图片就是一个字符串格式了,不需要发送请求,可以减小服务器压力,但是图片体积会变得大一点(不会大很多)
配置

官网->asset->inline 资源(inlining asset)
Webpack_第4张图片
若是我们想要将小于10KB的图片转化为base64的文件时,可以这样进行配置

 module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif|webp|svg)$/, //将检测所有以png,jpg,gpeg,webp,svg结尾的文件,
        type: 'asset', // 通用资源(asset表示要转换为base64格式)
        parser: {
          // 改变成base64的条件
          dataUrlCondition: {
            maxSize: 10 * 1024 // 10kb
          }
        }
      }
    ]

配置完成通过 npx webpack去运行发现我的图片(大小为2kb)的没有出现在打包的dist文件夹中,但是在html中的应用还是可以正常显示,原因是因为它被变为base URL打包进js文件中了;而另外的图片(大小大于10kb)的被打包出现在dist文件夹中了,只是他的名字有所改变,如下图所示在这里插入图片描述
原因是:官方->asset->资源模块->自定义输出文件名
在这里插入图片描述
图片在进行打包时,会按照上述形式修改文件名

  • hash为原图片文件的hash值(因为hash值是唯一的,这样可以保证图片名不产生冲突);
    • 但是目前看hash值会比较长,这样会导致我们在打包后的css体积比较大,可以使用[hash:num]标识hash值只取num位;
  • ext为原图片文件的后缀名;
  • query为原图片使用url时后面携带的参数

修改文件的目录

通过上述操作我们会发现一个问题,目前的js、css、图片等都会打包到dist文件夹下,但是会处于同一层级,若是我们想要将文件按照我们书写时的方式进行打包输出,就需要对webpack进行配置
官网->output->对应属性

output: {
    path: path.resolve(__dirname, './dist'), // path属性表示整个项目打包后输出的文件夹的名字与位置-使用绝对路径
    filename: 'js/main.js' // filename属性表示入口文件打包后在文件夹中的位置-这表示在当前文件夹(dist)文件夹下的js文件下的main.js文件中
  },
  // assetModuleFilename属性表示静态资源打包后在文件夹中的位置与filename使用相同
  //->这表示在当前文件夹(dist)文件夹下的images文件夹下,文件名中hash值取8位
  // 注意:这里是所有静态资源哦~ 不仅是图片 ,若是还有其他静态资源(字体图标、视频、音频等)还是到module里面单独配置比较好
  assetModuleFilename: 'images/[hash:8][ext][query]' 
 

注:所有的filename属性都是以打包后生成的文件夹的根目录为启始路经,属性值不加./,否则就是以根目录为启始路径了!
打包后的文件格式如下
Webpack_第5张图片

修改图片文件的目录

若是仅仅是想将图片单独放置在某个文件夹中,可在module配置中直接添加

module: {
  rules: [
    {
      test: /\.(png|jpe?g|gif|webp|svg)$/, //将检测所有以png,jpg,gpeg,webp,svg结尾的文件,
      type: 'asset', // 通用资源(asset表示要转换为base64格式)
      generator: {
          filename:'images/[hash:8][ext][query]' // 这表示在当前文件夹(dist)文件夹下的images文件夹下,文件名中hash值取8位
      }
    }
  ]
修改字体图标文件打包后的位置

若是想修改字体图标文件打包后的位置,与图片资源处理方式相同

{
        test: /\.(woff2?||ttf)$/,
        type: 'asset/resource', // 这表示维持静态资源原本的样式(asset表示要将静态资源编译为base64文件),
        generator: {
          filename: 'media/[hash:8][ext]'
        }
      }
每次打包前先清空path目录

我们每次打包时如果上次内容没有清空会造成内容比较混乱,若是想要清空,可以配置clean为true; 这样每次打包前,webpack都会先将整个path目录进行清空,再进行打包。

  output: {
    clean: true 
  },

[4]处理js资源

(1)eslint

为了更好的进行团队开发,我们需要形成良好的代码规范,eslint是代码检查工具,用来检查你的代码是否符合指定的规范,方便修改。让我们形成良好的代码规范。

在webpack中使用eslint

官网->plugin->EslintWebpackPlugin
Webpack_第6张图片
optons配置项是一个对象,里面存储某些配置规则,如需要检查哪些文件,排除哪些文件不检查等,具体查看官方属性配置

const path = require('path')
const ESLintPlugin = require('eslint-webpack-plugin')
module。exports = {
  // 因为我们的代码写在src文件夹下,所以只检查src文件夹即可
  plugins: [new ESLintPlugin({ context: path.resolve(__dirname, './src') })], })],
}

编写配置文件

通过上述配置后,执行会报错,原因是我们并没有配置自己的eslint规则(eslint只是检查工具,不存在代码规范规则),所以我们需要自己配置
[1]在项目根目录创建.eslintrc.js配置文件
[2]通过eslint官方匹配规则在rules添加自己的规则

module.exports = {
  env: {
    node: true, // 启用node中全局变量
    browser: true, // 启用浏览器中全局变量
  },
  parserOptions: {
    ecmaVersion: 6,
    sourceType: "module",
  },
  extends: ["eslint:recommended"], // 继承 Eslint 规则
  // 若是有和继承的不一致的或另外想添加的可以在rules中添加
  rules: {
    "no-var": 2, // 不能使用 var 定义变量
  },
};
打包构建优化-eslint缓存(*)

eslint是对语法进行检查。假设js有很多个模块,若开发过程中只改变其中一个模块,编译时会重新打包所有模块,降低webpack的打包速度,与模块热替换类型(HMR).
若是我们对eslint检查的内容添加缓存,这样除了首次打包速度慢外,再次打包时速度将会加快。
语法

plugins: [new ESLintPlugin({ 
context: path.resolve(__dirname, './src') })],
exclude:/node_modules/,
cache:true, // 开启缓存
cacheLocation:path.resolve(__dirname,'../node_modules/.cache/eslinecache) // 缓存位置(因为不上传所以放在node_modules中)
 })],
多进程打包

配置threads属性

const workers = require('os').cpus().length // nodejs的os插件可以获取电脑cpu的数量
plugins: [new ESLintPlugin({ 
context: path.resolve(__dirname, './src') })],
exclude:/node_modules/,
cache:true, // 开启缓存
cacheLocation:path.resolve(__dirname,'../node_modules/.cache/eslinecache) // 缓存位置(因为不上传所以放在node_modules中)
 })],
 threads:workers-1  // 开启多进程打包并设置进程数量

总结:当项目较小时,使用多进程打包反而造成打包时间延长,因为进程之间通信产生的开销比多进程能够节约的时间更长。开发者应该视项目情况决定是否对某项打包任务采用多进程打包的方式,如果用得好,就能节约打包时间,用得不好,不如不用。

(2)babel

没有经过任何配置的wenpack只能对js的模块化语法进行编译,但是很多js高级(如es6)很多低版本是没有办法进行处理的。
babel主要用于将 ES6 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中.

在webpack中使用

webpack官网->loader-babel-loader
Webpack_第7张图片
presets 预设,简单理解:就是一组 Babel 插件, 扩展 Babel 功能

  • @babel/preset-env: 一个智能预设,允许您使用最新的 JavaScript。
  • @babel/preset-react:一个用来编译 React jsx 语法的预设
  • @babel/preset-typescript:一个用来编译 TypeScript 语法的预
    我们可以将persets配置放在module中,也可以单独放在babel配置文件babel.config.js中。
打包构建优化-babel缓存(*)

我们知道 项目中大部分的代码为js文件,babel是对js文件进行处理编译处理、兼容性处理,编译成浏览器能够识别的语法。假设js有很多个模块,若开发过程中只改变其中一个模块,编译时会重新打包所有模块,降低webpack的打包速度,与模块热替换类型(HMR).
若是我们对babel编译的内容添加缓存,这样除了首次打包速度慢外,再次打包时速度将会加快,另外我们的缓存文件不需要进行压缩(因为我们在上传时根本就不会将缓存文件上传)
Webpack_第8张图片
语法

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

构建之后可以看到在node_modules中多了一个cache文件
Webpack_第9张图片
这就是js编译后的缓存文件

打包体积优化-(*)

babel为编译的每个文件都插入了辅助代码,这导致打包后文件的体积变大。
我们可以将这些辅助代码作为一个独立模块,来避免代码重复引入。
@babel/plugin-transform-runtime插件可以做到以上需求
官网->babel-loader->疑难解答->Babel 在每个文件都插入了辅助代码,使代码体积过大
Webpack_第10张图片

 {
                loader: 'babel-loader',
                options: {
                  presets: ['@babel/preset-env'],
                  cacheDirectory: true,
                  cacheCompression: false,
                  plugins: ['@babel/plugin-transform-runtime'] // 引入插件减少体积
                }
              }
(3)js多进行打包-thread-loader(*)

webpack默认是单进程进行打包的,开启多个进程进行打包可以提升打包速度;但是每个进程的开启和交流都会有开销,其开销大约为 600ms 左右;因此请仅在耗时的操作中使用此 loader。
官网->loader->thread-loader
[1]下载

npm install --save-dev thread-loader

[2]babel配置
Webpack_第11张图片
由于babel-loader比较耗时,我们想要给babel-loader开启多进程打包,需要将其配置在babel-loader之前。

const workers = require('os').cpus().length // nodejs的os插件可以获取电脑cpu的数量
  {
            test: /\.m?js$/,
            exclude: /node_modules/,
            use: [
              {
                loader:"thread-loader",
                options: {
                  workers : workers - 1 // 最大数量为 cpu内核 - 1 (主核不能用)
                }
              },
              {
                loader: 'babel-loader',
                options: {
                  presets: ['@babel/preset-env'],
                  cacheDirectory: true,
                  cacheCompression: false
                }
              }
            ]
          },

总结:当项目较小时,使用多进程打包反而造成打包时间延长,因为进程之间通信产生的开销比多进程能够节约的时间更长。开发者应该视项目情况决定是否对某项打包任务采用多进程打包的方式,如果用得好,就能节约打包时间,用得不好,不如不用。

(4)js多进程压缩(*)

官网->plugin-> TerserWebpackPlugin插件
terser-webpack-plugin插件的作用是打包时对js进行压缩。
webpack v5 开箱即带有最新版本的 terser-webpack-plugin,如果你使用的是 webpack v5 或更高版本,同时希望自定义配置,仍需重新配置

[1]导入

const TerserPlugin = require(“terser-webpack-plugin”);

[2]配置

const workers = require('os').cpus().length // nodejs的os插件可以获取电脑cpu的数量
plugins:[
  new TerserPlugin({
    parallel: workers-1 // 开启多进程打包并设置进程数量
  })
]

总结:当项目较小时,使用多进程打包反而造成打包时间延长,因为进程之间通信产生的开销比多进程能够节约的时间更长。开发者应该视项目情况决定是否对某项打包任务采用多进程打包的方式,如果用得好,就能节约打包时间,用得不好,不如不用。

[5]处理html资源

我们目前是使用自己写的html文件然后手动引入webpack打包后的js文件,但是这样不是很方便,因为我们打包后不一定仅生成一个出口文件。
因此我们想打包后自动生成一个html文件并且将我们的打包后的js文件自动引入进去,可以使用webpac的关于html的插件HtmlWebpackPlugin.
官网->plugin->HtmlWebpackPlugin
[1]下载

npm install --save-dev html-webpack-plugin

[2]导入到webpack配置文件中并使用

const HtmlWebpackPlugin = require('html-webpack-plugin')
 plugins: [
    new ESLintPlugin({ context: path.resolve(__dirname, './src') }),
    new HtmlWebpackPlugin()
  ],

此时发现打包后确实生成了html文件并引入了打包后的出口文件,但是我们的dom结构没有了,因此需要添加一个配置项

new HtmlWebpackPlugin({
      template: path.resolve(__dirname, 'src/public/index.html') // 按照当前文件dom结构一致的情况下引入打包后的js文件
    })

渐进式网络应用程序PWA(*)

PWA 可以用来做很多事。其中最重要的是,在**离线(offline)**时应用程序能够继续运行功能。这是通过使用名为 Service Workers 的 web 技术来实现的。

使用

1.下载、引入、配置
Webpack_第12张图片
2、在入口文件中注册Service Workers

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

总结

(1)entry
作用

告诉webpack从哪个文件开始打包;

语法-单入口

若是仅仅是一个入口文件,则为字符串格式

entry:'入口文件的相对地址'
语法-多入口

若是有多个入口文件,则为对象格式

entry:{
 文件名:'入口文件的相对地址'文件名:'入口文件的相对地址'
}

注意:多入口对应多出口;

(2)output
作用

告诉 Webpack 将打包完的文件输出到哪里去,如何命名等

语法
output:{
  path:路经 // 绝对路径, 是文件打包后的位置及文件名,默认为'./dist'(当前文件夹下的dist文件夹)
  filename:'', // 相对于path,是js文件的打包后的位置及名称
  clean:bol, // 每次打包前是否要清空打包后的文件再进行打包
}
filename属性

(1)若是单入口 filename属性值对应入口文件打包后的位置与名称
(2)若是多入口应该如何使用呢?

filename:'[name].js' // name为文件在entry属性配置的属性名
  • 当然单入口也可以这样写
(3)module
作用

webpack本身功能比较有限,仅能解析js、json资源,若是想解析其他资源,需要借助loader或plugin;
module就是对loader、通用资源进行配置,拓宽webpack功能。

语法
语法-loader配置
module:{
  rules:[
    {
      test:/.xxx$/ // 匹配所有以.XXX结尾的文件
      loader:'', // 若是当前匹配规则仅配置一个loader可以使用loader属性
      // use的执行顺序是从右到左
      use:[], // 若是当前的匹配规则配置多个loader使用use属性
    }  // 每一条配置规则
  ]
}

每一个loader的配置规则如下

// 若是不需要任何其他配置,则为字符串,如下
use:['style-loader', 'css-loader']
// 若是需要设置其他配置项
use:[
'style-loader',
{
loader:'css-loader',
options:{
  // 配置项配置
}
}
]
语法-其他资源进行配置
module:{
  rules:[
    {
      test:/.xxx$/ // 匹配所有以.XXX结尾的文件
      // asset 表示要将资源转化为base URL; asset/resource 表示要将资源原格式打包输出
      type:'asset' 'asset/resource'generator: {
          filename:'文件输出位置'  // 在输出文件夹的什么位置
      }
    }  // 每一条配置规则
  ]
}
打包构建速度优化-oneOf(*)

loader的匹配是从上往下依次进行的。所有文件在执行的时候,都要将loader中的rules过一遍,如果符合,就被对应loader处理,不符合则直接过。
比如css文件,在rules中第一个就是处理css文件的loader,符合处理css;但是css文件还是会继续向下寻找至最后一个,这样就会比较浪费性能;因此为了避免这个问题,我们使用oneOf。

打包构建速度优化-include/exclude(*)

1、前提
当我们下载一些插件时,这些插件会放在node_modules文件中,这些文件是不需要处理的,因此我们在配置一些有关于js的loader时需要去除这些文件。
以babel-loader为例

{
            test: /\.m?js$/,
            exclude: /node_modules/,
            // include:path.resolve(__dirname, './src') includes与exclude写一个就好了
            use: {
              loader: 'babel-loader',
              options: {
                presets: ['@babel/preset-env']
              }
            }

2、作用
所有文件进行loader匹配时,只要遇到符合的loader就停止向后寻找。
3、语法

module:{
  rules:[
    {
      oneOf:[ // loader ]
    }
  ]
}
常用的loader
  • style-loader:动态创建style标签,将js中的css通过style标签引入到html文件中
  • css-loader:将css资源编译成commonjs的模块到js中
  • less-loader:将less文件编译为css文件
  • sass-loader:将sass或scss文件编译为css文件
  • postcss-loader:处理css兼容性问题
  • babel-loader:处理js兼容性问题
(4)plugins
作用

webpack本身功能比较有限,仅能解析js、json资源,若是想解析其他资源,需要借助loader或plugin;
plugins就是对plugin进行配置,拓宽webpack功能。

语法
// 插件需要先引入后使用,引入的是一个构造函数
plugins:[new 构造函数([options])]
打包构建速度优化-include/exclude(*)

当我们下载一些插件时,这些插件会放在node_modules文件中,这些文件是不需要处理的,因此我们在配置一些有关于js的plugins时需要去除这些文件。
以Eslint为例

plugins: [new ESLintPlugin({ 
context: path.resolve(__dirname, './src'),
exclude:/node_modules/ })], })],
常用的插件
  • HtmlWebpackPlugin:在打包时将html也自动打包
  • MiniCssExtractPlugin:获取一个loader,将css文件单独提取为一个文件;
  • CssMinimizerWebpackPlugin:在打包时将css进行压缩
  • EslintWebpackPlugin:代码格式检查工具
(5)mode
作用

告诉webpack,当前处于哪个模式下;

语法

[1]处于开发模式

mode:development

[2]处于生产模式下

mode:production;
举例-使用

我们现在要创建一个在 使用 html、 css+less、js的项目,过程中会使用图片、字体图标资源。 希望打包后将html+css+js进行打包减少体积,由于开发环境与生产环境不同,所以希望配置两个配置文件以降低耦合。

下载

官网-快速开始

npm install webpack webpack-cli --save-dev
配置

这是开发环境的配置文件,生产环境的配置文件以此做细微修改

const path = require('path')
const TerserPlugin = require('terser-webpack-plugin')
const workers = require('os').cpus().length // nodejs的os插件可以获取电脑cpu的数量

const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
/**
 * html:希望打包时将html文件打包进去并自动引入打包后的js文件、打包后压缩,打包后到指定的位置
 * css: 希望webpack能够识别css、less并将其单独抽出1个css文件通过link引入,语法使得大多数浏览器能够兼容,打包后能够被压缩,打包后到指定的位置
 * js: 希望webpack能够做兼容处理,打包后到指定的位置
 * 图片,字体图标等资源,打包后到指定的位置
 * 错误1:filename路经带了./,导致css,图片,iconfont打包时没有打包到dist文件夹中
 * 错误2:htmlwebpackplugin插件没有配置template属性,导致打包后的html页面没有dom结构
 * 错误3(细节):postcss-loader在配置时,报错Cannot find module 'postcss-preset-env',原因按照官网没有下载该插件
 * 提示:图片没有没打包进去,刚开始以为有问题,后来检查发现图片<1kb
 */

module.exports = {
  entry: './src/main.js', // 以根目录做相对路经
  output: {
    path: path.resolve(__dirname, '../dist'), // 绝对路径要以文件夹的对应的位置写路经
    filename: 'src/js/main.js',
    clean: true
  },
  module: {
    rules: [
      {
        oneOf: [
          {
            test: /\.css$/i,
            use: [
              MiniCssExtractPlugin.loader,
              'css-loader',
              //   {
              //     loader: 'postcss-loader',
              //     options: {
              //       postcssOptions: {
              //         plugins: [['postcss-preset-env']]
              //       }
              //     }
              //   }
              {
                loader: 'postcss-loader',
                options: {
                  postcssOptions: {
                    plugins: [
                      'postcss-preset-env' // 能解决大多数样式兼容性问题
                    ]
                  }
                }
              }
            ]
          },
          {
            test: /\.less$/i,
            use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
          },
          {
            test: /\.m?js$/,
            exclude: /node_modules/,
            use: [
              {
                loader: 'thread-loader',
                options: {
                  workers: workers - 1 // 最大数量为 cpu内核 - 1 (主核不能用)
                }
              },
              {
                loader: 'babel-loader',
                options: {
                  presets: ['@babel/preset-env'],
                  cacheDirectory: true,
                  cacheCompression: false,
                  plugins: ['@babel/plugin-transform-runtime']
                }
              }
            ]
          },
          {
            test: /\.(jpe?g|png|gif|svg)$/,
            type: 'asset',
            generator: {
              filename: 'assets/images/[hash:8][ext][query]'
            }
          },
          {
            test: /\.(woff2?|ttf)$/,
            type: 'asset/resource',
            generator: {
              filename: 'assets/media/[hash:8][ext]'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, '../public/index.html')
    }), // 以当前html为原件打包生成并引入打包后的js文件
    new MiniCssExtractPlugin({
      filename: 'src/css/main.css'
    }), // 实例话对象存在一个loader用于将js单独提取为一个文件
    new CssMinimizerPlugin(), // 打包时将css压缩
    new TerserPlugin({
      parallel: workers - 1 // 开启多进程打包并设置进程数量
    })
  ],
  devServer: {
    host: 'localhost',
    port: 3000,
    open: true,
    static: './dist'
  },
  mode: 'development',
  devtool: 'cheap-module-source-map'
}

}

创建多个webpack配置文件

在开发环境的配置会存在细微的差别,因此我们可以配置两个配置文件。

├── webpack-test (项目根目录)
    ├── config (Webpack配置文件目录)
    │    ├── webpack.dev.js(开发模式配置文件)
    │    └── webpack.prod.js(生产模式配置文件)

注意点 :我们在项目打包地时候看的是执行的时候文件对于配置文件的位置,因此相对路经不用改变(还是把配置文件当作在根目录下就好了),绝对路经需要按照当前配置信息的位置进行修改

执行 npx webpack 命令本质是执行 npx webpack --config ./webpack.config.js;只是因为这是默认的webpack文件名所以可以省略后面

因此在当前结构下

  • 要是我们使用开发环境服务器需要执行命令 npx webpack serve --config ./config/webpack.dev.js
    • 要是我们对生产环境进行打包需要执行命令 npx webpack --config ./config/webpack.prod.js
      但是每次执行这个命令又比较长,因此我们可以在包文件(package.json)中进行配置
  • package.json中存在一个属性scripts就是对执行命令进行配置
  • Webpack_第13张图片
  • 当我们想要开启开发环境服务器就可以执行 npm run dev ;
  • 当想要生产环境打包就可以执行 npm run build;
  • 由于开发环境比较常用,就使用start又添加了一条命令,因此开启开发环境服务器也可以执行npm start
  • 注:只有start可以不用加run,其余必须加run,
项目的目录结构

Webpack_第14张图片

webpack的其他概念

[1]搭建开发服务器

现在我们每次更改项目的的任何内容都需要重新进行打包才能够被编译,但是这并不是我们想要的效果,为此我们可以搭建一个开发环境的服务器,让我们修改的内容能够实时展示在页面上。
官网->DevServer
Webpack_第15张图片

配置
devServer: {
    host: 'localhost',
    port: 3000,
    open: true,
    static: './dist' 
  },
  • host:启动服务器的域名
  • port:启动服务器端口号,
  • open: 是否自动打开浏览器
  • static:告诉webpack-dev-server;将哪个文件夹下的文件 serve 到 当前服务器下,默认dist文件夹
启动

若是配置了开发服务器,则需要使用 npx webpack serve启动开发服务器,可以实时监听;若是使用 npx webpack 为打包(配置了devServer也不行)

  • npx webpack 打包
  • npx webpack serve 启动开发服务器 (不会输出任何东西,是在内存中编译打包的—>若是我们删除了打包后的文件,使用此命令不会生成打包后的文件)
开发体验优化-模块热替换(*)
前提

webpack4在打包时默认会对所有文件进行重新打包,也就是说当我们仅修改了一个文件时,就要在入口文件开始重新对所有文件进行打包,导致速度比较慢,此时我们就需要使用模块热替换hot module replacement;

作用

模块热替换(HMR - hot module replacement)功能会在应用程序运行过程中,替换、添加或删除 模块,而无需重新加载整个页面
也就是说当我们修改某快代码时,就只有这个模块的代码需要重新打包编译,会大大提升打包速度。

配置

注:模块热替换只能对生产环境有作用,开发环境打包还是要全部重新打包。
在webpack4中需要在devServe中添加hot属性

devServer: {
    host: 'localhost',
    port: 3000,
    open: true,
    static: './dist' ,
    hot:true
  },

而webpack5,这已经是默认选项了。
需要注意的是js文件还是没有默认模块热替换,修改任何js文件还是会重新打包,若是需要js也进行模块热替换需要做以下配置

// 在入口文件引入时配置
if (module.hot) {
  module.hot.accept('./js/getsum', function () {
    // 若是需要getsum文件更新做别的操作可以添加此参数
  })
} 

但是这样也是比较麻烦的,每次写一个js文件都要添加配置。若是我们使用vue、React等框架时可以使用 vue-loader、react-hot-loader,就不用我们单独配置了。

[2]开发体验优化-调试工具(*)

我们在开发过程中可能会产生一些错误如下如
在这里插入图片描述
我们一般会点击跳转到指定位置取查看是什么问题,但是webpack打包后的代码是经过编译的,挑战的位置也是编译之后的位置,通常是不准确的。
若是我们想要将代码中对映的位置进行显示,可以使用sourceMap.
官网->devToop

本质

Sourcemap 本质上是一个信息文件,里面储存着代码转换前后的映射关系。

作用

SourceMap 的主要作用是为了方便调试!
Sourcemap 构建了处理前以及处理后的代码之间的一座桥梁,方便定位生产环境中出现 bug 的位置。
当构建后代码出错了,浏览器会通过map文件,从构建后代码出错位置通过映射关系找到源码位置并在控制台进行提示,这样就会让程序员调试轻松、简单很多。

配置
devtool:'cheap-module-source-map' // 在出错时只会生成行的对应关系
devtool:'source-map' // 在出错时行与列的对应关系都会生成

Webpack_第16张图片

运行

配置后重新打包
然后就可以看到错误的具体位置了
在这里插入图片描述

[3]代码分割(*)

官网->配置->优化(Optimization)
Optimization配置存在一个splitChunks属性,作用就是根据不同的策略来进行分割打包后的chunk(每一个打包出的js文件就是一个chunk)
以下为代码分割的的默认选项

optimization: {
    splitChunks: {
      // chunks 表示分割策略; 共有3个值"initial","async"和"all"。配置后,代码分割优化仅选择初始块,按需块或所有块进行分割
      chunks: 'async',
      minSize: 20000, // 分割的chunk(在min或gz之前)最小的体积为20KB
      minRemainingSize: 0, // 提取的文件大小不能为0
      minChunks: 1, // chunk在项目中最少被引用一次
      maxAsyncRequests: 30, // 按需加载时并行加载的最大数量
      maxInitialRequests: 30, // 入口文件的最大请求数量
      enforceSizeThreshold: 50000, // 默认超过50KB的文件单独打包(会忽视上述条件)
      // 缓存组-缓存组可以继承和/或覆盖来自 splitChunks.* 的任何选项。但是 test、priority 和 reuseExistingChunk 只能在缓存组级别上进行配置。将它们设置为 false以禁用任何默认缓存组。
      cacheGroups: {
        defaultVendors: { // 组名
          test: /[\\/]node_modules[\\/]/, // 当 webpack 处理文件路径时,它们始终包含 Unix 系统中的 / 和 Windows 系统中的 \。这就是为什么在 test 字段中使用 [\\/] 来表示路径分隔符的原因。在test 中的 / 或 \ 会在跨平台使用时产生问题。
          priority: -10, // 权重(数量越大权重越高)
          reuseExistingChunk: true, // 若是当前chunk 包含被从主chunk中拆分出来的模块,则它将会被重用,而不是生成新的模块
        },
        // default里面的权重优先级会比上面的优先级高
        // 若是里面配置了使用里面的,里面没有配置到就使用上面配置的
        default: { 
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
举例说明
├── webpack-test (项目根目录)
    ├── src
    │    ├── js
    │    │    ├── js1.js
    │    │    ├── js2.js
    │    │    ├── 。。。
    │    ├── utils
    │    │    ├── utils1.js
    │    │    ├── utils2.js
    │    │    ├── 。。。
    │    └── webpack.prod.js(生产模式配置文件)
    ├── entry1.js
    ├── entry2.js

如上所示文件夹,我们希望webpack进行打包时
-[1] 从entry1.js entry2.js两个入口文件开始打包,生成main1.js,main2.js出口文件;

  • [2]若是有公共文件(大于10KB),将公共文件打包到单独文件中再引入;
  • [3]将utils下的工具文件单独打包到一个js文件中
 optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        commons: {
          name: 'chunk-utils',
          test: /[\\/]utils[\\/]/,
          minChunks: 1, // 至少引入到一个文件中
          minSize: 0,
          priority: 20 // 权重比default高
        },
        default: {
          minSize: 10,
          minChunks: 2 // 超过两个以上的引入才会被打包
        }
      }
    }
  }
文件按需导入

官网->指南->懒加载
文件按需引入之后在打包时会将其打包到单独的js文件中,等待条件成立,再将其引入
1、语法

import(/* webpackChunkName: '文件名' */ "文件路经").then({ 方法名 })=>{
  // 进行处理
}

通过上述语法按需导入,然后在output中配置文件打包后的名字

output:{
   chunkFilename:'static/js/[name].chunk.js' // 配置按需引入文件打包后的位置
}
预加载

官网->指南->代码分离->预加载
Webpack_第17张图片

总结-优化

[1]提升开发体验

(1) 模块热替换(HMR)
(2) 调试工具(sourceMap)-添加与源文件的映射关系

[2]打包构建速度优化

(1) loader中-oneOf属性
(2) 减少js文件的处理-include/exclude
(3) 对js编译后的文件或检查后的文件添加缓存-Eslint插件、babel-loader文件缓存;
(4)js多进程打包-thread-loader([θred])、TerserWebpackPlugin插件、Eslint

[3]减少打包后的体积

(1)Tree Shaking

Tree Shaking([ˈʃeɪkɪŋ]) 是一个术语,通常指的是移除Javascript中没有使用上的代码(包含我们下载的插件中没有使用到德js代码),它依赖于Es Module.
在webpack中,这个功能是默认配置的。
举例说明

export function test (value) {
  return value
}

export function getValue (value) {
  return value.toLocaleString()
}
  • 在上述js文件中按需导出了两个函数,但是直在入口文件按需引入了getValue方法,bulid打包后发现test方法并没有出现在打包后的js文件中;
    (2) babel将依赖作为独立区域,避免重复引入;
    (3)本地图片打包时进行压缩

[4]代码拆分

目前打包代码时会将所有的js代码打包到一个js文件中,若是项目体积过大将会影响到我们的首屏渲染速度。
因此我们希望在打包时将生成的文件进行代码分割,生成多个js文件,渲染哪个页面就只加载某个js文件,以提升用户体验

你可能感兴趣的:(2022_浏览器,webpack,javascript,前端)