用react也有段时间了, 是时候看看人家源码了. 看源码之前看到官方文档 有这么篇文章介绍其代码结构了, 为了看源码能顺利些, 遂决定将其翻译来看看, 小弟英语也是半瓢水, 好多单词得查词典, 不当之处请批评. 直接从字面翻译的, 后面看源码后可能会在再修改下.
下面是翻译
这部分将给你介绍下react代码的基本结构, 代码约定和它的基本实现.
如果你想为react贡献代码的话, 我们希望这篇指南能让你写代码更加舒服.
我们不推荐将这些约定用在react应用中, 因为这些约定大多是基于一些历史原因存在的, 随着时间推移可能会发生变化.
外部依赖react 几乎没有外部依赖. 通常require()指向的是react自己代码库的一个文件. 但是也有一些例外.
由于react想要通过库共享一些诸如Relay的小工具, 所以存在fbjs repository, 而且我们让他们是同步的. 我们没有依赖任何node生态系统下的小模块, 因为我们希望facebook的工程师的能能再任何必要的时候修改他们. fbjs中的任何工具都不能被认为是公共api, 并且他们只是为Facebook的一些工程使用, 比如react.
一级目录克隆了react的仓库后你会发现在里边有几个一级目录.
packages目录包括一些元数据(如package.json)和react库提供的所有包的源码(src的下面), 如果你想修改代码, src下面就是你要花时间最多的地方.
fixtures目录包括了为贡献者准备的一些小的react的测试应用
build是react打包输出的目录. 他不在代码库管理范畴, 但是当你第一次打包后就会生成.
文档是放在和react不同的另一个仓库管理的.
还有一些其他一级目录, 他们大多是工具层面的, 在你贡献代码时可能不会用到他们能.
共同测试(Colocated Tests)我们没有搞个一级目录来做单元测试. 我们把它放在了被测试文件相邻的被称为__tests__的目录.
举个例子, 对于setInnerHTML.js这个文件的测试被放在与他同级的__tests__/setInnerHTML-test.js这个里边.
这个词不知道怎么翻译
Warnings and Invariantsreact中使用warning模块显示警告信息.
var warning = require('warning'); warning( 2 + 2 === 4, 'Math is not working today.' );当警告条件是false的时候会展示警告信息
可以这么理解, 条件应该指示正常的情况, 而不是异常的情况. 就是说第一个参数是true表示的是正常, false是异常.
最好避免使用console取代warnings.
var warning = require('warning'); var didWarnAboutMath = false; if (!didWarnAboutMath) { warning( 2 + 2 === 4, 'Math is not working today.' ); didWarnAboutMath = true; }警告只会在开发模式被开启. 生产环境下被去掉了. 如果你想阻止某些代码块的执行, 那么你可以用invariant模块.
var invariant = require('invariant'); invariant( 2 + 2 === 4, 'You shall not pass!' );当条件为false时, 这个方法会直接抛出异常.
“Invariant” 就是说这个条件为真, 你可以认为他就是做了个断言.
保持开发环境和生产环境一致是很重要的, 因此invariant在生产环境和开发环境都可以抛出异常. 生产环境下的错误消息被自动替换成错误码, 以防增加代码体积.
Development and Production你可以使用__DEV__这个为全局变量指定仅仅在开发环境才执行的代码块.
他是在编译过程中工作的, 他是在commonjs编译的时候检查process.env.NODE_ENV !== 'production'这个值.
单独编译的时候, 他在未压缩版是true, 在压缩版直接被去掉了.
if (__DEV__) { // 这里边的代码只会带开发环境执行 } Flow我们最近开始引入flow做静态类型检查, 在文件头的注释里标注了@flow的使用了类型检查.
我们接受在现有代码加入flow类型检查的pull request (不错哎, 可以试着提个pull request哦). Flow的签名类似下面这样.
ReactRef.detachRefs = function( instance: ReactInstance, element: ReactElement | string | number | null | false, ): void { // ... }时机成熟的时候, 新代码要用Flow 签名, 你可以在本地运行yarn flow用Flow检查你的代码.
动态植入react在一些模块使用了动态植入. 但是这个东西不太好, 因为他让代码比较难理解了. 他存在的理由是react一开始只把支持dom作为目标的. 但是后来杀出了个React Native, 他是基于react的, 我们不得不加入动态植入好让react native 重载一些行为.
你可能会看到模块像下面这样声明它的动态依赖
// Dynamically injected var textComponentClass = null; // Relies on dynamically injected value function createInstanceForText(text) { return new textComponentClass(text); } var ReactHostComponent = { createInstanceForText, // Provides an opportunity for dynamic injection injection: { injectTextComponentClass: function(componentClass) { textComponentClass = componentClass; }, }, }; module.exports = ReactHostComponent;注入的部分没有以任何方式特殊处理. 但是规定, 它的意思是这个模块想在运行时有一些依赖(可能是平台特定的)被注入进去.
代码里边有几个注入的入口. 未来, 我们将废弃掉这种动态植入的机制, 方案是在编译时以静态方式处理他们.
多包react是个monorepo, 他的仓库包含了多个独立的包, 因此他们的修改可以合在一起, 而且issues也可以放在一个地方.
React核心react的核心是所有顶级api, 包括:
React.createElement()
React.Component
React.Children
react核心只包括定义组件必要的api, 并不包括reconciliation算法和平台特定代码. React DOM和React Native都使用了他们.
react核心的相关代码在packages/react里边. npm使用时在react这个包里边, 浏览器版的是react.js, 他挂载一个被称为React的全局变量.
Renderersreact起初是为DOM创造的, 但是后台通过RN被用来支持原生环境了. 这里介绍加react内部的“renderers”的理念.
“renderers”管理了react树如何变成平台可调用的东西.
Renderers也在packages里边