hot-reloading

本文介绍了ReactNative中的热加载功能,详细解释了热模块更换(HMR)的工作原理及其在保持应用状态的同时进行实时更新的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

介绍热加载

React Native的目标是为您提供最佳的开发人员体验。其中很大一部分是您保存文件并能够看到更改所需的时间。我们的目标是让这个反馈循环不到1秒钟,即使您的应用程序增长。

我们通过三个主要功能接近这个理想:

  • 使用JavaScript作为语言没有长的编译周期时间。
  • 实施一个名为Packager的工具,将工具将es6 / flow / jsx文件转换为VM∂可以理解的普通JavaScript。它被设计为将内部中间状态保留在内存中以实现快速增量更改并使用多个内核的服务器。
  • 构建一个名为Live Reload的功能,可在保存时重新加载应用程序。

在这一点上,开发人员的瓶颈不再是重新加载应用程序所需的时间,而是失去了应用程序的状态。一个常见的情况是处理一个远离启动屏幕的多个屏幕的功能。每次重新加载时,您都必须一次又一次地点击相同的路径,以恢复您的功能,使该周期长达几秒钟。

热加载

热加载的想法是保持应用运行,并注入您在运行时编辑的文件的新版本。这样,如果您调整UI,您不会失去任何特别有用的状态。

简单实现

现在我们已经看到了为什么我们想要它和如何使用它,有趣的部分开始:它是如何实际工作的。

热加载建立在热模块更换或HMR 功能之上。它是Webpack首次引入,我们在React Native Packager内部实现了它。HMR使Packager监视文件更改,并将HMR更新发送到应用程序中包含的薄型HMR运行时间。

简而言之,HMR更新包含更改的JS模块的新代码。当运行时接收到它们时,它将使用新的代码替换旧模块的代码:

img

HMR更新包含的不仅仅是我们要更改的模块代码,因为替换它,运行时不足以接收更改。问题是模块系统可能已经缓存了我们要更新的模块的导出。例如,说你有一个由这两个模块组成的应用程序:

// log.js
function log(message) {
  const time = require('./time');
  console.log(`[${time()}] ${message}`);
}

module.exports = log;

// time.js
function time() {
  return new Date().getTime();
}

module.exports = time;

该模块log打印出提供的消息,包括模块提供的当前日期time

当应用程序捆绑时,React Native使用该__d功能在模块系统上注册每个模块。对于这个应用程序,在许多__d定义中,将有一个log

