Webpack

165 阅读14分钟

webpakck官网是英文版的,中文版的官网有错误,所以按照你的需求来看文档。

概述

webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具

安装webpack, 为方便起见,以一种熟悉的打包方式呈现,先建立一个你熟悉的文件目录

image.png

package-lock.json 和 package.json 的作用

package.json:里面定义的npm安装文件的版本,而node_modules中是npm实际安装的文件,指用来描述项目的依赖,需要注意的点:项目的名字会自动使用文件夹的名字,所以此时文件夹的名字不能是中文,特殊大写字母(执行了npm init -y初始化包描述文件)

package-lock.json:锁定安装时的包的版本号及包的依赖的版本号,保证其他人在使用npm install时下载的依赖包是一致的(执行npm i 或者 npm i xxx命令自动生成package-lock.json文件)

npx的用法

npxnpm包执行器(作用:调用项目内部安装的模块),比如调用xxx模块,只能在项目脚本和 package.json 的scripts字段里面,如果想在命令行下调用,必须像下面这样。

     npx webpack ./src/main.js --mode=development 
     // 或者执行: node_modules/.bin/webpack ./src/main.js --mode=development

webpack官网上说:想要运行本地安装的 webpack,可以通过node_modules/.bin/webpack来访问它的二进制版本。另外,如果你使用的是 npm v5.2.0 或更高版本,则可以运行 npx webpack 来执行。同样的,阮一峰的网络日志-npx 使用教程 中也提到过。

<script src="../main.js"></script>
// 因为我的main用了Es Module的导入导出语法,而浏览器是不识别ES Module,所以报错
<script src="../dist/main.js"></script>
// webpack打包后就可以使用ES Module语法了,这也是webpack的基本功能

简单总结下

Npm script是一个任务执行者。Npm script是Npm内置的一个功能,允许在package.json文件中使用scripts字段定义任务。

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

底层原理是通过Shell去运行脚本命令,例如执行npm run dev命令等同于执行了webpack serve --config ./config/webpack.dev.js

"dev": "webpack serve --config ./config/webpack.dev.js", 
**以下三种命令等效执行这个脚本**
// ./node_modules/.bin/webpack serve --config ./config/webpack.dev.js
// npx webpack serve --config ./config/webpack.dev.js
// npm run dev

"serve":"serve dist" //./node_modules/.bin/serve dist,
**以下三种命令等效执行这个脚本**
// ./node_modules/.bin/serve dist
// npx serve dist
// npm run serve

"build": "webpack --config ./config/webpack.prod.js",  
**以下三种命令等效执行这个脚本**
// ./node_modules/.bin/webpack --config ./config/webpack.prod.js
// npx webpack --config ./config/webpack.prod.js
// npm run build

聊到Npm Script,不得不提一个常见的面试题

对比development和production环境

打包到本地开发环境:node_modules/.bin/webpack ./src/main.js --mode=development

image.png

打包到线上生产环境:npx webpack ./src/main.js --mode=production

image.png

五个核心概念

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

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

loader加载器:让webpack能够去处理那些非javascript资源css、img等,将它们处理成webpack能够识别的资源,可以理解为一个编译过程(webpack自身只能js和json文件)。值得关注的是:rules中的配置项:test、include、exclude是数组类型时,里面的项是"或"的关系,满足一个条件即可。

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

mode模式:mode指示webpack的使用相应模式的配置 \ 开发模式:(development):配置比较简单,能让代码调用本地的环境
生产模式:(production):代码需要不断优化达到性能最好,能让代码优化上线运行时的环境

chunk是什么?Chunk是Webpack打包过程中,一堆module的集合 我们知道Webpack的打包是从一个入口文件开始,也可以说是入口模块,入口模块引用这其他模块,模块再引用模块。Webpack通过引用关系逐个打包模块,这些module就形成了一个Chunk。*

知道上面这些概念后,我们回头看看打包的操作,除了前面提到那两种,我们用这些概念去实现打包的功能,具体应用如下:

