要了解 JSX,首先先了解什么三个主要问题,什么事 VDOM,差别更新和 JSX 建模:html
VDOM,也叫虚拟 DOM,它是仅存于内存中的 DOM,由于还未展现到页面中,因此称为 VDOMreact
var vdom = document.createElement("div");
上面这一句就是最简单的虚拟 DOMgit
var vdom = document.createElement("div"); document.body.append(vdom);
上面这两句就是把虚拟 DOM 转化为 真实 DOM,其实就是把节点 append 到页面中es6
常见 DOM 操做,就三类:增、删、改。对应的 DOM 操做以下:github
DOM 操做 | DOM 方法 |
---|---|
增长一个节点 | appendChild |
删除一个节点 | removeChild |
更改一个节点 | replaceChild |
之前咱们写代码常常会拼接完模板,简单粗暴的用$(el).html(template)
整块节点替换算法
这样作最大的问题在于性能,若是页面比较小,问题不大,但若是页面庞大,这样会出现卡顿,用户体验会不好,因此解决办法就是差量更新json
差量更新就是只对局面的 HTML 片断进行更新。好比你加了一个节点,那么我就只更新这个节点,我无需整个模板替换。这样作效率就会提升。但问题在于,不知道哪一个节点更新了,哪一个节点删除了,哪一个节点替换了,因此咱们须要对 DOM 建模数组
DOM 建模,简单点说就是用一个 JS 对象来表示 VDOM。
若是咱们能够用一个 JS 对象来表示 VDOM,那么这个对象上多一个属性(增长节点),少一个属性(删除节点),或者属性值变了(更改节点),就很清醒了性能优化
DOM 也叫 DOM 树,是一个树形结构,DOM 树上有不少元素节点,要对 VDOM 进行建模,本质上就是对一个个元素节点进行建模,而后再把节点放回 DOM 树的指定位置babel
JSX 建模,每一个节点都是由如下三部分组成:
{type:"div",props: null, children:[ {type:"img",props:{"src":"avatar.png", "className":"profile"},children:[], {type:"h3",props: null, children:[{[user.firstName, user.lastName].join(' ')}], ]}
上面 VDOM 建模是用下面的 HTML 结构转出来的
var profile = ( <div> <img src="avatar.png" className="profile" /> <h3>{[user.firstName, user.lastName].join(" ")}</h3> </div> );
但这段代码并非合法的 js 代码,它是一种被称为 jsx 的语法扩展,经过它咱们就能够很方便的在 js 代码中书写 html 片断
本质上,jsx 是语法糖,上面这段代码会被 babel 转换成以下代码
React.createElement( "div", null, React.createElement("img", { src: "avatar.png", className: "profile" }), React.createElement("h3", null, [user.firstName, user.lastName].join(" ")) );
而上面的这段被转化的代码是 将咱们的 VDOM 配合React.createElement
(通常应该是createElement
函数)转化为真实 DOM
注意,若是是自定义组件<App />
会转化为React.createElement(App, null)
,由于组件是class App extends React.Component {}
这样定义的,因此 App 进入createElement
函数里面就会变成是一个对象
这里咱们能够把这个函数放进createElement()
里面生成一个 VDOM 对象,而后用生成的 VDOM 对象,配合render()
生成一个 DOM 插入页面,从而转变成真实 DOM 结构
React 元素,它是 React 中最小基本单位,咱们可使用上面提到的 JSX 语法轻松地建立一个 React 元素:
const element = <div>It is element</div>;
这个元素通过 babel 转化以后会变成带 React.createElement
的函数,而React.createElement()
构建 React 元素的时候。它接受三个参数,第一个参数能够是一个标签名。如 div、p,或者 React 组件。第二个参数为传入的属性,如 class,style。第三个以及以后的参数,皆做为组件的子组件。
React.createElement(type, [props], [...children]);
React.createElement
它执行后是一个普通的对象,因为 React 元素不是真实的 DOM 元素,因此也没办法直接调用 DOM 原生的 API。上面的 JSX 转译后的对象大概是这样的:
{ "_context": Object, "_owner": null, "key": null, "props": { "className": null, "children": "It is element" }, "ref": null, "type": "div" }
而 React 中有三种方法构建组件:
React.createClass()
因为是旧版本的,咱们重点讲两种就够了,一种是函数式(无状态函数),一种是类式(ES6 类),就是用 ES6 class
咱们全部的组件都继承自React.Component
函数式很简单,就像咱们日常写函数一个,接受一个参数做为输入,而后进行相应的输出,只不过它输出的 JSX 格式,注意组件只能有一个根元素:
function Wscats(props) { return <h1> {props.name}</h1>; } //ES6 const Wscats = ({ props }) => ( <div> <h1>{props.name}</h1> </div> );
类式组件以下,是一个纯函数:
import React from 'react'; //推荐这种 class Wscats extends React.Component { render() { return <h1> {this.props.name}</h1> } } //or 这种方法将要废弃 var Wscats = React.createClass({ render() { return <h1> {this.props.name}</h1> } }
React.createClass()
和ES6 class
构建的组件的数据结构本质都是类,而无状态组件数据结构是纯函数,但它们在 React 被能视为组件,综上所得组件是由元素构成的,元素是构造组件的重要部分,元素数据结构是普通对象,而组件数据结构是类或纯函数。
类组件有生命周期和状态,而函数组件则没有。
React 给类组件定义了很是完善的生命周期函数,类组件渲染到页面中叫挂载mounting
,因此渲染完成后,叫作componentDidMount
, 类组件的卸载叫Unmount
,因此类组件将要卸载 叫作componentWillUnmount
。咱们想要在何时使用状态,就能够直接调用生命周期函数,把想要作的事情写到函数里面,生命周期函数直接写在类组件内部,类组件在初始化时会触发 5 个钩子函数:
id | 钩子函数 | 用处 |
---|---|---|
1 | getDefaultProps() | 设置默认的 props,也能够用 defaultProps 设置组件的默认属性 |
2 | getInitialState() | 在使用 es6 的 class 语法时是没有这个钩子函数的,能够直接在 constructor 中定义 this.state。此时能够访问 this.props |
3 | componentWillMount() | 组件初始化时只调用,之后组件更新不调用,整个生命周期只调用一次,此时能够修改 state |
4 | render() | react 最重要的步骤,建立虚拟 dom,进行 diff 算法,更新 dom 树都在此进行。此时就不能更改 state 了 |
5 | componentDidMount() | 组件渲染以后调用,能够经过 this.getDOMNode()获取和操做 dom 节点,只调用一次 |
类组件在更新时也会触发 5 个钩子函数:
id | 钩子函数 | 用处 |
---|---|---|
6 | componentWillReceivePorps(nextProps) | 组件初始化时不调用,组件接受新的 props 时调用 |
7 | shouldComponentUpdate(nextProps, nextState) | react 性能优化很是重要的一环。组件接受新的 state 或者 props 时调用,咱们能够设置在此对比先后两个 props 和 state 是否相同,若是相同则返回 false 阻止更新,由于相同的属性状态必定会生成相同的 dom 树,这样就不须要创造新的 dom 树和旧的 dom 树进行 diff 算法对比,节省大量性能,尤为是在 dom 结构复杂的时候。不过调用 this.forceUpdate 会跳过此步骤 |
8 | componentWillUpdate(nextProps, nextState) | 组件初始化时不调用,只有在组件将要更新时才调用,此时能够修改 state |
9 | render() | 同上 |
10 | componentDidUpdate() | 组件初始化时不调用,组件更新完成后调用,此时能够获取 dom 节点。还有一个卸载钩子函数 |
11 | componentWillUnmount() | 组件将要卸载时调用,一些事件监听和定时器须要在此时清除 |
好比,页面渲染完成后时间自动加一秒,这时还要涉及到类组件的状态更改。React 不容许直接更改状态, 或者说,咱们不能给状态(如: date)进行赋值操做, 必须调用组件的setState()
方法去更改状态。这里写一个函数changeTime
来更改状态,详情看 setState 更改状态
changeTime
函数也能够直接写到组件里面,根据 ES6 class
语法的规定,直接写在类中的函数都会绑定在原型上,因此this.changeTime
能够调用。但要保证 this 指向的是咱们这个组件,而不是其余的东西, 这也是在 setInterval
中使用箭头函数的缘由:
//类式组件 class Wscats extends React.Component { constructor(props) { super(props); this.state = { date: new Date() }; // 给组件添加状态 } changeTime() { this.setState({ date: new Date() }); } // 生命周期函数 componentDidMount() { setInterval(() => { this.changeTime(); }, 1000); } render() { return ( <div> <h1>{this.props.name}</h1> <h2>如今时间是:{this.state.date.toLocaleTimeString()}</h2> </div> ); } } //组件的组合 function App() { return ( <div> <Wscats name="Oaoafly" /> <Wscats name="Eno" /> </div> ); }
PureCompoent
是更具性能的Component
的版本。它为你提供了一个具备浅比较的 shouldComponentUpdate
方法,也就是上面咱们提到的那个类组件的生命周期,除此以外PureComponent
和 Component
基本上彻底相同。当状态发生改变时,PureComponent
将对 props 和 state 进行浅比较。另外一方面,而Component
是不会比较的,当 shouldComponentUpdate
被调用时,组件默认的会从新渲染,因此能够在Component
里面本身手动调用shouldComponentUpdate
进行比较来获取更优质的性能。
props(properties 的缩写)和 state 都是普通的 JS 对象。它们都是用来保存信息的,这些信息能够控制组件的渲染输出。
而它们的一个重要的不一样点就是:
class Test extends React.Component { constructor(props) { super(props); this.state = { message: "Hello World" }; } render() { return ( <div> <h1>{this.state.message} {this.props.message}</h1> </div> ); } } // 传递Props给组件,message=”Hi“会被 Test组件里面的 props接受 React.render(<Test message="Hi">, document.getElementById('#demo'))
因此,状态(State)与属性(Props)很相似,但 state 是组件私有的控制的,除了自身外部任何组件都没法访问它,而 props 是组件从外部获取的值,相似形参。
若是文章和笔记能带您一丝帮助或者启发,请不要吝啬你的赞和收藏,你的确定是我前进的最大动力😁