JSX
在JS中可以使用html的语法书写代码,是JS的一种扩展,通过JSX可以生成React元素。他在编译时会被Babel编译为React.createElement()方法的返回结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const App = ()=>{ return <h1 disabled>hello</h1> }
"use strict"; const App = () => { return React.createElement("h1", { disabled: true }, "hello"); };
function App() { return _jsx('h1', { disabled: !0, children: "Hello" }); }
|
React.createElement方法返回如下结果
1 2 3 4 5 6 7 8 9 10 11
| { "type": "h1", "key": null, "ref": null, "props": { "disabled": true, "children": "Hello" }, "_owner": null, "_store": {} }
|
Fiber
在新的React架构中,分为三个部分:Scheduler(调度器:调度任务的优先级),Reconciler(协调器:找出变化的组件),Renderer(渲染器:将变化的组件渲染到页面上)。而Reconciler内部就采用了Fiber的架构。
FIber并不是React特有的一个概念,与协程类似,也可以理解为协程的一种实现(在JavaScript中,协程的实现便是Generator 但相较于Generator,FIber可以更好实现高优更新),Fiber架构的提出源自于老的React架构中不能支撑异步更新。
当首屏渲染时,根据组件返回的JSX在内存中依次创建Fiber节点(保存对DOM的描述)并连接起来生成Fiber树,称为workInProgress FIber树(由于是首屏渲染 因此current Fiber为空)workInProgress FIber会尽量复用current FIber中的节点(Diff算法)当workInProgress FIber构建完成交给Renderer进行渲染,此时workInProgress FIber就会变为current FIber,React就是通过这个的方式进行DOM的更新。
在Fiber中使用了双缓存技术,在React中至多会存在两颗Fiber树,当前屏幕显示内容对应的成为current FIber,正在内存中构建的成为workInProgress FIber。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function App() { const [num, add] = useState(0); return ( <p onClick={() => add(num + 1)}>{num}</p> ) }
8ReactDOM.render(<App/>, document.getElementById('root'));
1.首次执行ReactDOM.render会创建fiberRoot和rootFiber。其中fiberRoot是整个应用的根节点,rootFiber是<App/>所在组件树的根节点。 12之所以要区分fiberRoot与rootFiber,是因为在应用中我们可以多次调用ReactDOM.render渲染不同的组件树,他们会拥有不同的rootFiber。但是整个应用的根节点只有一个,那就是fiberRootNode。fiberRootNode的current会指向当前页面上已渲染内容对应Fiber树,即current Fiber树。
2.接下来进入render阶段,根据组件返回的JSX在内存中依次创建Fiber节点并连接在一起构建workInProgress Fiber。(下图中右侧为workInProgress Fiber,左侧为current Fiber) 在构建workInProgress Fiber树时会尝试复用current Fiber树中已有的Fiber节点内的属性,在首屏渲染时只有rootFiber存在对应的current fiber(即rootFiber.alternate)。
3.图中右侧已构建完的workInProgress Fiber在commit阶段渲染到页面。
|

1 2 3
| 1.当点击p节点触发状态改变,会重新进行render并构建一个新的workInProgress Fiber并尽可能复用current Fiber对应的节点数据。 2.workInProgress Fiber在render阶段完成构建后进入commit阶段渲染到页面上。渲染完毕后,workInProgress Fiber变为current Fiber。
|

JSX与Fiber
JSX是一种描述当前组件内容的数据结构,他不包含组件schedule、reconcile、render所需的相关信息。例如:
- 组件更新中的优先级
- 组件的state
- 组件被打上的用于Renderer的标记
在mount中,Reconciler根据JSX生成对应Fiber节点,在update中,Reconciler将JSX与Fiber节点保存的数据进行对比,生成新的FIber节点并根据结果为FIber打上标记。
函数组件
在React中,可以通过编写JS函数的形式来定义函数组件。它接受任意的入参props,并返回用于描述页面展示内容的React元素。
通过编写JS函数或者ES6的class来定义组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| const App = (props)=>{ return <h1>hello world</h1> }
const App = props => { return React.createElement("h1", null, "hello world"); };
class App extends React.Component { render() { return <h1>hello world</h1>; } }
16class App extends React.Component { render() { return React.createElement("h1", null, "hello world"); } }
let a = <App name={'App'}/>;
let a = React.createElement(App, { name: 'App' });
|
那么React怎么区分类组件和函数式组件呢?
类组件的父类(Component)的原型上存在isReactComponent属性,而函数式组件不存在该属性。
state
- state的更新可能是异步的
- 出于性能考虑可能会把多个setState合并成一个进行更新(事件处理函数结束后进行批量更新)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| let isBatchingUpdate = false; const queue = []; let state = { number: 0 }; function setState(newState) { if (isBatchingUpdate) { queue.push(newState); } else { state = { ...state, ...newState }; } }
function handleClick() { isBatchingUpdate = true; setState({ number: 1 }); console.log(state); setState({ number: 2 }); console.log(state); state = queue.reduce((newState, action) => { return { ...newState, ...action }; }, state); } handleClick(); console.log(state);
|
事件处理
React会将JSX中书写的事件绑定到document/容器上,然后通过eventTarget的方式进行事件的触发。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function dispatchEvent(event) { let { type, target } = event; const eventType = `on${type}`; updateQueue.isBatchingUpdate = true; const syntheticEvent = createSyntheticEvent(event); while (target) { const { store } = target; const hanlder = store && store[eventType]; hanlder && hanlder.call(target, syntheticEvent); target = target.parentNode; }
updateQueue.isBatchingUpdate = false; updateQueue.batchUpdate(); }
|
ref
React提供了一种方式,允许访问DOM节点或者render方法创建的React元素。原理就时先初始化一个ref元素{current:null};然后在创建真实DOM的时候将current赋值为真实dom或者类组件的实例。
对于函数组件,由于执行后就会被销毁,不存在实例,因此需要使用其他方法获取到函数组件的ref(通过forwardRef包裹获取ref)
1 2 3 4 5 6 7 8 9 10
| 1function createDOM(vdom) { const { type, props, ref } = vdom; let dom; ... vdom.dom = dom; if (ref) ref.current = dom; return dom; }
|
生命周期
- old lifecycle
- 挂载
- constructor
- componentWillMount
- render
- componentDidMount
- 更新
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
- render
- componentDidUpdate
- 卸载