// webpack.config.js文件
const path = require("path"); //nodejs中的
// __dirname
// 配置文件是在node.js环境下运行的,所以采用的是common.js模块化
module.exports = {
  // 入口 相对路径
  entry: "./src/main.js",
  // 出口
  output: {
    // __dirname 当前文件的文件夹目录,即WEBPACKDEMO下的dist文件
    // filename的路径是相对于output.path
    path: path.resolve(__dirname, "dist"), // 绝对路径
    filename: "main.js",
  },
  module: {
    rules: [],
  },
  plugins: [],
  mode: "production",//development
};

image.png

说明:执行npx webpack,命令会找webpackDemo文件下的webpack.config.js文件,并执行其中的打包逻辑代码

// package.json文件
...
  "scripts": {
    "toBuild": "webpack"
  },
...

image.png

说明:执行npm run toBuild,执行效果和npx webpack一样,即我们简单的就实现了一个自定义的打包命令了!

样式文件css-loader 和less-loader等

// webpack.config.js文件
...
  module: {
    rules: [
      {
        test: /\.css$/i,// 正则中的test
        use: [
          "style-loader",  // 将js中的css通过创建style标签添加到html文件中
          "css-loader", // 将css资源编译成commonjs的模块到js中
        ], // 从右到左执行
      },
    ],
  },
...

image.png

style-loader的作用效果:

image.png

sass-loader、stylus-loader同理,这里不做赘述。

图片资源

// webpack.config.js文件
...
 {
    test: /\.(png|svg|jpg|jpeg|gif)$/i,
    type: 'asset/resource',
    parser: {
      dataUrlCondition: {
        maxSize: 4 * 1024 //小于4kb的会转成base64,而不满足条件的会打包成一个.png文件
      }
    }
  },
...

不需要下载loader,可以使用内置的 Asset Modules加载 images 图像 。文章看到这,当你打包文件时可以看到打包生成的文件时杂乱无章的,怎么解决呢?

image.png

配置js文件的打包路径和images的打包路径就ok了,如下:

// webpack.config.js文件
...
 output: {
    // __dirname 当前文件的文件夹目录,即WEBPACKDEMO下的dist文件
    // filename的路径是相对于output.path
    path: path.resolve(__dirname, "dist"), // 绝对路径
    filename: "static/js/main.js",
    clean:true, // 打包前自动清空之前的打包文件,在生成文件之前清空 output 目录
  },
...

...
 {
    test: /\.(png|svg|jpg|jpeg|gif)$/i,
    type: 'asset/resource',
    parser: {
      dataUrlCondition: {
        maxSize: 4 * 1024 //小于4kb的会转成base64
      }
    },
    generator: {
      filename: 'static/images/[hash][ext][query]'
    }
  },
...

配置好的打包资源路径如下:

image.png

字体图标资源、音视频资源等

跟图片资源类似,不需要下载loader,下面以字体为例:

// webpack.config.js文件
...
 {
    test: /\.(woff|woff2|eot|ttf|otf)$/i,
    type: 'asset/resource',
    generator: {
      filename: 'static/media/[hash:10][ext][query]' // hash取前10位
    }
  }
...

配置好的打包资源路径如下:

image.png

Eslint

配置文件写法: 只需要存在以下一种写法即可!

  • .eslintrc.*:新建文件,位于项目根目录
    • .eslintrc
    • .eslintrc.js
    • .eslintrc.json
  • packge.json中eslintConfig:不需要创建配置文件,在原有文件基础上写

具体写法,以.eslintrc.json为例:

// eslintrc.json文件
{
    "parserOptions": {
        "ecmaVersion": 6,
        "sourceType": "module",
        "ecmaFeatures": {
            "jsx": true
        }
    },
    "rules": {
        "semi": "error"
    }
}

rules规则具体文档,规则太多,实际上规则是可以继承已有的模块。

