webpack学习配置笔记(二)

千里之行,始于足下!

模块热替换

模块热替换(Hot Module Replacement 或 HMR)是 webpack 提供的最有用的功能之一。它允许在运行时更新各种模块,而无需进行完全刷新。

HMR 不适用于生产环境,这意味着它应当只在开发环境使用。更多详细信息,请查看生产环境构建指南。

启用HMR

启动这个功能我们只需要更新一下webpack-dev-server的配置就可以了。

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
    // entry: './src/index.js',
    entry:{
        // app:'./src/index.js',
        // print:'./src/print.js'
        app: './src/index.js'
    },
    output: {
        // filename: 'bundle.js',
        filename:'[name].bundle.js',
        path: path.resolve(__dirname, 'dist'),
        publicPath:'/'
    },
    module:{
        rules:[
            ...
        ]
    },
    plugins:[
        new CleanWebpackPlugin(['dist']),//清理的文件夹
        new HtmlWebpackPlugin({
            title:'Output Management'
        }),
        new webpack.NamedModulesPlugin(),
        new webpack.HotModuleReplacementPlugin()
    ],
    devtool:'inline-source-map',
    devServer:{
        contentBase:'./dist',
        hot: true
    }
};

其中我们添加了两个webpack的插件,NamedModulePluginHotModuleReplacementPluginNamedModulePlugin可以更容易查看要修补的(patch)的依赖,
然后通过npm start 命令来启动并运行dev server.

现在,我们来修改 index.js 文件,以便当 print.js 内部发生变更时可以告诉 webpack 接受更新的模块。

import _ from 'lodash';
import './style.css';
import printMe from './print';
function component() {
    var element = document.createElement('div');
    var btn = document.createElement('button');
    element.innerHTML = _.join(['Hello', 'webpack'], ' ');
    btn.innerHTML = 'Click Me and check the console!';
    btn.onclick = printMe;
    element.appendChild(btn);
    return element;
}

document.body.appendChild(component());
//添加的部分
if (module.hot) {
    module.hot.accept('./print.js', function () {
        console.log('Accepting the updated printMe module!');
        printMe();
    })
}

更改print.js文件中的输出内容。

console.log('Updating print.js...')

我们看一下console

Hash: 0fa02a6e57bd19fe1c28
Version: webpack 3.9.1
Time: 753ms
                               Asset       Size  Chunks                    Chunk Names
e18f911935d945f7f5b0.hot-update.json   44 bytes          [emitted]         
                       app.bundle.js    2.39 MB       0  [emitted]  [big]  app
0.38e076a538bfd3d7f51e.hot-update.js    1.05 kB       0  [emitted]         app
38e076a538bfd3d7f51e.hot-update.json   43 bytes          [emitted]         
                          index.html  193 bytes          [emitted]         
[./node_modules/webpack/hot ^\.\/log$] ./node_modules/webpack/hot nonrecursive ^\.\/log$ 170 bytes {0} [built]
[./src/print.js] ./src/print.js 128 bytes {0} [built]
    + 33 hidden modules
Child html-webpack-plugin for "index.html":
                                   Asset      Size  Chunks             Chunk Names
    e18f911935d945f7f5b0.hot-update.json  44 bytes          [emitted]  
     + 1 hidden asset
       4 modules
webpack: Compiled successfully.

通过 Node.js API

当使用 webpack dev server 和 Node.js API 时,不要将 dev server 选项放在 webpack 配置对象(webpack config object)中。而是,在创建选项时,将其作为第二个参数传递。例如:

new WebpackDevServer(compiler, options)

想要启用 HMR,还需要修改 webpack 配置对象,使其包含 HMR 入口起点。webpack-dev-server package 中具有一个叫做 addDevServerEntrypoints 的方法,你可以通过使用这个方法来实现。这是关于如何使用的一个小例子:

dev-server.js

const webpackDevServer = require('webpack-dev-server');
const webpack = require('webpack');

const config = require('./webpack.config.js');
const options = {
  contentBase: './dist',
  hot: true,
  host: 'localhost'
};

webpackDevServer.addDevServerEntrypoints(config, options);
const compiler = webpack(config);
const server = new webpackDevServer(compiler, options);

server.listen(5000, 'localhost', () => {
  console.log('dev server listening on port 5000');
});

详情请看:模块热更新

Tree Shaking

tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块系统中的静态结构特性,例如 import 和 export。这个术语和概念实际上是兴起于 ES2015 模块打包工具 rollup。就是在打包的时候精简代码的。

现在我们试一下,首先我们添加一个通用模块。在项目中添加src/math.js,添加两个导出的函数。

