# base-react **Repository Path**: lisa_zhu2012/base-react ## Basic Information - **Project Name**: base-react - **Description**: react 基础学习 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-12-22 - **Last Updated**: 2022-07-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # React > 快捷键 ``` rcc 类组件 rfc 函数式组件 imr 引入react af () => {} ``` # 第1章:React入门 ## 介绍 > 1. 用于动态构建用户界面的JavaScript库(只注重于视图) > 2. 由Facebook开源 ### 特点 1. 采用**组件化**模式、**声明式编码**,提高开发效率及组件复用率。 2. 在React Native中可以使用React语法进行**移动端开发**。 3. 使用**虚拟DOM**+优秀的**Diffing算法**,尽量减少与真实DOM的交互。 ### React高效的原因 1. 使用虚拟(virtual)DOM,不总是直接操作页面真实DOM 2. DOM Diffing算法,最小化页面重绘 ## React的基本使用 > 学习React之前你要掌握的js基础知识:判断this的指向,class(类),es6语法规范,npm包管理器,原型、原型链,数组常用方法,模块化 ### 相关js库 1. react.js:React核心库 2. react-dom.js:提供操作DOM的react扩展库 3. babel.min.js:解析JSX语法代码转为JS代码的库 ### 创建虚拟DOM的两种方式 1. 纯JS方式(一般不用) ```bash # 1. 创建虚拟DOM # const VDOM = React.createElement('h1', {id: 'title'}, 'Hello,React') const VDOM = React.createElement('h1', {id: 'title'}, React.createElement('span', null, 'Hello,React')) # 2. 渲染虚拟DOM到页面 ReactDOM.render(VDOM, document.getElementById('test')) ``` 2. JSX方式 关于虚拟DOM: 1. 本质是Object类型的对象(一般对象) 2. 虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性。 3. 虚拟DOM最终会被React转化为真实DOM,呈现在页面上。 ## React JSX ### JSX 1. 全称JavaScript XML 2. **react定义的一种类似于XML的JS扩展语法:JS+XML** > XML早起用与存储和传输数据Tom19 => json 3. 本质是React.createElement(component, props, ...children)方法的语法糖 4. 作用:用来简化创建虚拟DOM - 写法:var ele =

Hello JSX!