简单模拟下eslint检查代码的具体操作:项目根目录新建一个.eslintrc.js文件

// .eslintrc.js文件
...
module.exports = {
  extends: ["eslint:recommended"],
  env: {
    node: true,
    browser: true,
  },
  parserOptions: {
    ecmaVersion: 6,
    sourceType: "module",
  },
  rules: {
    "no-var": 2,
  },
};
...

image.png

这个配置会根据你vscode中下载的eslint插件同步,也就是这个规则会应用到编译器的插件中。

Babel

主要用于将ES6语法编写的代码,转化为向后兼容的javascript语法,以便正常运行在低版本的浏览器环境 中

配置文件写法: 只需要存在以下一种写法即可!

  • babel.config.*:新建文件,位于项目根目录
    • babel.config.js
    • babel.config.json
  • babelrc.*:新建文件,位于项目根目录
    • babelrc
    • babelrc.js
    • babelrc.json
  • packge.json中babel:不需要创建配置文件,在原有文件基础上写

具体写法,以.eslintrc.json为例:

module.exports = {
    // 预设
    presets:[]
}
// 写法1
// webpack.config.js文件
...
module: {
  rules: [
    {
      test: /.m?js$/,
      exclude: /(node_modules|bower_components)/,
      use: {
        loader: 'babel-loader', // 通过Loader接入Babel
        //options: {
          //presets: ['@babel/preset-env']
        //}
      }
    }
  ]
}
...

// babel.config.js文件
exports.module = {
    presets: ['@babel/preset-env'] // presets预设属性,其实是一组Plugins的集合
} 
// 写法2
// webpack.config.js文件
...
module: {
  rules: [
    {
      test: /.m?js$/,
      exclude: /(node_modules|bower_components)/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env']
        }
      }
    }
  ]
}
...

image.png

babel环节需要单独配置一个文件:babel.config.js文件。

处理html资源

首先,需要安装 eslint-webpack-plugin

// webpack.config.js文件
// 1.安装
npm install --save-dev html-webpack-plugin

// 2.引入
const HtmlWebpackPlugin = require('html-webpack-plugin');

// 3.使用
module.exports = {
  plugins: [new HtmlWebpackPlugin()],
};
image.png

优化代码:

// webpack.config.js文件
// 使用
module.exports = {
   // 打包输出的文件是原文件,它会自动引入这个文件
   new HtmlWebpackPlugin({template: path.resolve(__dirname, "public/index.html")}),
};
image.png

打包前后的文件就多了一个自动引入的那行<script/>代码,跟plugin插件有关的,三步走:下载=> 引入=> 使用。

DevServer服务

webpack-dev-server 可用于快速开发应用程序。 完成打包操作,但是我每次跑最新代码前都要打包一遍,显得特变繁琐,下面的操作会自动打包并运行最新的代码到一个服务器,如:http://localhost:3000/

// webpack.config.js文件
// 开发服务器
...
devServer:{
    host:'localhost',
    port:'3000',
    open:true
},
mode: "development", //production
...

image.png

一定要注意的是,这里运行的是:npx webpack serve命令。

真实开发项目中的应用

把之前的webpack.config.js写在config文件中,放置开发环境和生产环境的config,如下:

image.png

配置文件相关的代码,如下;