/**
 * Created by jfy on 2017/12/4.
 * 千里之行,始于足下!
 */

export function square(x) {
    return x * x;
}

export function cube(x) {
    return x * x * x;
}

接着更新入口,使用其中的一个新方法,并且为了简单,我们把lodash删除:
src/index.js

import './style.css';
import printMe from './print';
import {cube} from './math.js';
function component() {
   
    var element = document.createElement('pre');

    element.innerHTML = ['Hello webpack!','5 cubed is qeual to '+cube(5)].join('\n\n');
    return element;
}

document.body.appendChild(component());

注意,我们并未从 src/math.js 模块中 import 导入 square 方法。这个功能是所谓的“未引用代码(dead code)”,也就是说,应该删除掉未被引用的 export。现在让我们运行我们的npm 脚本 npm run build,并检查输出的 bundle:

/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* unused harmony export square */
/* harmony export (immutable) */ __webpack_exports__["a"] = cube;
function square(x) {
  return x * x;
}

function cube(x) {
  return x * x * x;
}

精简输出

我们已经通过 import and export 语法,标识出了那些“未引用代码(dead code)”,但是我们仍然需要从 bundle 中删除它们。要做到这一点,我们将添加一个能够删除未引用代码(dead code)的压缩工具(minifier) - UglifyJSPlugin - 在配置对象中添加……

npm i --save-dev uglifyjs-webpack-plugin

然后将其添加到我们的配置中:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
+ const UglifyJSPlugin = require('Uglifyjs-webpack-plugin');
module.exports = {
    // entry: './src/index.js',
    entry:{
        // app:'./src/index.js',
        // print:'./src/print.js'
        app: './src/index.js'
    },
    output: {
        // filename: 'bundle.js',
        filename:'[name].bundle.js',
        path: path.resolve(__dirname, 'dist'),
        publicPath:'/'
    },
    module:{
        rules:[
            ...
        ]
    },
    plugins:[
        new CleanWebpackPlugin(['dist']),//清理的文件夹
        new HtmlWebpackPlugin({
            title:'Output Management'
        }),
        new webpack.NamedModulesPlugin(),
        new webpack.HotModuleReplacementPlugin(),
+       new UglifyJSPlugin()
    ],
    devtool:'inline-source-map',
    devServer:{
        contentBase:'./dist',
        hot: true
    }
};

准备就绪后,然后运行另一个命令 npm run build,看看输出结果有没有发生改变。

你发现 dist/bundle.js 中的差异了吗?显然,现在整个 bundle 都已经被精简过,但是如果仔细观察,则不会看到 square 函数被引入,但会看到 cube 函数的修改版本(function r(e){return eee}n.a=r)。现在,随着 tree shaking 和代码压缩,我们的 bundle 减小几个字节!虽然,在这个特定示例中,可能看起来没有减少很多,但是,在具有复杂的依赖树的大型应用程序上运行时,tree shaking 或许会对 bundle 产生显著的体积优化。

警告

请注意,webpack 本身并不会执行 tree-shaking。它需要依赖于像 UglifyJS 这样的第三方工具来执行实际的未引用代码(dead code)删除工作。有些情况下,tree-shaking 可能不会生效。例如,考虑以下模块:

transforms.js

import * as mylib from 'mylib';

export const someVar = mylib.transform({
  // ...
});

export const someOtherVar = mylib.transform({
  // ...
});

index.js

mport { someVar } from './transforms.js';

// 使用 `someVar`...

在上面的代码中,webpack 不能确定是否调用 mylib.transform 会引发任何副作用(side-effects)。因此,为保险起见,将在 bundle 代码中保留 someOtherVar。

一般来说,当一个工具不能保证某些特定的代码路径(path)不会导致副作用(side-effects)时,即使你确信它不应该存在生成的 bundle 中,但这个代码仍然会保留。常见的情况有:从第三方模块中调用一个函数,webpack 和/或 压缩工具(minifier)无法检查此模块;从第三方模块导入的函数被重新导出,等等。

本指南中使用的代码假设你使用 UglifyJS 插件来执行 tree-shaking。然而,还有其他工具,如 webpack-rollup-loaderBabel Minify Webpack Plugin,根据你的设置它们可能产生不同的结果。

结论

为了学会使用 tree shaking,你必须……

使用 ES2015 模块语法(即 import 和 export)。
引入一个能够删除未引用代码(dead code)的压缩工具(minifier)(例如 UglifyJSPlugin)。
你可以将应用程序想象成一棵树。绿色表示实际用到的源码和 library,是树上活的树叶。灰色表示无用的代码,是秋天树上枯萎的树叶。为了除去死去的树叶,你必须摇动这棵树,使它们落下。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335