一、元素和组件
const div = React.createElement( 'div',...) 这是一个React元素(d小写)
const Div = ( )=>React.createElement('div',... ) 这是一个React组件(D大写)
JSX文件/JS文件中一个返回 React 对象的函数就是组件,(前者用标签后者用React API)
二、类组件和函数组件
定义组件最简单的方式就是编写 JavaScript 函数:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}该函数是一个有效的 React 组件,因为它接收唯一带有数据的 “props”(代表属性)对象与并返回一个 React 元素。这类组件被称为“函数组件”,因为它本质上就是 JavaScript 函数。
<Welcome name="xxx"/>
你同时还可以使用 ES6 的 class 来定义组件:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}<Welcome name="xxx"/>
三、JSX做了什么
JSX在遇到我们写的标签后,JSX就是语法糖
- <div />会被翻译为React.createElement('div') ,遇到HTML原生标签会传入字符串
- <Welcome />翻译为React.createElement(Welcome) ,遇到不认识的标签会传入变量
React.createElement 的不同参数
- 参数是字符串"div":创建一个div元素
- 参数是函数:自动调用这个函数,获取其返回值
- 参数是类:创建实例,调用实例的render方法获取返回值
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
<Welcome name="xxx">hi</Welcome>babel成
function Welcome(props) {
return React.createElement("h1", null, "Hello, ", props.name);
}
React.createElement(Welcome, {
name: "xxx"
}, "hi");React.createElement 调用 Welcome 函数,获取到React.createElement("h1", null, "Hello, ", props.name) 创建的React元素,并传入属性和子元素。
我们最好只留一个页面入口和App.vue一样,只渲染<App/>即可
四、组件的使用
和Vue一样我们只需要<xxx/>即可,传入属性的方法一样,React也不会接受<xxx></xxx>标签里的内容,正如同没有slot的 Vue 会忽视里面的内容
function App() {
return (
<div className="App">
爸爸
<Son />
</div>
);
}这种方式会把组件的嵌套固定住(很像没有用到slot的Vue),如果我们像创建一个Layout组件,就需要用到props.children API
五、外部数据
类组件:this.props.xxx
函数组件:函数参数.xxx
父组件传入子组件props时,<Welcome xxx="xxx"/>,<Welcome xxx={xxx}/>,和Vue的v-bind一样
六、内部数据
在 React 中,this.props 和 this.state 都代表着已经被渲染了的值,即当前屏幕上显示的值。props 是传递给组件的(类似于函数的形参),而 state 是在组件内被组件自己管理的(类似于在一个函数内声明的变量)。
class Son extends React.Component {
constructor() {
super();
this.state = {
n: 0
};
}
add() {
this.setState({ n: this.state.n + 1 });
}
render() {
return (
<div className="Son">
儿子 n: {this.state.n}
<button onClick={() => this.add()}>+1</button>
<Grandson />
</div>
);
}
}看完上面代码,你可能有几个疑问:
- setState 是什么,为什么使用,有什么用,
- 为什么 add 函数里不可以直接操作this.state.n
理解关键:props(“properties” 的缩写)和 state 都是普通的 JavaScript 对象
七、React的数据响应式
在 Vue 里面,直接对 data 里的 n 进行变化,Vue 会知道 n 变化了会去更新视图,Vue 劫持了 data 里的数据。
但是在 React 里并没有对 props 和 data 劫持 ,是已经渲染好的值,需要我们手动更新视图,具体怎么更新(Diff)不用深究。
新对象(setState里的对象)==>旧对象(this.state)的映射(析构)
八、给 setState 传递一个对象与传递一个函数的区别
incrementCount() {
this.setState((state) => {
// 重要:在更新的时候读取 `state`,而不是 `this.state`。
return {count: state.count + 1}
});
}
因为 setState 是异步的 —— 不要指望在调用 setState 之后,this.state 会立即映射为新的值。如果你需要基于当前的 state 来计算出新的值,那你应该传递一个函数,而不是一个对象。
incrementCount() {
this.setState({count: this.state.count + 1});
}
handleSomething() {
this.incrementCount();
this.incrementCount();
this.incrementCount();
} this.setState((state) => {
return {count: state.count + 1}
});
}
handleSomething() {
this.incrementCount();
this.incrementCount();
this.incrementCount();
}setState 函数用于更新 state。它接收一个新的 state 值并将组件的一次重新渲染加入队列。
九、函数组件的state
const Grandson = () => {
const [n, setN] = React.useState(0);
return (
<div className="Grandson">
孙子 n:{n}
<button onClick={() => setN(n + 1)}>+1</button>
</div>
);
}; const [n, setN] = React.useState(0) -- 析构
const [state, setState] = useState(initialState);第一个参数是旧数据,第二个参数新数据,右边为初始值
React.useState(0) 返回一个 state,以及更新 state 的函数。所以拿数组来析构。在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同。
区别:
类组件的 setState 异步改变this.state
函数组件的 setState 不会改变 state,创建新的state
十、类组件的注意事项
- this.state.n += 1无效 其实n已经改变了,只不过UI不会自动更新而已 调用setState才会触发UI更新(异步 更新) 因为React没有像Vue监听data一样监听state
- setState会异步更新UI setState之后,state不会马 上改变,立马读state会失败
十一、两种编程模式
Vue的编程模式:
一个对象,对应一个虚拟DOM,当对象的属性改变时,Vue 通过建立一个虚拟 DOM 来追踪自己要如何改变真实 DOM。,把属性相关的DOM节点全部更新 。(一个对象)
React的编程模式:
一个对象,对应一个虚拟DOM,另一个对象,对应另一个虚拟DOM
对比两个虚拟DOM,(DOM diff)
最后局部更新DOM。(state对象和setState里的新对象)。 (多个对象)
十二、复杂state
class Son extends React.Component {
constructor() {
super();
this.state = {
n: 0,
m: 0
};
}
addN() {
this.setState({ n: this.state.n + 1 });
// m 会被覆盖为 undefined 吗?
}
addM() {
this.setState({ m: this.state.m + 1 });
// n 会被覆盖为 undefined 吗?
}
render() {
return (
///
);
}
}类组件的setState会自动合并第一层属性,第二层不会
const Grandson = () => {
const [n, setN] = React.useState(0);
const [m, setM] = React.useState(0);
return (
<div className="Grandson">
孙子 n:{n}
<button onClick={() => setN(n + 1)}>n+1</button>
m:{m}
<button onClick={() => setM(m + 1)}>m+1</button>
</div>
);
};不推荐写法
const Grandson = () => {
const [state, setState] = React.useState({
n: 0,
m: 0
});
return (
<div className="Grandson">
孙子 n:{state.n}
<button onClick={() => setState({ n: state.n + 1 })}>n+1</button>
m:{state.m}
<button onClick={() => setState({ m: state.m + 1 })}>m+1</button>
</div>
);
};函数组件的setState不会自动合并
<button onClick={() => setState({ ...state, n: state.n + 1 })}>n+1</button>
<button onClick={() => setState({ ...state, m: state.m + 1 })}>m+1</button>使用Object.assign或者展开操作符
changeUser() {
const user = Object.assign({}, this.state.user);
user.name = "jack";
this.setState({
// m 和 n 不会被置空
user: user
});
}十三、事件绑定-this的深入理解
<button onClick={() => this.addN()}>n+1</button> //true
<button onClick={this.addN}>n+1</button> //false
<button onClick={this.addN.bind(this)}>n+1</button>//true
<button onClick={this._addN}>n+1</button> //true- 要理解jsx的大括号真的只是大括号,不是对象
- 第一种由于箭头函数的特性,所以这里的this就是创建时的this
- 第二种 this 变成 window/null , button.onclick.call(null)
<button onClick={this.handleClick}>n+1</button>的实际含义和 method = obj.method 一样,method的this是window,而obj.method里面的this是obj,this.handleClick的里面的this(不是handleClick的this)是实例,而onClick里面的this是undefined
- 第三种 this.addN.bind(this) 两个this是同一个对象(实例)
- 第四种 前提是 this._addN = ()=> this.addN()
- 第五种如下
constructor(){
super()
this.addN = ()=> this.setState({n: this.state.n + 1})
}
render(){
return <button onClick={this.addN}>n+1</button>
}逻辑分析:
- 依然对比 method = obj.method 这个代码
- onClick={this.addN}
- 我们需要担心的是 obj.method 里面的this,原来是obj 现在变成 window
- this.addN 里面的this(不是addN前面的那个this),由于放在了箭头函数里所以this不会改变依然是实例
- 关键在于理解里面的this的含义
constructor(){
super()
this.addN = ()=> this.setState({n: this.state.n + 1})
}等价于
constructor(){
super()
}
addN = ()=> this.setState({n: this.state.n + 1})类的方法可以直接放在constructor里也可以在外面,区别在于addN前面加不加this
十四、类的课外小知识
constructor(){
super()
this.addN = ()=> this.setState({n: this.state.n + 1})
}等价于
constructor(){
super()
}
addN = ()=> this.setState({n: this.state.n + 1})这两种addN最后会出现在实例上而
constructor(){
super()
}
addN(){
this.setState({n: this.state.n + 1})
}
或
addN:function(){
this.setState({n: this.state.n + 1})
}
出现在原型__proto__上
十五、题外话
这么点内容咋写了十几个标题晕了