// webpack.prod.js文件
const path = require("path"); //nodejs
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ESLintPlugin = require('eslint-webpack-plugin');
// __dirname
// 配置文件是在node.js环境下运行的,所以采用的是common.js模块化
module.exports = {
  // 入口 相对路径
  entry: "./src/main.js",
  // 出口
  output: {
    // __dirname 当前文件的文件夹目录,即WEBPACKDEMO下的dist文件
    // filename的路径是相对于output.path
    path: path.resolve(__dirname, "../dist"), // 绝对路径
    filename: "static/js/main.js",
    clean: true, // 打包前自动清空之前的打包文件
  },
  module: {
    rules: [
      {
        test: /\.css$/i, // 正则中的test
        use: [
          "style-loader", // 将js中的css通过创建style标签添加到html文件中
          "css-loader", // 将css资源编译成commonjs的模块到js中
        ], // 从右到左执行
      },
      {
        test: /\.less$/i,
        use: ["style-loader", "css-loader", "less-loader"],
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: "asset/resource",
        parser: {
          dataUrlCondition: {
            maxSize: 4 * 1024, //小于4kb的会转成base64
          },
        },
        generator: {
          filename: "static/images/[hash][ext][query]",
        },
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: "asset/resource",
        parser: {
          dataUrlCondition: {
            maxSize: 4 * 1024, //小于4kb的会转成base64
          },
        },
        generator: {
          filename: "static/images/[hash][ext][query]",
        },
      },
      {
        test: /\.m?js$/,
        exclude: /(node_modules|bower_components)/,
        loader: "babel-loader",
        // use: {
        //   loader: 'babel-loader',
        //   options: {
        //     presets: ['@babel/preset-env']
        //   }
        // }
      },
    ],
  },
  plugins: [
    // 打包输出的文件是原文件,它会自动引入这个文件
    new HtmlWebpackPlugin({template: path.resolve(__dirname, "../public/index.html")}),
    new ESLintPlugin({ context: path.resolve(__dirname, "../src") }),
  ],
  // 开发服务器,能运行出,但是不会打包到dist目录,直接用了打包后的资源
  // devServer:{
  //   host:'localhost',
  //   port:'3000',
  //   open:true
  // },
  // 生产模式不需要devServer,只需要打包输出
  mode: "production",//development
};

// webpack.dev.js文件
const path = require("path"); //nodejs
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ESLintPlugin = require('eslint-webpack-plugin');
// __dirname
// 配置文件是在node.js环境下运行的,所以采用的是common.js模块化
module.exports = {
  // 入口 相对路径
  entry: "./src/main.js",
  // 出口
  output: {
    // __dirname 当前文件的文件夹目录,即WEBPACKDEMO下的dist文件
    // filename的路径是相对于output.path
    // path: path.resolve(__dirname, "../dist"), // 绝对路径
    path:undefined,
    filename: "static/js/main.js",
    clean: true, // 打包前自动清空之前的打包文件
  },
  module: {
    rules: [
      {
        test: /\.css$/i, // 正则中的test
        use: [
          "style-loader", // 将js中的css通过创建style标签添加到html文件中
          "css-loader", // 将css资源编译成commonjs的模块到js中
        ], // 从右到左执行
      },
      {
        test: /\.less$/i,
        use: ["style-loader", "css-loader", "less-loader"],
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: "asset/resource",
        parser: {
          dataUrlCondition: {
            maxSize: 4 * 1024, //小于4kb的会转成base64
          },
        },
        generator: {
          filename: "static/images/[hash][ext][query]",
        },
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: "asset/resource",
        parser: {
          dataUrlCondition: {
            maxSize: 4 * 1024, //小于4kb的会转成base64
          },
        },
        generator: {
          filename: "static/images/[hash][ext][query]",
        },
      },
      {
        test: /\.m?js$/,
        exclude: /(node_modules|bower_components)/,
        loader: "babel-loader",
        // use: {
        //   loader: 'babel-loader',
        //   options: {
        //     presets: ['@babel/preset-env']
        //   }
        // }
      },
    ],
  },
  plugins: [
    // 打包输出的文件是原文件,它会自动引入这个文件
    new HtmlWebpackPlugin({template: path.resolve(__dirname, "../public/index.html")}),
    new ESLintPlugin({ context: path.resolve(__dirname, "../src") }),
  ],
  // 开发服务器,能运行出,但是不会打包到dist目录,直接用了打包后的资源
  devServer:{
    host:'localhost',
    port:'3000',
    open:true
  },
  mode: "development", //production
};

