走进 React 的内部
2019-06-02 本文已影响3人
zidea
react
今天我们来分析一下 React 的源码,以及在 React 中应用了那些 ES6 的新特性。
首先感谢 Nir Kaufman 大神的分享,最近看了他分享 Redux Pattern 受益匪浅。
nir kaufman
下面代码想必大家不会陌生,我们通过
通过上面演示我们可以查看,当
今天我们来分析一下 React 的源码,以及在 React 中应用了那些 ES6 的新特性。
首先感谢 Nir Kaufman 大神的分享,最近看了他分享 Redux Pattern 受益匪浅。
nir kaufman
下面代码想必大家不会陌生,我们通过
createElement
方法来创建 create 元素,在 create 我们并没有找到 document 来对应于createElement
来创建元素。
class App extends Component {
render() {
return (
React.createElement('h1',null,"hello react")
)
}
}
ReactDom.render(
React.createElement(App,null),
document.getElementById('app')
)
通过ReactElement
方法来创建一个 react 的 element 元素,通过分析ReactElement
下面的源码,返回一个 element 对象,下面是其源码。
var ReactElement = function (type, key, ref, self, source, owner, props) {
var element = {
// This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
type: type,
key: key,
ref: ref,
props: props,
// Record the component responsible for creating this element.
_owner: owner
};
{
// The validation flag is currently mutative. We put it on
// an external backing store so that we can freeze the whole object.
// This can be replaced with a WeakMap once they are implemented in
// commonly used development environments.
element._store = {};
// To make comparing ReactElements easier for testing purposes, we make
// the validation flag non-enumerable (where possible, which should
// include every environment we run tests in), so the test framework
// ignores it.
Object.defineProperty(element._store, 'validated', {
configurable: false,
enumerable: false,
writable: true,
value: false
});
// self and source are DEV only properties.
Object.defineProperty(element, '_self', {
configurable: false,
enumerable: false,
writable: false,
value: self
});
// Two elements created in two different places should be considered
// equal for testing purposes and therefore we hide it from enumeration.
Object.defineProperty(element, '_source', {
configurable: false,
enumerable: false,
writable: false,
value: source
});
if (Object.freeze) {
Object.freeze(element.props);
Object.freeze(element);
}
}
return element;
};
通过 React.createElement 创建元素 el 后,我们尝试修改 el 的 myName 属性。
class App extends Component{
render(){
const el = React.createElement('h1',null,"hello react")
el.myName = 'zidea';
console.log(el);
return el;
// return (
// <div>
// <Greeting/>
// <Greeting/>
// </div>
// )
}
}
export default App;
运行程序提示出现错误
TypeError: Cannot add property myName, object is not extensible
这是因为这个对象 el 是被冻结,我们无法对其添加属性或进行修改。可以通过Object.isFrozen
方法可以坚持 el 是否被冻结。
const el = React.createElement('h1',null,"hello react")
// el.myName = 'zidea';
console.log(Object.isFrozen(el));
返回 true 表示对象已经被冻结,冻结对象对象我们无法再为其添加新的属性。
true
const Title = ({title}) => React.createElement('h1',null,title)
class App extends Component{
render(){
return React.createElement(Title,{title:"hello"})
}
}
export default App;
const Title = ({title}) => React.createElement('h1',null,title)
class App extends Component{
constructor(props){
super(props);
this.state = {title:'zidea'}
}
render(){
return React.createElement(Title,{title:this.state.title})
}
}
尝试修改 props 同样可以可能会发生错误。
if (Object.freeze) {
Object.freeze(element.props);
Object.freeze(element);
}
class App extends Component{
constructor(props){
super(props);
this.state = {title:'zidea'}
}
render(){
return React.createElement('h1',null,this.state.title)
}
}
freeze
change name
seal
通过上面演示我们可以查看,当
Object.freeze()
, 我们无法通过 delete 来删除对象的属性,也无法修改冻结对象的属性值,而使用 seal 虽然无法删除属性,但却可以修改对象的属性。
// an immutable object with a single mutable value
function createRef() {
var refObject = {
current: null
};
{
Object.seal(refObject);
}
return refObject;
}
看看 refObject 源码,这里通过Object.seal(refObject)
,因此我们可以修改 current 属性指向我们要引用的 DOM。
class App extends Component{
constructor(props){
super(props);
this.titleRef = { current:{}};
this.state = {title:'zidea'}
}
componentDidMount(){
console.log(this.titleRef.current);
}
render(){
return React.createElement('h1',{ref:this.titleRef},this.state.title)
}
}
从上面代码我们不难看出 current 的属性是可以修改
var hasSymbol = typeof Symbol === 'function' && Symbol.for;
var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for('react.element') : 0xeac7;
var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca;
var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for('react.fragment') : 0xeacb;
var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for('react.strict_mode') : 0xeacc;
var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for('react.profiler') : 0xead2;
var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for('react.provider') : 0xeacd;
var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for('react.context') : 0xeace;
var REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for('react.concurrent_mode') : 0xeacf;
var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;
var REACT_SUSPENSE_TYPE = hasSymbol ? Symbol.for('react.suspense') : 0xead1;
var REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3;
var REACT_LAZY_TYPE = hasSymbol ? Symbol.for('react.lazy') : 0xead4;
Symbol 可以创建一个唯一的 key 值,这个值是唯一的、可以通过一个实例来了解 Symbol 使用方法。我们可以通过 Symbol 来实现类似 java 中的接口。
上面语句不难看出我们通过判断是否支持 Symbol,如果支持就使用 Symbol 来作为类型的唯一标识,否则直接通过序列数来表示。
定义 Tut 类,然后提供一个 print 方法,调用可使用 tut.print
检查是否为 react 的元素。保护你的代码,其实并没有那么复杂。
$$typeof: REACT_ELEMENT_TYPE,
class App extends Component{
constructor(props){
super(props);
this.titleRef = React.createRef();
this.state = {title:'zidea'}
}
componentDidMount(){
console.log(this.titleRef.current);
}
changetTitle(){
this.setState({title:'tina'})
}
render(){
return React.createElement('div',null,[
React.createElement('h1',{key:1},this.state.title),
React.createElement('button',{key:2, onClick:()=> this.changetTitle()},'click')
]
)
}
}
通过打印this.updater
可以输出 this.updater 的方法,这里需要说一下更新 setState 方法是异步的,可以通过 enqueueSetState 方法可以更新 state 属性值。
changetTitle(){
console.log(this.updater);
}
updater
changetTitle(){
this.updater.enqueueSetState(this,{title:'new title'});
}