webpack-dev-middleware和wepack-hot-middleware的使用

1,435 阅读4分钟

我们在开发开发过程中,可以使用webpack-dev-server来作为我们的开发服务器,webpack-dev-server可以在我们的文件发生变化的时候,进行热替换或自动刷新。

那么我们已经有了webpack-dev-server为什么还需要webpack-dev-middleware和wepack-hot-middleware呢?

因为webpack-dev-server是封装好的webpack-dev-middleware和wepack-hot-middleware,我们在webpack.config.js里配置webpack-dev-server就可以使用开发服务器,webpack-dev-server里面已经处理好了对webpack-dev-middleware和wepack-hot-middleware的使用。

但是有时候我们可能不想要使用webpack-dev-server,我们想要定制化自己的node服务。这个时候我们就需要使用到webpack-dev-middleware和wepack-hot-middleware了。

webpack

首先我们需要知道webpack除了可以作为指令使用,也是可以在node里作为函数直接使用的。 如下:

const webpack = require('webpack');
const config = require('./webpack.config');

// 在node里直接调用webpack函数进行打包,和使用命令行打包是一样的
const compile = webpack(config, (error) => {
    if (error) {
        console.log(error);
    }
});

所以我们想要对资源进行打包的时候,可以直接在node脚本里面使用webpack进行打包。

webpack-dev-middleware

webpack-dev-middleware是一个中间件,webpack负责打包,webpack-dev-middleware负责监听文件变化,并重新调用compiler进行打包,且打包的资源会放到内存中。

const express = require('express');
const webpack = require('webpack');
const WebpackDevMiddleware = require('webpack-dev-middleware');
const config = require('./webpack.config');

// 获取webpack的编译器,这里注意一下webpack只有1个config参数,没有callback参数
// 只有1个参数的情况,只会返回compiler,不会将打包的内容输出到硬盘上
// webpackDevMiddleware会监听文件变化,且将打包出的输出到内存
// 当express接收到请求时,webpackDevMiddleware会将对应的资源从内存中读取踹并返回
const compile = webpack(config);
const app = express();

const webpackDevMiddleware = WebpackDevMiddleware(compile, {
    // 使用webpack-dev-middleware需要设置publicPath路径,这个路径是资源在内存中存放的路径
    // 比如路径为/dist/,然后打包出来的资源名字为 bundle.js
    // 则该资源的访问路径为 localhost:port/dist/bundle.js
    // 这时候webpack.config.output里不需要配置任务路径
    publicPath: '/dist/'
});

app.use(webpackDevMiddleware);

const port = 5123;

const uri = `http://localhost:${port}`;
webpackDevMiddleware.waitUntilValid(() => console.log(`Listening at ${uri}.\n`));

app.listen(port);

webpack-hot-middleware

热替换就是说当我们编写的代码变化的时候,变化的代码会同步到浏览器,且浏览器不用重新刷新,之前的状态都保留。

使用webpack-hot-middleware我们的node启动脚本变成这样:

const express = require('express');
const webpack = require('webpack');
const WebpackDevMiddleware = require('webpack-dev-middleware');
// 新增
const WebpackHotMiddleware = require('webpack-hot-middleware');
const config = require('./webpack.config');

const compile = webpack(config);
const app = express();

const webpackDevMiddleware = WebpackDevMiddleware(compile, {
  publicPath: '/dist/'
});
// 新增
const webpackHotMiddleware = WebpackHotMiddleware(compile, {
  log: false,
  heartbeat: 2000,
});

app.use(webpackDevMiddleware);
// 新增
app.use(webpackHotMiddleware);

const port = 5123;

const uri = `http://localhost:${port}`;
webpackDevMiddleware.waitUntilValid(() => console.log(`Listening at ${uri}.\n`));

app.listen(port);

这样我们服务端的热替换代码就写好了,热替换功能需要服务端和客户端进行通信,也就是进行websocket连接,客户端也需要被注入热替换的代码,所以我们需要在entry里面加入客户端热替换的代码,如下:

module.exports = {
  mode: 'development',
  entry: {
    index: [
      // 需要加入这一行代码,在客户端文件里注入热替换相关的代码
      'webpack-hot-middleware/client?path=/__webpack_hmr&noInfo=true&reload=true',
      path.resolve(__dirname, './index.js')
    ]
  },
  plugins: [
    // 我们的插件里也需要添加HotModuleReplacementPlugin这个插件
    new webpack.HotModuleReplacementPlugin(),
  ]
}

这样我们的热替换配置就好了,可是热替换功能配置好了之后,就可以正常的进行热替换了吗,其实这里我们只是搭建好了热替换需要的框架,当代码变化的时候,如何具体实现热替换是需要我们自己编写的。例如我们有个index.js文件,index.js引用了a.js,当a.js变化的时候我们需要做热替换:

import { print } from './a.js';

print();

// 接收热更新输出,只有accept才能被更新
// 代码里必须加这一段才能监听a.js的变化,且在变化的时候执行热更新函数,
// 热更新函数就是我们下面写的这个回调函数,会打印`a.js 变化了`
// 留意这里的热更新具体执行的函数是需要我们自己写的
if (module.hot) {
  module.hot.accept('./a.js', () => {
    console.log('a.js 变化了');
  });
}

上面说了热更新的函数都是需要我们自己写的,可是我们写的Vue代码,我们更改了代码后,就直接执行了热更新,我们也没有写热更新具体的代码呀?这是因为vue-loader里已经帮你写好了针对Vue模块变化的时候相关的热更新代码,所以热更新功能直接可以用了,否则还是要自己写的。

参考文档: segmentfault.com/a/119000002…