概览 React
React 是如何构成的,又该如何使用,一小篇文章足以解释。
最重要的一点:react
是一个完全遵从 javascript
开发模式的类库,语言层面仅仅有几个 API
而已,没有配置。react
更多的是需要开发者从设计模式上去考虑如何构建一个 web
项目。
概念
react
主要有两方面的功能,
- 根据数据构建虚拟树
- 根据树构建
web
页面
react
开发者仅仅需要关注于第一点即可,后续步骤由 react
按一定顺序进行下去,不需要开发者关心。
虚拟树
一个网页的 html
的代码就可以大致被认为是一个树结构(DOM
树),而虚拟树就是为了模拟 DOM
树结构存在的一种结构。
有了虚拟树结构,我们就能将页面的结构保存在 js
环境下,这样 js
的操作就可以不用依赖 DOM API
,也就是当数据更新的时候我们仅仅需要的是重新生成一个虚拟树即可,至于后续的从虚拟树生成 web
页面我们不关心。
那么如何生成虚拟树?
使用 React.createElement()
即可生成一个树节点。但在 react
中并不直接使用该函数(当然直接使用也可以),大部分情况下使用 jsx
。
React.createElement
该方法需要传入 3
个参数,节点名称(或是组件),props
,子节点(由 React.createElement
创建,或纯文字)。
// 一个 p 节点,带了一些 props,没有子节点
let p = React.createElement('p', {className: 'line', propsName: 'some'});
// 一个 span 节点,带了一些 props,有一个纯文字子节点
let span = React.createElement('span', {className: 'text', propsName: 'some'}, ['some text']);
// 一个 div 节点,带了一些 props,有 p 和 span 两个子节点
let divTree = React.createElement('div', {className: 'box', propsName: 'some'}, [p, span]);
console.log(divTree)
以上代码可在有 React
的环境下执行,可查看最终的结果。

可以看出最终输出的对象通过 props.children
字段实现了子节点的保存。
最新版本的 React.createElement
可以通过 (type, props, ...children)
获取子节点。
jsx
jsx
是 js
的语法扩展,目的是将 React.createElement
这个方法简洁化,将编程的写法改成描述的写法,我们来改写上面的例子:
let divTree = (
<div className="box" propsName="some">
<p className="line" propsName="some"></p>
<span className="text" propsName="some">some text</span>
</div>
)
这样就更贴近了 html 语法,并且比编程的写法更容易让人弄清虚拟树的结构。
在正常的 js
环境中是不认识 jsx
语法的,可以在 babel
的 repl
中将 jsx
翻译成 js
语法。
如何在 jsx 中使用 js
let num = 123;
let divTree = (
<div className="box" propsName="some">
<p className="line" propsName="some">{num}</p>
<span className="text" propsName="some">some text</span>
</div>
)
将表达式用 {}
包含即可。
虚拟树 to Dom
有了虚拟树我们就可以把虚拟树渲染到浏览器上:
ReactDom.render(divTree, document.body);
react 组件的两种存在形式
ok 了解了虚拟树的生成,那么如何去组织这个虚拟树的生成?这就涉及到 react
组件。
函数组件
let Com1 = function(props){
console.log(props);
props.children
return (
<div>
<p>{props.propsName}</p>
<div>
)
}
函数组件最终返回一个虚拟树结构,注这里不是 html
代码。
那么如何使用?
// 调用组件时,参一 为组件的引用
let com1 = React.createElement(Com1, {propsName: 'some'}, 'some text');
// 非组件调用时 参一 为字符串
let divTree2 = React.createElement('div', {propsName: 'some'}, com1);
console.log(divTree2);
ReactDOM.render(divTree2, document.body);
组件可以作为 React.createElement
的第一个参数传入,React.createElement
检测到 第一个参数为函数时,将处理好的 props
作为参数调用它,获得函数的返回,处理后作为调用 React.createElement
的调用返回。
那么 jsx
呢?该怎么写,正常写就 ok
了
let divTree2 = (
<div propsName="some">
<Com1 propsName="some">some text</Com1>
</div>
)
这里需要注意,由于 babel
的转换的原因,如果是组件那么组件的引用的名字必须大写。
类组件
class Com2 extends React.Component {
render(){
let props = this.props;
return (
<div>
<p>Com1 text</p>
<div>
)
}
}
let tree = new Com2(props).render()
Com2
和 Com1
实现的功能完全一致。调用方式也完全一致,区别在于 React.createElement
检测到第一个参数为类时,会将处理好的 props
作为类实例化时的参数,并在实例化完成时调用 render
函数,获取返回值处理后作为 React.createElement
函数调用的返回。
两种组件的区别
除了类组件多了一些生命钩子和 state
其他的区别其实都是 js
语法中类和函数的区别,这里不深究。
关于事件
react
库本身实现了事件系统,这不是浏览器里面的事件系统,所以也就不能和浏览器的事件系统混用。
react
中的事件是根据 W3C
标准事件模型实现的,也就是说 react
里的事件能阻止冒泡到 react
的事件上,但不能阻止正常的浏览器标准事件。
而浏览器的事件是能阻止事件冒泡到 react
事件中的,原因是 react
把所有的事件都注册到了 document
上,所有浏览器的事件如果阻止冒泡到 document
上,那么 react
事件系统里面的相应事件就执行不了了。
结论:只用 react
的事件系统,如果不得已使用浏览器的事件系统,在明确结果的情况下阻止冒泡。