上面两个配置文件中,都是我直接复制之前webpack.config.js的文件,只是它们和webpack.config.js文件的区别是:dev文件是本地环境,需要把path.resolve()相关的绝对路径在我目前的环境中要返回到上一层也就是加上../。prod文件是线上环境,也需要把path.resolve()相关的绝对路径在我目前的环境中要返回到上一层也就是加上../,还要把devServer自动打开这个关掉,没必要再打开这个服务器了。

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

package.json文件配置命令脚本,代替npx webpack serve --config ./config/webpack.dev.js 和 npx webpack --config ./config/webpack.prod.js这两串这么长的命令!

特别说明下,当我们把网络调至低速3G运行时,你会发现我强制刷新浏览器,会出现这样的一个现象:

  • 普通刷新:F5、地址栏左侧刷新、ctr + R
  • 强制刷新: ctrl + F5、ctrl + 地址栏左侧刷新、ctr + shift + R

延迟.gif

出现延迟或闪屏的原因是:css文件被打包到js文件中,js会创建一个style标签生成样式

image.png

解决的方案:单独打包生成一个css文件,通过link标签加载(通过下面的方式,打包后的html文件中有link标签)

npm install --save-dev mini-css-extract-plugin

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

rules: [
  {
    test: /.css$/i,
    use: [MiniCssExtractPlugin.loader, "css-loader"], // 把style-loader字符串换成 MiniCssExtractPlugin.loader插件
  },
],

plugins: [
    new MiniCssExtractPlugin({ filename: "static/css/main.css" }), // 打包到
],

在一些特定的时间段,刷新时不会闪屏的,如下:

image.png

css兼容性处理

可参考一个简单易理解的文章

    npm install --save-dev postcss-loader postcss postcss-preset-env -D

postcss-loader要放在css-loader 和 style-loader 之后, less-loader 之前 ,不然会报错

// webpack.prod.js文件
 use: [
  "style-loader",
  "css-loader",
  {
    loader: "postcss-loader",
    options: {
      postcssOptions: {
        plugins: [
          [
            "postcss-preset-env",
            {
              // Options
            },
          ],
        ],
      },
    },
  },
],

// package.json文件
  "scripts": {
    "start": "npm run dev",
  },
  "browserslist":["ie > 8"] // 或者 "browserslist":["last 2 version",">1%","not dead"] "browserslist":["last 2 version",">1%","not dead"] 

mian.css文件中:

// mian.css文件中  
...
  display:ms-flex;  // 可以看到把felx打包成了兼容写法ms-flex
...

css压缩、html压缩和js压缩

npm install css-minimizer-webpack-plugin --save-dev

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

module.exports = {
 optimization: {
    minimizer: [
      // For webpack@5 you can use the `...` syntax to extend existing minimizers (i.e. `terser-webpack-plugin`), uncomment the next line
      // `...`,
      new CssMinimizerPlugin(),
    ],
  },
  plugins: [new MiniCssExtractPlugin()],
};

可见css代码压缩成了一行:

image.png

最后像html和js文件在开发环境就自动进行了压缩,而css可以要下载plugin才能压缩。

soursemap

soursemap:一种源代码和构建后代码的映射。 如果代码出错会在控制台显示出错的具体位置,但这个位置是打包后文件的位置,不能定位到源代码出错的位置,怎么解决?用以下方法在某些情况下会映射到源代码的具体位置:

// 开发模式
devtool:"cheap-module-source-map" // 默认值是false,即默认不生成Source map
// 生产模式
devtool:"source-map"

可以参考webpack文档对于sourcemap的计算

HotModuleReplacement

可以提升提升打包构建速度,开发修改其中一个模块的代码,webpack默认会将所有模块都重新打包,当文件越来越大速度会特别慢所以,需要做到只更新更改模块的代码,其他模块不变怎么实现呢? 启用热模块更换,也称为 HMR。vue或者react项目中有它们自动实现热模块的替换功能。