__d ('log'function () {   ... // module's code } );

这种调用将每个模块的代码包装成一个匿名函数,我们通常将其称为工厂函数。模块系统运行时跟踪每个模块的工厂功能,无论是否已经执行,以及执行(导出)的结果。当需要模块时,模块系统首次提供已缓存的导出或执行模块的出厂功能,并保存结果。

所以说你开始你的应用程序并要求log。在这一点上,也log没有time执行任何工厂功能,因此没有缓存出口。然后,用户修改time以返回日期MM/DD

// time.js
function bar() {
  var date = new Date();
  return `${date.getMonth() + 1}/${date.getDate()}`;
}

module.exports = bar;

Packager会将时间的新代码发送到运行时(步骤1),并且当log最终需要导出的函数被执行时,它将通过time更改执行此操作(步骤2):

img

现在说log要求的代码time作为顶级要求:

const time = require('./time'); // top level require

// log.js
function log(message) {
  console.log(`[${time()}] ${message}`);
}

module.exports = log;

什么时候log需要,运行时将缓存其导出和time一个。(步骤1)。然后,time修改后,HMR进程在替换time代码后不能简单完成。如果是这样,当log被执行时,它将使用time(旧代码)的缓存副本执行。

为了log获取time更改,我们需要清除缓存的导出,因为它所依赖的模块之一被热交换(步骤3)。最后,当log再次需要时,其工厂功能将被执行,要求time并获得新的代码。

img

HMR API

React Native中的HMR通过引入hot对象来扩展模块系统。这个API是基于Webpack的。该hot对象暴露了一个调用的函数,accept它允许您定义当模块需要热插拔时将执行的回调。例如,如果我们改变time代码如下,每次我们节省时间,我们将在控制台中看到“更改时间”

// time.js
function time() {
  ... // new code
}

module.hot.accept(() => {
  console.log('time changed');
});

module.exports = time;

请注意,只有在极少数情况下,您需要手动使用此API。对于最常见的用例,Hot Reloading应该开箱即用。

HMR运行时

正如我们以前看到的,有时仅接受HMR更新是不够的,因为使用热插拔的模块可能已被执行,并且其导入已被缓存。例如,假设电影应用程序示例的依赖关系树具有MovieRouter依赖于MovieSearchMovieScreen视图的顶级,这取决于前面示例中的模块logtime模块:

img

如果用户访问电影的搜索视图而不是另一个,除了所有模块都MovieScreen将缓存导出。如果对模块进行了更改time,运行时将必须清除其导出log以接收time更改。该过程不会在那里完成:运行时将重复此过程,直到所有的父母都被接受。所以,它会抓住依赖的模块log并尝试接受它们。因为MovieScreen它可以保释,因为它还没有被要求。因此MovieSearch,它必须清除其出口并递归处理其父母。最后,它会做同样的事情MovieRouter,完成它,因为没有模块依赖它。

为了运行依赖关系树,运行时间在HMR更新中从Packager接收到逆依赖关系树。对于这个例子,运行时将会收到像这样一个JSON对象:

{
  modules: [
    {
      name: 'time',
      code: /* time's new code */
    }
  ],
  inverseDependencies: {
    MovieRouter: [],
    MovieScreen: ['MovieRouter'],
    MovieSearch: ['MovieRouter'],
    log: ['MovieScreen', 'MovieSearch'],
    time: ['log'],
  }
}

反应组件

反应组件有点难以使用热加载。问题是,我们不能简单地用旧的代码替换旧的代码,因为我们会松开组件的状态。对于阵营的Web应用程序,达恩·阿勃拉莫夫实施了巴贝尔变换使用的WebPack的HMR API来解决这个问题。简而言之,他的解决方案通过在转换时间为每个单一的React组件创建一个代理。代理保存组件的状态,并将生命周期方法委托给实际组件,这些组件是我们热加载的组件:

img

除了创建代理组件之外,变换还accept使用一段代码来定义函数,以强制React重新渲染组件。这样,我们可以重新加载渲染代码,而不会丢失任何应用程序的状态。

React Native附带的默认变压器使用babel-preset-react-native,它被配置为使用react-transform与使用Webpack的React Web项目相同的方式。

Redux商店

要在Redux商店上启用热重新加载,您只需要使用HMR API,就像使用Webpack的Web项目所做的一样:

// configureStore.js
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducer from '../reducers';

export default function configureStore(initialState) {
  const store = createStore(
    reducer,
    initialState,
    applyMiddleware(thunk),
  );

  if (module.hot) {
    module.hot.accept(() => {
      const nextRootReducer = require('../reducers/index').default;
      store.replaceReducer(nextRootReducer);
    });
  }

  return store;
};                                   

当您更改reducer时,接受该reducer的代码将被发送到客户端。然后,客户端会意识到,reducer不知道如何接受自己,所以它会查找引用它的所有模块并尝试接受它们。最终,流程将到达单个存储区,该configureStore模块将接受HMR更新。

结论

如果您有兴趣帮助热加载变得更好,我鼓励您阅读Dan Abramov在未来的热加载中发表的文章,并做出贡献。例如,Johny Days将使其与多个连接的客户端协同工作。我们依靠你们来维护和改进这个功能。

使用React Native,我们有机会重新思考我们构建应用程序的方式,以使其成为一个伟大的开发人员体验。热加载只是一个难题,还有什么其他疯狂的黑客可以做得更好?

作者:李柿贤;原文地址
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值