↓
一、开发周期
React 开发周期要量[1]
我早已把这那哥印在脑袋里头了,没事儿就他们写字,尾端收敛许多他们的思索。u1s1,不晓得 react 的开发周期重新命名为何要是不是长~~~, 小流程,vue 的都较为短。即便采用的振幅却是极高的,Hooks 仅限。
image.png1、constructor
constructor 是类通用型的缺省,常见于初始化,称得上开发周期的劳特尔。React 后来的版Cubzac模块也能不写。
特别注意:在缺省中采用时,super URL将原则上再次出现,因此要在采用 this URL以后采用。super URL也能用以初始化父第一类上的表达式。MDN 表明[2]
class JJTest extends React.Component{
// constructor 读法 constructor(props) {
super(props);
this.state = {
count:0,
};
this.handleClick = this.handleClick.bind(this);
}
// 直接声明state = {
count: 0,
};
}
2、getDerivedStateFromProps
触发时机:state 变化、props 变化、forceUpdate,如上图。
这是一个静态方法, 是一个和模块自身”不相关”的角色. 在这个静态方法中, 除了两个默认的位置参数 nextProps 和 currentState 以外, 你无法访问任何模块上的数据。
// 初始化/更新时初始化staticgetDerivedStateFromProps(nextProps, currentState) {
console.log(nextProps, currentState, “getDerivedStateFromProps方法执行”);
// 返回值是对currentState进行修改 return{
fatherText: nextProps.text,
};
}
3、render
render 表达式返回的 JSX 结构,用于描述具体的渲染内容, render 被初始化时,它会检查 this.props 和 this.state 的变化并返回以下类型之一:
React 元素。通常通过 JSX 创建。例如,
会被 React 渲染为 DOM 节点, 会被 React 渲染为自定义模块,无论是
却是 均为 React 元素。
数组或 fragments。使得 render 方法能返回多个元素。欲了解更多详细信息,请参阅 fragments 文档。
Portals。能渲染子节点到不同的 DOM 子树中。欲了解更多详细信息,请参阅有关 portals 的文档
字符串或数值类型。它们在 DOM 中会被渲染为文本节点
布尔类型或 null。什么都不渲染。(主要用于支持返回 test && 的模式,其中 test 为布尔类型。)
特别注意:如果 shouldComponentUpdate() 返回 false,则不会初始化 render()。
Hooks 不需要写 render 表达式。要特别注意的一点是,即使 Hooks 不需要写 render, 没有用到 React.xxx,模块内却是要import React from “react”;的(至于原因,后续深入 Hooks 学一下,大哥们也能解释下)。React 官方也说了,后续的版会优化掉这一点。
4、componentDidMount
主要用于模块加载完成时做某些操作,比如发起网络请求或者绑定事件。当做 vue 的 mounted 用就行了,这里需要特别注意的是:
componentDidMount() 里直接初始化 setState()。它将触发额外渲染,也就是两次 render,不过问题不大,主要却是理解。
5、shouldComponentUpdate
该方法通过返回 true 或者 false 来确定是否需要触发新的渲染。因为渲染触发最后一道关卡,所以也是性能优化的必争之地。通过添加判断条件来阻止不必要的渲染。特别注意:首次渲染或采用 forceUpdate() 时不会初始化该方法。
React 官方提供了一个通用型的优化方案,也就是 PureComponent。PureComponent 的核心原理就是默认实现了 shouldComponentUpdate 表达式,在这个表达式中对 props 和 state 进行浅较为,用以判断是否触发更新。
当然 PureComponent 也是有缺点的,采用的时候一定要特别注意:由于进行的是浅较为,可能由于深层的数据不一致导致而产生错误的否定判断,从而导致页 面得不到更新。不适合采用在含有多层嵌套第一类的 state 和 prop 中。
shouldComponentUpdate(nextProps, nextState) {
// 浅较为仅较为值与引用,并不会对 Object 中的每一项值进行较为 if (shadowEqual(nextProps, this.props) || shadowEqual(nextState, this.state) ) {
return true}
return false}
6、getSnapshotBeforeUpdate
在 DOM 更新前被初始化,返回值将作为 componentDidUpdate 的第三个参数。
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log(“getSnapshotBeforeUpdate方法执行”);
return “componentDidUpdated的第三个参数”;
}
7、componentDidUpdate
首次渲染不会执行此方法。能采用 setState,会触发重渲染,但一定要小心采用,避免死循环
componentDidUpdate(preProps, preState, valueFromSnapshot) {
console.log(“componentDidUpdate方法执行”);
console.log(, valueFromSnapshot);
}
8、componentWillUnmount
主要用于许多事件的解绑,资源清理等,比如取消定时器,取消订阅事件
小结
开发周期一定要好好理解,一定要动手写,看一下每种情况下,开发周期的执行结果。上述代码中在React-TypeScript 仓库[3]中都有,能 clone 下来跑跑看,或者直接访问俊劫学习系统 LifeCycle[4]。还有些其他的开发周期,componentDidCatch, UNSAFE_componentWillMount()等等,简单了解下就行。
二、JSX
1、循环列表
jsx 中一般用 map 来渲染列表循环类的,vue 中直接在 template 中写 v-for 即可
{
list.map((item, index) =>{
return <AppCard key={index} title={item.title} onClick={item.onClick} />;
});
}
2、样式
(1)className原则上写一个 class 是能的,动态拼接需要借助classnames[5] 库
import style from ./style.css<div className={style.class1 style.class2}</div>
(2)style需要特别注意的:两个括号(样式被当做一个第一类来解析),类似-连接的样式属性需要转换成小驼峰读法。
<div style={{ marginTop: 8 }}>样式</div>
(3)css 隔离u1s1,css 隔离这块却是 vue 的 scoped 好用
css-modulecreate-react-app 中内置了采用 CSS Modules 的配置,和 vue 的 scoped 原理相似,都是在生成的 class 后面加了 hash 值
// style.module.css.text {
color: blue
}
// app.tsximport s from “./style.module.css”;
class App extends Component{
render() {
return <div className={s.text}>css-module text</div>;
}
}
// 编译后.text_3FI3s6uz {
color: blue;
}
styled-components目前社区里最受欢迎的一款 CSS in JS 方案,个人感觉有点别扭,不太喜欢
//引入styled-componentsimport styled from “styled-components”;
//修改了div的样式const Title = styled.div`
font-size: 30px;
color: red;
`;
class App extends Component{
render() {
return(
<> <Title>CSS in JS 方案</Title> </>);
}
}
3、一个 JSX
刚开始从 vue 转过来会有些不适应(话说有多少人直接在 vue 里头写 JSX 的),以后用的都是 Vue Sfc 读法,当然多写写就熟悉了。至于 React 采用 JSX 的优劣势,评论区各抒己见哈。
代码对应页面预览[6]
image.pngrender() {
return(
<> <Alert title=“控制台展示父子模块开发周期的过程” /> <div className=“fatherContainer”> <Button onClick={this.changeText} type=“primary”>修改父模块文本内容
</Button> <Button onClick={this.hideChild} type=“danger”>{this.state.hideChild ? “显示” : “隐藏”}子模块
</Button>{this.state.hideChild ? null : (
<LifeCycle text={this.state.text} count={1} />)}
</div> <div> <BlockLoading loading={this.state.loading} iconSize={64} /> <iframe src={this.state.lifeCycle} title=“navigation” width=“100%” height=“600px” onLoad={this.onLoading} onError={this.onLoading} ></iframe> </div> </>);
}
三、此基础模块
模块这块,个人感觉和 vue 差别却是较为大的,颗粒度更细致,当然也增加了一定难度。这里就简单例举一个TS版的,带 Icon 的标题模块
import cn from “classnames”;
import React from “react”;
import “./style/index.less”;
import{ Icon,IIconProps }from “zent”;
interface IProps {
title: string;
iconType?: IIconProps[type];
isShowIcon?: boolean;
iconClassName?: string;
titleClassName?: string;
}
export const ContentTitle: React.FC<IProps> = (props) =>{
const { title, iconType = youzan, isShowIcon = false, iconClassName, titleClassName, …popProps } = props;
return(
<div className={cn(“content-title“, titleClassName)}>{title}
{isShowIcon && <Icon className={cn(“content-title__icon“, iconClassName)}
{…popProps}
type={iconType} />}
</div>);
};
export defaultContentTitle;
四、高阶模块 HOC
1、含义
和 vue mixins 相同,都是为了解决代码复用的问题,但 react 中早已废弃 mixins,vue 中也不推荐采用。主要是会带来重新命名冲突,相互依赖,不方便维护等许多缺点。
高阶模块其实就是处理 react 模块的表达式,简单理解就是和 ES6 中提供的 export/import 作用相似,不同点在于:高阶模块会进行加工后再导出你需要的东西。类似于方程式:y = ax + b, x 是入口(模块),会根据 a 和 b 进行计算,得到最终的 y(处理后的模块) 给到你用。
2、Demo
官网的实现 Demo: 高阶模块[7]
一个简单的高阶模块(实现有两种方式:属性代理和反向继承):
// 属性代理: 模块属性的许多修改const JJHOC = (WrappedComponent) =>{
return class NewComponent extends React.Component{
render() {
const newProps = { type: “HOC”};
return <WrappedComponent {…this.props} {…newProps} />;
}
};
};
// 反向继承: 在render() 方法中返回 super.render() 方法const JJHOC = (WrappedComponent) =>{
return class NewComponent extends WrappedComponent{
render() {
return super.render();
}
};
};
3、常见 HOC
react-router wit五、模块通信
1、props
和 vue 不同的是,react props 传值能直接写,不需要声明。在 props 上挂载 function,就相当于是 vue 的$emit。同样需要特别注意的是子模块不能修改 props 的值
import React from “react”;
function Child(props){
const sendMsg = (msg) =>{
props.onClick(“子模块的消息”);
};
return(
<div> <div>子模块标题:{props.title}</div> <button onClick={() => sendMsg(“子模块消息”)}> 子传父 </button> </div>);
}
function Father(){
const onClick = (msg) =>{
console.log(`父模块接收:${msg}`);
};
return(
<div> <Child title=“模块props传值测试” onClick={onClick}></Child> </div>);
}
export defaultFather;
2、context
React Context 官网表明[8],跨模块传值。创建了一个上下文,同 context 内的模块都能 通过 Provider 配合 value 采用数据
import * as React from “react”;
import { Button } from “zent”;
// Context 能让我们无须明确地传遍每一个模块,就能将值深入传递进模块树。// 为当前的 theme 创建一个 context(“primary”为默认值)。constThemeContext = React.createContext(“primary”);
export default class App extends React.Component{
render() {
// 采用一个 Provider 来将当前的 theme 传递给以下的模块树。 // 无论多深,任何模块都能读取这个值。 // 在这个例子中,我们将 danger 作为当前的值传递下去。 return(
<ThemeContext.Provider value=“danger”> <Toolbar /> </ThemeContext.Provider>);
}
}
// 尾端的模块再也不必指明往下传递 theme 了。function Toolbar(){
return(
<div> <ThemedButton /> </div>);
}
class ThemedButton extends React.Component{
// 指定 contextType 读取当前的 theme context。 // React 会往上找到最近的 theme Provider,然后采用它的值。 // 在这个例子中,当前的 theme 值为 “danger”。 staticcontextType = ThemeContext;
render() {
return <Button type={this.context}>context测试</Button>;
}
}
3、Redux
Redux 中文文档[9]
redux 的三大核心:
action:action 能说是一个动作,用以描述将要触发的事件。state:单一数据源,用以存储我们的数据。reducer:通过触发的 action 事件来改变 state 的值。不一定非要用,很多项目 context 就早已够用了
(1)挂载采用 createStore 创建一个 store 并通过 Provider 把它放到容器模块中
// index.jsconststore = createStore(appReducer);
ReactDOM.render(
<Provider store={store}> <App /> </Provider>,
document.getElementById(“root”);
);
(2)创建修改的方法和 vuex 相似,都是通过 action 来修改数据
// action.jsexport constaddConst =(payload) =>{
type: “ADD_CONST”,
}
export const minusConst = (payload) =>{
type: “MINUS_CONST”,
}
(3)创建一个 store 集合当 dispatch 触发相应的方法,执行对应的操作,修改 store 数据。
// appReducer.jsconst initialState = { count: 0};
constreducer =(state = initialState, action) =>{
switch(action.type) {
case “ADD_CONST”:
return { count: count + 1};
case “MINUS_CONST”:
return { count: count – 1};
default:
returnstate;
}
};
export defaultreducer;
(4)模块中 redux 采用姿势import React from “react”;
import { connect } from “react-redux”;
const ReduxDemo: React.FC = (props) =>{
const addCount = () =>{
const{ dispatch } = props;
dispatch({
type: “ADD_CONST”,
});
};
const minusCount = () =>{
const{ dispatch } = props;
dispatch({
type: “MINUS_CONST”,
});
};
return(
<div> <button onClick={addCount}>加</button> <button onClick={minusCount}>减</button> <div>{props.state}</div> </div>);
};
const mapStateToProps = (state) =>{
return{
count: state.count,
};
};
export defaultconnect(mapStateToProps)(ReduxDemo);
六、模块校验
React 官网 采用 PropTypes 进行类型检查[10] react props 不是要声明的,但是如果项目规范起来,就需要在 propTypes 中声明 props 类型,特别注意需要引入prop-types库
不过现在更多的是通过 typescript 来校验类型了,开发阶段就能发现问题。
import * as React from “react”;
import PropTypes from “prop-types”;
interface IProps {
name: string;
}
const PropsDemo: React.FC<IProps> = ({ name }) =>{
return <h1>Hello, {name}</h1>;
};
PropsDemo.propTypes = {
name: PropTypes.string,
};
七、React Router
React Router 官网[11] 英文版React Router 中文文档[12] 感觉写的不是很清楚1、特别注意
react-router: 实现了路由的核心功能, react-router 3.x 版还包括操作 dom 的方法,4.x 以上就没有了。react-router-dom: 基于 react-router,加入了在浏览器运行环境下的许多功能,例如:Link 模块,会渲染一个 a 标签,Link 模块源码 a 标签行; BrowserRouter 和 HashRouter 模块,前者采用 pushState 和 popState 事件构建路由,后者采用 window.location.hash 和 hashchange 事件构建路由。react-router-native: 基于 react-router,类似 react-router-dom,加入了 react-native 运行环境下的许多功能2、一个 Demo
import React, { Component } from “react”;
import Admin from “./pages/admin/admin”;
import Login from “./pages/login/Login”;
import { HashRouter, Route, Switch } from “react-router-dom”;
class App extends Component{
render() {
return(
<HashRouter> <Switch> <Route path=“/” component={Admin}></Route> <Route path=“/login” component={Login}></Route> </Switch> </HashRouter>);
}
}
export defaultApp;
3、路由传参
(1)params// router<Route path=/path/:idcomponent={Path}/>
// 传参<link to=“/path/789”>xxx</Link>this.props.history.push({pathname:`/path/${id}`});
this.props.match.params.id
(2)query// router<Route path=/querycomponent={Query}/>
// 传参<Link to={{ path : /query , query : { id : 789 }}}>xxx</Link>this.props.history.push({pathname:“/query”,query: { id : 789}});
this.props.location.query.id
(3)Hooks// 跳转lethistory = useHistory();
history.push(“/”);
useLocation();
useParams();
useRouteMatch();
4、exact 属性
exact 是 Route 下的一条属性,一般而言,react 路由会匹配所有匹配到的路由组价,exact 能够使得路由的匹配更严格许多。
exact 的值为 bool 型,为 true 是表示严格匹配,为 false 时为正常匹配。
如在 exact 为 true 时,’/link’与’/’是不匹配的,但是在 false 的情况下它们又是匹配的。<Route path=”/home” component={Home} exact></Route>
八、归纳
学完开发周期,多练习 JSX,配合 React Router 和 Redux 多写写模块,基本就能上手开发了。没有过多的 API 需要学习,写起来也较为自由。React 虽然生态强大,选着性较为多,但是这样产生了一个问题:什么是 React 的最佳实践?
相关代码仓库:
仓库:React-TypeScript
https://github.com/alexwjj/React-TypeScript
线上预览:俊劫 React 学习系统
https://alexwjj.github.io/study
参考资料
[1]React 开发周期要量: https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
[2]MDN 表明: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/super#%E6%8F%8F%E8%BF%B0
[3]React-TypeScript 仓库: https://github.com/alexwjj/React-TypeScript
[4]俊劫学习系统 LifeCycle: https://alexwjj.github.io/study/#/demo
[5]classnames: https://github.com/JedWatson/classnames
[6]代码对应页面预览: https://alexwjj.github.io/study/#/demo
[7]高阶模块: https://zh-hans.reactjs.org/docs/higher-order-components.html
[8]React Context 官网表明: https://zh-hans.reactjs.org/docs/context.html
[9]Redux 中文文档: http://cn.redux.js.org/
[10]React 官网 采用 PropTypes 进行类型检查: https://zh-hans.reactjs.org/docs/typechecking-with-proptypes.html
[11]React Router 官网: https://reactrouter.com/web/guides/quick-start
[12]React Router 中文文档: http://react-guide.github.io/react-router-cn/
转自:掘金 – 俊劫
https://juejin.cn/post/6960556335092269063
– EOF –
推荐阅读 点击标题可跳转3、Vue 在哪些方面比 React 做得更好?
觉得本文对你有帮助?请分享给更多人
点赞和在看就是最大的支持❤️