阅读 244

webpack 5 入门指南-开发体验篇

前言

在实际的项目开发中,基于框架提供的脚手架等工具,我们可以非常方便地实现监听文件变化,并自动打包、打包完成后再开启本地服务,通过浏览器访问打包后的页面。整个过程都是自动的,不需要手动处理。本文会分拆这几个过程,针对每个过程涉及的工具,一步一步完成各个过程中涉及的工具链引入及基本使用。

自动打包

给 webpack 执行命令添加 --watch 参数即可实现自动编译打包。

yarn webpack --watch
复制代码

当 webpack 监听到源码文件内容有变化时,会重新执行打包命令,生成 dist 文件。

自动刷新

使用 BrowserSync 实现。安装及使用命令如下:

// 安装 browser-sync
yarn add browser-sync dist --files "**/*"
// 监听 dist 文件夹下面所有文件的变化
yarn browser-sync dist --files "**/*"
复制代码

自动打包+页面刷新的实现:

  1. 修改源代码;
  2. webpack 监听到代码文件内容有变化变更,自动打包,生成一个新的 dist 包;
  3. 使用 browser-sync 监听到 dist 文件夹,会重新触发浏览器页面刷新。

本地开发服务器

DevServer

webpack 的 DevServer 自动集成了【自动打包】和【浏览器刷新】的功能。

  1. 在项目中安装开发依赖 webpack-dev-server。
yarn add webpack-dev-server --dev
复制代码
  1. 启动 webpack-dev-server
yarn webpack-dev-server
复制代码
  1. 启动并同时打开浏览器
yarn webpack-dev-server --open
复制代码

PS:webpack-dev-server 启动时,会将打包后的 dist 文件夹存放在内存中,浏览器访问的是内存中的 dist 文件夹,所以在项目根目录中不会出现 dist 文件夹。

静态资源访问

默认情况下,通过 webpack-dev-server 我们访问的是 dist 文件夹中的资源。不仅如此,它还支持其他静态资源文件的访问。

在 webpack.config.js 中添加以下配置:

devServer: {
    static: {
      directory: path.join(__dirname, "public"),
    },
    compresstrue,
    port9000
}
复制代码

static 中配置的是额外的静态资源访问路径。webpack 针对静态资源的访问策略是这样的:浏览器访问 index.html 文件时,若 dist 文件夹中存在同名文件,本地服务会优先返回 dist 文件中的 html 文件。若访问不同文件时,则本地服务器会在不同查找同名文件。

本地代理服务

wepack 支持在开发阶段给我们提供代理服务,当项目打包并进行同源部署后。此代理服务配置就不会再起作用了。

devServer: {
    ...
    proxy: {
      "/api/": {
        target"https://api.github.com",
        pathRewrite: {
          "^/api""",
        },
        changeOrigintrue,
      },
    },
},
复制代码

proxy 配置说明如下:

https://localhost:8088/api/users’ 变成 'api.github.com/users'

  • changeOrigin: 设置为 true,会将 http 请求中的 host 字段改为非 localhost 的请求 ip。因为有些服务器针对 host 字段为 localhost 的请求时,不会正常响应。

devtool 设置

source map

开发过程中的代码和实际生产运行的代码会存在较大差异,Source Map 是源码和编译后代码之间的映射。当生产代码出现问题时,通过 source map 我们可以定位到出错的源代码。

在压缩后的 JS 中添加注释,注释中指定 source map 的文件路径即可。

//# sourceMappingURL=jquery-3.4.1.min.map
复制代码

开启 source map,webpack.config.js 添加以下配置即可:

module.exports = {
  ...
  devtool: "source-map",
  ...
};
复制代码

eval 模式

浏览器控制台中以 eval 方式执行 js 代码,通过 sourceURL 可以指定 JS 执行环境所属的上下文。

eval('console.log(123) //# sourceURL=./foo/bar.js')
复制代码

webpack 设置 devtool 为 eval 值,打包后,报错源码会直接定位到 webpack mode 为 development 的模块代码中。

module.exports = {
  ...
  devtool: "eval",
  ...
};
复制代码

配置项

webpack 提供 20 余种 devtool 选项供我们选择。具体可参考:webpack.js.org/configurati… webpack 创建多个打包配置,针对 devtool 的不同选项生成不同的打包页面。并在源码中添加异常代码。访问打包页面后控制台的异常报错信息,再定位异常代码位置。

webpack.config.js 如下:

const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");

const allModes = [
  "eval",
  ...
  "hidden-cheap-source-map",
  "hidden-cheap-module-source-map",
  "hidden-source-map",
];

