Webpack 4 教程 - 6. 增强开发体验

我们今天要涉及到把mode属性设置为development(时的情形)。这会让webpack自动配置成更适合开发(时用)。此外,我们也将涉及到webpack-dev-server和webpack-serve - 其包括了热模块替换(译注:webpack-serve现在已不推荐使用,用好webpack-dev-server就够了,详见https://github.com/webpack-co...)。开始吧!

Webpack 4教程: 一个开发服务器

增强开发体验的一个前置步骤是以观察模式运行webpack,试着以webpack --watch启动,现在,每当你做了些修改,webpack就会重建你的项目且输出相应的信息(译注: 原文是”and outputs he code“,小小疑惑一下)。而Webpack-dev-server就更强大了,做得更多。(使用Webpack-dev-server时)输出文件不是写到磁盘的目标目录中,而是在内存里处理。构建之后,这些输出启动了一个本地服务器。

运行webpack-dev-server

首先要安装

npm install webpack-dev-server

其次在package.json中添加脚本

"scripts": {
    "build": "webpack",
    "start": "webpack-dev-sever"
}

现在启动它,只需输入 npm start(译注:在前面的教程中,作者写的启动脚本的命令形如npm run build,实际上在较高版本的npm中,可以省略这个run了)。你就会看到下面的信息:

「wds」: Project is running at http://localhost:8080/

剩下的事情就只需你在浏览器中打开http://localhost:8080/了

每当对代码做了些修改,站点就会得到更新(因为重建项目了啊),刷新页面之后,你就会看到这些改变了的效果。

热模块替换

为了使开发体验更佳,可以使用热模块替换来避免总是手动刷新。比如,每当你改变了一些样式,你无需重载整个应用来观察改变的效果。

注意,在教程4中我们用到了MiniCssExtractPlugin这个插件,到写这篇文章的时候为止,MiniCssExtractPlugin还不支持热模块替换。更多信息请参考GitHub issue。因此现在你在开发模式下还需继续要用到style-loader这个加载器。(译注:style-loader的作用就是把你的样式插入到<style>标签中,在开发模式下,输出都是放在内存中,因此把css抽取出来还是放在<style>标签中,都无所谓了,不用考虑太多)

正规情况下,webpack-dev-server也是通过同一个配置文件(即webpack.config.js)来运行。能在webpack.config.js中添加一个叫devServer的参数来设置附加的配置选项。我们在其上启用热模块替换。

devServer: {
    hot: true,
}

剩下最后一件事情就是将webpack.HotModuleReplacementPlugin写到plugins属性里。

// webpack.config.js

const webpack = require('webpack');

module.exports = {
    devServer: {
        hot: true
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ]
}
注意启动webpack-dev-server时加上 -hot参数也会加载HotModuleReplacementPlugin插件。你要是不经意加载了两次,可能会给你带来一些麻烦。

现在工作于CSS中已经非常奏效了,但在javascript中,我们还需要进一步。

index.js

import { divide } from "./divide";

console.log(`6 / 2 = ${divide(6,2)}`);

if(module.hot) {
    module.hot.accept();
}
// divider.js

export function divide(a, b) {
    return a/b;
}

运行module.hot.accept()将使得模块可替换。这也会在它导出的模块身上起效果。上面的代码例子意味着在index.js中运行了accept(),则它导出的divide模块也变成可替换的了。(译注:我们在一些开箱即用的框架中往往看到作者为你配了一个类似dev-client.js的文件,这个文件的作用就是让后面被它导出的模块能够热替换)。

函数module.hot.accept()可以附加配置来运行,如果感兴趣,请参阅文档

使用HotModuleReplacementPlugin插件时,如果在输出文件名中用了chunkhash,会碰到问题。因此,最好HotModuleReplacementPlugin只用在开发服务器中(且不使用chunkhash)。

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

const isDevServer = require.main.filename.includes('webpack-dev-server');
const plugins = [
    new HtmlWebpackPlugin({ template: './src/index.html' }),
];
if(isDevServer) {
    plugins.push(new webpack.HotModuleReplacementPlugin());
}

module.exports = {
    output: {
        filename: isDevServer ? '[name].bundle.js' : '[name].[chunkhash].bundle.js',
        path: path.resolve(__dirname, 'dist'),
    },
    plugins,
    devServer: {
        hot: true
    }
}

webpack-serve

(译注:因为webpack-serve官方已不推荐使用,我们略过这一节的巴拉巴拉)

mode: “development”

在前面的教程中,我们已经涉及了mode为production时的情况。现在我们来看看当其值为development时,对我们做了什么。

DefinePlugin插件

就象前面所说的,这个插件创建全局常量编译时解析。

更多细节讲述请参阅教程5。

现在其值是 "process.env.NODE_ENV": JSON.stringify("development") :

module.exports = {
    mode: "development",
    // using mode: "development" attaches the following configuration:
    plugins: [
        new webpack.DefinePlugin({
            "process.env.NODE_ENV": JSON.stringify("development")
        }),
    ]
}

NamedModulesPlugin插件

这是在设置mode:"development"时另一个默认加载的内置插件。在使用热模块替换时起作用。由于NamedModulesPlugin我们能看到替换模块的相对路径。

[WDS] App updated. Recompiling...
[WDS] App hot update...
[HMR] Checking for updates on the server...
[HMR] Updated modules:
[HMR]  - ./src/style.css
[HMR] App is up to date

否则,我们只能看到用模块id来代表某模块了(比如./src/style.css)。

NamedChunksPlugin插件

NameChunksPlugin插件的作用与NamedModulesPlugin插件有点相似。有了NamedChunksPlugin插件,不但你的模块有了名字,chunks也有了。 当在浏览器中运行应用时,你能在window.webpackJsonp属性中找到它们。

NamedModulesPlugin和NamedChunksPlugin另外一个优势是你无需使用模块id(ids),ids在增删依赖时可能会发生变化。因为ids或名字用在实际输出的文件里,即使你模块的内容没有变化,改变ids或名字也会改变文件的hash。所以使用NamedModulesPlugin和NamedChunksPlugin插件将帮助你更好的应付浏览器缓存机制。我们比较一下输出代码。

// Without NamedModulesPlugin and NamedChunksPlugin

(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[2],{
  /***/ 6:
  (...) // divide.js module output code

  /***/ 7:
  (...) // substract.js module output code
]);
// Using NamedModulesPlugin and NamedChunksPlugin:

(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["utilities~main"],{
  /***/ "./src/utilities/divide.js":
  (...) // divide.js module output code

  /***/ "./src/utilities/substract.js":
  (...) // substract.js module output code
]);

devtool

除了加载了一些插件,设置mode: "development"(译注:原文误为production)时还多做了一件事,它通过设置devtool属性为eval来启用源码映射(Source Maps)。

module.exports = {
    mode: "development",
    // using mode: "development" attaches the following configuration:
    devtool: "eval"
}

转换,丑化,打包你的代码会让用户(体验)更爽,在这些措施之后,代码变小了,执行变快了。然而调式这些代码变困难了。因此,引入了源码映射机制。它们映射输出代码到源文件,使得调式器用起来更简单及能在源代码中打断点。我们将在接下来的教程中深挖源码映射,但如果你现在就想去定制,请查阅文档

小结

Webpack是开发现代web应用强有力的工具。它不但优化你的产品代码,也能通过定制的方式来提升开发体验。这一次我们涉及到了运行一个开发服务器和设置mode属性为development。我们也学习了如何使用热模块替换。这些结合起来会大大帮助你更快更容易的开发应用。

相关推荐