https://facebook.github.io/react-native/docs/debugging.html
热加载
RN的目标是极致的开发体验,修改文件后能在1秒内看到变化,通过以下三个特征来实现:
- js的编译时间短
- 本地packager服务器可以把源文件(ES6,JSX,flow等)转换为通用的js代码,使用多个核心,转换中用到的中间代码保存在内存中,用于下次更快的增量修改
- 内置了live reload自动刷新
有了以上功能,剩余的瓶颈就是刷新后会丢失app当前的状态,手动还原了当前要调试的状态需要花费比较多时间。解决这个问题需要在app运行的时候动态注入新的代码,这样app的状态就不会丢失了。
因为js是有状态,所以热加载并不是十分完美。热加载在大部分时候是管用的,但出现意外后可以使用reload全部刷新,全部刷新总是很管用的。在RN0.22以后,点击开发者菜单的enable hot reloading启用热加载。
热加载原理
RN中的热加载是通过packager实现的,基础是webpack的hmr。packager通过hmr来检测文件变化,然后发送HMR update(包含了更新的模块代码以及json格式的模块间的依赖关系)到app里边的hmr runtime,接着runtime就会把新的代码替换上去。
哪一个模块被修改了,那个模块就会收到一个hmr update。hmr update必须要被accept,否则会一直沿着依赖链往上传递。hmr update所经过的模块,这个模块的缓存就会被清除。
热加载中,accept的删除是无效的,只能被替换和添加,替换和添加会马上生效。如C中没有accept,往C中添加一个accept,则C的代码会重新执行,刚添加的accept回调函数也会执行。
哪个模块把hmr update accept了,这个模块的代码会重新执行(如果这个hmr update是从依赖的模块里传递上来的,则这个依赖也会被重新执行,因为这个依赖的缓存被清除过了),最后再调用accept的回调函数。假如A中有accept并且accept了一个hmr update,则A重新执行了。但是如果依赖于A的模块B早就require了A,则B使用的也还是旧缓存A,在B中需要再次require A才能获取到读取最新的A代码。
对于react组件的热加载有些困难。在组件的jsx转换时,转换器会为每个组件都创建一个与之对应的代理和一个accept函数(里面的代码可以强制重新渲染页面),这些代理管理组件的状态并且触发对应组件的生命周期方法。这样就可以直接替换代码而且不会丢失状态了,因为状态保存在代理中,代理不会发生变化。使用这种转换需要配置babel-preset-react-native,如果要用在webpack中则使用react-transform
明白了以上的道理后,下面这个例子也很好理解了:
// 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; };
当reducers发生变化后,reducers中没有accept,所以hmr update往上传递到了上面这个store中,hmr update在这里被accept了,然后就把最新的reducer替换上去了。
API
在需要被热更新的模块内写上:
module.hot.accept(() => {
console.log('changed');
});
当模块代码被改变时,以上回调函数中的代码就会被执行。在少数情况下需要手动调用这个API,大部分时候,RN中的热加载是开箱即用的,而不需要自己写任何代码
自动刷新
启用开发者菜单中的enable live reload,当源文件发生变化时,packager会通知app重新刷新,会再次读取js bundle,js bundle会被packager再次打包然后响应回给app。
当往资源目录中添加了资源(如图片)或者修改了native原生代码,需要重新构建app。
错误和警告
错误和警告在开发模式下会显示到app界面上。
当app内发生错误,或者调用了console.error,则app界面会显示一个红色全屏的错误信息;当出现警告,或者调用了console.warn,则app底部会出现一个黄色工具栏显示警告信息。
在chrome中调试js
点击开发者菜单,启用debug js remotely。这会打开一个chrome标签(http://localhost:8081/debugger-ui)。在这个标签中打开调试窗口,可以看到app内的console输出,进入source,对js打断点,还可以进行js调试