- React 的核心特征是“数据驱动视图”,UI = render (data) 或者 UI = f (data)。
- jsx 是 JavaScript 的一种语法扩展,它和模板语言很接近,但是它充分具备 JavaScript 的能力。jsx 的本质是 React.createElement 这个 JavaScript 调用的语法糖,jsx 与 html 相似有更高的可读性。
- createElement 对传入参数进行格式化,并在处理之后 return ReactElement方法的调用。
- ReactDOM.render 将虚拟DOM转换为真实DOM。
ReactDOM.render(
// 需要渲染的元素(ReactElement)
element,
// 元素挂载的目标容器(一个真实DOM)
container,
// 回调函数,可选参数,可以用来处理渲染结束后的逻辑
[callback]
)
- React 15 的生命周期:
import React from "react";
import ReactDOM from "react-dom";
// 定义子组件
class LifeCycle extends React.Component {
constructor(props) {
console.log("进入constructor");
super(props);
// state 可以在 constructor 里初始化
this.state = { text: "子组件的文本" };
}
// 初始化渲染时调用
componentWillMount() {
console.log("componentWillMount方法执行");
}
// 初始化渲染时调用
componentDidMount() {
console.log("componentDidMount方法执行");
}
// 父组件修改组件的props时会调用
componentWillReceiveProps(nextProps) {
console.log("componentWillReceiveProps方法执行");
}
// 组件更新时调用
shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate方法执行");
return true;
}
// 组件更新时调用
componentWillUpdate(nextProps, nextState) {
console.log("componentWillUpdate方法执行");
}
// 组件更新后调用
componentDidUpdate(nextProps, nextState) {
console.log("componentDidUpdate方法执行");
}
// 组件卸载时调用
componentWillUnmount() {
console.log("子组件的componentWillUnmount方法执行");
}
// 点击按钮,修改子组件文本内容的方法
changeText = () => {
this.setState({
text: "修改后的子组件文本"
});
};
render() {
console.log("render方法执行");
return (
<div className="container">
<button onClick={this.changeText} className="changeText">
修改子组件文本内容
</button>
<p className="textContent">{this.state.text}</p>
<p className="fatherContent">{this.props.text}</p>
</div>
);
}
}
- React 16 的生命周期:
import React from "react";
import ReactDOM from "react-dom";
// 定义子组件
class LifeCycle extends React.Component {
constructor(props) {
console.log("进入constructor");
super(props);
// state 可以在 constructor 里初始化
this.state = { text: "子组件的文本" };
}
// 初始化/更新时调用
static getDerivedStateFromProps(props, state) {
console.log("getDerivedStateFromProps方法执行");
return {
fatherText: props.text
}
}
// 初始化渲染时调用
componentDidMount() {
console.log("componentDidMount方法执行");
}
// 组件更新时调用
shouldComponentUpdate(prevProps, nextState) {
console.log("shouldComponentUpdate方法执行");
return true;
}
// 组件更新时调用
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("getSnapshotBeforeUpdate方法执行");
return "haha";
}
// 组件更新后调用
componentDidUpdate(preProps, preState, valueFromSnapshot) {
console.log("componentDidUpdate方法执行");
console.log("从 getSnapshotBeforeUpdate 获取到的值是", valueFromSnapshot);
}
// 组件卸载时调用
componentWillUnmount() {
console.log("子组件的componentWillUnmount方法执行");
}
// 点击按钮,修改子组件文本内容的方法
changeText = () => {
this.setState({
text: "修改后的子组件文本"
});
};
render() {
console.log("render方法执行");
return (
<div className="container">
<button onClick={this.changeText} className="changeText">
修改子组件文本内容
</button>
<p className="textContent">{this.state.text}</p>
<p className="fatherContent">{this.props.text}</p>
</div>
);
}
}
render 阶段:纯净且没有副作用,可能会被 React 暂停、终止或重新启动。
pre-commit 阶段:可以读取 DOM。
commit 阶段:可以使用 DOM,运行副作用,安排更新。
render 阶段在执行过程中允许被打断,而 commit 阶段则总是同步执行的。
- React 15 和 16 生命周期的区别:
Mounting 阶段:组件的初始化渲染(挂载)
Updating 阶段:组件的更新
- React 16 废弃的是哪些生命周期:componentWillMount、componentWillUpdate、componentWillReceiveProps。
(1)完全可以转移到其他生命周期(尤其是 componentDidxxx)里去做。
(2)在 Fiber 带来的异步渲染机制下,可能会导致非常严重的 Bug。
(3)即使你没有开启异步,React 15 下也有不少人能把自己“玩死”。
-
数据是如何在 React 组件之间流动的?
(1)父 → 子;
(2)子 → 父;
(3)子A → 父 → 子B;
(4)利用“发布-订阅”模式驱动数据流:
(5)Context:
(6)Redux:在 Redux 的整个工作过程中,数据流是严格单向的。
1. 使用 createStore 来完成 store 对象的创建
// 引入 redux
import { createStore } from 'redux'
// 创建 store
const store = createStore(
reducer,
initial_state,
applyMiddleware(middleware1, middleware2, ...)
);
2. reducer 的作用是将新的 state 返回给 store
const reducer = (state, action) => {
// 此处是各种样的 state处理逻辑
return new_state
}
3. action 的作用是通知 reducer “让改变发生”
const action = {
type: "ADD_ITEM",
payload: '<li>text</li>'
}
4. 派发 action,靠的是 dispatch
import { createStore } from 'redux'
// 创建 reducer
const reducer = (state, action) => {
// 此处是各种样的 state处理逻辑
return new_state
}
// 基于 reducer 创建 state
const store = createStore(reducer)
// 创建一个 action,这个 action 用 “ADD_ITEM” 来标识
const action = {
type: "ADD_ITEM",
payload: '<li>text</li>'
}
// 使用 dispatch 派发 action,action 会进入到 reducer 里触发对应的更新
store.dispatch(action)
发布-订阅
class myEventEmitter {
constructor() {
// eventMap 用来存储事件和监听函数之间的关系
this.eventMap = {};
}
// type 这里就代表事件的名称
on(type, handler) {
// hanlder 必须是一个函数,如果不是直接报错
if (!(handler instanceof Function)) {
throw new Error("哥 你错了 请传一个函数");
}
// 判断 type 事件对应的队列是否存在
if (!this.eventMap[type]) {
// 若不存在,新建该队列
this.eventMap[type] = [];
}
// 若存在,直接往队列里推入 handler
this.eventMap[type].push(handler);
}
// 别忘了我们前面说过触发时是可以携带数据的,params 就是数据的载体
emit(type, params) {
// 假设该事件是有订阅的(对应的事件队列存在)
if (this.eventMap[type]) {
// 将事件队列里的 handler 依次执行出队
this.eventMap[type].forEach((handler, index) => {
// 注意别忘了读取 params
handler(params);
});
}
}
off(type, handler) {
if (this.eventMap[type]) {
this.eventMap[type].splice(this.eventMap[type].indexOf(handler) >>> 0, 1);
}
}
}
// 实例化 myEventEmitter
const myEvent = new myEventEmitter();
// 编写一个简单的 handler
const testHandler = function (params) {
console.log(`test事件被触发了,testHandler 接收到的入参是${params}`);
};
// 监听 test 事件
myEvent.on("test", testHandler);
// 在触发 test 事件的同时,传入希望 testHandler 感知的参数
myEvent.emit("test", "newState");