webpack 基本使用

105 阅读5分钟

webpack 基本使用



为什么选择 webpack

浏览器中运行 JS 的方式:

    1. 引用脚本,难扩展,太多导致网络瓶颈;
    1. 包含所有项目代码的大型 js 文件,作用域,文件大小,可读性,可维护性问题

依赖自动收集

  • webpack 根据 import require 等关键字自动构建给予引用到处的依赖图谱。

主要功能

  • 前端模块化。作用域污染,公共模块抽离;
  • 代码压缩混淆。提升速度,防止代码盗用;
  • 浏览器兼容性。babel-loader转es6为es5实现浏览器代码兼容;
  • 性能优化。例如小图转 base64, css 压缩;

基本配置

mkdir webpack-demo && cd webpack-demo
npm init -y
npm install --save-dev webpack webpack-cli

webpack.config.js

const path = require("path");
module.exports = {
  entry: "./src/index.js",
  output: {
    filename: "main.js",
    path: path.resolve(__dirname, "dist"),
  },
};

安装 webpack-cli 的情况下

{
  "scripts": {
    "build": "webpack --config webpack.config.js"
  }
}

如果不想安装 webpack-cli 的情况下

npx webpack --config webpack.config.js

常用的 plugin 介绍

webpack 官网导航 Plugins 有简介

plugin 名称作用来源
html-webpack-plugin生成 html 文件npm
DefinePlugin定义全局常量webpack
HotModuleReplacementPlugin热更新webpack
clean-webpack-plugin清理 dist 目录npm
mini-css-extract-plugin分离 cssnpm
uglifyjs-webpack-plugin压缩 jsnpm
webpack-bundle-analyzer可视化 webpack 输出文件体积npm

html-webpack-plugin

output 目录中生成 html 文件,在文件中使用 script 标签引入打包后的 js 文件

yarn add html-webpack-plugin -D // 安装
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = {
  plugins: [new HtmlWebpackPlugin({})],
};

DefinePlugin

定义全局变量,应用:未开发环境和生产环境引用不同的配置

// ./webpack.config.js
const webpack = require("webpack");
module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      HOST: JSON.stringify("https://api.dev.com"),
    }),
  ],
};
// ./webpack.prod.config.js
const webpack = require("webpack");
module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      HOST: JSON.stringify("https://api.prod.com"),
    }),
  ],
};
// ./package.json 配置不同环境执行的命令
{
  "scripts": {
    "build:dev": "webpack --config webpack.config.js",
    "build:prod": "webpack --config webpack.prod.config.js"
  }
}
// ./index.js 此处 webpack 在编译时会替换掉对应的 HOST 值
function fetchList() {
  fetch(HOST + "/users/octocat/repos");
}

常见的 loader

loader作用
css-loader加载 css 文件,能够在 JS 做文件中引入使用
style-loader把 css 文件插入到 html 中,在 html 中通过 style 标签引入
less-loaderless 文件转成 css 文件
sass-loadersass 文件转成 css 文件
babel-loaderes6 转成 es5
ts-loaderts 转成 js
file-loader打包图片,打包字体图标
url-loader和 file-loader 类似,当文件小于设定的 limit 的时候回,可以返回一个 DataUrl(提升网页性能 )
html-withimg-loader打包 html 文件中的图片
eslint-loadereslint 检查 JS 代码错误,也可以用于代码规范

css-loader

# 两者必须同时使用
npm i css-loader style-loader -D
// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
};
html {
  background: pink;
}
// index.js
import "./style.css";

此时并没有生成 css 文件,而是在对应的 bundle.js 中插入了动态生成 style 标签的代码

打包优化- Code Splitting 代码分割

假设有两个入口文件 index.js another-module.js,同时在两个文件中引入一个第三方 npmunderscore , 结果是在两个文件中都对 underscore 进行了打包,应该提取公共部分,减少重复打包。

未优化的原始配置

