React JSX
XML 被设计用来传输和存储数据
HTML 被设计用来显示数据
- JSX 就是 JavaScript,会发现react的写法与传统的HTML不一样,比如class 在react写成className,for在react写成htmlFor ,这些标签语法就是JSX。
const blackBtn = <button className="btn-black">black btn</button>
JSX如何判断条件和渲染列表
react条件判断就和javasctipt一样:用if else,三元表达式,逻辑运算符 && ||
class ConditionDemo extends React.Component {
constructor(props) {
super(props)
this.state = {
theme: 'black'
}
}
render() {
const blackBtn = <button className="btn-black">black btn</button>
const whiteBtn = <button className="btn-white">white btn</button>
// // if else
if (this.state.theme === 'black') {
return blackBtn
} else {
return whiteBtn
}
// // 三元运算符
return <div>
{ this.state.theme === 'black' ? blackBtn : whiteBtn }
</div>
// &&
return <div>
{ this.state.theme === 'black' && blackBtn }
</div>
}
}
export default ConditionDemo
React渲染
要将React元素渲染到根DOM节点中,把React元素传递给 ReactDOM.render() 的方法来将其渲染到页面上渲染列表
用map、key,react在render函数里面,把map遍历的对象,或者key遍历的对象放在{}表达式里面,把它渲染出来
React事件为何使用bind绑定this
Javascript是一种基于对象(object-based)的语言
class 的方式是 function 方式的语法糖,ES6中 class 的类型还是 function
JSX 回调函数中的 this,类的方法默认是不会绑定 this 的
react组件 ,this指向是指向当前的组件,当组件实例化之后,this指向的是新生成的实例,在新生成的实例使用this.state.xxx是找不到的,所以需要在构造器constructor重新绑定一下this
React 事件和 DOM 事件
event.preventDefault() 方法阻止元素发生默认的行为(如,当点击提交按钮时阻止对表单的提交)。
- JSX 的语法进行事件处理,需要传入一个函数作为事件处理函数,而不是一个字符串(DOM 元素的写法)
HTML 通常写法
<button onclick="activateLasers()">
激活按钮
</button>
React 中写法
<button onClick={activateLasers}>
激活按钮
</button>
React 中的event是一个合成事件,不是原生的Event
- 通过 bind 方式向监听函数传参,在类组件中定义的监听函数,事件对象 e 要排在所传递参数的后面
<li key={item.id} onClick={this.clickHandler4.bind(this, item.id, item.title)}>
index {index}; title {item.title}
</li>
// 传递参数
clickHandler4(id, title, event) {
console.log(id, title)
console.log('event', event) // 最后追加一个参数,即可接收 event
}
React表单
在React中,可变的状态通常保存在组件的状态属性中,并且只能用 setState() 方法进行更新
- react受控组件:类似双向数据绑定,不过在react中需要开发人员之间编写代码。比如用户在输入框input输入文字会同时更新显示到界面上
设置了输入框 input 值 value = {this.state.value}。在输入框值发生变化时我们可以更新 value。我们可以使用 onChange 事件来监听 input 的变化,并修改 value
class HelloMessage extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'Hello word!'};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
var value = this.state.value;
return <div>
<input type="text" value={value} onChange={this.handleChange} />
<h4>{value}</h4>
</div>;
}
}
ReactDOM.render(
<HelloMessage />,
document.getElementById('example')
);
- 非受控组件
非受控也就意味着我可以不需要设置它的state属性,而通过ref来操作真实的DOM
父组件
调用:let dataref=this.onRef.getData();
console.log('dataref',dataref)
//调用子组件
<RefTest ref={r=>(this.onRef)=r} />
//子组件
getData=()=>{
console.log('获取数据')
}
React父子组件通讯
通讯是单向的,数据必须是由一方传到另一方
1.父组件与子组件间的通信
在 React 中,父组件可以向子组件通过传 props 的方式,向子组件进行通讯
2.子组件传值父组件
子组件通过调用父组件传递到子组件的方法向父组件传递消息的
//子组件
this.props.listCall(list);
listCall=()=>{
}
<子组件 listCall={listCall}/>
- 父组件管理数据
- 子组件渲染数据
- 表单提交数据
react处理数据,把状态或者数据提到最高的组件上,最高级别的组件管理数据,然后向list子组件下发数据,list子组件渲染数据,input输入框下发一个函数,input组件执行这个事件,最高级别的组件负责把数据拼接好,执行完之后再在知list子组件
import React from 'react'
import PropTypes from 'prop-types'
class Input extends React.Component {
constructor(props) {
super(props)
this.state = {
title: ''
}
}
render() {
return <div>
<input value={this.state.title} onChange={this.onTitleChange}/>
<button onClick={this.onSubmit}>提交</button>
</div>
}
onTitleChange = (e) => {
this.setState({
title: e.target.value
})
}
onSubmit = () => {
const { submitTitle } = this.props
submitTitle(this.state.title) // 'abc'
this.setState({
title: ''
})
}
}
// props 类型检查
Input.propTypes = {
submitTitle: PropTypes.func.isRequired
}
class List extends React.Component {
constructor(props) {
super(props)
}
render() {
const { list } = this.props
return <ul>{list.map((item, index) => {
return <li key={item.id}>
<span>{item.title}</span>
</li>
})}</ul>
}
}
// props 类型检查
List.propTypes = {
list: PropTypes.arrayOf(PropTypes.object).isRequired
}
class Footer extends React.Component {
constructor(props) {
super(props)
}
render() {
return <p>
{this.props.text}
{this.props.length}
</p>
}
componentDidUpdate() {
console.log('footer did update')
}
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.text !== this.props.text
|| nextProps.length !== this.props.length) {
return true // 可以渲染
}
return false // 不重复渲染
}
// React 默认:父组件有更新,子组件则无条件也更新!!!
// 性能优化对于 React 更加重要!
// SCU 一定要每次都用吗?—— 需要的时候才优化
}
class TodoListDemo extends React.Component {
constructor(props) {
super(props)
// 状态(数据)提升
this.state = {
list: [
{
id: 'id-1',
title: '标题1'
},
{
id: 'id-2',
title: '标题2'
},
{
id: 'id-3',
title: '标题3'
}
],
footerInfo: '底部文字'
}
}
render() {
return <div>
<Input submitTitle={this.onSubmitTitle}/>
<List list={this.state.list}/>
<Footer text={this.state.footerInfo} length={this.state.list.length}/>
</div>
}
onSubmitTitle = (title) => {
this.setState({
list: this.state.list.concat({
id: `id-${Date.now()}`,
title
})
})
}
}
export default TodoListDemo
setState为何使用不可变值
- 在修改状态时千万不能改变原来的状态state,修改后的数据不能影响原来的数据,是因为在react中的shouldMountUpdate声明周期数据将要改变的值与之前的数据做个比较,来决定是否更新,以setState不可变值来作为性能优化。
ssetState是同步还是异步
- setState函数中传入的是对象,是异步的,拿不到最新的值
- setState函数传入的是回调函数,是同步的,可以拿到最新的值
this.setState({
count: this.state.count + 1
}, () => {
console.log('count by callback', this.state.count) // 回调函数中可以拿到最新的 state
})
console.log('count', this.state.count) // 异步的,拿不到最新值
- setTimeout函数里面的ssetState是同步的,可以拿到最新的值
// setTimeout 中 setState 是同步的
setTimeout(() => {
this.setState({
count: this.state.count + 1
})
console.log('count in setTimeout', this.state.count)
}, 0)
- 自己定义的 DOM 事件,setState 是同步的
bodyClickHandler = () => {
this.setState({
count: this.state.count + 1
})
console.log('count in body event', this.state.count)
}
componentDidMount() {
// 自己定义的 DOM 事件,setState 是同步的
document.body.addEventListener('click', this.bodyClickHandler)
}
- state 异步更新的话,更新前会被合并,异步更新的话是传入对象,会被合并(类似 Object.assign ),执行结果只一次 +1
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
-state同步更新的话,不会被合并, 传入函数,执行结果是 +3
this.setState((prevState, props) => {
return {
count: prevState.count + 1
}
})
this.setState((prevState, props) => {
return {
count: prevState.count + 1
}
})
this.setState((prevState, props) => {
return {
count: prevState.count + 1
}
})
React组件生命周期
https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
react组件不是一个真正的Dom,react会生成一个虚拟Dom,虚拟Dom会经历挂载、更新,销毁阶段。
- 挂载:组件挂载到dom树上
- 更新:重新渲染页面
- 删除:组件从dom树上删除
挂载过程
constructor:调用构造函数
componentWillMount:组件将要更新
render:组件渲染到页面上
componentDidMount:组件已经加载或者已经挂载到页面上。更新过程
componentWillReceiveProps:组件将要接受参数
shouldComponentUpdata:组件是否应该更新,必须retruntrue或者false,true表示更新,false表示不更新。
componentWillUpdata:组件将要更新
render:组件渲染到页面
getSnaphotBeforeUpdata:获取截图在更新前
componentWillUpdata:组件已经更新卸载过程
componentWillUnmount:组件讲要卸载
React基本使用总结
- JSX基本使用
- 条件渲染条件判断
- 列表渲染
- setState
- 生命周期
- 事件
- 表单
- 组件和props(组件之间的通讯)
React函数组件和calss组件的区别
- 纯函数,输入props,输出JSX
- 没有实例,没有生命周期,没有state
- 不能扩展其他方法
什么是react非受控组件
- 非受控组件
- ref
- defaultValue defaultChecked
- 手动操作DOM元素
非受控组件使用场景
- 必须手动操作DOM 元素,setSate实现不了
- 文件上传<input type=file>
- 某些富文本编辑器,需要传入DOM元素
注意:优先使用受控组件,符合React设计原则,必须操作DOM时,在使用非受控组件(操作DOM影响性能)
import React from 'react'
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
name: 'xiaowu',
flag: true,
}
this.nameInputRef = React.createRef() // 创建 ref
this.fileInputRef = React.createRef()
}
render() {
// input defaultValue
return <div>
{/* 使用 defaultValue 而不是 value ,使用 ref */}
<input defaultValue={this.state.name} ref={this.nameInputRef}/>
{/* state 并不会随着改变 */}
<span>state.name: {this.state.name}</span>
<br/>
<button onClick={this.alertName}>alert name</button>
</div>
// checkbox defaultChecked
return <div>
<input
type="checkbox"
defaultChecked={this.state.flag}
/>
</div>
// file
return <div>
<input type="file" ref={this.fileInputRef}/>
<button onClick={this.alertFile}>alert file</button>
</div>
}
alertName = () => {
const elem = this.nameInputRef.current // 通过 ref 获取 DOM 节点
alert(elem.value) // 不是 this.state.name
}
alertFile = () => {
const elem = this.fileInputRef.current // 通过 ref 获取 DOM 节点
alert(elem.files[0].name)
}
}
export default App
什么场景需要用React Portals
- 组件渲染到父组件以外
组件默认会按照既定层次嵌套渲染
// 正常渲染
return <div className="modal">
{this.props.children} {/* vue slot */}
</div>
// 使用 Portals 渲染到 body 上。
// fixed 元素要放在 body 上,有更好的浏览器兼容性。
return ReactDOM.createPortal(
<div className="modal">{this.props.children}</div>,
document.body // DOM 节点
)
React Context
Context使用场景
- 公共信息(语言、主题)如何传递给每个子组件
- 用props太繁琐
- 用redux没有必须要
import React from 'react'
// 创建 Context 填入默认值(任何一个 js 变量)
const ThemeContext = React.createContext('light')
// 底层组件 - 函数是组件
function ThemeLink (props) {
// const theme = this.context // 会报错。函数式组件没有实例,即没有 this
// 函数式组件可以使用 Consumer
return <ThemeContext.Consumer>
{ value => <p>link's theme is {value}</p> }
</ThemeContext.Consumer>
}
// 底层组件 - class 组件
class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// static contextType = ThemeContext // 也可以用 ThemedButton.contextType = ThemeContext
render() {
const theme = this.context // React 会往上找到最近的 theme Provider,然后使用它的值。
return <div>
<p>button's theme is {theme}</p>
</div>
}
}
ThemedButton.contextType = ThemeContext // 指定 contextType 读取当前的 theme context。
// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar(props) {
return (
<div>
<ThemedButton />
<ThemeLink />
</div>
)
}
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
theme: 'light'
}
}
render() {
return <ThemeContext.Provider value={this.state.theme}>
<Toolbar />
<hr/>
<button onClick={this.changeTheme}>change theme</button>
</ThemeContext.Provider>
}
changeTheme = () => {
this.setState({
theme: this.state.theme === 'light' ? 'dark' : 'light'
})
}
}
export default App
React异步加载组件
- import()
- React.lazy()
- React.Suspense
import React from 'react'
const ContextDemo = React.lazy(() => import('./ContextDemo'))
class App extends React.Component {
constructor(props) {
super(props)
}
render() {
return <div>
<p>引入一个动态组件</p>
<hr />
<React.Suspense fallback={<div>Loading...</div>}>
<ContextDemo/>
</React.Suspense>
</div>
// 1. 强制刷新,可看到 loading (看不到就限制一下 chrome 网速)
// 2. 看 network 的 js 加载
}
}
export default App
性能优化
react性能优化, 其主要目的就是防止不必要的子组件渲染更新。
- setSate不可变值
- shouldComponentUpdate
- PureComponet和React.memo
- 不可变值immutable.js
使用shouldComponentUpdat生命周期函数,如果更新之后的值不等于this.state.count的值可以渲染,否则不能渲染,减少渲染操作次数,优化性能
使用PureComponentPureComponent 对状态的对比是浅比较,当父组件修改跟子组件无关的状态时,再也不会触发自组件的更新了
// 子组件
import React, { PureComponent } from 'react';
class SonComponent extends PureComponent {
render() {
const { sonMsg } = this.props;
console.log('sonMsg', sonMsg + ' || ' + Date.now());
return (
<div>{sonMsg}</div>
)
}
}
export default SonComponent;
// 父组件
import React, { Component } from 'react';
import './App.css';
import SonComponent from './SonComponent';
class FatherComponent extends Component {
constructor() {
super();
this.state = {
fatherMsg: "who's your daddy",
sonMsg: {
val: "I'm son"
}
}
}
render() {
const { fatherMsg, sonMsg } = this.state;
console.log('fatherMsg', fatherMsg);
return (
<div>
<button
onClick={() => {
sonMsg.val = 'son' + Date.now();
this.setState({ sonMsg })}
}>
变换sonMsg值</button>
<SonComponent sonMsg={sonMsg}></SonComponent>
</div>
);
}
}
export default FatherComponent;
React.memo 为高阶组件,它与 React.PureComponent 非常相似,但它适用于函数组件,但不适用于 class 组件。
function Mycomponet(props){
// 使用props渲染
}
function areEqual(prevProps,nextProps){
//如果把nextProps(更新之后的数据)传入render方法的返回结果与将prevProps(更新之前的数据)传入render方法的返回结果一致返回ture,否则返回false
}
export default React.memo(Mycomponet,areEqual)
参考:https://www.jianshu.com/p/3275e2b9a928
// 演示 shouldComponentUpdate 的基本使用
shouldComponentUpdate(nextProps, nextState) {
if (nextState.count !== this.state.count) {
return true // 可以渲染
}
return false // 不重复渲染
}
React性能优化shouldComponentUpdate默认返回什么
shouldComponentUpdate默认返回true
React性能优化-shouldComponentUpdate配合不可变值
- shouldComponentUpdate默认返回true
- 必须配合不可变值一起使用
- 可先不用shouldComponentUpdate,有性能问题时再考虑使用
// 增加 shouldComponentUpdate
shouldComponentUpdate(nextProps, nextState) {
// _.isEqual 做对象或者数组的深度比较(一次性递归到底)
if (_.isEqual(nextProps.list, this.props.list)) {
// 相等,则不重复渲染
return false
}
return true // 不相等,则渲染
}
//配合不可变值使用
onSubmitTitle = (title) => {
this.setState({
list: this.state.list.concat({ //使用concat,原数据不影响新数据,故不可变值
id: `id-${Date.now()}`,
title
})
})
}
完整代码
class Input extends React.Component {
constructor(props) {
super(props)
this.state = {
title: ''
}
}
render() {
return <div>
<input value={this.state.title} onChange={this.onTitleChange}/>
<button onClick={this.onSubmit}>提交</button>
</div>
}
onTitleChange = (e) => {
this.setState({
title: e.target.value
})
}
onSubmit = () => {
const { submitTitle } = this.props
submitTitle(this.state.title)
this.setState({
title: ''
})
}
}
// props 类型检查
Input.propTypes = {
submitTitle: PropTypes.func.isRequired
}
class List extends React.Component {
constructor(props) {
super(props)
}
render() {
const { list } = this.props
return <ul>{list.map((item, index) => {
return <li key={item.id}>
<span>{item.title}</span>
</li>
})}</ul>
}
// 增加 shouldComponentUpdate
shouldComponentUpdate(nextProps, nextState) {
// _.isEqual 做对象或者数组的深度比较(一次性递归到底)
if (_.isEqual(nextProps.list, this.props.list)) {
// 相等,则不重复渲染
return false
}
return true // 不相等,则渲染
}
}
// props 类型检查
List.propTypes = {
list: PropTypes.arrayOf(PropTypes.object).isRequired
}
class TodoListDemo extends React.Component {
constructor(props) {
super(props)
this.state = {
list: [
{
id: 'id-1',
title: '标题1'
},
{
id: 'id-2',
title: '标题2'
},
{
id: 'id-3',
title: '标题3'
}
]
}
}
render() {
return <div>
<Input submitTitle={this.onSubmitTitle}/>
<List list={this.state.list}/>
</div>
}
onSubmitTitle = (title) => {
// 正确的用法
this.setState({
list: this.state.list.concat({
id: `id-${Date.now()}`,
title
})
})
// // 为了演示 SCU ,故意写的错误用法
// this.state.list.push({
// id: `id-${Date.now()}`,
// title
// })
// this.setState({
// list: this.state.list
// })
}
}
export default TodoListDemo
React性能优化-PureComponent和memo
- PureComponent,shouldComponentUpdate中实现了浅比较
- memo,函数组件中的PureComponent
- 浅比较已使用大部分情况(尽量不要做深度比较)
class Input extends React.Component {}
class List extends React.PureComponent {
...
shouldComponentUpdate() {/*浅比较*/}
}
完整代码
import React from 'react'
import PropTypes from 'prop-types'
class Input extends React.Component {
constructor(props) {
super(props)
this.state = {
title: ''
}
}
render() {
return <div>
<input value={this.state.title} onChange={this.onTitleChange}/>
<button onClick={this.onSubmit}>提交</button>
</div>
}
onTitleChange = (e) => {
this.setState({
title: e.target.value
})
}
onSubmit = () => {
const { submitTitle } = this.props
submitTitle(this.state.title)
this.setState({
title: ''
})
}
}
// props 类型检查
Input.propTypes = {
submitTitle: PropTypes.func.isRequired
}
class List extends React.PureComponent {
constructor(props) {
super(props)
}
render() {
const { list } = this.props
return <ul>{list.map((item, index) => {
return <li key={item.id}>
<span>{item.title}</span>
</li>
})}</ul>
}
shouldComponentUpdate() {/*浅比较*/}
}
// props 类型检查
List.propTypes = {
list: PropTypes.arrayOf(PropTypes.object).isRequired
}
class TodoListDemo extends React.Component {
constructor(props) {
super(props)
this.state = {
list: [
{
id: 'id-1',
title: '标题1'
},
{
id: 'id-2',
title: '标题2'
},
{
id: 'id-3',
title: '标题3'
}
]
}
}
render() {
return <div>
<Input submitTitle={this.onSubmitTitle}/>
<List list={this.state.list}/>
</div>
}
onSubmitTitle = (title) => {
// 正确的用法
this.setState({
list: this.state.list.concat({
id: `id-${Date.now()}`,
title
})
})
// // 为了演示 SCU ,故意写的错误用法
// this.state.list.push({
// id: `id-${Date.now()}`,
// title
// })
// this.setState({
// list: this.state.list
// })
}
}
export default TodoListDemo
React.PureComponent 中的 shouldComponentUpdate() 仅作对象的浅层比较。如果对象中包含复杂的数据结构,则有可能因为无法检查深层的差别,产生错误的比对结果。仅在你的 props 和 state 较为简单时,才使用 React.PureComponent,或者在深层数据结构发生变化时调用 forceUpdate() 来确保组件被正确地更新。你也可以考虑使用 immutable 对象加速嵌套数据的比较
React.memo()是一个高阶函数,它与 React.PureComponent类似,但是一个函数组件而非一个类
function Child({seconds}){
console.log('I am rendering');
return (
<div>I am update every {seconds} seconds</div>
)
};
export default React.memo(Child)
React性能优化-了解immutable.js
- 彻底拥抱"不可变值"
- 基于共享数据(不是深拷贝),速度好
- 有一定的学习和迁移成本,按需使用
影响性能的关键,Javascript中的对象一般都是可变的,因为多数都是引用赋值
React高阶组件
- 关于公共组件逻辑的抽离
高阶组件HOC
Render Props
高阶组件只是一个包装了另外一个 React 组件的 React 组件
import React from 'react'
// 高阶组件
const withMouse = (Component) => {
class withMouseComponent extends React.Component {
constructor(props) {
super(props)
this.state = { x: 0, y: 0 }
}
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
})
}
render() {
return (
<div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}>
{/* 1. 透传所有 props 2. 增加 mouse 属性 */}
<Component {...this.props} mouse={this.state}/>
</div>
)
}
}
return withMouseComponent
}
const App = (props) => {
const a = props.a
const { x, y } = props.mouse // 接收 mouse 属性
return (
<div style={{ height: '500px' }}>
<h1>The mouse position is ({x}, {y})</h1>
<p>{a}</p>
</div>
)
}
export default withMouse(App) // 返回高阶函数
什么是React Render Props
- 高级函数,模式简单,但会增加组件层级
- Render Props代码简洁,学习成本高
- 按需使用
import React from 'react'
import PropTypes from 'prop-types'
class Mouse extends React.Component {
constructor(props) {
super(props)
this.state = { x: 0, y: 0 }
}
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
})
}
render() {
return (
<div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}>
{/* 将当前 state 作为 props ,传递给 render (render 是一个函数组件) */}
{this.props.render(this.state)}
</div>
)
}
}
Mouse.propTypes = {
render: PropTypes.func.isRequired // 必须接收一个 render 属性,而且是函数
}
const App = (props) => (
<div style={{ height: '500px' }}>
<p>{props.a}</p>
<Mouse render={
/* render 是一个函数组件 */
({ x, y }) => <h1>The mouse position is ({x}, {y})</h1>
}/>
</div>
)
/**
* 即,定义了 Mouse 组件,只有获取 x y 的能力。
* 至于 Mouse 组件如何渲染,App 说了算,通过 render prop 的方式告诉 Mouse 。
*/
export default App
高级特性
- 性能优化
- 高阶组件HOC
- Render Props
Redux使用
- 和Vuex作用相同,但比Vuex学习成本高
- 不可变值,纯函数
基本概念
全局数据状态管理工具(状态管理机),用来做组件通信等。
Redux 的适用场景:多交互、多数据源
某个组件的状态,需要共享
某个状态需要在任何地方都可以拿到
一个组件需要改变全局状态
一个组件需要改变另一个组件的状态单项数据流
react-redux
4.异步action
5.中间件
UI层非常简单,没有很多互动,Redux 就是不必要的,用了反而增加复杂性
用户的使用方式非常简单
用户之间没有协作
不需要与服务器大量交互,也没有使用 WebSocket
视图层(View)只从单一来源获取数据
Redux单项数据流
基本概念
- store state
Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store
如果想拿到某个数据,Store对象包含所有数据。如果想得到某个时点的数据,就要对 Store 生成快照。这种时点的数据集合,就叫做 State。当前时刻的 State,可以通过store.getState()拿到
- action
State 的变化,会导致 View 的变化。但是,用户接触不到 State,只能接触到 View。所以,State 的变化必须是 View 导致的。Action 就是 View 发出的通知,表示 State 应该要发生变化了
- reducer
Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。
Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State;
单项数据流的概述
- dispatch(action)
store.dispatch()是 View 发出 Action 的唯一方法
- reducer-newState
State 的计算过程就叫做 Reducer
- subscribe触发通知
Store 允许使用store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数
react-redux
- <Provider>connect
React-Redux 提供Provider组件,可以让容器组件拿到state
- connect
React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来
- mapStateToProps mapDispatchToProps
react-redux中构造出了state与dispatch,connect中的两个参数就可以将它们引入props
Redux action如何处理异步
- redux-thunk
- redux-promise
- redux-sage
简述Redux中间件原理
React-routers
React-router使用
- 路由模式(hash、H5 histoty)同vue-router
- 路由配置(动态路由、懒加载)同vue-router
React-router路由模式
hash模式(默认),比如:http://abc.com/#/user/10
H5 histoty模式,比如: http://abc.com/user/10
后者需要server端支持,因此无特殊需求可选择前者