目录
- 类组件和函数组件
- 如何使用 props 和 state
外部数据和内部数据,对应Vue的props 和 data - 如何绑定事件
- 复习this
1.组件 component
1.1 Element 和 component
const div = React.createElement('div',...)
这是一个React元素(d小写)const Div = ()=> React.createElement('div',...)
这是一个React组件(D大写)
1.2 什么是组件
- 能跟其他物件组合起来的物件,就是组件
- 在React中,一个返回 React元素 的 函数就是组件
- 在Vue中,一个构造选项就可以表示一个组件
data、method、template、render
1.3 React两种组件(函数组件和类组件)
- 一般推荐使用函数组件,类组件已经过时了
- 一、函数组件
使用方法:<Welcome name="frank" />
1.组件当标签用,所有函数组件的第一个参数都可以接收一个外部数据
2.name="frank"
会自动变成props的key
和value
function Welcome(props){
//相当于 return 一个 React.creareElement
return <h1>Hello, {props.name}</h1>
}
- 二、类组件
使用方法:<Welcome name="frank" />
props需要在this上面取拿
class Welcome extends React.Component{
render(){ // render 渲染组件视图
return <h1>Hello, {this.props.name}</h1>
}
}
1.4 <Welcom />
- React 中的这种标签不是在写HTML
- babel 就把 XML 语法翻译为JS语法
<div className="red" />
会被 babel 翻译为:React.createElement('div',{className="red"})
如果是原生的标签,则会变成字符串<Welcome/>
会被翻译为:React.createElement(Welcome,{})
如果是自定义的组件,后面的welcome就是函数
1.5 React.createElement的逻辑
- 如果传入一个字符串 ‘div’,则会创建一个 div(虚拟DOM元素)
- 如果传入一个函数,则会调用该函数,获取其返回值
1.如传入Welcome函数,就会调用这个函数,然后获取它return的值
2.返回值会代替原来标签的位置,<Welcom />
就不存在了 - 如果传入一个类,则在类前面加个 new
1.这会导致执行 constructor对应函数,获取一个组件对象
2.然后调用对象的 render 方法,获取其返回值(重点还是获得其返回值)
3.constructor 就是 new 对应的函数
4.要初始化 state 就要在 constructor(构造函数)里面初始化
class Welcome extends React.Component{
constructor(){
super()
this.state={n:0}
}
add(){},
render(){
return <h1>Hello, {this.props.name}</h1>
}
}
2.案例
- 代码
- 一个小tip:析构函数
const [n, setN] = React.useState(0);
声明一个变量为0,然后对它进行读(n)和写(setN)
setN之后就会得到一个新的n,而不是改变原有的n
2.1 函数组件和类组件使用 props(外部数据)
- 外部数据只能:读
- 传递:messageFor+组件名=‘字符串’/{变量}
<Son messageForSon="儿子你好"/{1+1} />
- 子类组件直接读取属性:this.props.xxx
{this.props.messageForSon}
- 函数子组件直接读取传入的参数props:props.xxx
const Grandson = (props)=>{
return (
<div>{props.messageForSon}</div>
)
}
- 外部数据只能读,不能写
2.2 函数组件和类组件使用 state(内部数据)
- state也就是 Vue 的data
- state的最重要的三点:初始值、读、写
- 类组件
1.直接写在 constructor里面:this.state = {n:0}
2.读取:this.state.n
3.写:this.setState({ n : this.state.n + 1})
——使用setState的时候对象要重写一遍
——为什么不直接在原来的上面改?
——React的思想,数据不可变,要变就产生新的对象
4.在之前的Vue中可以直接变化n,因为Vue会监听n的变化然后刷新UI,但React没有监听n,因此不能直接修改n
5.那么如何告诉React我修改了数据:使用 this.setState(新对象)
add() {
// this.state.n += 1 为什么不行
this.setState({ n: this.state.n + 1 });
}
- 类组件:setState的更优写法:this.setState(函数)
问题:setState是一个异步更新UI的过程,它会等整个add函数代码运行完才更新n,如果add中有其他对n的操作,就没办法对最新的n值操作
解决:this.setState 接收一个函数,这个函数就是对 state的操作,然后返回新的state值(原来是直接接受新state对象)
add(){
this.setState((state)=>{
//接收旧的state,返回新的state
const n = state.n+1 //获取新的n
console.log(n)//操作最新的n
return {n}
})
}
- 函数组件:React.useState 返回数组(只有两项)
const [n, setN] = React.useState(0);
1.返回的第一项读,第二项写(一般用析构简写)
2.用的时候给一个初始值就行
3…setN永远不会改变n,会产生一个新的n
const n = React.useState(0)[0]
const setN = React.useState(0)[1]
2.3 类组件注意事项
- this.state.n += 1 无效
1.其实n已经改变,只不过UI不会自动更新而已
2.调用setState才会触发 UI 更新(异步更新)
3.因为 React 没有像 Vue 监听 data 一样监听 state - setState 会异步更新UI
1.setState之后,state 不会马上改变,立马读 state 会读旧的
2.更推荐:setState(函数) - this.setState(this.state) 可行但不推荐
1.也就是说我们修改了旧的state,然后再次将旧state传给setState是可行的
2.但React希望我们不要修改旧state(不可变数据)
3.常用代码:setState({n:state.n+1})
2.4 函数组件
- 使用 setX(新值) 来更新UI
- 跟类组件不同的地方:没有this
一律使用参数和变量
3.两种编程模型
- Vue的编程模型:对数据的修改,会直接映射到UI上面
1.一个对象对应一个虚拟DOM
2.当对象属性改变时,把属性相关的DOM节点全部更新
3.如当n发生变化,则Vue把n出现的地方全部重新渲染 - React的编程模型:不能修改之前的数据,直接新建一个数据对象
1.一个对象对应一个虚拟DOM
2.另一个对象,对应另一个虚拟DOM
3.对比两个虚拟DOM,找不同(DOM diff),然后局部更新DOM
4.复杂 state
- 类组件里有n和m
1.setState的时候可以只set n,不用写m,因为类组件的setState会自动合并第一层属性
2.当修改 user.name 的时候,属于第二层,setState不会合并第二层属性
——解决:使用...
操作符
this.state = {
n:0,
m:0,
user:{
name:"frank",
age:18
}
}
this.setState({n:this.state.n+1})
this.setState({...this.state.user,name:"jack"})
- 函数组件里有n和m
1.推荐m和n分开写
2.因为当m和n写在一起时,set其中一个值(如m),React不会自动合并以前的属性,如果不写就会把n设置为undefined
3.用...state
把之前的数据拷贝过来(推荐分开写)
const [n,setN] = React.useState(0)
const [m,setM] = React.useState(0)
正常setN和setM
//等价于
const [state,setState] = React.useState({n:0,m:0})
setState({...state,m:state.m+1})
- 总结
1.类组件的setState会自动合并第一层属性
2.但不会合并第二层属性,需要使用Object.assign或者...
操作符
3.函数组件的setX则完全不会合并
4.要合并只能用...
操作符
5.函数组件和类组件的事件绑定
- 类组件的事件绑定
1.第一种方法是最好的,因为第二种会让 add 函数里的 this 变成window
2.由于this.addN 一定会去调用 this.setState
3.当用户点击button的时候,React会调用 button.onClick.call(null,event)
——因此在onClick函数中,this就是null,window自动补位
——因此addN里面的this会变成window
——而箭头函数没办法改变this,addN的this就还是当前实例
//写法一
<button onClick={ ()=>this.addN() }>n+1</button>
//写法二
<button onClick={ this.addN() }>n+1</button>
- 还是觉得在标签里写箭头函数太长
1.则令一个变量this._addN = ()=>this.addN()
将其替换掉
2.而addN函数声明后只使用了一次,整合起来 - 最终的事件绑定写法
1.组委会约定声明:事件名=箭头函数
——写在state外面,且省掉声明的this
2.使用:this.函数事件名
//事件名赋值,写在state外面
addN = ()=> this.setState({n: this.state.n + 1})
//绑定事件
<button onClick={ this.addN }>n+1</button>
6.React 和Vue
- 相同点
1.都是对视图的封装,React是用类和函数表示一个组件,而Vue是通过构造选项构造一个组件
2.都提供了createElement的XML缩写,React提供的是JSX语法,而Vue提供的是模板写法template - 不同点
React是把HTML放在JS里面写(HTML in JS),而Vue是把JS放在HTML里面写(JS in HTML)