这几天读了胡子大哈老师的《React.js 小书》,感受获益良多,真心推荐你们能够看一看!css
示例代码请点这里html
jsx 的本质是 React.createElement 的语法糖,在 React 文档中,有这么一段话:react
JSX 对使用React 不是必须的。当你不想在你的构建环境中设置编译器,那么不使用 JSX 的 React 是很是方便的。git
每个 JSX 元素都是调用 React.createElement(component, props, ...children) 的语法糖,所以,任何你使用 JSX 来作的事均可以经过纯 JavaScript 实现。github
<div class="root">
<div class="child">hello man</div>
</div>
复制代码
观察以上代码,咱们能够知道,每一个 DOM 其实都只包含了三个信息:标签名称、属性、子元素。所以,每一个 DOM 元素,咱们均可以用一个 js 对象来标示。上面这段代码咱们能够这样表示:数组
{
tag: 'div',
attr: {
class: 'root'
},
child: {
tag: 'div',
attr: {
class: 'child'
},
child: 'hello man'
}
}
复制代码
在 React 中,咱们使用 React.createElement 来将上面的这种 js 对象来转化成为真正的 DOM 元素,至于 React.createElement 的内部实现,这里暂不作深究。浏览器
若是咱们直接在代码中调用 React.createElement ,经过这种 js 对象的方式来生成 DOM 的话,代码看起来会有些冗长繁琐,不太符合咱们追求简单的想法,因而,React 发明了 JSX 语法,经过 JSX ,咱们能够直接在 js 代码中书写 html 结构,最后交给 babel 来编译为上面这种 js 描述 DOM 的对象,最后经过 React.createElement 来构建真正的 DOM。bash
在一些业务场景下,咱们或许须要处理大量的具备相同总体布局但是包含子节点的布局内容却又彻底不一样的 DOM,例以下图所示: babel
咱们能够看到,虽然左右两个元素中的内部元素彻底不一样,但是却又有着相同的总体布局!这个时候,虽然咱们能够用相同的命名,引入相同的 css 来减小咱们的工做量,但是,当这些元素分别位于不一样的组件时,咱们就要为每一个组件都引入同一份 css ,还要给他们一样的类名,显然,这样有些繁琐。若是这时用一个能够公用的容器组件,咱们只须要引入这个容器组件,而后往里边填充相应的内容便可,显然可让代码显得更加整洁,使用起来也更加方便。app
咱们用 create-react-app 新建一个 React 项目(示例代码请点这里),清除掉一些不相干的内容,让咱们的项目看起来更加清晰,而后新建两个组件:
Container.js
import React from 'react';
export default class Container extends React.Component {
constructor(){
super()
}
render(){
return (
<div>
Container
</div>
)
}
}
复制代码
head.js
import React from 'react';
export default class Head extends React.Component {
constructor() {
super()
}
render() {
return (
<div>
head
</div>
)
}
}
复制代码
咱们在 index.js 中引入这两个组件,将 Head 组件插入 Container 组件中:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Container from './container';
import Head from './head';
ReactDOM.render(
<Container>
<div>hello man</div>
<Head></Head>
</Container>,
document.getElementById('root')
);
复制代码
打开浏览器,咱们看到,在页面上,并无出现 head ,也没有出现 hello man,而是显示出了 container :
咱们在控制台将 container 打印出来:
能够看到在 container 的 props.children
属性中,储存着咱们插入进 Container 组件的内容,咱们只须要将这些信息渲染出来就能够了,咱们改写一下 Container 组件的代码:
import React from 'react';
export default class Container extends React.Component {
constructor(){
super()
}
render(){
return (
<div className="container">
<div className="head">{this.props.children[0]}</div>
<div className="body">{this.props.children[1]}</div>
</div>
)
}
}
复制代码
打开浏览器,咱们看到,以前填充在 Container 组件中的内容已经正常显示,而且总体布局结构也和咱们预期同样。咱们能够根据具体的业务,来填充具体的内部元素。
这里有个问题须要注意,当咱们注释掉插入 Container 组件的 Head 组件时,咱们发现,插入的 hello man
也不能正常显示:
ReactDOM.render(
<Container>
<h1>hello man</h1>
{/* <Head></Head> */}
</Container>,
document.getElementById('root')
);
复制代码
props.children
已经再也不是一个数组,而是一个对象,因此咱们在 Container 组件内部经过下标来取值的作法,显然是有问题的。咱们须要来对
props.children
进行判断,来肯定取值方式,具体实现并不难,这里就再也不写示例代码了。
首先,咱们来看一下这些定义:
高阶组件是一个函数,可以接受一个组件并返回一个新的组件。
高阶组件是一个纯函数,不会改变原来的组件,没有反作用。
简单来讲,知足下面条件的函数能够称之为纯函数:
返回结果彻底依赖传入的参数。
执行过称中没有反作用。
一样的输入获得一样的输出。const gen = Math.random() 就不是纯函数。(感谢FateRiddle指正)
咱们来看下面这两个函数,了解下什么是返回结果彻底依赖传入的参数:
let d = 1;
function add(a,b){
return a + b
}
function all(c){
return c + d
}
复制代码
当咱们执行 add
和 all
时,add
的返回结果彻底依赖于传入的参数 a
和 b
的数值,add(3,6)
必定返回 9,但是当 all
执行时, all(3)
的返回之就不必定是 4,当 d
的值变为 2 的时候, all(3)
的返回值就变成了 5,因此,在这里 all
就不能称之为是一个纯函数,而 add
则是一个纯函数。
但是当咱们从新声明一个变量,改写下add
:
let obj = {
x: 2
}
function add(obj,b){
obj.x = 1;
return obj.x + b
}
复制代码
咱们再次调用 add(obj,6)
,这个时候,虽然 add
的返回结果依旧彻底依赖与传入的参数,可是,传入的 obj
对象的 x
属性的值却发生了变化,这就是产生的反作用,因此,add
就不是一个纯函数。
而高阶函数是一个纯函数,不会改变传入的组件。
说了这么多,但是高阶组件有哪些用处呢?
咱们来看下面这两段代码:
组件一:
import React from 'react';
export default class One extends React.Component {
constructor(props) {
super(props)
this.state = {
higher: 0
}
}
componentWillMount(){
let higher = this.props.higher * 2
this.setState({
higher: higher
})
}
render() {
return (
<div>{this.state.higher}</div>
)
}
}
组件二:
import React from 'react';
export default class Two extends React.Component {
constructor(props) {
super(props)
this.state = {
higher: 0
}
}
componentWillMount(){
let higher = this.props.higher * 2
this.setState({
higher: higher
})
}
render() {
return (
<h1>{this.state.higher}</h1>
)
}
}
复制代码
咱们看到,除了组件返回的标签元素外,其余代码彻底相同。两个组件都在 componentWillMount
时对传入的数据作了逻辑处理,这时,咱们就能够利用高阶组件,将公共的逻辑代码抽离出来:
const higher = function(Component,data){
class Higher extends React.Component {
constructor(props) {
super(props)
this.state = {
higher: 0
}
}
componentWillMount() {
let higher = data * 2
this.setState({
higher: higher
})
}
render() {
return (
<Component higher={this.state.higher}></Component>
)
}
}
return Higher
}
复制代码
咱们将原始组件和高阶组件插入页面:
let CopyOne = higher(One,30);
let CopyTwo = higher(Two, 40);
ReactDOM.render(
<div>
<CopyOne></CopyOne>
<CopyTwo></CopyTwo>
<One higher={10}></One>
<Two higher={20}></Two>
{/* <Container>
<h1>hello man</h1>
<Head></Head>
</Container> */}
</div>,
document.getElementById('root')
);
复制代码
打开浏览器咱们看到:
这篇笔记简要的梳理了一下本身对 React 的理解,如有描述不对之处,还望你们批评指正!