第一次渲染 First Render
首先我们应该注意到 React(浏览器环境) 代码的入口 render 函数
ReactDOM.render(<App />, domContainer)
这个 render 过程中, React 需要做到的是根据用户创造的 JSX 语法,构建出一个虚拟的树结构(也就是 ReactElement 和 Fiber )来表示用户 期望中 页面中的元素结构。当然对于这个过程相对并不复杂(误),因为此时的 document 内还是一片虚无。就思路上而言,只需要根据虚拟 DOM 节点生成真实的 DOM 元素然后插入 document ,第一次渲染就算圆满完成。
createReactElement
通常我们会通过 Babel 将 JSX 转换为一个 JS 执行函数。例如我们在 React 环境下用 JSX 中写了一个标题组件
<h1 className='title'>
<div>Class Component</div>
</h1>
那么这个组件被 Babel 转换之后将会是
React.createElement('h1', { className: 'title' }, [
React.createElement('div', null, [ 'Class Component' ]
])
传统编译讲究一个 JSON 化,当然 JSX 和 React 也没有什么关系, JSX 只是 React 推荐的一种拓展语法。当然你也可以不用 JSX 直接使用 React.createElement 函数,但是对比上面的两种写法你就也能知道,使用纯 JS 的心智成本会比简明可见的 JSX 高多少。我们可以看出, React.createElement 需要接收 3 个参数,分别是 DOM 元素的标签名,属性对象以及一个子元素数组,返回值则是一个 ReactElement 对象。
事实上, JSX 编译后的 json 结构本身就是一个对象,即使不执行 React.createElement 函数也已经初步可以使用了。那么在这个函数中我们做了什么呢。
一个 ReactElement 元素主要有 5 个关键属性,我们都知道要构建成一个页面需要通过 html 描述元素的类型和结构,通过 style 和 class 去描述元素的样式呈现,通过 js 和绑定事件来触发交互事件和页面更新。
所以最重要的是第一个属性,元素类型 type
。如果这个元素是一个纯 html 标签元素,例如 div ,那么 type 将会是字符串 div ,如果是一个 React 组件,例如
function App() {
return (
<div>Hello, World!</div>
)
}
那么 type
的值将会指向 App 函数,当然 Class 组件 也一样(众所周知 ES6 的 Class 语法本身就是函数以及原型链构成的语法糖)
第二个属性是 props
,我们在 html 标签中写入的大部分属性都会被收集在 props
中,例如 id 、 className 、 style 、 children 、点击事件等等。
第三个第四个属性分别是 key
和 ref
,其中 key
在数组的处理和 diff 过程中有重要作用,而 ref
则是引用标识,在这里就先不做过多介绍。
最后一个属性是 $$typeof
,这个属性会指向 Symbol(React.element)
。作为 React 元素的唯一标识的同时,这个标签也承担了安全方面的功能。我们已经知道了所谓的 ReactElement 其实就是一个 JS 对象。那么如果有用户恶意的向服务端数据库中存入了某个有侵入性功能的 伪 React 对象,在实际渲染过程中被当做页面元素渲染,那么将有可能威胁到用户的安全。而 Symbol
是无法在数据库中被存储的,换句话说, React 所渲染的所有元素,都必须是由 JSX 编译的拥有 Symbol
标识的元素。(如果在低版本不支持 Symbol 的浏览器中,将会使用字符串替代,也就没有这层安排保护了)
ok,接下来回到 render 函数。在这个函数中到底发生了什么呢,简单来说就是创建 Root
结构。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论