module.exports = allModes.map((mode) => {
  return {
    mode"none",
    devtool: mode,
    entry"./src/main.js",
    output: {
      filename`js/${mode}.js`,
    },
    module: {
      rules: [
        {
          test/.js$/,
          use: {
            loader"babel-loader",
            options: {
              presets: ["@babel/preset-env"],
            },
          },
        },
      ],
    },
    plugins: [
      new HtmlWebpackPlugin({
        template"./src/index.html",
        filename: path.join(__dirname, `./dist/html/${mode}.html`),
      }),
    ],
  };
});
复制代码

配置项说明如下:

  • eval 模式:模块代码会被放到 eval 函数中执行。只能定位到模块代码所在文件,无法具体到实际位置。
  • eval-source-map:可以定位到具体代码的行和列。
  • cheap-eval-source-map: 只能定位到具体的行,没有具体的列信息,显示的是代码经过 ES6 转换的结果,空行也会被剔除。
  • cheap-module-eval-source-map: 打包后的代码不会被 loader 进行处理,在开发阶段建议使用此模式。
  • inline-source-map: 打包后 js 文件引入 map 文件的方式为 DataURL 形式,不是直接文件形式,基本用不上。
  • hidden-source-map: 在开发工具中无法查看 source-map 文件信息,是因为在 js 文件中没有进行引用。开发第 3 方包的时候可以用到。
  • nosources-source-map: 只能查看源代码的信息,在开发者工具中看不到具体源代码的位置和内容。

总结:

以不同关键字开头的配置项表示不同类型的模块代码压缩配置。

  • eval-标识是否使用 eval 执行模块代码
  • cheap: source-map 是否包含行信息
  • module: 是否能够得到 Loader 处理之前的源代码

使用场景:

  • 开发模式下,使用 cheap-module-eval-source-map 模式。
  • 生产模式:使用 none,不生成 source-map。避免暴露源代码,这种情况不能定位到具体源码位置。
  • nosources-source-map: 可以定位到代码位置,但是在开发者工具中看不到源码内容。

HMR(Hot Module Replacement)

模块热替换,实时替换应用程序中的某个模块,不改变应用的运行状态。简单点说,当我们对某个模块进行修改后,页面只更新修改后的模块,其他模块不受影响。通过这种方式可以避免页面刷新导致的状态丢失。在 Vue 或 React 等前端框架中,已内置了 HMR 的特性。下面介绍的是非前端框架的 HMR 处理。

开启 HMR

HMR 已集成在 webpack-dev-server 中,使用 webpack-dev-server 命令时添加 --hot 参数即可启用。

yarn webpack-dev-server --hot
复制代码

webpack.config.js 添加配置

const webpack = require("webpack");

module.exports = {
    devServer: {
        hottrue,
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
    ]
}
复制代码

此配置添加后,修改样式文件不会影响运行状态,因为 style-loader 已经内置了样式的 HMR 处理。但是修改 JS 文件还是会导致页面自动刷新。这时候我们需要手动处理。

在 bundle.js 中搜索 "module.hot.accept" 可以找到关键字:

module.hot.accept(
      /*! !!../node_modules/css-loader/dist/cjs.js!./global.css */ "./node_modules/css-loader/dist/cjs.js!./src/global.css",
      __WEBPACK_OUTDATED_DEPENDENCIES__ => { /* harmony import */ _node_modules_css_loader_dist_cjs_js_global_css__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! !!../node_modules/css-loader/dist/cjs.js!./global.css */ "./node_modules/css-loader/dist/cjs.js!./src/global.css");
(function ({
        if (!isEqualLocals(oldLocals, isNamedExport ? _node_modules_css_loader_dist_cjs_js_global_css__WEBPACK_IMPORTED_MODULE_6__ : _node_modules_css_loader_dist_cjs_js_global_css__WEBPACK_IMPORTED_MODULE_6__["default"].locals, isNamedExport)) {
                module.hot.invalidate();

                return;
              } 
复制代码

手动处理 JS 模块 HMR

在 module.hot.accept 函数的回调参数中添加处理逻辑。

let lastEditor = editor;
module.hot.accept("./editor"() => {
  console.log("editor module updated...", lastEditorText);

  const lastEditorText = editor.innerHTML;
  document.body.removeChild(lastEditor);
  const newEditor = createEditor();
  newEditor.innerHTML = lastEditorText;
  document.body.append(newEditor);
  lastEditor = newEditor;
});
复制代码

手动处理图片模块 HMR

// 手动图片模块
module.hot.accept("./editor-wrapper-bg.jpg"() => {
  img.src = editorWrapperBg;
  console.log("img module updated...");
});
复制代码

注意事项

  • 在 hot 模式下,手动添加的 HMR 代码出现异常时,会导致页面自动刷新。
  • 使用 only 模式,添加配置后重启解决 hot 模式产生的问题。
devServer: {    
    hot"only"
}
复制代码

总结

没啥总结的,以上...

文章分类
前端
文章标签