Webpack 热加载插件的实现原理

概述

最近面试被问到了 webpack 热加载的实现原理,所以去研究了一下,记录下来供以后开发时参考,相信对其它人也有用。

热加载原理

这一部分我没有去看源码,只是看了别人的分析理清了一下思路,参考资料:

Webpack HMR 原理解析

从零实现webpack热更新HMR

主要流程如下:

1.首先 webpack-dev-server 会建立一个服务器,并且和浏览器建立 websocket 通信。

2.服务器监听文件变化,当文件变化的时候,会重新打包相应的 chunk,然后向浏览器发射 hash 和 ok 事件,通知浏览器对应的 chunkid 等信息。

3.浏览器监听 hash 和 ok 事件,再接受信息之后,通过 jsonp 向服务端请求对应的热更新代码。

4.最后浏览器把 jsonp 获得的代码注入到 html 的 head 里面去执行,从而实现了对应的模块替换。

vue-loader 是怎么热更新的

vue-loader的源码如下:

const hotReloadAPIPath = JSON.stringify(require.resolve('vue-hot-reload-api'))

const genTemplateHotReloadCode = (id, request) => {
  return `
    module.hot.accept(${request}, function () {
      api.rerender('${id}', {
        render: render,
        staticRenderFns: staticRenderFns
      })
    })
  `.trim()
}

exports.genHotReloadCode = (id, functional, templateRequest) => {
  return `
/* hot reload */
if (module.hot) {
  var api = require(${hotReloadAPIPath})
  api.install(require('vue'))
  if (api.compatible) {
    module.hot.accept()
    if (!api.isRecorded('${id}')) {
      api.createRecord('${id}', component.options)
    } else {
      api.${functional ? 'rerender' : 'reload'}('${id}', component.options)
    }
    ${templateRequest ? genTemplateHotReloadCode(id, templateRequest) : ''}
  }
}
  `.trim()
}

可以看到,它其实在编译单文件组件的时候,会把热加载相关的代码注入到编译后的代码里面去,从而实现热更新的。

vue-loader 热更新的状态怎么保留

通过vue-loader说明,我们可以看到对于状态的保留规则:

1.当编辑一个 template 的时候,组件会重新渲染。(rerender)

2.当编辑一个 script 的时候,组件会就地销毁并重新创建。(reload)

3.当编辑一个 style 的时候,通过 vue-style-loader 自行热重载,并不影响状态。

vue-loader 源码里面的 api 是什么

里面的 api 其实是 vue-hot-reload-api,这个库其实就控制了组件在重新渲染(rerender)和重新创建(reload)的时候做的事。

module.hot.accept

通过上面的源码可以看到,module.hot.accept其实并没有传递参数,这是什么用法呢?通过查阅webpack官方文档,这个通常写在对应的模块里面,作用是标明这个模块可以热加载,在这个模块改变的时候会热加载这个模块。

所以,vue-loader 的热重载原理其实是:打包后的单文件组件在修改之后,会借用 devserver 的热重载功能更新单文件组件,并且通过监听module.hot.data的变化,使用module.hot.accept中的 api 对组件进行重新渲染或者重新创建。