virtual DOM
真实页面对应一个DOM
树。在传统页面的开发模式中,每一次需要更新页面的时候,都需要手动操作DOM
来进行更新。
DOM
的操作是十分昂贵的,我们在前端开发中,性能消耗最大的就是DOM
操作,而且这部分的代码会让整体项目的代码变得难以维护。React
把真实的DOM
树转换为JavaScript
对象树,也就是virtual DOM
。
每一次数据更新后,重新计算Virtual DOM
,并和上一次生成的Virtual DOM
进行比对,对发生变化的部分进行批量更新。Rect
也提供直接的shouldComponentUpdate
声明周期回调,来减少数据变化后的不必要的Virtual DOM
对比的过程,用来保证性能。
JSX语法
JSX
和React
有什么关系?简单的来说,React
为了方便view
层组件化,承载了构建HTML
结构化页面的职责,React
通过创建于与跟新虚拟元素(virtual DOM
)来管理整个Virtual DOM
。
React
创建虚拟元素可以划分为两类,DOM
元素(DOM element
)与组件元素(component element
),分别对应原生的DOM
元素和自定义元素,而JSX
与创建元素有很大的关系。
JSX
的官方定义是类XML
语法的ECMAScript
扩展。它完美的利用了JavaScript
自带的语法和特性,并且使用大家熟悉的HTML
语法来创建虚拟元素。可以说,JSX
基本语法被XML
囊括了,但是也有少许不同的地方。
XML基础语法
使用类XML
语法的好处是标签可以任意嵌套,我们可以像HTML
一样清晰看到DOM
树结构和其属性,比如我们构建一个List
组件。
const List = () =>{
<div>
<Title>This is Title</Title>
<ul>
<li>list item</li>
</ul>
</div>
}
写List
的过程就像写HTML
一样,只不过它被包裹在JavaScript
的方法中,但是在这个过程中需要注意以下几点:
- 标签一定要闭合
- 定义标签的时候,只允许被一个标签包裹。所以上面的写法是错误的。
const List = () =>{
<div>
<Title>This is Title</Title>
<ul>
<li>list item</li>
</ul>
</div>
<div>
<Title>This is Title</Title>
<ul>
<li>list item</li>
</ul>
</div>
}
元素类型
我们都知道JSX
语法中的元素分为DOM
元素(DOM element
)与组件元素(component element
),JSX
对应的规则是HTML
标签是否为小写字母,其中小写字母对应DOM
元素,而逐渐元素自然对应的是首字母大写。
注释
事实上JSX
还是JavaScript
,依然可以用简单的方法使用注释,唯一需要注意的是,在一个组件的子元素位置使用注释需要{}
进行包裹,实例代码如下:
const App = (
<Nav>
{/*节点注释*/}
<Person
/*多行
注释*/
name = {window.isLoggedIn?window.name : ''}
/>
</Nav>
)
元素属性
在JSX
中,不论是DOM
元素还是组件元素,它们都是有属性的,不同的是,DOM
元素的属性是标准规范属性,但是有两个例外--class
和for
,这是因为在JavaScript
中这两个单词是关键词,因此我们这么转换。
-
class
属性修改为className
-
for
属性改为htmlFor
而组件元素的属性是完全自定义的,也可以理解成组件需要的参数,举个栗子:
const Header = ({title,children})=>{
<h3 title = {title}>{children}</h3>
};
<Header title='hello world'>hello world</Header>
我们可以对属性进行展开展开,这个应用场景主要是,当我们知道组件的全部属性的时候,使用JSX
可以这么写:
//方法一
const component = <Component name = {name} value = {value}/>;
//方法二
const component = <Component / >;
component.props.name = name;
component .props.value = value;
// 方法三:使用ES6特性来提高效率
const data = {name:'foo', value:'bar'};
const component = <Component name = {data.name} value = {data.value}/>;
//方法三可以使用ES6来进行简写
const data = {name:'foo', value:'bar'};
const component = < Component {...data} />;
javaScript属性表达式
属性值要使用表达式的话,只要使用{}
进行替换即可。
//输入(JSX)
const person = <Person name ={window.isLoggedIn ? window.name:''} />
//输出(JavaScript)
const person = React.createElement (
person,
{name:window.isLoggedIn ? window.name:' '}
);
其中子组件也可以作为表达式使用:
//输入(JSX)
const content = <Container>{window.idLoggedIn ? <Nav /> :<login />}</Container>
//输出(JavaScript)
const content = React.createElement (
Container,
null,
{name:window.isLoggedIn ? React.createElement(Nav) : React.createElement(Login)}
);
小结:
-
JSX
是一个看起来很像XML
的JavaScript
语法扩展,这种语法允许你在写JavaScript
中写可嵌套的闭合标签。 -
JSX
与HTML
语法很像,可以嵌套,可以自定义属性。 -
JSX
允许在闭合标签中使用JavaScript
表达式,但是要被{}
所包裹 -
JSX
中的内联样式也是通过style
属性来定义的,但属性值不能是字符串而必须为对象,而且要注意对象中的属性名需要使用驼峰命名法。 - 在
JSX
中,标签子节点内的注释应该写在{}
内。 -
JSX
中的数组会自动的展开所有的成员,但是需要注意的是,如果数组或是迭代器中的每一项都是HTML
标签或是组件的话,那么他们必须要拥有唯一的key
属性
React组件
其实对组件的封装很类似与面向对象的思想,交互基本上以操作DOM
为主,逻辑上是结构上哪里需要变,我们就操作哪里。此外,对于JavaScript
的结构,我们得到了几项规范标准组件的信息。
- 基本的封装性:尽管说
JavaScript
没有真正的面向对象的方法,但是我们还是可以通过实例化的方法来制造对象。 - 简单的生命周期:最明显的两个方法是:
constructor
和destroy
,代表了组件的挂载卸载的过程,但是除此之外,其他过程(如更新时的声明周期)并没有体现。 - 明确的数据流动:这里的数据指的是地道用组件的参数。一旦确定参数的值,就会解析传进来的参数,根据参数的不同做出不同的响应,从而得到渲染的结果。
Web Components
规范:这个规范想要同意web
端关于组件的定义,它可以通过定义Custom Elements
(自定义元素)的方式来统一组件。每一个自定义元素可以定义自己对外提供的属性、方法、还有事件,内部可以像写一个页面一样,专注于实现功能来完成对组件的封装。
Web Components
定义了一切我们想要的组件化的概念,其中HTML Template
定义了之前的模板概念,Customer Element
定义了组件的展现形式,shadow DOM
定义了组件的作用域范围,可以包括样式,HTML Imports
提出了新的引入方式。
React组件的构建
React
组件即为组件元素:组件元素描述成纯粹的Json
对象,意味着可以使用方法或是类来构建。React
组件即为组件元素,组件基本上由3个部分组成-- 属性(props
)、状态(state
)以及生命周期方法,这里我们从一张图来简单的概括React
。
React
组件可以接受参数,也可以有自身的状态,一旦接受到的参数或是自身的状态有所改变,React
组件就会执行相对应的生命周期的方法,最后渲染,整个过程完全符合传统组件所定义的组件职责。
React和web component的关系
React
和web component
传达的理念是一致的,但是二者的实现方式是不同的,
-
React
自定义元素的库是自己构建的,与web component
规范是不一致的 -
React
渲染过程包含了模板的概念,也就是之前说的JSX
-
React
组件的实现均在方法和类中,因此做到了相互的隔离,但是不包含样式。 -
React
引用方式遵循ES6
标准
官方在React
组件构建上提供了3种不同的方法:React.createClass
,ES6 classes
和无状态函数。
React.createClass
方法是构建组件最传统,也是兼容性最好的方法,实例代码如下:
const Button = React.createClass({
getDefaultProps(){
return {
color:'red',
text:'cancle',
};
},
render(){
const {color, text} = this.props;
return (
<button className ={`btn btn - ${color}`}>
<em>{text}</em>
</button>
);
}
});
当组件需要调用Button组件的时候,只需要写<Button />
,就可以被解析成React.createElement(Button)
方法来创建Button
实例,这意味着在一个应用中调用几次Button
,就会创建几次Button
的实例。
ES6 classes
ES6 classes
的写法是通过ES6
标准的类语法的方式来构建方法:
import React, {Component } from 'react';
class Button extends Component{
constructor (props){
super(props);
}
static defaultProps ={ // 用来设置默认属性
color:'blue',
text:'Confirm',
};
render(){
const {color,text} = this.props;
return (
<button className = {`btn btn-${color}`}>
<em>{text}</em>
</button>
);
}
}
这里的直观感受是从调用内部方法变成了类来实现,与createClass
的结果相同的是,调用类实现的组件会创建实例对象。在React
组件开发中,常用的方式是将组件拆分到合理的粒度,用组合的方式合成业务组件。
无状态函数
使用无状态函数构建的组件称为无状态组件,这样的写法比较受官方推崇。我们看一下示例代码:
function Button({color = 'blue',text = 'confirm'}){
return (
<button className = {`btn btn-${color}`}>
<em>{text}</em>
</button>
);
}
无状态组件只传入props
和context
两个参数;也就是说,它不存在state
,也没有生命周期方法,组件本身即上面两种React
组件构建方法中的render
方法。不过,像propTypes
和defaultProps
还是可以通过向方法设置静态属性来实现。
我们使用React
实现Tabs
组件:
import React,{Component,PropType} from 'react';
class Tabs extends Component {
constructor(props){
super(props);
}
//...
render(){
return <div className = 'ui-tabs'></div>
}
};
export defaults Tabs;