// webpack.dev.js文件
...
  devServer: {
    host: "localhost",
    port: "3000",
    open: true,
    hot:false, // 默认hot:true,默认开启HMR方案,效率更高 
  },
...

//  main.js文件
if(moudle.hot){
    module.hot.accept('.xxx.js',fn)
}

oneOf

// webpack配置文件中
...
// 常规写法,找到匹配的loader还会进行继续向后查找
 rules: [ 
          {
            test: /\.less$/i,
            use: ["style-loader", "css-loader", "less-loader"],
          },
          ...
      ]
...
// oneof写法,匹配到其中的一个loader文件进行处理就不在向后查找
 rules: [ 
          {
           oneOf:[
                   { 
                    test: /\.less$/i,
                    use: ["style-loader", "css-loader", "less-loader"],
                   }
               ]
          },
          ...
      ]

cache缓存

使用缓存,提升性能

...
      {
        test: /\.m?js$/,
        exclude: /(node_modules|bower_components)/,
        loader: "babel-loader",
        options: {
          cacheDirectory: true, //开启babel缓存
          cacheCompression: false, //关闭缓存文件压缩
        },
      },
...

缓存除了node_modules的文件

image.png

...
  new ESLintPlugin({ 
      context: path.resolve(__dirname, "../src"), 
      exclude: "node_modules", // 除去这个文件的默认值
      cache: true,
      cacheLocation:path.resolve(__dirname,"../node_modules/.cache/eslintCache")
  }),
...

eslint也可以进行缓存

image.png

多进程配置

模拟多进程打包提升打包速度

npm i thread-loader -D
const os = require("os");
const threads = os.cpus().length; // cpu的核数
const TerserWebpackPlugin = require("terser-webpack-plugin");  // 这个插件已经内置了,不需要 install
...
{
    test: /\.m?js$/,
    exclude: /(node_modules|bower_components)/,
    use: [
      {
        loader: "thread-loader", //开启多线程
        options: {
          workers: threads, //进程数量
        },
      },
      {
        loader: "babel-loader",
        options: {
          cacheDirectory: true, //开启babel缓存
          cacheCompression: false, //关闭缓存文件压缩
        },
      },
    ],
},
new TerserWebpackPlugin({
  parallel: threads, //开启
}),
...

Tree-shaking

Tree-shaking的本质是消除无用的js代码,依赖Es Module环境,webpack默认已经开启了这个功能,简单的模拟下这个过程:

image.png

image.png

image.png

main.js文件中只是打包你引入了的文件,未引入的js文件不会被打包

压缩babel、压缩图片

下载对应插件在webpack文件中进行设置就能达到效果,可以自行百度了解,这里不做赘述

代码分割

一种应用场景中:渲染哪个页面加载对应的js文件,而不是整个main.js文件,这样加载资源少,所以加载的速度更快(spa单页面中只有一个入口文件,所以不适用单页面应用),简单模拟下:

// webapck配置文件
// 多入口
entry:{
    app:"src/app/js",
    main:"src/main/js",
}
output:{
    path:path.resolve(__dirname, "dist"),
    filename: [name].js, // [name]以文件名自己命名
}

执行结果,如下:

image.png

如果app.js和main.js中引入了公共的代码,正常打包后的这两个文件都会有这个公共的代码部分,这个是可以被webpack优化的。执行打包,这个52.js文件就是公共部分,在app.js和main.js中引入的是这个文件,所以达到了复用公共代码的目的,如下:

image.png

按需加载

什么场景需要哪个文件才引入哪个文件,而不是一开始就全部引入

image.png

点击这个按钮,会在点击时引入这个单独的js,而不必依赖app.js或者main.js这整个js文件

按需加载.gif

如果是spa单页面应用,单入口文件也能实现按需加载,加上如下配置:

image.png

你可能现在有疑问了,这个587.main.js文件可以自定义名字吗?webpack也可以实现,如下:

点击事件中加上: image.png