// webpack.config.js
const path = require("path");
module.exports = {
  mode: "development",
  entry: {
    index: "./src/index.js",
    another: "./src/another-module.js",
  },
  output: {
    filename: "[name].bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
};

防止重复引入 (Prevent Duplication)

  1. 使用 shared (entry.shared) 属性
// webpack.config.js
const path = require("path");
module.exports = {
  mode: "development",
  entry: {
    index: {
      import: "./src/index.js",
      dependOn: "shared",
    },
    another: {
      import: "./src/another-module.js",
      dependOn: "shared",
    },
    shared: "underscore",
  },
  output: {
    filename: "[name].bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
};
  1. SplitChunksPlugin

此项为一个 optimization 中的一个配置项 splitChunkswebpack 可以自己判断有哪些模块可以复用并抽离

// webpack.config.js
const path = require("path");
module.exports = {
  mode: "development",
  entry: {
    index: "./src/index.js",
    another: "./src/another-module.js",
  },
  output: {
    filename: "[name].bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
  optimization: {
    splitChunks: {
      chunks: "all",
    },
  },
};
  1. 动态导入 Dynamic Import
import _ from "lodash";

const { default: _ } = await import("lodash");
  1. Caching 缓存

contenthas

outputfilename 可以添加属性 [contenthash],当文件内容改变时会跟着一同改变,防止文件被浏览器缓存

// webpack.config.js
module.exports = {
  entry: "./index.js",
  plugins: [
    new HtmlWebpackPlugin({
      title: "Caching",
    }),
  ],
  outpout: {
    filename: "[name].[contenthash].js", // 添加 [contenthash]
    path: path.resolve(__dirname, "dist"),
    clean: true, // 清除 dist 文件夹
  },
};
  1. 提取文件范例 webpack 提供了一个优化功能,可以使用 optimization.runtimeChunk 选项将 runtime 代码拆分为一个单独的 chunk 。将其设置为 single 来为所有的 chunk 穿件一个 runtime buldle:
// webpack.config.js
const path = require("path");
module.exports = {
  mode: "development",
  entry: {
    index: "./src/index.js",
    another: "./src/another-module.js",
  },
  output: {
    filename: "[name].bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
  optimization: {
    splitChunks: {
      chunks: "all",
    },
    runtimeChunk: "single", // 提取 webpack runtime 代码
  },
};

专业术语解释

runtimeChunk 是 Webpack 中用于优化构建输出的一个配置项。它的主要作用是将 Webpack 的运行时代码(runtime)提取到一个单独的 chunk 文件中。运行时代码是 Webpack 用来管理和加载模块的引导代码,它负责解析模块依赖关系、执行懒加载等功能。

通过将 runtimeChunk 设置为 "single",Webpack 会为所有生成的 chunks 创建一个单一的 runtime bundle。这有以下几个好处:

  1. 减少重复代码:多个入口文件或按需加载的 chunks 都会包含相同的运行时代码,将其提取到一个单独的文件可以避免重复。
  2. 缓存效率提升:由于 runtime 代码通常不会频繁变化,将其独立出来后,浏览器可以更高效地缓存这部分代码,而其他业务代码的变化不会影响到 runtime 文件的缓存。
  3. 简化依赖管理:当多个 chunks 共享同一个 runtime 时,它们之间的依赖关系更加清晰和一致。

生动形象的比喻

想象你正在组织一场大型音乐会,音乐会有很多不同的表演环节(类似于 Webpack 构建中的多个 chunks)。为了确保每个环节顺利进行,你需要有一个指挥来协调所有的演出安排(类似于 Webpack 的运行时代码)。

如果没有 runtimeChunk,每个表演环节都需要有自己的指挥,这样不仅浪费资源,而且容易出现混乱。但如果有一个统一的总指挥(相当于 runtimeChunk: "single"),他可以统筹安排所有的表演环节,确保每个环节都能顺利进行,同时也不会因为某个环节的变化而影响到整个音乐会的协调工作。

此外,这个总指挥的指令集(runtime 代码)相对固定,不需要经常更改,因此观众(浏览器)只需要记住一次总指挥的指令,下次再来观看时就不需要重新学习这些指令了,从而提高了整体效率。

希望这个解释能帮助你更好地理解 runtimeChunk 的意义。