- 注意1:它不是字符串,也不是HTML/XML标签 - 注意2:它最终产生的就是一个JS对象 5. 标签名任意:HTML标签或其他标签 6. 基本语法规则 - 遇到 < 开头的代码, 以标签的语法解析: html同名标签转换为html同名元素, 其它标签需要特别解析 - 遇到以 { 开头的代码,以JS语法解析: 标签中的js表达式必须用{ }包含 7. babel.js的作用 - 浏览器不能直接解析JSX代码, 需要babel转译为纯JS的代码才能运行 - 只要用了JSX,都要加上type="text/babel", 声明需要babel来处理 jsx语法规则: 1. 定义虚拟DOM时,不要写引号; 2. 标签中混入JS表达式时要用{}; 3. 样式的类名指定不要用class,要用className; 4. 内联样式:要用style={{key:value}}的形式去写; 5. 只有一个根标签; 6. 标签必须闭合; 7. 标签首字母: - 若小写字母开头,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错; - 若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错; 一定注意区分:【js语句(代码)】与【js表达式】 1. 表达式:一个表达式会产生一个值,可以放在任何一个需要值得地方 下面这些都是表达式: - a (读出一个值) - a+b - demo(1) - arr.map() - function test() {} 2. 语句(代码): 下面这些都是语句(代码)(控制代码走向) - if(){} - for(){} - switch(){case:xxxx} ### 渲染虚拟DOM(元素) 1. 语法: ReactDOM.render(virtualDOM, containerDOM) 2. 作用: 将虚拟DOM元素渲染到页面中的真实容器DOM中显示 3. 参数说明 - 参数一: 纯js或jsx创建的虚拟dom对象 - 参数二: 用来包含虚拟DOM元素的真实dom元素对象(一般是一个div) ## 模块与组件和模块化与组件化的理解 ### 模块 1. 理解:向外提供特定功能的js程序,一般就是一个js文件 2. 为什么要拆成模块:随着业务逻辑增加,代码越来越多且复杂。 3. 作用:复用js,简化js的编写,提高js运行效率 ### 组件 1. 理解:用来实现局部功能效果的代码和资源的集合(html/css/js/image等等) 2. 为什么要用组件: 一个界面的功能更复杂 3. 作用:复用编码, 简化项目编码, 提高运行效率 ### 模块化 当应用的js都以模块来编写的, 这个应用就是一个模块化的应用 ### 组件化 当应用是以多组件的方式实现, 这个应用就是一个组件化的应用 # 第2章:React面向组件编程 ## react中定义组件 函数式组件:用函数定义的组件(适用于【简单组件】的定义) 类式组件:用类定义的组件(适用于【复杂组件】的定义) 类的基本知识: 1. 类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时才写。 2. 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。 3. 类中所定义的方法,都是放在了类的原型对象上,供实例去使用。 人 状态 影响 行为 组件 状态 驱动 页面 ### 组件实例的三大核心属性 state 1. state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合) 2. 组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件) 注意: * 组件中render方法中的this为组件实例对象 * 组件自定义的方法中this为undefined,如何解决? - 强制绑定this:通过函数对象的bind() - 箭头函数:要用赋值语句的形式+箭头函数 * 状态数据,不能直接修改或更新 ### 组件实例的三大核心属性 props 1. 每个组件对象都会有props(properties的简写)属性 2. 组件标签的所有属性都保存在props中 作用: 1. 通过标签属性从组件外向组件内传递变化的数据 2. 注意:组件内部不要修改props数据 编码操作: 1. 内部读取某个属性值```this.props.name``` 2. 对props中的属性值进行类型限制和必要性限制 第一种方式(React v15.5开始已启用): ```Person.propTypes = { name: React.PropTypes.sting.isRequired, age: React.PropTypes.number } ``` 第二种方式(新): 使用prop-types库进行限制(需要引入prop-types库) ``` Person.propTypes = { name: PropTypes.sting.isRequired, age: PropTypes.number } ``` 3. 扩展属性:将对象的所有属性通过props传递 ``` ``` 4. 默认属性值 ``` Person.defaultProps = { age: 18, sex: '男' } ``` 5. 组件类的构造函数 > 构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props ``` constructor(props) { super(props) console.log(props) // 打印所有属性 } ``` ### 组件实例的三大核心属性 refs与事件处理 组件内的标签可以定义ref属性来标识自己 编码: 1. 字符串形式的ref `````` 2. 回调函数形式的ref ``` {this.input1 = c}}/> ``` 3. createRef创建ref容器 ```myRef = React.createRef() this.myRef.current.value ``` React.createRef 调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的 ## 事件处理 1. 通过onXxx属性指定事件处理函数(注意大小写) - React使用的是自定义(合成)事件,而不是使用的原生DOM事件 —— 为了更好的兼容性 - React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) —— 为了高效 2. 通过event.target得到发生事件的DOM元素对象 —— 勿过度使用ref ## 收集表单数据 包含表单的组件分类 * 受控组件 * 非受控组件 * 高阶函数:如果一个函数符合下面2个规范中的任何一个,那么该函数就是高阶函数。 1. 若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。 2. 若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。 常见的高阶函数有:Promise、setTimeout、arr.map()等等 * 函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。 ``` function sum(a) { return (b) => { return (c) => { return a + b + c } } } const result = sum(1)(2)(3) console.log(result) ``` ## 组件生命周期 ### 生命周期 1. 组件从创建到死亡它会经历一些特定的阶段; 2. React组件中包含一系列钩子函数(生命周期回调函数),会在特定的时刻调用; 3. 我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。 * 生命周期的三个阶段(旧) 1. 初始化阶段:由ReactDOM.render()触发---初次渲染 constructor() componentWillMount() render() componentDidMount() ==> 常用 一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息 2. 更新阶段:由组件内部this.setState()或父组件重新render触发 shouldComponentUpdate() componentWillUpdate() render() ==> 必须使用的一个 componentDidUpdata() 3. 卸载组件:由ReactDOM.unmountComponentAtNode()触发 componentWillUnmount() ==> 常用 一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消消息 * 生命周期的三个阶段(新) 1. 初始化阶段:由ReactDOM.render()触发---初次渲染 constructor() getDerivedStateFromProps render() componentDidMount() ==> 常用 一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息 2. 更新阶段:由组件内部this.setState()或父组件重新render触发 getDerivedStateFromProps shouldComponentUpdate() render() ==> 必须使用的一个 getSnapshotBeforeUpdate componentDidUpdata() 3. 卸载组件:由ReactDOM.unmountComponentAtNode()触发 componentWillUnmount() ==> 常用 一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消消息 ### 重要的钩子 1. render 初始化渲染或更新渲染调用 2. componentDidMount 开启监听,发送ajax请求 3. componentWillUnmount 做一些收尾工作,如:清理定时器 ### 即将废弃的钩子 componentWillMount componentWillReceiveProps componentWillUpdate 现在使用会出现警告,下一个大版本需要加上UNSAEF_前缀才能使用,以后可能会被彻底废弃,不建议使用 ## 虚拟DOM与DOM Diffing算法 经典面试题: 1). react/vue中的key有什么作用?(key的内部原理是什么?) 2). 为什么遍历列表时,key最好不要用index? 1. 虚拟DOM中key的作用: 1).简单的说:key是虚拟DOM对象的标识,在更新显示时key起着极其重要的作用。 2).详细的说:当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下: a.旧虚拟DOM中找到了与新虚拟DOM相同的key; (1).若虚拟DOM中内容没变,直接使用之前的真实DOM (2).若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM b.旧虚拟DOM中未找到与新虚拟DOM相同的key: 根据数据创建新的真实DOM,随后渲染到页面 2. 用index作为key可能会引发的问题: 1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作: 会产生没有必要的真实DOM更新 ==> 界面效果没问题,但效率低。 2. 如果结构中还包含输入类的DOM: 会产生错误DOM更新 ==> 界面有问题 3. 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。 3. 开发中如何选择key? 1. 最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值 2. 如果确定只是简单的展示数据,用index也是可以的 # 第3章:react应用(基于react脚手架) ## 使用create-react-app创建react应用 > Yarn Facebook 贡献的 Javascript 包管理器 ### react脚手架 1. xxx脚手架:用来帮助程序员快速创建一个基于xxx库的**模板项目** * 包含了所有需要的配置(语法检查、jsx编译、devServer...) * 下载好了所有相关的依赖 * 可以直接运行一个简单效果 2. react提供了一个用于创建react项目的脚手架库:create-react-app 3. 项目的整体技术架构为:react + webpack + es6 + eslint 4. 使用脚手架开发的项目的特点:模块化,组件化,工程化 ### 创建项目并启动 ```bash # 全局安装 npm install -g create-react-app # 切换到想创建项目的目录,使用命令 create-react-app hello-react # 进入项目文件夹 cd hello-react # 启动项目 npm start # 查看全局安装的库 npm list -g --dept 0 # 强制清理缓存 npm cache clean --force ``` ### 功能界面的组件化编码流程(通用) 1. 拆分组件:拆分界面,抽取组件 2. 实现静态组件:使用组件实现静态页面效果 3. 实现动态组件 动态显示初始化数据 数据类型,数据名称,保存在哪个组件 交互(从绑定事件监听开始) #### todoList案例相关知识点 1. 拆分组件、实现静态组件;注意:className、style的写法 2. 动态初始化列表,如何确定将数据放在哪个组件的state中? - 某个组件使用:放在其自身的state中 - 某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升) 3. 关于父子之间通信: - 【父组件】给【子组件】传递数据:通过props传递 - 【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数 4. 注意:defaultChecked和checked的区别,类似的还有:defaultValue和value 5. **状态在哪里,操作状态的方法就在哪里** nanoid prop-types # 第4章:react ajax ## 理解 ### 前置说明 1. React本身只关注于界面,并不包含发送ajax请求的代码 2. 前端应用需要通过ajax请求与后台进行交互(json数据) 3. react应用中需要继承第三方ajax库(或自己封装) ### 常用的ajax请求库 * jQuery 比较重,如果需要另外引入不建议使用 * axios 轻量级,建议使用 - 封装XMLHttpRequest对象的ajax - promise风格 - 可以用在浏览器端和node服务器端 ``` var Ajax = { get: function(url, fn) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); xhr.onreadystatechange = function() { // readyState == 4说明请求已完成 if (xhr.readyState == 4) { if (xhr.status == 200 || xhr.status == 304) { // console.log(xhr.responseText); // fn.call(xhr.responseText); } } } xhr.send(); }, post: function(url, data, fn) { var xhr = new XMLHttpRequest(); xhr.open('POST', url, false); // 添加http头,发送信息至服务器时内容编码类型 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { if (xhr.status == 200 || xhr.status == 304) { // console.log(xhr.responseText); // fn.call(xhr.responseText); } } } xhr.send(data); } } function ajax(url,funWin,funFaild){ //回调函数 //1.创建ajax对象 var xhr= window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject(“Microsoft.XMLHTTP”); //2.与服务器建立连接 xhr.open(“GET”,url,true); //3.发送请求 xhr.send(); //4.接收返回 xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ //请求状态 if(xhr.status === 200){ if(typeof funWin === "function"){ funWin(xhr.responseText); } } else { if(typeof funFaild === "function"){ funFaild(); } } } } } ``` ## axios ### react脚手架配置代理总结 #### 方法一 > 在package.json中追加如下配置 ```json "proxy":"http://localhost:5000" ``` 说明: 1. 优点:配置简单,前端请求资源时可以不加任何前缀。 2. 缺点:不能配置多个代理。 3. 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源) #### 方法二 1. 第一步:创建代理配置文件 ``` 在src下创建配置文件:src/setupProxy.js ``` 2. 编写setupProxy.js配置具体代理规则: ```js const proxy = require('http-proxy-middleware') module.exports = function(app) { app.use( proxy('/api1', { //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000) target: 'http://localhost:5000', //配置转发目标地址(能返回数据的服务器地址) changeOrigin: true, //控制服务器接收到的请求头中host字段的值 /* changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000 changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000 changeOrigin默认值为false,但我们一般将changeOrigin值设为true */ pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置) }), proxy('/api2', { target: 'http://localhost:5001', changeOrigin: true, pathRewrite: {'^/api2': ''} }) ) } ``` 说明: 1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。 2. 缺点:配置繁琐,前端请求资源时必须加前缀。 ### 消息订阅-发布机制 ```bash # 引入 import PubSub from 'pubsub-js' # 订阅 PubSub.subscribe('delete', function(msg, data){ }); # 发布消息 PubSub.publish('delete', data) ``` ### 扩展:[fetch](https://github.github.io/fetch/) > https://segmentfault.com/a/1190000003810652 * 特点 - fetch原生函数,不再使用XMLHttpRequest对象提交ajax请求 - 老版本浏览器可能不支持 * GET请求 ``` fetch(url).then(function(response) {    return response.json() }).then(function(data) {    console.log(data) }).catch(function(e) {    console.log(e) }); ``` * POST请求 ``` fetch(url, {   method: "POST",   body: JSON.stringify(data) }).then(function(data) {   console.log(data) }).catch(function(e) {   console.log(e) }) ``` #### github搜索案例相关知识点 1. 设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办。 2. ES6小知识点:解构赋值+重命名 ``` let obj = {a:{b:1}} const {a} = obj; // 传统解构赋值 const {a:{b}} = obj; // 连续解构赋值 const {a:{b:value}} = obj; // 连续解构赋值 + 重命名 ``` 3. 消息订阅与发布机制 * 先订阅,再发布(理解:有一种隔空对话的感觉) * 适用于任意组件间通信 * 要在组件的componentWillUnmount中取消订阅 4. fetch发送请求(关注分离的设计思想) ``` try { const response = await fetch(`/api1/search/users2?q=${keyWord}`) const data = await response.json() console.log(data) } catch (error) { console.log('请求出错', error) } ``` # 第5章:react-router ## 相关理解 ### SPA的理解 1. 单页Web应用(single page web application, SPA) > 单页面多组件 2. 整个应用只有一个完整的页面 3. 点击页面中的点解不会刷新页面,只会做页面的局部更新 4. 数据都需要通过ajax请求获取,并在前端异步展现 ### 路由的理解 #### 什么是路由? - 一个路由就是一个映射关系(key:value) - key为路径, value可能是function或component #### 路由分类 1. 后端路由: 1)理解:value是function, 用来处理客户端提交的请求。 2)注册路由:router.get(path, function(req, res)) 3)工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据 2. 前端路由: 1)浏览器端路由:value是component,用于展示页面内容。 2)注册路由: 3)工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件 ### react-router-dom的理解 > web native any 1. react的一个插件库 2. 专门用来实现一个SPA应用 3. 基于react的项目基本都会用到此库 ## react-router相关API ### 路由的基本使用 1. 明确好界面中的导航区、展示区 2. 导航区的a连接改为Link标签 Demo 3. 展示区写Route标签进行路径的匹配 4. 最外侧包裹了一个 ### 路由组件与一般组件 1. 写法不同: 一般组件: 路由组件: 2. 存放位置不同: 一般组件:components 路由组件:pages 3. **接收到的props不同**: 一般组件:写组件标签时传递了什么,就能收到什么 路由组件:接收到三个固定的属性 ``` history: go: ƒ go(n) goBack: ƒ goBack() goForward: ƒ goForward() location: {pathname: "/about", search: "", state: undefined} push: ƒ push(path, state) replace: ƒ replace(path, state) location: pathname: "/about" search: "" state: undefined match: isExact: true params: {} path: "/about" url: "/about" ``` ### NavLink与封装NavLink 1. NavLink可以实现路由链接的高亮,通过activeClassName指定样式名 2. 标签体内容是一个特殊的标签属性 3. 通过this.props.children可以获取标签体内容 ### Switch 1. 通常情况下,path和component是一一对应的关系。 2. Switch可以提高路由匹配效率(单一匹配)。 ### 解决多级路径刷新页面样式丢失的问题 1. public/index.html 中引入样式时不写 ./ 写 /(常用) 2. public/index.html 中引入样式时不写 ./ 些 %PUBLIC_URL%(常用) 3. 使用HashRouter ### 路由的严格匹配与模糊匹配 1. 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致) 2. 开启严格匹配: 3. 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由 ### Redirect的使用 1. 一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由 2. 具体编码: ``` ``` ### 嵌套路由 1. 注册子路由时要写上父路由的path值 2. 路由的匹配是按照注册路由的顺序进行的 ### 向路由组件传递参数 > ajax: query、params、body{urlencode json} 1. params参数 路由链接(携带参数):详情 注册路由(声明接收): 接收参数:this.props.match.params 2. search参数 路由链接(携带参数):详情 注册路由(无需声明,正常注册即可): 接收参数:this.props.location.search 备注:获取到的search是urlencoded编码字符串,需要借助querystring解析 3. state参数 路由链接(携带参数):``` 详情 ``` 注册路由(无需声明,正常注册即可): 接收参数:this.props.location.state 备注:刷新也可以保留住参数(清除缓存单独访问url不能保留参数) ### 编程式路由导航 借助this.props.history对象上的API对操作路由跳转、前进、后退 this.props.history.push()/replace()/goBack()/goForward()/go() ### withRouter函数 withRouter可以加工一般组件,让一般组件具备路由组件所特有的API withRouter的返回值是一个新组件 ### BrowserRouter与HashRouter的区别 1. 底层原理不一样 BrowserRouter:使用的是H5的history API,不兼容IE9及以下版本。 HashRouter:使用的是URL的哈希值。 2. path表现形式不一样 BrowserRouter:路径中没有#,例如:localhost:3000/demo/test HashRouter:路径包含#,例如:localhsot:3000/#/demo/test 3. 刷新后对路由state参数的影响 BrowserRouter:没有任何影响,因为state保存在history对象中 HashRouter:刷新后会导致路由state参数的丢失 4. 备注:HashRouter可以用于解决一些路径错误相关的问题(ps:路径丢失问题)。 ### 内置组件 ### 其他 history对象 mathch对象 withRouter函数 # 第6章:react UI组件库 流行的开源React UI组件库 [material-ui(国外)](http://www.material-ui.com) [ant-design(国内蚂蚁金服)](https://ant.design/index-cn) ## antd的按需引入+自定义主题 1. 安装依赖: ``` yarn add react-app-rewired customize-cra babel-plugin-import less less-loader ``` 2. 修改package.json ``` "scripts": { "start": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test", "eject": "react-scripts eject" }, ``` 3. 根目录下创建config-overrides.js ``` //配置具体的修改规则 const { override, fixBabelImports,addLessLoader} = require('customize-cra'); module.exports = override( fixBabelImports('import', { libraryName: 'antd', libraryDirectory: 'es', style: true, }), addLessLoader({ lessOptions:{ javascriptEnabled: true, modifyVars: { '@primary-color': 'green' }, } }), ); ``` 4. 备注:不用在组件里亲自引入样式了,即:import 'antd/dist/antd.css’应该删掉 # 第7章:redux ## redux理解 ### 学习文档 1. 英文文档: https://redux.js.org/ 2. 中文文档: http://www.redux.org.cn/ 3. Github: https://github.com/reactjs/redux ### redux是什么 1. redux是一个专门用于做状态管理的JS库(不是react插件库)。 2. 它可以用在react, angular, vue等项目中, 但基本与react配合使用。 3. 作用: 集中式管理react应用中多个组件共享的状态。 ### 什么情况下需要使用redux 1. 某个组件的状态,需要让其他组件可以随时拿到(共享)。 2. 一个组件需要改变另一个组件的状态(通信)。 3. 总体原则:能不用就不用, 如果不用比较吃力才考虑使用。 ### redux工作流程 原理图 \base-react\原理图\redux原理图.png ### redux的三个核心概念 #### action 1. 动作的对象 2. 包含2个属性 * type:标识属性,值为字符串,唯一,必要属性 * data:数据属性,值类型任意,可选属性 3. 例子{type: ADD_STUDENT, data: {name: 'tom', age: 18}} #### reducer 1. 用于初始化状态,加工状态 2. 加工时,根据旧的state和action,产生新的state的纯函数。 #### store 1. 将state、action、reducer联系在一起的对象 2. 如何得到此对象? ``` import {createStore} from 'redux' import reducer from './reducers' const store = createStore(reducer) ``` 3. 此对象的功能? * getState():得到state * dispatch(action):分发action,触发reducer调用,产生新的state * subscribe(listener):注册监听,当产生了新的state时,自动调用 ### redux的核心API #### createstore() 作用:创建包含指定reducer的store对象 #### store对象 1. 作用:redux库最核心的管理对象 2. 它内部维护着:state reducer 3. 核心方法:getState() dispatch(action) subscribe(listener) 4. 具体编码:store.getState() store.dispatch({type:'INCREMENT',number}) store.subscribe(render) #### applyMiddleware() 作用:应用上基于redux的中间件(插件库) #### combineReducers() 作用:合并多个reducer函数 ### redux异步编程 1. 理解: redux默认是不能进行异步处理的;某些时候应用中需要在redux中执行异步任务(ajax,定时器) 2. 使用异步中间件 redux-thunk ### react-redux 1. 理解: * 一个react插件库 * 专门用来简化react应用中使用redux 2. react-Redux将所有组件分成两大类 * UI组件 1)只负责 UI 的呈现,不带有任何业务逻辑 2)通过props接收数据(一般数据和函数) 3)不使用任何 Redux 的 API 4)一般保存在components文件夹下 * 容器组件 1)负责管理数据和业务逻辑,不负责UI的呈现 2)使用 Redux 的 API 3)一般保存在containers文件夹下 3. 相关API * Provider:让所有组件都可以得到state数据 ```    ``` * connect:用于包装 UI 组件生成容器组件 ``` import { connect } from 'react-redux'   connect(     mapStateToprops,     mapDispatchToProps )(Counter) ``` * mapStateToprops:将外部的数据(即state对象)转换为UI组件的标签属性 ``` const mapStateToprops = function (state) {   return {     value: state   } } ``` * mapDispatchToProps:将分发action的函数转换为UI组件的标签属性 ### 纯函数和高阶函数 * 纯函数 1. 一类特别的函数:只要是同样的输入(实参),必定得到同样的输出(返回) 2. 必定遵守以下一些约束 - 不得改写参数数据 - 不会产生任何副作用,例如网络请求,输入和输出设备 - 不能调用Date.now()或者Math.random()等不纯的方法 3. redux的reducer函数必须是一个纯函数 * 高阶函数 1. 理解:一类特别的函数 - 情况1:参数是函数 - 情况2:返回是参数 2. 常见的高阶函数: - 定时器设置函数 - 数组的forEach()/map()/filter()/reduce()/find()/bind() - promise - react-redux中的connect函数 3. 作用:能实现更加动态,更加可扩展的功能 const startTime = performance.now(); something(); const endTime = performance.now(); console.log(`Cpde take ${startTime - endTime} milliseconds`)