快来跟我一起学 React(Day8)
简介
我们继续上一节的内容,开始分析 React 官网:https://reactjs.org/docs/accessibility.html 的 “高级指引” 部分,这一部分会涉及到性能优化、Portals、Render Props 等概念的分析,跟上节奏,我们一起出发吧!
知识点
- 性能优化
- Portals
- Render Props
- 类型检查
准备
我们直接用上一节中的 react-demo-day5
项目来作为我们的 Demo
项目,还没有创建的小伙伴可以直接执行以下命令 clone
一份代码:
git clone -b dev https://gitee.com/vv_bug/react-demo-day5.git
接着进入到项目根目录 react-demo-day5
,并执行以下命令来安装依赖与启动项目:
npm install --registry https://registry.npm.taobao.org && npm start
等项目打包编译成功,浏览器会自动打开项目入口,看到上面截图的效果的时候,我们的准备工作就完成了。
性能优化
UI 更新需要昂贵的 DOM 操作,而 React 内部使用几种巧妙的技术以便最小化 DOM 操作次数。对于大部分应用而言,使用 React 时无需专门优化就已拥有高性能的用户界面。尽管如此,你仍然有办法来加速你的 React 应用。
shouldComponentUpdate 的作用
我们先贴一张官方的组件生命周期图:
可以看到,我们可以根据组件的 shouldComponentUpdate
方法去控制是否继续往下走 render
方法,以及后面的 节点 diff
对比,最后更新 DOM
节点等操作。
可以看到,我们在平时项目中,要尽量避免不必要的 render
操作,我们可以利用组件的 shouldComponentUpdate
方法去控制。
我们在 src/advanced-guides
目录下创建一个 optimizing
目录:
mkdir ./src/advanced-guides/optimizing
然后在 src/advanced-guides/optimizing
目录下创建一个 index.tsx
文件:
import React, {useState} from "react";
import HobbiesCom from "./hobbies.com";function Optimizing() {// 输入框 ref 引用let inputRef = React.createRef<HTMLInputElement>();// 额外的 state 数据let [count, setCount] = useState<number>(0);// hobbies 数据let [hobbies] = useState<Array<string>>(["打游戏", "做美食"]);/*** 添加 hobby*/function handleAddHobby() {if (inputRef.current) {setCount(++count);}}return (<div><input placeholder="请输入你的爱好" ref={ inputRef }/><button onClick={ handleAddHobby }>添加</button>{/* hobby 列表组件 */ }<HobbiesCom hobbies={ hobbies }/>{ count }</div>);
}export default Optimizing;
可以看到,我们简单创建了一个输入框,然后通过 “添加” 按钮修改了 count
的值,而 count
是我们额外定义的一个 State
数据。
接着我们在 src/advanced-guides/optimizing
目录下创建一个 hobbies.com.tsx
组件:
import React from "react";export type Prop = {hobbies: Array<string>,
};class HobbiesCom extends React.Component<Prop> {state = {choosed: -1}handleClick(index: number) {this.setState({choosed: index});}render() {return (<ul>{this.props.hobbies.map((hobby, index) => (<listyle={this.state.choosed === index ? {backgroundColor: "red"} : undefined}key={`hobby-${hobby}-index`}onClick={this.handleClick.bind(this, index)}>{hobby}</li>))}</ul>);}
}export default HobbiesCom;
可以看到,我们在 HobbiesCom
组件中渲染了 hobbies
的数据。
我们重新运行项目看结果:
npm start
可以看到,即使我们没有修改 HobbiesCom
组件中的 habbies
属性值,但是 HobbiesCom
组件仍然走了 render
函数,我们希望的是当 habbies
数组的数据没有变化的时候, HobbiesCom
组件不需要重新进行渲染,那我们该怎么做呢?
我们修改一下 src/advanced-guides/optimizing/hobbies.com.tsx
组件:
import React from "react";export type Prop = {hobbies: Array<string>,
};class HobbiesCom extends React.Component<Prop> {state = {choosed: -1}shouldComponentUpdate(nextProps: Prop, nextState: any) {// 浅对比新老属性的 hoobies 变量跟新老 state 的 choosed 属性return nextProps.hobbies !== this.props.hobbies || this.state.choosed !== nextState.choosed;}...
}export default HobbiesCom;
可以看到,只有当 shouldComponentUpdate
方法返回是 true
的时候,组件才会走 render
方法,我们对属性跟 State
做了一个浅对比,所以避免了不必要 render
方法的执行。
其实在 React
中,提供了一个 PureComponent
组件,它的作用就是在 shouldComponentUpdate
方法中对属性跟 State
进行了一个浅对比。
我们可以用 PureComponent
组件改造一下 src/advanced-guides/optimizing/hobbies.com.tsx
组件:
import React from "react";export type Prop = {hobbies: Array<string>,
};class HobbiesCom extends React.PureComponent<Prop> {state = {choosed: -1}handleClick(index: number) {this.setState({choosed: index});}render() {return (<ul>{ this.props.hobbies.map((hobby, index) => (<listyle={ this.state.choosed === index ? {backgroundColor: "red"} : undefined }key={ `hobby-${ hobby }-index` }onClick={ this.handleClick.bind(this, index) }>{ hobby }</li>)) }</ul>);}
}export default HobbiesCom;
可以看到,我们只需要把继承的 React.Component
改成 React.PureComponent
即可,效果跟上面的一样,我就不演示了哈,小伙伴自己跑跑。
React.memo
我们上面说的都是 “类组件” 中的性能优化,在 “类组件” 中我们可以通过自定义 shouldComponentUpdate
方法或者 React.PureComponent
来减少不必要的 render
操作,但是在 “函数式组件” 中我们该怎么做呢?
在 React17+
,官方已经给给我们提供了一个高阶组件 React.memo
。
我们直接在 src/advanced-guides/optimizing
目录下创建一个 hobbies.func.tsx
函数式组件:
import React, {useState} from "react";export type Prop = {hobbies: Array<string>,
};function HobbiesFunc(props: Prop) {let [choosed, setChoosed] = useState(-1);function handleClick(index: number) {setChoosed(index);}return (<ul>{ props.hobbies.map((hobby, index) => (<listyle={ choosed === index ? {backgroundColor: "red"} : undefined }key={ `hobby-${ hobby }-index` }onClick={ () => {handleClick(index);} }>{ hobby }</li>)) }</ul>);
}function areEqual(prevProps: Prop, nextProps: Prop) {/*如果把 nextProps 传入 render 方法的返回结果与prevProps 传入 render 方法的返回结果一致则返回 true,否则返回 false*/return prevProps.hobbies === nextProps.hobbies;
}export default React.memo<Prop>(HobbiesFunc, areEqual);
可以看到,我们通过 React.memo
高阶组件中的第二个参数 areEqual
方法中做了判断,当新老 Props
中的 habbies
数据相等的时候,不执行组件的 render
操作。
React.memo
仅检查 props 变更。如果函数组件被 React.memo
包裹,且其实现中拥有 useState
,useReducer
或 useContext
的 Hook,当 context 发生变化时,它仍会重新渲染。
默认情况下其只会对复杂对象做浅层对比,如果你想要控制对比过程,那么请将自定义的比较函数通过第二个参数传入来实现,所以我们 Demo
中其实不需要定义第二个参数,默认就可以达到我们想要的效果了。
效果就不演示啦,跟前面的类组件一致,小伙伴自己跑跑看效果哦。
我们分别通过 Demo
演示了 “类组件” 跟 “函数式组件” 中的性能优化,小伙伴一定要自己打断点跑跑看效果哦。
Portals
Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。
ReactDOM.createPortal(child, container)
第一个参数(child
)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment。第二个参数(container
)是一个 DOM 元素。
通常来讲,当你从组件的 render 方法返回一个元素时,该元素将被挂载到 DOM 节点中离其最近的父节点:
render() {// React 挂载了一个新的 div,并且把子元素渲染其中return (<div> {this.props.children}</div> );
}
然而,有时候将子元素插入到 DOM 节点中的不同位置也是有好处的:
render() {// React 并*没有*创建一个新的 div。它只是把子元素渲染到 `domNode` 中。// `domNode` 是一个可以在任何位置的有效 DOM 节点。return ReactDOM.createPortal(this.props.children,domNode );
}
一个 portal 的典型用例是当父组件有 overflow: hidden
或 z-index
样式时,但你需要子组件能够在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框。
我们还是通过 Demo
来演示一下。
首先在 src/advanced-guides
目录下创建一个 portals
目录:
mkdir ./src/advanced-guides
接着我们在 src/advanced-guides/portals
目录下创建一个组件 index.tsx
:
import React, {useState} from "react";
import Model from "./model";function Portals() {let [isShowModel, setShowModel] = useState(false);function handleShow() {if (!isShowModel) {setTimeout(() => {setShowModel(false);}, 1000);}setShowModel((show) => {return !show;});}return (<React.Fragment>{isShowModel && (<Model><div className="model">hello model</div></Model>)}<div onClick={handleShow}>点我 show model</div></React.Fragment>);
}export default Portals;
可以看到,我们用了一个 isShowModel
状态去控制 Model
组件的展示。
然后我们在 src/advanced-guides/portals
目录下创建一个 model.tsx
组件:
import ReactDOM from "react-dom";function Model(props: any) {return ReactDOM.createPortal(props.children, document.body);
}export default Model;
很简单,我们直接用 ReactDOM.createPortal
定义并返回了一个 React
元素,并且把 Model
组件的子元素都绑定到了 document.body
节点上了。
接着我们去 src/main.scss
样式文件中添加一个 model
样式:
.root{font-size: 16px;
}
.model{position: fixed;top: 0;left: 0;right: 0;bottom: 0;width: 200px;height: 200px;margin: auto;line-height: 200px;text-align: center;background-color: rgba(0,0,0,0.7);
}
我们重新运行项目看结果:
可以看到,当我们点击 “show model” 按钮的时候,页面中显示了我们 Model
组件的子元素,并且这些子元素最后都被挂载到了 document.body
节点上了。
下面我们换一种实现方式,我们来实现一个 Toast
组件,让它能像 Model
组件一样,挂载到 body
节点上,然后指定时间后自动消失。
我们在 src/advanced-guides/portals
目录底下创建一个 toast.tsx
组件:
import ReactDom from "react-dom";
import React from "react";class Toast {static divEle: HTMLDivElement | null;static show(msg: string, timing: number = 1000) {Toast.hide();Toast.divEle = document.createElement("div");ReactDom.render((<div className="model">{msg}</div>),Toast.divEle);document.body.appendChild(Toast.divEle);setTimeout(() => {Toast.hide();}, timing);}static hide() {Toast?.divEle && document.body.removeChild(Toast.divEle);Toast.divEle = null;}
}export default Toast;
可以看到,我们用 ReactDom.render
创建了一个 React
根元素,把 Toast
的内容挂载到了一个 div
元素中,接着又把这个 div
元素挂在到了 document.body
节点上了。
我们修改一下 src/advanced-guides/portals/index.tsx
组件,用一下我们的 Toast
组件:
import React, {useState} from "react";
import Model from "./model";
import Toast from "./toast";function Portals() {let [isShowModel, setShowModel] = useState(false);function handleShow() {if (!isShowModel) {setTimeout(() => {setShowModel(false);}, 1000);}setShowModel((show) => {return !show;});}function apiShowModel() {Toast.show("你好呀", 2000);}return (<React.Fragment>{isShowModel && (<Model><div className="model">hello model</div></Model>)}<div onClick={handleShow}>点我 show model</div><div onClick={apiShowModel}>Api Show</div></React.Fragment>);
}export default Portals;
可以看到,我们在 apiShowModel
方法中调用了 Toast.show("你好呀", 2000)
方法创建了个一个 Toast
。
我们重新运行项目看结果:
可以看到,跟前面 Model
组件的效果一致。
下面我们用 ReactDom.createPortal
改造一下 Toast
组件:
import ReactDom from "react-dom";
import React from "react";class Toast {static divEle: React.RefObject<HTMLDivElement> | null;static show(msg: string, timing: number = 1000) {Toast.hide();Toast.divEle = React.createRef<HTMLDivElement>();ReactDom.render(ReactDom.createPortal((<div ref={Toast.divEle} className="model">{msg}</div>), document.body),document.createElement("div"));setTimeout(() => {Toast.hide();}, timing);}static hide() {Toast?.divEle?.current && document.body.removeChild(Toast.divEle.current);Toast.divEle = null;}
}export default Toast;
可以看到,我们把之前的 document.body.appendChild
改成了 ReactDom.createPortal
,效果跟前面的一致,我就不演示啦,很明显了吧,小伙伴有没有弄懂 ReactDom.createPortal
的原理呢?
Render Props
术语 “render prop” 是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术,在 vue
跟 Angular
中叫 “插槽”。
具有 render prop 的组件接受一个返回 React 元素的函数,并在组件内部通过调用此函数来实现自己的渲染逻辑。
说起来抽象,我们还是通过 Demo
来分析一下。
比如我们现在有一个组件支持 “头部”、“内容”、“尾部” 的展示,其中 “头部”、“尾部” 支持自定义方式。
我们首先在 src/advanced-guides
目录下创建一个 render-props
目录:
mkdir ./src/advanced-guides/render-props
然后我们在 src/advanced-guides/render-props
目录下创建一个 index.tsx
组件:
import PropTypes from "prop-types";type Prop = {renderHeader: () => React.ElementType,renderFooter: () => React.ElementType,
};function RenderProp(props: Prop | undefined) {return (<div style={{backgroundColor: "darkcyan"}}>{/* 渲染头部 */}{props?.renderHeader()}{/* 渲染内容 */}<div>我是 RenderProps 组件内容模块</div>{/* 渲染尾部 */}{props?.renderFooter()}</div>);
}RenderProp.propTypes = {renderHeader: PropTypes.func,renderFooter: PropTypes.func,
};
RenderProp.defaultProps = {renderHeader: () => (<div>我是默认头部内容</div>),renderFooter: () => (<div>我是默认尾部内容</div>),
};
export default RenderProp;
可以看到,我们创建了一个 RenderProp
组件,RenderProp
组件中渲染了三个部分 “头部”、“内容”、“尾部”,并且 “头部”、“尾部” 支持自定义。
我们修改一下 src/advanced-guides/index.tsx
组件,引入 RenderProp
组件:
/*** 核心概念列表*/
import CodeSplit from "./code-split";
import Context from "./context";
import ErrorBoundaries from "./error-boundaries";
import ErrorCom from "./error";
import ForwardRef from "./forward-ref";
import Optimizing from "./optimizing";
import Portals from "./portals";
import RenderProps from "./render-props";
function AdvancedGuides() {return (<ErrorBoundaries><div>{/* 代码分割 */ }<CodeSplit/>{/* Context */ }<Context/>{/* 报错的组件 */ }<ErrorCom/>{/* Refs 转发 */ }<ForwardRef/>{/* 性能优化 */ }<Optimizing/>{/* Portals */ }<Portals/>{/* Render Props */ }<RenderProps/></div></ErrorBoundaries>);
};
export default AdvancedGuides;
然后我们重新运行项目看结果:
可以看到,RenderProp
组件渲染了默认的内容。
接下来我们修改一下 src/advanced-guides/index.tsx
组件,自定义 RenderProps
的 “头部” 跟 “尾部” 内容:
/*** 核心概念列表*/
import CodeSplit from "./code-split";
import Context from "./context";
import ErrorBoundaries from "./error-boundaries";
import ErrorCom from "./error";
import ForwardRef from "./forward-ref";
import Optimizing from "./optimizing";
import Portals from "./portals";
import RenderProps from "./render-props";function AdvancedGuides() {return (<ErrorBoundaries><div>{/* 代码分割 */}<CodeSplit/>{/* Context */}<Context/>{/* 报错的组件 */}<ErrorCom/>{/* Refs 转发 */}<ForwardRef/>{/* 性能优化 */}<Optimizing/>{/* Portals */}<Portals/>{/* Render Props */}<RenderPropsrenderHeader={() => (<div>我是自定义头部内容</div>)}renderFooter={() => (<div>我是自定义尾部内容</div>)}/></div></ErrorBoundaries>);
};
export default AdvancedGuides;
然后重新运行项目看结果:
可以看到,我们成功的自定义了 RenderProp
组件的头部跟尾部内容。
有小伙伴要疑问了 “这样做的目的是什么呢?”我举个例子吧:
比如你的领导需要你开发一个 RenderProp
组件,告诉你需要支持 “头部”、“内容”、“尾部” 的渲染。ok,你能力很强,很快就完成了领导的需求,你开心的去休假去了,然后你领导把这个组件直接给到了另外一个开发手中,领导心血来潮了,说 “头部内容样式需要改改,赶紧把那个休假的人叫回来!!”,哈哈,这个时候你是不是就很无语了呢?那如果你的组件支持自定义功能,你就可以很牛逼的告诉领导:“ 不想用默认样式的话,支持自定义头部跟尾部的,爱怎么玩就怎么玩”。
使用 PropTypes 进行类型检查
随着你的应用程序不断增长,你可以通过类型检查捕获大量错误。对于某些应用程序来说,你可以使用 Flow 或 TypeScript 等 JavaScript 扩展来对整个应用程序做类型检查。但即使你不使用这些扩展,React 也内置了一些类型检查的功能。要在组件的 props 上进行类型检查,你只需配置特定的 propTypes
属性,比如我们上面的 RenderProp
组件:
import PropTypes from "prop-types";type Prop = {renderHeader: () => React.ElementType,renderFooter: () => React.ElementType,
};function RenderProp(props: Prop | undefined) {return (...);
}RenderProp.propTypes = {renderHeader: PropTypes.func,renderFooter: PropTypes.func,
};
RenderProp.defaultProps = {renderHeader: () => (<div>我是默认头部内容</div>),renderFooter: () => (<div>我是默认尾部内容</div>),
};
export default RenderProp;
我们使用了 ts
静态类型校验跟 prop-types
的动态校验,如果在使用组件的时候不按规定传递属性类型的话,开发模式中直接就会报错了。
我们修改一下 src/advanced-guides/index.tsx
文件:
{/* Render Props */}<RenderPropsrenderHeader={1}renderFooter={() => (<div>我是自定义尾部内容</div>)}/>
可以看到,首先是 IDE
报错了,说我们需要的是 function
类型,但是你传递的是 number
类型,Webpack
编译也直接提示报错了。
接着是页面中的提示 :
PropTypes
以下提供了使用不同验证器的例子:
import PropTypes from 'prop-types';MyComponent.propTypes = {// 你可以将属性声明为 JS 原生类型,默认情况下// 这些属性都是可选的。optionalArray: PropTypes.array,optionalBool: PropTypes.bool,optionalFunc: PropTypes.func,optionalNumber: PropTypes.number,optionalObject: PropTypes.object,optionalString: PropTypes.string,optionalSymbol: PropTypes.symbol,// 任何可被渲染的元素(包括数字、字符串、元素或数组)// (或 Fragment) 也包含这些类型。optionalNode: PropTypes.node,// 一个 React 元素。optionalElement: PropTypes.element,// 一个 React 元素类型(即,MyComponent)。optionalElementType: PropTypes.elementType,// 你也可以声明 prop 为类的实例,这里使用// JS 的 instanceof 操作符。optionalMessage: PropTypes.instanceOf(Message),// 你可以让你的 prop 只能是特定的值,指定它为// 枚举类型。optionalEnum: PropTypes.oneOf(['News', 'Photos']),// 一个对象可以是几种类型中的任意一个类型optionalUnion: PropTypes.oneOfType([PropTypes.string,PropTypes.number,PropTypes.instanceOf(Message)]),// 可以指定一个数组由某一类型的元素组成optionalArrayOf: PropTypes.arrayOf(PropTypes.number),// 可以指定一个对象由某一类型的值组成optionalObjectOf: PropTypes.objectOf(PropTypes.number),// 可以指定一个对象由特定的类型值组成optionalObjectWithShape: PropTypes.shape({color: PropTypes.string,fontSize: PropTypes.number}),// An object with warnings on extra propertiesoptionalObjectWithStrictShape: PropTypes.exact({name: PropTypes.string,quantity: PropTypes.number}),// 你可以在任何 PropTypes 属性后面加上 `isRequired` ,确保// 这个 prop 没有被提供时,会打印警告信息。requiredFunc: PropTypes.func.isRequired,// 任意类型的必需数据requiredAny: PropTypes.any.isRequired,// 你可以指定一个自定义验证器。它在验证失败时应返回一个 Error 对象。// 请不要使用 `console.warn` 或抛出异常,因为这在 `oneOfType` 中不会起作用。customProp: function(props, propName, componentName) {if (!/matchme/.test(props[propName])) {return new Error('Invalid prop `' + propName + '` supplied to' +' `' + componentName + '`. Validation failed.');}},// 你也可以提供一个自定义的 `arrayOf` 或 `objectOf` 验证器。// 它应该在验证失败时返回一个 Error 对象。// 验证器将验证数组或对象中的每个值。验证器的前两个参数// 第一个是数组或对象本身// 第二个是他们当前的键。customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {if (!/matchme/.test(propValue[key])) {return new Error('Invalid prop `' + propFullName + '` supplied to' +' `' + componentName + '`. Validation failed.');}})
};
限制单个元素
你可以通过 PropTypes.element
来确保传递给组件的 children 中只包含一个元素。
import PropTypes from 'prop-types';class MyComponent extends React.Component {render() {// 这必须只有一个元素,否则控制台会打印警告。const children = this.props.children;return (<div>{children}</div>);}
}MyComponent.propTypes = {children: PropTypes.element.isRequired
};
默认 Prop 值
您可以通过配置特定的 defaultProps
属性来定义 props
的默认值:
class Greeting extends React.Component {render() {return (<h1>Hello, {this.props.name}</h1>);}
}// 指定 props 的默认值:
Greeting.defaultProps = {name: 'Stranger'
};// 渲染出 "Hello, Stranger":
ReactDOM.render(<Greeting />,document.getElementById('example')
);
如果你正在使用像 transform-class-properties 的 Babel 转换工具,你也可以在 React 组件类中声明 defaultProps
作为静态属性。此语法提案还没有最终确定,需要进行编译后才能在浏览器中运行。要了解更多信息,请查阅 class fields proposal。
class Greeting extends React.Component {static defaultProps = {name: 'stranger'}render() {return (<div>Hello, {this.props.name}</div>)}
}
defaultProps
用于确保 this.props.name
在父组件没有指定其值时,有一个默认值。propTypes
类型检查发生在 defaultProps
赋值后,所以类型检查也适用于 defaultProps
。
函数组件
如果你在常规开发中使用函数组件,那你可能需要做一些适当的改动,以保证 PropsTypes 应用正常。
假设你有如下组件:
export default function HelloWorldComponent({ name }) {return (<div>Hello, {name}</div>)
}
如果要添加 PropTypes,你可能需要在导出之前以单独声明的一个函数的形式,声明该组件,具体代码如下:
function HelloWorldComponent({ name }) {return (<div>Hello, {name}</div>)
}export default HelloWorldComponent
接着,可以直接在 HelloWorldComponent
上添加 PropTypes:
import PropTypes from 'prop-types'function HelloWorldComponent({ name }) {return (<div>Hello, {name}</div>)
}HelloWorldComponent.propTypes = {name: PropTypes.string
}export default HelloWorldComponent
上面的这些内容我们在前面的 Demo
中都有演示过,我们就不再演示了,小伙伴自己多敲敲哦!
总结
ok,React
的高级指引部分我们就算是分析完毕了,认认真真看到这里的小伙伴想必搞定面试跟简单的 React
项目应该是问题不大了,后面章节我们将会会介绍 React
中的 Hook
、“全家桶”、“源码分析” 等等。
这节到这就结束啦,下节见~
欢迎志同道合的小伙伴一起交流,一起学习。
Demo 项目全部代码:https://gitee.com/vv_bug/react-demo-day5/tree/dev/
快来跟我一起学 React(Day8)相关推荐
- 快来跟我一起学 React(Day5)
简介 上一节我们完成了从 0 开始搭建一个企业级 React 项目的全部内容,项目是有了,但是我们一直都没有近距离接触过 React,所以接下来我们就快速撸一遍 React 官方文档内容,弄清楚一些概 ...
- 有了vue为什么还学react?
有了vue为什么还学react?因为react可弥补vue的不足.下面本篇文章就来给大家介绍一下.有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助. vue的优缺点 vue是Evan Y ...
- 手挽手带你学React:四档(上)一步一步学会react-redux (自己写个Redux)
手挽手带你学React入门四档,用人话教你react-redux,理解redux架构,以及运用在react中.学完这一章,你就可以开始自己的react项目了. 之前在思否看到过某个大神的redux搭建 ...
- 从零学React Native之13 持久化存储
数据持久化就是指应用程序将某些数据存储在手机存储空间中. 借助native存储 这种方式不言而喻,就是把内容传递给native层,通过原生API存储,详见从零学React Native之05混合开发 ...
- node都会 react_学react需要node吗
学react需要node吗 学习react不需要安装node,react.js和node.js没有太大的关联性.完全可以独立的学习react.js. 但我们通常都会使用react提供的脚手架搭建项目结 ...
- 手挽手带你学React:三档 React-router4.x的使用
手挽手带你学React入门三档,带你学会使用Reacr-router4.x,开始创建属于你的React项目 什么是React-router React Router 是一个基于 React 之上的强大 ...
- 小白学react之页面BaseLayout框架及微信的坑
上一篇<小白学react之SASS实战>我们学习了如何通过运用sass来为我们的应用页面"上色",加入css的支持. 但是我们到现在为止,每个页面的标题还是系统默认的, ...
- 会Vue还有必要学React吗?
这里说一下我的切身感受,为什么说学了Vue还要学React. Vue我有三年的开发经验,写过3个大型项目和其他的一些中小型项目,以前我一直觉得只要是能解决问题,用什么框架都一样,我把Vue学到极致就可 ...
- 跟我一起学 React + dva + Mockjs(9 个视频)
跟我一起学 React + dva + Mockjs(9 个视频) 跟我一起学 React + dva + Mockjs #1 课程介绍「04:21」 跟我一起学 React + dva + Mock ...
最新文章
- python最新版本-Python 3.8 已发布 现在是切换至新版本的好时机吗?
- 滑动拼图验证码操作步骤:_拼图项目:延期的后果
- bs 程序用户个性化设置保存_想更改win10设置,这12种打开方法你不可不知,方便又快捷...
- Delphi Note
- LM算法+推导+C++代码实践
- 水利系统防雷,如何做好洪水预报警报系统的雷电防护
- 项目实训(十三)安装pun,pun的基础使用和概念
- 2022高教社杯 国赛数学建模 B题思路
- 零基础最详细html和css
- 【Python网络爬虫】基本原理
- 用 HI3559A / Hi3519A 接入 BT1120或BT656视频
- Golang 小技巧
- 深度解析推荐系统的算法原理
- 和讯网分行业和分省份社会责任指数(2010-2020年)
- 数据库开发面试问题汇总
- 喜大普奔——WIN10 虚拟机上可以跑NVIDIA CUDA,Hyper-v或是WSL?
- Batch Normalization究竟学到了数据的什么信息?
- 波奇宠物IPO的荣与忧:市场可期,但依赖性难解,还多次被罚
- 有哪些dns攻击类型呢?如何防护网站DNS不被恶意攻击?
- 关闭iTunes的强制自动文件备份 -(电脑空间不足,无法升级iPhone)
热门文章
- 目标检测: Camvid 语义标签转化为bbox标签
- InterSystems IRIS、IRIS for Health和HealthShare Health Connect的2021.1版本的预览版现在已经发布!...
- 浏览器的渲染机制-入门详细图解
- jdk源码写过注释后debug提示source code does not match the bytecode
- 启动报错this is very likely to create a memory leak
- ddr3ddr4 lpddr4速率_超极本的LPDDR3与笔记本的DDR4性能差多少
- B站《后浪》刷屏全网:谁不是一边实现梦想,一边和现实对抗?
- 网站快速通过ICP备案的方法!
- [附源码]计算机毕业设计springboot居家养老服务系统小程序
- 新农慕课python答案第零周答案_智慧树慕课答案农产品加工工艺学参考答案公众号...