配置文件加上,即可实现名字自定义模块命名:

image.png

Preload和Prefetch

Preload: 告诉浏览器立即加载资源
Prefetch: 告诉浏览器空闲时加载资源
简单区别:Preload优先级比Prefetch高;Preload只加载当前页面使用的资源,而Prefetch可以加载任何页面的资源
缺点:兼容性目前很差

npmjs.com网站中查看用法

npm install --save-dev @vue/preload-webpack-plugin
...
plugins: [
  new HtmlWebpackPlugin(),
  new PreloadWebpackPlugin({
    rel: 'preload',
    as: 'script'
    // rel :'prefetch'
  })
]
...

image.png

如果设置成:rel :'prefetch',这里的lowest表示权重很低,在浏览器空闲时就会下载这个文件

image.png

core.js 和 babel

1.'core.js''babel' 是两种概念
 1.1.Babel 官方的介绍'Babel is a JavaScript compiler'也就是说'Babel 其实就是一个 JavaScript 的编译器'
 1.2.'core.js' 它是JavaScript标准库的 polyfill(垫片/补丁)
2.core-js 又和 Babel 深度绑定,二者其实是两个东西,但又相互彼此的支持可以应用在一起

引入core.js,正确使用打包后会生成另外的js文件,里面就是兼容性的自定义实现代码

   npm i core-js -D 
   // import "core-js" // 完整引入,文件大小200多kb
   import "core-js/es/promise" // 按需加载,这个文件时路径是: node_modules/core-js/es/promise,文件大小30多kb 

但是上面有个问题,虽然时按需引入但是不是智能的,要手动按需引入,这个时候可以结合bebel来实现智能按需引入

// babel.config.js文件
module.exports = {
  presets: [
    "@babel/preset-env", // babel智能预设
    {
      useBuiltIns: "usage",  // 按需加载自动引入
      corejs: 3,
    },
  ],
};

PWA

Progress web apps:渐进式网络开发应用程序。作用:渐进式网络开发应用程序(离线可访问)

image.png

// webpack.config.js文件
// 下载
npm install workbox-webpack-plugin --save-dev
// 引入
const WorkboxPlugin = require('workbox-webpack-plugin');
...
// 注册 Service Worker
new WorkboxPlugin.GenerateSW({
  // 这些选项帮助快速启用 ServiceWorkers
  // 不允许遗留任何“旧的” ServiceWorkers
  clientsClaim: true,
  skipWaiting: true,
}),
...

// main.js文件
// 注册 Service Worker 兼容性判断
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);
      });
  });
}

离线缓存是通过Service Workers技术来实现的。Service Workers是在浏览器后台运行的脚本,它的生命周期完全独立于网页。无法直接访问dom,但是可以通过postMessage接口发送消息和ui进程通信。 这里补充下:

    npm i serve
    serve dist
    http://localhost:3000  // 启动一个开发服务器,部署当前文件夹下的dist资源。同理,因为live Serve访问的地址有后缀:http://127.0.0.1:5500/dist/index.html所以不予采纳。 

可以查看Application-Cache-Cache Stroage一栏,查看当前页面缓存的资源

image.png

即可实现一个离线加载资源的网站,如下图:

pwa.gif

总结

文章参考了开发文档视频讲解吴浩麟版本-深入浅出webpack这本书 ,通过 Webpack 的基本使用,掌握了以下功能:

  1. 两种开发模式
  • 开发模式:代码能编译自动化运行
  • 生产模式:代码编译优化输出
  1. Webpack 基本功能
  • 开发模式:可以编译 ES Module 语法
  • 生产模式:可以编译 ES Module 语法,压缩 js 代码
  1. Webpack 配置文件
  • 5 个核心概念

    • entry
    • output
    • loader
    • plugins
    • mode
  • devServer 配置

  1. Webpack 脚本指令用法
  • webpack 直接打包输出
  • webpack serve 启动开发服务器,内存编译打包没有输出