青岛网站建设eoe,ydg wordpress theme,成立公司的流程以及资料,wordpress折腾怕了文章目录 一、React基本介绍1.虚拟DOM优化1.1 原生JS渲染页面1.2 React渲染页面 2.需要提前掌握的JS知识 二、入门1.React基本使用2.创建DOM的两种方式2.1 使用js创建#xff08;一般不用#xff09;2.2 使用jsx创建 3.React JSX3.1 JSX常见语法规则3.2 for循环渲染数据 4.模… 文章目录 一、React基本介绍1.虚拟DOM优化1.1 原生JS渲染页面1.2 React渲染页面 2.需要提前掌握的JS知识 二、入门1.React基本使用2.创建DOM的两种方式2.1 使用js创建一般不用2.2 使用jsx创建 3.React JSX3.1 JSX常见语法规则3.2 for循环渲染数据 4.模块与组件、模块化与组件化4.1 模块4.2 组件4.3 模块化4.4 组件化 三、面向组件编程1.安装React开发者调试工具2.自定义组件2.1 函数式组件2.2 类式组件 3.组件实例(class)三大核心属性3.1 状态-state3.1.1 基础使用(不适用)3.1.2 代码优化 3.2 属性-props3.2.1 基础用法3.2.2 校验props属性值3.2.3 函数式组件实现 3.3 refs和事件处理3.3.1 字符串形式不推荐3.3.2 回调函数形式3.3.3 refs容器形式3.3.4 事件处理 4.收集表单数据4.1 非受控组件案例4.2 受控组件案例4.3 函数柯里化4.4 不使用函数柯里化实现方式 5.组件的生命周期5.1 生命周期-案例5.2 生命周期旧5.2.1 流程图5.2.2 示例代码 5.3 生命周期新5.3.1 流程图5.3.2 代码案例5.3.2 getSnapshotBeforeUpdate-案例 5.4 DOM的diffing算法 四、react应用基于react脚手架1.创建react应用1.1 react脚手架1.2 创建项目并启动1.3 项目结构 2.组件目录定义和导入3.样式模块化4.vscode安装react插件5.组件化编码流程6.组件组合使用-TodoList案例6.1 拆组件6.2 依赖安装6.3 总结 五、react-axios1.安装axios2.代理配置2.1 方法一不使用2.2 方法二常用 3.github搜索案例 六、react-router1.相关理解1.1 SPA理解1.2 路由的理解1.3 react-router-dom的理解 2.react-router-dom2.1 安装2.2 路由组件使用2.3 路由组件和一般组件的区别2.4 NavLink2.5 封装NavLink组件2.6 Switch使用2.7 样式丢失2.8 路由模糊匹配2.9 Redirect重定向2.10 嵌套多级路由2.11 向路由组件传递参数数据2.11.1 传递params参数2.11.2 传递search参数2.11.3 传递state参数 2.12 路由跳转的两个模式2.12.1 push模式2.12.2 replace模式 2.13 编程式路由导航2.14 让一般组件也有路由组件的参数-withRouter2.15 BrowserRouter与HashRouter的区别 七、组件库-Ant Design1.安装2.入门体验3.按需引入样式4.自定义主题4.1 安装包4.2 修改package.json4.3 创建配置文件 八、redux1.理解1.1 redux是什么1.2 使用场景 2.redux的核心API2.1 安装redux2.2 redux工作流程2.3 使用案例2.3.1 求和案例-纯react版本2.3.2 求和案例-redux精简版2.3.3 求和案例-redux完整版 2.4 异步action2.4.1 安装2.4.2 注册中间件2.4.3 异步aciton定义 九、react-redux1.模型图2.安装3.相关概念4.基础操作单组件4.1 index.js4.2 redux相关4.3 容器4.4 UI组件 5.基础操作代码优化5.1 容器优化5.2 redux状态变化机制5.3 容器传入store优化-Provider5.4 整合UI组件和容器组件 6.基础操作多组件数据共享6.1 实现效果6.2 redux目录结构6.3 相关代码 7.纯函数7.1 reducer注意事项7.2 纯函数 8.redux开发者工具8.1 安装扩展程序8.2 安装库8.3 store.js注册插件 十、react打包十一、react扩展1.setState1.1 对象式1.2 函数式 2.路由懒加载lazyLoad2.1 无懒加载2.2 懒加载 3.Hooks3.1 是什么3.2 三个常用的Hook3.3 State Hook3.4 Effect Hook3.5 Ref Hook 4.Fragment5.Context5.1 使用5.2 类组件代码示例5.3 函数组件代码示例 6.组件优化6.1 解决办法16.2 解决办法2推荐 7.render props7.1 如何向组件内部动态传入带内容的结构标签7.2 children props7.3 render props 8.错误边界9.组件通信方式总结 十二、React Router61.概述2.component2.1 BrowerRouter2.2 HashRouter2.3 Routes与Route2.4 Link2.5 NavLink2.6 Navigate2.7 Outlet 3.Hooks3.1 useRoutes()3.2 useNavigate()3.3 useParams()3.4 useSearchParams()3.5 useLocation()3.6 useMatch()3.7 useInRouterContext()3.8 useNavigationType()3.9 useOutlet()3.10 useResolvedPath() 4.综合案例4.1 useRouters路由表4.2 嵌套路由4.3 路由传参4.4 编程式路由导航 一、React基本介绍
React是用于构建用户界面的JavaScript的库将数据渲染为HTML视图的开源库。三部曲 1.发送请求获取数据2.处理数据过滤、整理格式等3.操作DOM呈现页面 为什么要学 1.原生JavaScript操作DOM繁琐、效率低DOM-API操作UI。2.原生JavaScript直接操作DOM浏览器会进行大量的重绘重排3.原生JavaScript没有组件化编码方案代码复用率低。 React特点 1.采用组件化模式、声明式编码提高开发效率及组件复用率。2.在React Native中可以使用React语法进行移动端开发。3.使用虚拟DOM优秀的Diffing算法尽量减少与真实DOM的交互。 官网 英文官网https://react.dev/中文官网 https://zh-hans.react.dev/https://react.docschina.org
1.虚拟DOM优化
1.1 原生JS渲染页面 1.2 React渲染页面 2.需要提前掌握的JS知识
判断this的指向class(类)ES6语法规范npm包管理器原型、原型链数组常用方法模块化
二、入门
相关react练习的js在本文的资源绑定里面
1.React基本使用
babel的作用 1.将ES6的语法转化为ES52.将JSX语法转为JS
!DOCTYPE html
html langen
headmeta charsetUTF-8titlehello_react/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库 --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建虚拟DOM(一定不要写引号不需要写引号)const VDOM h1Hello, React/h1// 2.渲染虚拟DOM到页面ReactDOM.render(VDOM, document.getElementById(test));/script
/body
/html2.创建DOM的两种方式
为什么React使用JSX语法而不是JS JSX写法更适用于复杂的页面渲染场景 关于虚拟DOM 1.本质是Object类型的对象一般对象2.虚拟DOM绑定的函数很少真实DOM比较多因为虚拟DOM是React内部在用无需真实DOM上那么多的属性。3.虚拟DOM最终会被React转化为真实DOM呈现在页面上。
2.1 使用js创建一般不用
...省略!-- 创建一个容器 --div idtest/divscript typetext/babel// 1.创建虚拟DOMconst VDOM React.createElement(h1, {id: title}, React.createElement(span, {}, Hello,React))// 2.渲染虚拟DOM到页面ReactDOM.render(VDOM, document.getElementById(test));/script
...省略2.2 使用jsx创建
...省略!-- 创建一个容器 --div idtest/divscript typetext/babel// 1.创建虚拟DOM(一定不要写引号不需要写引号)const VDOM h1 idtitlespanHello, React/span/h1// 2.渲染虚拟DOM到页面ReactDOM.render(VDOM, document.getElementById(test));/script
...省略3.React JSX
全称JavaScript XMLreact定义的一种类似于XML的JS扩展语法:JS XML↵本质是React.createElement(component,props,.….children)方法的语法糖作用用来简化创建虚拟DOM操作 写法var ele h1Hello JSX/hw注意它不是字符串也不是HTML/XML标签最终产生的是一个JS对象
3.1 JSX常见语法规则
JS语法规则 1.定义虚拟DOM时不要写引号2.标签中混入JS表达式时要用{} 表达式就是能被变量接收的var a 表达式;表达式和代码的区别 表达式 aabdemo(1)arr.map()function test () {} 代码 if(){}for(){}switch(){case:xxxx} 3.样式的类名指定不要用class要用className。4.内联样式要用style{{key:value}}的形式去写。 如果是需要定义font-size属性那么需要改成驼峰命名fontSize 5.虚拟DOM里面只能有一个根标签6.标签必须闭合 例如input标签两种写法 input typetext/input typetext//input 7.标签首字母 (1)若小写字母开头则将改标签转为html中同名元素若html中无该标签对应的同名元素则报错。(2)若大写字母开头react就去渲染对应的组件若组件没有定义则报错。
!DOCTYPE html
html langen
headmeta charsetUTF-8titlehello_react/titlestyle.title{background-color:blueviolet;}/style
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库 --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babelconst a A;const b B;// 1.创建虚拟DOM(一定不要写引号不需要写引号)const VDOM (divh1 classNametitlespan style{{color: white, fontSize: 29px}}{a.toLocaleLowerCase()}/span/h1h1span style{{color: blue, fontSize: 29px}}{b.toLocaleLowerCase()}/span/h1/div)// 2.渲染虚拟DOM到页面ReactDOM.render(VDOM, document.getElementById(test));/script
/body
/html3.2 for循环渲染数据
由于React的虚拟DOM的原因如果遍历数据需要有个keyDOM优化算法就是根据key进行判断是否存在。
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库 --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babelconst title 前端js框架列表;const framework_list [{value: 1, label: Angular}, {value: 2, label: React}, {value: 3, label: Vue}, ];// 1.创建虚拟DOM(一定不要写引号不需要写引号)const VDOM (divh1 classNametitlespan{title}/span/h1ul{framework_list.map((item){return li key{item.value}{item.label}/li})}/ul/div)// 2.渲染虚拟DOM到页面ReactDOM.render(VDOM, document.getElementById(test));/script
/body4.模块与组件、模块化与组件化
4.1 模块
理解向外提供特定功能的 js 程序,一般就是一个 js 文件↵为什么要拆成模块随着业务逻辑增加代码越来越多且复杂。作用复用 js,简化js的编写,提高js 运行效率↵
4.2 组件
理解用来实现局部功能效果的代码和资源的集合(html/css/js/image等等)为什么一个界面的功能更复杂作用复用编码简化项目编码提高运行效率
4.3 模块化
当应用的js都以模块来编写的这个应用就是一个模块化的应
4.4 组件化
当应用是以多组件的方式实现这个应用就是一个组件化的应用
三、面向组件编程
1.安装React开发者调试工具
需要使用chrome浏览器
2.自定义组件
2.1 函数式组件
!DOCTYPE html
html langen
headmeta charsetUTF-8titlehello_react/titlestyle.title{background-color:blueviolet;}/style
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库 --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建函数式组件function Demo(){// 组件里面的this会经过babel但是babel会开启严格模式不允许this指向window所以会打印undefinedconsole.log(this)return h2我是组件/h2}// 2.渲染组件到页面ReactDOM.render(Demo/, document.getElementById(test))/*执行了ReactDOM.render(Demo/...…………之后发生了什么?1.React解析组件标签找到了Demo组件。2.发现组件是使用函数定义的随后调用该函数将返回的虚拟DOM转为真实DOM随后呈现在页面中。*//script
/body
/html2.2 类式组件
!DOCTYPE html
html langen
headmeta charsetUTF-8title类组件/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库 --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建类式组件class MyComponent extends React.Component {render(){//render是放在哪里的—MyComponent的原型对象上供实例使用。//render中的this是谁?-MyComponent的实例对象。return h2我是类定义的组件/h2}}// 2.渲染组件到页面ReactDOM.render(MyComponent/, document.getElementById(test))/*执行了ReactDOM.render(MyComponent/...…………之后发生了什么?1.React解析组件标签找到了MyComponent组件。2.发现组件是使用类定义的随后new出来该类的实例并通过该实例调用到原型上的render方法3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。*//script
/body
/html3.组件实例(class)三大核心属性
3.1 状态-state
理解状态其实就是数据驱动页面的显示内容 state 是组件对象最重要的属性,值是对象(可以包含多个 key-value 的组合)组件被称为状态机,通过更新组件的 state 来更新对应的页面显示(重新渲染组件) 注意事项 1.组件中 render方法中的this为组件实例对象组件自定义的方法中this为undefined如何解决? a.强制绑定this:通过函数对象的bind() b.箭头函数 2.状态的数据不可以直接更新需要使用setState进行更改React才会同步更新到页面显示。 3.更新是一种合并不是替换。只会替换setState传入的key和value没有传入的不会进行删除。 3.1.1 基础使用(不适用)
添加按钮绑定事件流程 1.定义一个类组件2.编写render方法3.编写点击事件函数4.在构造器中绑定点击函数
!DOCTYPE html
html langen
headmeta charsetUTF-8title类组件/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库 --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建类式组件class Weather extends React.Component {// 构造器只会调用一次调用几次组件才会调用几次。constructor(props){super(props)// 初始化状态this.state {isHot: true, wind: 微风}//解决changeWeather中this指向问题this.changeWeather this.changeWeather.bind(this)}// render调几次? 1n次 1是初始化的那次 n是状态更新的次数render(){// 读取状态const {isHot, wind} this.statereturn h2 onClick{this.changeWeather}今天天气很{isHot ? 炎热 : 凉爽}{wind}/h2}// changeWeather调用几次?——点几次调几次changeWeather(){//changeweather放在哪里? Weather的原型对象上供实例使用//由于changeWeather是作为onclick的回调所以不是通过实例调用的是直接调用//类中的方法默认开启了局部的严格模式所以changeWeather中的this为undefined// 解决办法this.changeWeather this.changeWeather.bind(this)//console.log(this)// 注意状态(state)不可直接更改下面这行就是直接更改// this.state.isHot ! this.state.isHot// 注意状态必须通过setState进行更新且更新是一种合并不是替换。this.setState({isHot: ! this.state.isHot})}}// 2.渲染组件到页面ReactDOM.render(Weather/, document.getElementById(test))/script
/body
/html3.1.2 代码优化
优化内容 1.避免在构造器中写大量的绑定代码需要改成箭头函数箭头函数会寻找外层的this2.避免在构造器中赋值state而是直接在类里面命名
!DOCTYPE html
html langen
headmeta charsetUTF-8title类组件/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库 --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建类式组件class Weather extends React.Component {state {isHot: true, wind: 微风}render(){const {isHot, wind} this.statereturn h2 onClick{this.changeWeather}今天天气很{isHot ? 炎热 : 凉爽}{wind}/h2}//自定义方法——要用赋值语句的形式箭头函数changeWeather (){this.setState({isHot: ! this.state.isHot})}}// 2.渲染组件到页面ReactDOM.render(Weather/, document.getElementById(test))/script
/body
/html3.2 属性-props
作用通过标签属性从组件外向组件内传递变化的数据注意 1.props是只读的无法修改2.函数式组件有props但是没有state和refs如果需要实现需要用到新特性hooks
3.2.1 基础用法
!DOCTYPE html
html langen
headmeta charsetUTF-8titleprops基本使用/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库 --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建类式组件class Person extends React.Component {render(){const {name,sex,age} this.propsreturn (ulli姓名{name}/lili性别{sex}/lili年龄{age}/li/ul)}}// 2.渲染组件到页面ReactDOM.render(Person nametom sex女 age18/, document.getElementById(test))// 下面使用解包进行传递但是ES语法是不允许是解包一个对象的下面是因为babel识别到了React标签用了这个语法特殊进行了转换才可以使用const p {name:tom, sex:女, age:18}ReactDOM.render(Person {...p}/, document.getElementById(test))/script
/body
/html3.2.2 校验props属性值
从React16开始需要引入prop-types.js才可以使用
!DOCTYPE html
html langen
headmeta charsetUTF-8titleprops基本使用/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库全局会多了一个对象React --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM。全局会多了一个对象ReactDOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- 引入prop-types用于对组件标签属性进行限制。全局会多了一个对象PropTypes --script typetext/javascript src../js/prop-types.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建类式组件class Person extends React.Component {// 属性值校验static propTypes {// name必传的字符串类型name: PropTypes.string.isRequired,// sex非必填的字符串类型sex: PropTypes.string,// 非必传的数字类型age: PropTypes.number,// 传入一个函数speak: PropTypes.func}// 属性值不传的默认值static defaultProps {sex: 不知道男女,age: 18}render(){const {name,sex,age} this.propsreturn (ulli姓名{name}/lili性别{sex}/lili年龄{age}/li/ul)}}// 2.渲染组件到页面const p {name: tom, sex:女, age:18, speak: speak}ReactDOM.render(Person {...p}/, document.getElementById(test))function speak () {console.log(说话)}/script
/body
/html3.2.3 函数式组件实现
!DOCTYPE html
html langen
headmeta charsetUTF-8titleprops基本使用/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库全局会多了一个对象React --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM。全局会多了一个对象ReactDOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- 引入prop-types用于对组件标签属性进行限制。全局会多了一个对象PropTypes --script typetext/javascript src../js/prop-types.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建函数式组件function Person (props) {const {name,sex,age} propsreturn (ulli姓名{name}/lili性别{sex}/lili年龄{age}/li/ul)}// 属性值校验Person.propTypes {// name必传的字符串类型name: PropTypes.string.isRequired,// sex非必填的字符串类型sex: PropTypes.string,// 非必传的数字类型age: PropTypes.number,}// 属性值不传的默认值Person.defaultProps {sex: 不知道男女,age: 18}// 2.渲染组件到页面const p {name: tom, sex:女, age:18}ReactDOM.render(Person {...p}/, document.getElementById(test))/script
/body
/html3.3 refs和事件处理
refs组件内的标签可以定义 ref 属性来标识自己类似组件的里面的id平时开发只需要使用内联函数形式即可注意ref不要过度使用发生事件的DOM元素和操作的DOM元素这个时候就可以不写ref而是使用事件处理
3.3.1 字符串形式不推荐
官方不推荐因为存在效率问题
!DOCTYPE html
html langen
headmeta charsetUTF-8titleref和事件处理/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库全局会多了一个对象React --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM。全局会多了一个对象ReactDOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- 引入prop-types用于对组件标签属性进行限制。全局会多了一个对象PropTypes --script typetext/javascript src../js/prop-types.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建类式组件class Demo extends React.Component {showData (){const {input1} this.refsalert(input1.value)}render(){const {name,sex,age} this.propsreturn (divinput typetext refinput1 placeholder点击按钮提示数据/button onClick{this.showData}点我提示左侧的数据/button/div)}}// 2.渲染组件到页面ReactDOM.render(Demo/, document.getElementById(test))/script
/body
/html3.3.2 回调函数形式
更新数据的时候重新render会调用两次常用*
如果ref回调函数是以内联函数的方式定义的在更新过程中它会被执行两次第一次传入参数null然后第二次会传入参数DOM元素。这是因为在每次渲染时会创建一个新的函数实例所以React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成class的绑定函数的方式可以避免上述问题但是大多数情况下它是无关紧要的。下面是内联函数的写法更新的时候会调用两次
!DOCTYPE html
html langen
headmeta charsetUTF-8titleref和事件处理/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库全局会多了一个对象React --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM。全局会多了一个对象ReactDOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- 引入prop-types用于对组件标签属性进行限制。全局会多了一个对象PropTypes --script typetext/javascript src../js/prop-types.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建类式组件class Demo extends React.Component {showData (){console.log(this)alert(this.input1.value)this.setState({})}render(){const {name,sex,age} this.propsreturn (divinput typetext ref{(currentNode) {this.input1 currentNode;console.log(, currentNode);}} placeholder点击按钮提示数据/button onClick{this.showData}点我提示左侧的数据/button/div)}}// 2.渲染组件到页面ReactDOM.render(Demo/, document.getElementById(test))/script
/body
/html通过将 ref 的回调函数定义成class的绑定函数的方式可以避免上述问题不常用
!DOCTYPE html
html langen
headmeta charsetUTF-8titleref和事件处理/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库全局会多了一个对象React --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM。全局会多了一个对象ReactDOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- 引入prop-types用于对组件标签属性进行限制。全局会多了一个对象PropTypes --script typetext/javascript src../js/prop-types.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建类式组件class Demo extends React.Component {showData (){console.log(this)alert(this.input1.value)this.setState({})}saveInput (currentNode){this.input1 currentNode}render(){const {name,sex,age} this.propsreturn (divinput typetext ref{this.saveInput} placeholder点击按钮提示数据/button onClick{this.showData}点我提示左侧的数据/button/div)}}// 2.渲染组件到页面ReactDOM.render(Demo/, document.getElementById(test))/script
/body
/html3.3.3 refs容器形式
React.createRef该容器只能存储一个ref而且current是当前节点
!DOCTYPE html
html langen
headmeta charsetUTF-8titleref和事件处理/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库全局会多了一个对象React --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM。全局会多了一个对象ReactDOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- 引入prop-types用于对组件标签属性进行限制。全局会多了一个对象PropTypes --script typetext/javascript src../js/prop-types.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建类式组件class Demo extends React.Component {/*React.createRef调用后可以返回一个容器该容器可以存储被ref所标识的节点*/myRef React.createRef()showData (){alert(this.myRef.current.value)}render(){const {name,sex,age} this.propsreturn (divinput typetext ref{this.myRef} placeholder点击按钮提示数据/button onClick{this.showData}点我提示左侧的数据/button/div)}}// 2.渲染组件到页面ReactDOM.render(Demo/, document.getElementById(test))/script
/body
/html3.3.4 事件处理
!DOCTYPE html
html langen
headmeta charsetUTF-8titleref和事件处理/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库全局会多了一个对象React --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM。全局会多了一个对象ReactDOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- 引入prop-types用于对组件标签属性进行限制。全局会多了一个对象PropTypes --script typetext/javascript src../js/prop-types.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建类式组件class Demo extends React.Component {/*1).通过onXxx属性指定事件处理函数注意大小写a.React使用的是自定义合成事件而不是使用的原生DOM事件 --- 为了更好的兼容性b.React中的事件是通过事件委托方式处理的委托给组件最外层的元素) --- 为了的高效2通过event.target得到发生事件的DOM元素对象*/myRef React.createRef()showData (event){alert(event.target.value)}render(){const {name,sex,age} this.propsreturn (divinput onBlur{this.showData} typetext placeholder失去焦点提示数据//div)}}// 2.渲染组件到页面ReactDOM.render(Demo/, document.getElementById(test))/script
/body
/html4.收集表单数据
表单的组件分类 受控组件好处是可以省略ref非受控组件现用现取
4.1 非受控组件案例
!DOCTYPE html
html langen
headmeta charsetUTF-8titleref和事件处理/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库全局会多了一个对象React --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM。全局会多了一个对象ReactDOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- 引入prop-types用于对组件标签属性进行限制。全局会多了一个对象PropTypes --script typetext/javascript src../js/prop-types.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建类式组件class Demo extends React.Component {handleSubmit (event){// 阻止表单提交event.preventDefault()const {username, password} thisalert(你的用户名是${username.value}你的密码是${password.value})}render(){return (form actionhttp://127.0.0.1 onSubmit{this.handleSubmit}用户名input ref{c this.username c} typetext nameusername/密码input ref{c this.password c} typepassword namepassword/button登录/button/form)}}// 2.渲染组件到页面ReactDOM.render(Demo/, document.getElementById(test))/script
/body
/html4.2 受控组件案例
!DOCTYPE html
html langen
headmeta charsetUTF-8titleref和事件处理/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库全局会多了一个对象React --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM。全局会多了一个对象ReactDOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- 引入prop-types用于对组件标签属性进行限制。全局会多了一个对象PropTypes --script typetext/javascript src../js/prop-types.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建类式组件class Demo extends React.Component {//初始化状态表单信息state {username: , //用户名password: // 密码}// 保存用户名到状态中saveUsername (event){this.setState({username: event.target.value})}// 保存密码到状态中savePassword (event){this.setState({password: event.target.value})}handleSubmit (event){// 阻止表单提交event.preventDefault()const {username, password} this.statealert(你的用户名是${username}你的密码是${password})}render(){return (form actionhttp://127.0.0.1 onSubmit{this.handleSubmit}用户名input onChange{this.saveUsername} typetext nameusername/密码input onChange{this.savePassword} typepassword namepassword/button登录/button/form)}}// 2.渲染组件到页面ReactDOM.render(Demo/, document.getElementById(test))/script
/body
/html4.3 函数柯里化
解决的问题由于4.2中每个字段都需要写一个onChange函数会造成很多的重复工作。函数的柯里化:通过函数调用继续返回函数的方式实现多次接收参数最后统一处理的函数编码形式。常见的高阶函数有:Promise、setTimeout、arr.map()等等
!DOCTYPE html
html langen
headmeta charsetUTF-8title函数柯里化/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库全局会多了一个对象React --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM。全局会多了一个对象ReactDOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- 引入prop-types用于对组件标签属性进行限制。全局会多了一个对象PropTypes --script typetext/javascript src../js/prop-types.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel/*高阶函数:如果一个函数符合下面2个规范中的任何一个那该函数就是高阶函数。1.若A函数接收的参数是一个函数那么A就可以称之为高阶函数。2.若A函数调用的返回值依然是一个函数那么A就可以称之为高阶函数。函数的柯里化:通过函数调用继续返回函数的方式实现多次接收参数最后统一处理的函数编码形式。*/// 1.创建类式组件class Demo extends React.Component {//初始化状态表单信息state {username: , //用户名password: // 密码}// 保存表单数据到状态中saveFormData (datType){return (event){this.setState({[datType]: event.target.value})}}handleSubmit (event){// 阻止表单提交event.preventDefault()const {username, password} this.statealert(你的用户名是${username}你的密码是${password})}render(){return (form actionhttp://127.0.0.1 onSubmit{this.handleSubmit}用户名input onChange{this.saveFormData(username)} typetext nameusername/密码input onChange{this.saveFormData(password)} typepassword namepassword/button登录/button/form)}}// 2.渲染组件到页面ReactDOM.render(Demo/, document.getElementById(test))/script
/body
/html4.4 不使用函数柯里化实现方式
!DOCTYPE html
html langen
headmeta charsetUTF-8titleref和事件处理/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库全局会多了一个对象React --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM。全局会多了一个对象ReactDOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- 引入prop-types用于对组件标签属性进行限制。全局会多了一个对象PropTypes --script typetext/javascript src../js/prop-types.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel/*高阶函数:如果一个函数符合下面2个规范中的任何一个那该函数就是高阶函数。1.若A函数接收的参数是一个函数那么A就可以称之为高阶函数。2.若A函数调用的返回值依然是一个函数那么A就可以称之为高阶函数。函数的柯里化:通过函数调用继续返回函数的方式实现多次接收参数最后统一处理的函数编码形式。*/// 1.创建类式组件class Demo extends React.Component {//初始化状态表单信息state {username: , //用户名password: // 密码}// 保存表单数据到状态中saveFormData (datType, event){this.setState({[datType]: event.target.value})}handleSubmit (event){// 阻止表单提交event.preventDefault()const {username, password} this.statealert(你的用户名是${username}你的密码是${password})}render(){return (form actionhttp://127.0.0.1 onSubmit{this.handleSubmit}用户名input onChange{(event) {this.saveFormData(username, event)}} typetext nameusername/密码input onChange{(event) {this.saveFormData(password, event)}} typepassword namepassword/button登录/button/form)}}// 2.渲染组件到页面ReactDOM.render(Demo/, document.getElementById(test))/script
/body
/html5.组件的生命周期
组件从创建到死亡它会经历一些特定的阶段。React 组件中包含一系列勾子函数生命周期回调函数会在特定的时刻调用。我们在定义组件时会在特定的生命周期回调函数中做特定的工作。
5.1 生命周期-案例
需求 1.标题需要2秒中从透明度1渐变到0再到12.点击按钮组件消失
!DOCTYPE html
html langen
headmeta charsetUTF-8title组件生命周期/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库全局会多了一个对象React --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM。全局会多了一个对象ReactDOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js -- script typetext/javascript src../js/babel.min.js/script!-- 引入prop-types用于对组件标签属性进行限制。全局会多了一个对象PropTypes --script typetext/javascript src../js/prop-types.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建类式组件class Life extends React.Component {state {opacity: 1}death () {// 卸载组件ReactDOM.unmountComponentAtNode(document.getElementById(test))}// 组件被挂载之后调用componentDidMount(){this.timer setInterval(() {// 获取原状态let {opacity} this.state// 减小0.1opacity - 0.1if(opacity 0) opacity 1// 设置新的透明度this.setState({opacity})}, 200);}// 组件将要卸载componentWillUnmount(){// 清除定时器clearInterval(this.timer)}// 初始化渲染和状态更新的时候会被调用render(){return (divh2 style{{opacity:this.state.opacity}}React学不会怎么办/h2button onClick{this.death}不活了/button/div)}}// 2.渲染组件到页面ReactDOM.render(Life/, document.getElementById(test))/script
/body
/html5.2 生命周期旧
5.2.1 流程图
新版本提出了两个新的钩子废弃了两个钩子总共有四种方式会触发钩子 组件挂载时父组件rendersetStateforceUpdate不想让状态更新但是想让页面刷新 componentWillReceiveProps组件第一次render的时候不会被调用而在第二次开始才会被调用 5.2.2 示例代码
!DOCTYPE html
html langen
headmeta charsetUTF-8title组件生命周期/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库全局会多了一个对象React --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM。全局会多了一个对象ReactDOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js --script typetext/javascript src../js/babel.min.js/script!-- 引入prop-types用于对组件标签属性进行限制。全局会多了一个对象PropTypes --script typetext/javascript src../js/prop-types.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建类式组件class Count extends React.Component {constructor(props){console.log(render - constructor)super(props)// 初始化状态this.state {count: 0, content: content}}add () {this.setState({count: this.state.count 1})}// 卸载组件按钮回调death () {ReactDOM.unmountComponentAtNode(document.getElementById(test))}// 强制更新的按钮回调force () {this.forceUpdate()}// 修改子组件的内容changeContent () {this.setState({content: content1})}// 组件将要挂载的钩子componentWillMount(){console.log(render - componentWillMount)}// 组件挂载完毕的钩子componentDidMount(){console.log(render - componentDidMount)}// 组件将要卸载的钩子componentWillUnmount(){console.log(render - componentWillUnmount)}// 控制组件是否更新的钩子(返回的布尔类型如果是false那么不会继续往下走)shouldComponentUpdate(){console.log(render - shouldComponentUpdate)return true}// 组件将要更新的钩子componentWillUpdate(){console.log(render - ComponentWillUpdate)}// 组件完成更新的钩子componentDidUpdate(){console.log(render - componentDidUpdate)}// 初始化渲染和状态更新的时候会被调用render(){console.log(render)return (divh2当前求和为{this.state.count}/h2button onClick{this.add}点我1/buttonbutton onClick{this.death}卸载组件/buttonbutton onClick{this.force}不更改任何状态中的数据强制更新一下/buttonbutton onClick{this.changeContent}修改传入子组件的参数内容/buttonChild content{this.state.content} //div)}}class Child extends React.Component{// 第二次开始接收父组件参数之前的钩子componentWillReceiveProps(props){console.log(render - componentWillReceiveProps, {props})}render(){return h1子组件的内容{this.props.content}/h1}}// 2.渲染组件到页面ReactDOM.render(Count/, document.getElementById(test))/script
/body
/html5.3 生命周期新
废弃三种钩子 componentWillMountcomponentWillUpdatecomponentWillReceiveProps 新增两种钩子但是不常用 getDerivedStateFromProps如果state的值在任何时候都取决于props那么可以用该钩子。如果使用了该组件会导致代码难维护。getSnapshotBeforeUpdate在组件渲染准备渲染前可以拿到渲染前的组件的props和state用于和渲染后的props和state做一些组件高度等对比实现某些场景功能。
5.3.1 流程图 从React18开始有三个钩子的函数名称需要加UNSAFE_前缀除了最后的销毁的钩子其他名称有WILL的名称的钩子那么都需要加上UNSAFE_ 1.UNSAFE_componentWillMount 2.UNSAFE_componentWillUpdate 3.UNSAFE_componentWillReceiveProps 加上UNSAFE_的原因由于后续需要推出异步渲染如果使用上述三种很有可能会出现其他的问题 5.3.2 代码案例
!DOCTYPE html
html langen
headmeta charsetUTF-8title组件生命周期/title
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库全局会多了一个对象React --script typetext/javascript src../js/react.development.js/script!-- 引入react-dom用于支持react操作DOM。全局会多了一个对象ReactDOM --script typetext/javascript src../js/react-dom.development.js/script!-- 引入babel用于将jsx转化为js --script typetext/javascript src../js/babel.min.js/script!-- 引入prop-types用于对组件标签属性进行限制。全局会多了一个对象PropTypes --script typetext/javascript src../js/prop-types.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建类式组件class Count extends React.Component {constructor(props){console.log(render - constructor)super(props)// 初始化状态this.state {count: 0}}add () {this.setState({count: this.state.count 1})}// 卸载组件按钮回调death () {ReactDOM.unmountComponentAtNode(document.getElementById(test))}// 强制更新的按钮回调force () {this.forceUpdate()}// 修改子组件的内容changeContent () {this.setState({content: content1})}// 组件挂载完毕的钩子componentDidMount(){console.log(render - componentDidMount)}// 组件将要卸载的钩子componentWillUnmount(){console.log(render - componentWillUnmount)}// 控制组件是否更新的钩子(返回的布尔类型如果是false那么不会继续往下走)shouldComponentUpdate(){console.log(render - shouldComponentUpdate)return true}// 组件完成更新的钩子componentDidUpdate(preProps, preState, snapshotValue){console.log(render - componentDidUpdate)}static getDerivedStateFromProps(props, state){console.log(render - getDerivedStateFromProps)return props}getSnapshotBeforeUpdate(){console.log(render - getSnapshotBeforeUpdate)return 该值会传入给 componentDidUpdate钩子}// 初始化渲染和状态更新的时候会被调用render(){console.log(render)return (divh2当前求和为{this.state.count}/h2button onClick{this.add}点我1/buttonbutton onClick{this.death}卸载组件/button/div)}}// 2.渲染组件到页面ReactDOM.render(Count/, document.getElementById(test))/script
/body
/html5.3.2 getSnapshotBeforeUpdate-案例
!DOCTYPE html
html langen
headmeta charsetUTF-8title组件生命周期/titlestyle.list{width: 200px;height: 150px;background-color: skyblue;overflow: auto;}.news{height: 30px;}/style
/head
body!-- 创建一个容器 --div idtest/div!-- 引入react核心库全局会多了一个对象React --script typetext/javascript src../js/react17/react.development.js/script!-- 引入react-dom用于支持react操作DOM。全局会多了一个对象ReactDOM --script typetext/javascript src../js/react17/react-dom.development.js/script!-- 引入babel用于将jsx转化为js --script typetext/javascript src../js/react17/babel.min.js/script!-- 引入prop-types用于对组件标签属性进行限制。全局会多了一个对象PropTypes --script typetext/javascript src../js/react17/prop-types.js/script!-- type需要写text.babel因为里面写的是jsx需要babel转化 --script typetext/babel// 1.创建类式组件class NewsList extends React.Component {state {newsArr:[]}componentDidMount(){setInterval(() {const newsArr this.state.newsArr// 模拟一条新闻const news 新闻 (newsArr.length 1)// 更新状态this.setState({newsArr:[news, ...newsArr]})}, 1000)}getSnapshotBeforeUpdate(){return this.refs.list.scrollHeight}componentDidUpdate(preProp, preState, height){this.refs.list.scrollTop this.refs.list.scrollHeight - height}// 初始化渲染和状态更新的时候会被调用render(){return (div classNamelist reflist{this.state.newsArr.map((n, index) {return div classNamenews key{index}{n}/div})}/div)}}// 2.渲染组件到页面ReactDOM.render(NewsList/, document.getElementById(test))/script
/body
/html5.4 DOM的diffing算法
虚拟DOM的key的作用 简单的说key是虚拟DOM对象的标识在更新显示时key起着极其重要的作用。详细的说:当状态中的数据发生变化时react会根据【新数据】生成【新的虚拟DOM】随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较比较规则如下: a.旧虚拟DOM中找到了与新虚拟DOM相同的key 1若虚拟DOM中内容没变直接使用之前的真实DOM2若虚拟DOM中内容变了则生成新的真实DOM随后替换掉页面中之前的真实DOM b.旧虚拟DOM中未找到与新虚拟DOM相同的key。根据数据创建新的真实DOM随后渲染到到页面。 用index作为key可能会引发的问题 1.若对数据进行逆序添加、逆序删除等破坏顺序操作会产生没有必要的真实DOM更新界面效果没问题但效率低。2.如果结构中还包含输入类的DOM会产生错误DOM更新界面有问题。3.注意如果不存在对数据的逆序添加、逆序删除等破坏顺序操作仅用于渲染列表用于展示使用index作为key是没有问题的。
使用index索引值做为key导致的问题导致key会频繁的变化导致页面不断地重新渲染初始数据{id:1,name:小1,age:18},{id:2,name:小2,age:19},
初始的虚拟DOMli key0小1 --- 18/lili key1小2 --- 19/li更新后的数据{id:3,name:小3,age:20},{id:1,name:小1,age:18},{id:2,name:小2,age:19},
更新后的的虚拟DOMli key0小3 --- 20/lili key1小1 --- 18/lili key2小2 --- 19/li开发中如何选择key? 1.最好使用每条数据的唯一标识作为key比如id、手机号、身份证号、学号等唯一值。2.如果确定只是简单的展示数据用index也是可以的。
四、react应用基于react脚手架
包安装
# 安装具体大版本reactnpm add react16
# 安装最新版本npm add react1.创建react应用
使用create-react-app模块创建应用
1.1 react脚手架
xxx脚手架用来帮助程序员快速创建一个基于xxx库的模板项目 a.包含了所有需要的配置语法检查、jsx编译、devServer…b.下载好了所有相关的依赖c.可以直接运行一个简单效果 react 提供了一个用于创建react 项目的脚手架库:create-react-app项目的整体技术架构为:react webpack es6 eslint使用脚手架开发的项目的特点模块化组件化工程化
1.2 创建项目并启动 注意使用npm需要安装node我安装的版本是v20.18.0 第一步全局安装npm i -g create-react-app第二步切换到想创建项目的目录使用命令create-react-app hello_react第三步进入项目文件夹cd hello_react第四步启动项目npm start 如果需要降级react版本 npm i react-dom16.x --legacy-peer-deps npm i react16.x --legacy-peer-deps 1.3 项目结构 public静态资源文件夹 favicon.icon网站页签标题index.html主页面核心文件有个idroot的div标签logo192.png192x192的logo图logo512.png512x512的logo图mainfest.json应用加壳的配置文件不使用加壳可以删除robots.txt爬虫协议文件 src源码文件夹 App.cssApp组件的样式Apps.jsApp组件是一个主组件会被index.js挂载到index.html的idroot的div节点里面App.test.js用于给App做测试几乎不用可以删除index.css样式index.js入口文件引入了React相关库会把App.js的主组件放到index.html的idroot的div节点里面logo.svglogo图
index.html
!DOCTYPE html
html langenheadmeta charsetutf-8 /!-- %PUBLIC_URL%代表public文件夹路径 --link relicon href%PUBLIC_URL%/favicon.ico /!-- 开启理想视口用于做移动端网页的适配 --meta nameviewport contentwidthdevice-width, initial-scale1 /!-- 用于配置浏览器页签地址栏的颜色(仅支持安卓手机浏览器)兼容性不太好 --meta nametheme-color content#000000 /!-- 描述信息搜索引擎收录我们网页信息的时候有用 --metanamedescriptioncontentWeb site created using create-react-app/!-- 用于指定网页添加到手机主屏幕快捷方式的图标苹果手机专用 --link relapple-touch-icon href%PUBLIC_URL%/logo192.png /!-- 应用加壳时的配置文件 --link relmanifest href%PUBLIC_URL%/manifest.json /titleReact App/title/headbody!-- 若浏览器不支持js则展示标签中的内容 --noscriptYou need to enable JavaScript to run this app./noscript!-- 总入口容器 --div idroot/div/body
/htmlindex.js
import React from react;
import ReactDOM from react-dom/client;
import ./index.css;
import App from ./App;
import reportWebVitals from ./reportWebVitals;const root ReactDOM.createRoot(document.getElementById(root));root.render(// 检查App/的所有子组件里面是否写的合理例如某些语法是过期了React.StrictModeApp //React.StrictMode
);reportWebVitals();
2.组件目录定义和导入 3.样式模块化
为什么需要模块化防止不同组件间定义了相同的标签的样式会互相影响。 错误案例
.title{background-color: orange;
}第一种方法使用组件名称常用
.component_name{.title{background-color: orange;}
}第二种方法导包传入不常用
步骤 index和css中间加module命名导入css样式需要拿变量接收接完了会变成一个对象将对象写入到对应的标签
4.vscode安装react插件 快捷键rcc类组件模板rfc函数式组件模板5.组件化编码流程
编码流程 拆分组件拆分界面抽取组件实现静态组件使用组件实现静态页面效果实现动态组件 动态显示初始化数据 数据类型数据名称保存在哪个组件 交互从绑定时间监听开始
6.组件组合使用-TodoList案例
需求 显示所有todo列表输入文本点击按钮显示到列表的首位并清除输入的文本
6.1 拆组件 6.2 依赖安装
# 安装uuid比uuid库更小
npm add nanoid
# 安装类型验证库
npm add prop-types6.3 总结
代码包在资源里面1.TODO-LIST案例.zip相关知识点 1.拆分组件、实现静态组件注意className、style的写法2.动态初始化列表如何确定将数据放在哪个组件的state中? 某个组件使用放在其自身的state中某些组件使用放在他们共同的父组件state中官方称此操作为状态提升 3.关于父子之间通信 1.【父组件】给【子组件】传递数据通过props传递2.【子组件】给【父组件】传递数据通过props传递要求父提前给子传递一个函数 4.注意defaultChecked 和 checked的区别类似的还有defaultValue和 value5.状态在哪里操作状态的方法就在哪里
五、react-axios
说明 React 本身只关注于界面,并不包含发送ajax请求的代码前端应用需要通过ajax请求与后台进行交互json数据react 应用中需要集成第三方 ajax 库或自己封装 常用的ajax请求库 jQuery比较重如果需要另外引入不建议使用axios轻量级,建议使用 封装XmlHttpRequest对象的ajaxpromise风格可以用在浏览器端和node服务器端
1.安装axios
npm add axios2.代理配置
解决开发的时候跨域问题
2.1 方法一不使用
只有react服务器没有的资源才会通过代理转发。会造成问题 如果访问/index.html 会访问到react的public的index.html文件而不是服务端的资源 优点配置简单前端请求资源时可以不加任何前缀。缺点不能配置多个代理。工作方式上述方式配置代理当请求了3000不存在的资源时那么该请求会转发给5000优先匹配前端资源
2.2 方法二常用
src/setupProxy.js是一个React脚手架默认加载的文件不能使用ES6语法去写需要用CJS语法。React会将这个配置文件加载webpack里面。
第一步创建代理配置文件
在src下创建配置文件src/setupProxy.js第二步编写setupProxy.js配置具体代理规则
const proxy require(http-proxy-middleware)module.exports function(app){app.use(// 要把哪些前缀的api代理转发到目标服务器// http-proxy-middleware 2以下版本写法proxy.createProxyMiddleware(/api1,{})// 下面是http-proxy-middleware 2以上版本写法proxy.createProxyMiddleware(/api1,{target:http://localhost:5000, // 请求转发给谁changeOrigin:true, // 控制服务器收到的请求头中Host的值建议必须/*changeOrigin设置为true时服务器收到的请求头中的host为localhost5000changeOrigin设置为false时服务器收到的请求头中的host为localhost3000changeOrigin默认值为false,但我们一般将changeOrigin值设为true*/pathRewrite:{^/api1: } // 重写请求路径必须}),proxy.createProxyMiddleware(/api2,{target:http://localhost:5001, changeOrigin:true, pathRewrite:{^/api2: }}))
}3.github搜索案例
代码包在资源里面2.github搜索案例.zip
axios使用案例
import React, { Component } from react
import axios from axiosexport default class Search extends Component {search (){const {keyWordNode:{value:keyWord}} thisconsole.log(keyWord)// 发送网络请求axios.get(https://api.github.com/search/users?q${keyWord}).then(response {console.log(成功了, this.props.loadUserList(response.data.items))},error {console.log(失败了, error)})}render() {return (section classNamejumbotronh3 classNamejumbotron-heading搜索Github用户/h3divinput ref{c this.keyWordNode c} typetext placeholder请输入关键词点击搜索 /nbsp;button onClick{this.search}搜索/button/div/section)}
}
六、react-router
1.相关理解
1.1 SPA理解
单页Web应用single page web application, SPA整个应用只有一个完整的页面。点击页面中的链接不会刷新页面只会做页面的局部更新。数据都需要通过ajax请求获取并在前端异步展现。
1.2 路由的理解
什么是路由 一个路由就是一个映射关系key: valuekey为路径value可能是function或component 路由分类 后端路由 value是function,用来处理客户端提交的请求。注册路由router.get(path,function(req,res))工作过程当node接收到一个请求时根据请求路径找到匹配的路由调用路由中的函数来处理请求返回响应数据 前端路由 浏览器端路由value 是component用于展示页面内容。↵注册路由: Route path/testcomponent{Test}↵工作过程:当浏览器的 path 变为/test 时,当前路由组件就会变为Test 组件
1.3 react-router-dom的理解
react的一个插件库。专门用来实现一个SPA 应用。基于react的项目基本都会用到此库。
2.react-router-dom
官网http://react-router.docschina.org/总共有三个类别 WEBweb开发使用NATIVE移动端开发使用ANYWHEREweb开发和移动端都可以使用但是最好不使用这个API会比较复杂。
2.1 安装
# 由于react-router-dom在2021年11月份升级到了6版本如果安装5版本需要使用如下命令
npm add react-router-dom52.2 路由组件使用
路由器两种 BrowserRouter使用的是H5的history API不兼容IE9及以下版本。路径中没有#,例如localhost:3000/demo/testHashRouter路径包含#,例如localhost:3000/#/demo/test 注意路由的切换和注册需要在同一个路由器的标签内。 一般是在App的最外侧包裹了一个BrowserRouter或HashRouter 路由跳转Link to “/xxxx” component{Demo}路由注册(页面展示的地方)Route path‘/xxxx’ component{Demo}
import React, { Component } from react
import {Link,BrowserRouter,Route} from react-router-dom
import Home from ./components/Home
import About from ./components/Aboutexport default class App extends Component {loadUserList (userList){this.setState({userList: userList})}render() {return (divdiv classNamerowdiv classNamecol-xs-offset-2 col-xs-8div classNamepage-headerh2React Router Demo/h2/div/div/divBrowserRouterdiv classNamerowdiv classNamecol-xs-2 col-xs-offset-2div classNamelist-group{/* 在原生html中靠a标签跳转不同的页面 */}{/* a classNamelist-group-item href./about.htmlAbout/aa classNamelist-group-item active href./home.htmlHome/a */}{/* 在React中靠路由连接实现切换组件 */}Link classNamelist-group-item to/aboutAbout/LinkLink classNamelist-group-item to/homeHome/Link/div/divdiv classNamecol-xs-6div classNamepaneldiv classNamepanel-body{/* 注册路由 */}Route path/about component{About}/RouteRoute path/home component{Home}/Route/div/div/div/div/BrowserRouter/div)}
}2.3 路由组件和一般组件的区别
写法不同 一般组件Demo/路由组件Route path‘/demo’ component{Demo}/ 存放位置不同 一般组件components路由组件pages 接收到的props不同 一般组件写组件标签时传递了什么就能收到什么路由组件会收到三个参数history、location、match history go(n)回退n步 n 0前进n步n 0回退n步 goBack()回退1步goForward()前进1步push(path,state)跳转留痕replace(path,state)跳转不留痕 location pathname: 路由地址search: 路由组件传入的search参数state路由组件传入的state参数 math paramsparams参数path路由地址url路由地址 借助this.prosp.history对象上的API对操作路由跳转、前进、后退 this.prosp.history.push()-his.prosp.history.replace()this.prosp.history.goBack()this.prosp.history.goForward()this.prosp.history.go()
2.4 NavLink
解决点击了路由对应页面的按钮需要高亮 原理是给当前的按钮的class加了一个active类名 如果不是active类名而是其他的可以通过参数activeClassName控制
NavLink activeClassNameactive classNamelist-group-item to/aboutAbout/NavLink2.5 封装NavLink组件
需求解决一些NavLink的属性重复编写的问题总结 NavLink可以实现路由链接的高亮通过activeClassName指定样式名标签体内容是一个特殊的标签属性通过this.props.children可以获取标签体内容 封装
import React, { Component } from react
import { NavLink } from react-router-domexport default class MyNavLink extends Component {render() {return (NavLink activeClassNameactive classNamelist-group-item {...this.props}/)}
}
使用
/*下面两个效果一样*/
MyNavLink to/homeHome/MyNavLink
MyNavLink to/home chilrenHome/细节MyNavLink标签中间的Home标题他会作为一个组件的children参数传入到this.props参数里面 2.6 Switch使用
通常情况下path和component是一一对应的关系。如果出现了两个路由的path是相同的但是component是不同的那么默认会都展示出来两个组件
Route path/about component{About}/Route
Route path/home component{Test}/Route
Route path/home component{Home}/RouteSwitch组件如果从上到下匹配到了一个path那么就不会继续往下进行匹配 使用场景一个路由组件以上使用
import {Switch,BrowserRouter,Route} from react-router-dom
SwitchRoute path/about component{About}/RouteRoute path/home component{Test}/RouteRoute path/home component{Home}/Route
/Switch2.7 样式丢失
问题如果path加了前缀的时候刷新请求资源的时候也会加上前缀 原因引入css的时候不能使用./css/xxxx.css 解决办法 1.去掉点/css/bootstrap.css2.换成PUBLIC_URL%PUBLIC_URL%/css/bootstrap.css
2.8 路由模糊匹配
默认是开启前缀模糊匹配 默认使用的是模糊匹配(简单记【输入的路径】必须包含要【匹配的路径】且顺序要一致) 开启严格匹配Route exact{true} path“/about” component{About}/ 严格匹配不要随便开启需要再开有些时候开启会导致无法继续匹配二级路由
MyNavLink to/home/a/b/cHome/MyNavLink
// 上面可以匹配到下面的路由
Route path/home component{Home}/Route开启精准匹配
MyNavLink to/homeHome/MyNavLink
Route exact{true} path/home component{Home}/Route2.9 Redirect重定向
一般写在所有路由注册的最下方当所有路由都无法匹配时跳转到Redirect指定的路由
SwitchRoute path/about component{About}/RouteRoute exact{true} path/home component{Home}/RouteRedirect to/about/
/Switch2.10 嵌套多级路由 注册子路由时要写上父路由的path值路由的匹配是按照注册路由的顺序进行的 一级路由
MyNavLink to/homeHome/MyNavLink
SwitchRoute path/home component{Home}/RouteRedirect to/about/
/Switch二级路由
MyNavLink to/home/newsNews/MyNavLink
SwitchRoute path/home/news component{News}/Route
/Switch2.11 向路由组件传递参数数据
使用频率高-低params参数、search参数需要解析参数、state参数敏感信息传递可以用这种 一级路由
MyNavLink to/homeHome/MyNavLink
SwitchRoute path/home component{Home}/RouteRedirect to/about/
/Switch二级路由
MyNavLink to/home/newsNews/MyNavLink
SwitchRoute path/home/news component{News}/Route
/Switch2.11.1 传递params参数
路由链接(携带参数):Link to‘/demo/test/tom/18’}详情/Link注册路由(声明接收)Route path/demo/test/:name/:agecomponent{Test}/接收参数const{id,title}this.props.match.params Message列表
import React, { Component } from react
import { Link,Route } from react-router-dom
import Detail from ./Detailexport default class Message extends Component {state {messageArr:[{id:01,title:消息1},{id:02,title:消息2},{id:03,title:消息3},]}render() {const {messageArr} this.statereturn (divul{messageArr.map((msgObj) {return (li key{msgObj.id}{/* 向路由组件传递params参数 */}Link to{/home/message/detail/${msgObj.id}/${msgObj.title}}{msgObj.title}/Link/li)})}/ulhr/{/* 声明接收params参数 */}Route path/home/message/detail/:id/:title component{Detail}//div)}
}
Message详情
import React, { Component } from reactexport default class Detail extends Component {render() {const {id,title} this.props.match.paramsreturn (ulliID:{id}/liliTITLE:{title}/li/ul)}
}
2.11.2 传递search参数
路由链接携带参数Link to‘/demo/test?nametomage18’}详情/Link注册路由(无需声明,正常注册即可)Route path/demo/test”component{Test}/接收参数const{search}this.props.location备注获取到的search是一个参数字符串需要自己另外处理转化为对象 Message列表
import React, { Component } from react
import { Link,Route } from react-router-dom
import Detail from ./Detailexport default class Message extends Component {state {messageArr:[{id:01,title:消息1},{id:02,title:消息2},{id:03,title:消息3},]}render() {const {messageArr} this.statereturn (divul{messageArr.map((msgObj) {return (li key{msgObj.id}{/* 向路由组件传递search参数 */}Link to{/home/message/detail/?id${msgObj.id}title${msgObj.title}}{msgObj.title}/Link/li)})}/ulhr/{/* 无需声明接收search参数 */}Route path/home/message/detail component{Detail}//div)}
}
Message详情
import React, { Component } from reactfunction queryUrl (search) {let obj {}let url search.slice(1)let arr url.split() //[namejack,age18]arr.forEach(item {let newArr item.split() //[name,jack] [age,18]obj[newArr[0]] newArr[1]})return obj}export default class Detail extends Component {render() {const {search} this.props.locationconst {id,title} queryUrl(search)return (ulliID:{id}/liliTITLE:{title}/li/ul)}
}
2.11.3 传递state参数
路由链接(携带参数):Link to{{path:‘/demo/test’,state:{name:‘tom’,age:18}}}详情/Link注册路由无需声明正常注册即可)Route path/demo/testcomponent{Test}/接收参数this.props.location.state备注刷新也可以保留住参数 Message列表
import React, { Component } from react
import { Link,Route } from react-router-dom
import Detail from ./Detailexport default class Message extends Component {state {messageArr:[{id:01,title:消息1},{id:02,title:消息2},{id:03,title:消息3},]}render() {const {messageArr} this.statereturn (divul{messageArr.map((msgObj) {return (li key{msgObj.id}{/* 向路由组件传递state参数 */}Link to{{pathname:/home/message/detail,state:{id:msgObj.id,title:msgObj.title}}}{msgObj.title}/Link/li)})}/ulhr/{/* 无需声明接收state参数 */}Route path/home/message/detail component{Detail}//div)}
}
Message详情
import React, { Component } from reactexport default class Detail extends Component {render() {// 接收state参数const {id,title} this.props.location.statereturn (ulliID:{id}/liliTITLE:{title}/li/ul)}
}
2.12 路由跳转的两个模式
两个模式的区别 push模式是默认的模式路由跳转会留下痕迹replace模式跳转不会留下痕迹
2.12.1 push模式
默认的路由组件就是push模式
2.12.2 replace模式
Link replace to/home/message/detail{msgObj.title}/Link2.13 编程式路由导航
需求 A组件展示后等三秒钟跳转到B组件跳转跳转可以点击按钮是push模式 还是replace模式实现回退、前进 路由组件 history go(n)回退n步 n 0前进n步n 0回退n步 goBack()回退1步goForward()前进1步push(path,state)跳转留痕replace(path,state)跳转不留痕 借助this.prosp.history对象上的API对操作路由跳转、前进、后退 this.prosp.history.push()-his.prosp.history.replace()this.prosp.history.goBack()this.prosp.history.goForward()this.prosp.history.go()
import React, { Component } from react
import { Link,Route } from react-router-dom
import Detail from ./Detailexport default class Message extends Component {state {messageArr:[{id:01,title:消息1},{id:02,title:消息2},{id:03,title:消息3},]}replaceShow (id,title){// replace跳转params参数this.props.history.replace(/home/message/detail/${id}/${title})// replace跳转携带search参数// this.props.history.replace(/home/message/detail?id${id}title${title})// replace跳转携带state参数// this.props.history.replace(/home/message/detail, {id,title})}pushShow (id,title){// push跳转params参数this.props.history.push(/home/message/detail/${id}/${title})// push跳转携带search参数// this.props.history.replace(/home/message/detail?id${id}title${title})// push跳转携带state参数// this.props.history.replace(/home/message/detail, {id,title})}back () {this.props.history.goBack()}forward () {this.props.history.goForward()}render() {const {messageArr} this.statereturn (divul{messageArr.map((msgObj) {return (li key{msgObj.id}{/* 向路由组件传递参数 */}Link to{/home/message/detail/${msgObj.id}/${msgObj.title}}{msgObj.title}/Linknbsp;button onClick{(){this.pushShow(msgObj.id,msgObj.title)}}push查看/buttonnbsp;button onClick{(){this.replaceShow(msgObj.id,msgObj.title)}}replace查看/button/li)})}/ulhr/{/* 需声明接收params参数 */}Route path/home/message/detail/:id/:title component{Detail}/{/* search参数无需声明接收正常注册路由即可*/}{/* Route path/home/message/detail component{Detail}/ */}{/* state参数无需声明接收正常注册路由即可*/}{/* Route path/home/message/detail component{Detail}/ */}button onClick{this.back}回退/buttonnbsp;button onClick{this.forward}前进/button/div)}
}
2.14 让一般组件也有路由组件的参数-withRouter
import React, { Component } from react
import {withRouter} from react-router-domclass Header extends Component {back () {this.props.history.goBack()}render() {return (divdivh2React Router Demo/h2/div/div)}
}// 让一般组件也有路由组件的三个props属性
export default withRouter(Header)
2.15 BrowserRouter与HashRouter的区别
1.底层原理不一样 BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。HashRouter使用的是URL的哈希值。 2.ur1表现形式不一样 BrowserRouter的路径中没有#,例如localhost:3000/demo/testHashRouter的路径包含#,例如localhost3000/#/demo/test 3.刷新后对路由state参数的影响 (1).BrowserRouter没有任何影响因为state保存在history对象中。(2).HashRouter刷新后会导致路由state参数的丢失。 4.备注HashRouter可以用于解决一些路径错误相关的问题。5.BrowerRouter用得比较多
七、组件库-Ant Design
web开发推荐Ant Design 官网地址https://ant-design.antgroup.com/index-cn 移动端开发推荐有赞团队的Vant 官网地址https://vant-ui.github.io/vant/#/zh-CN
1.安装
# 如果要安装4版本的主要执行npm install antd4
npm install antd2.入门体验
注意如果是4版本的需要引入css样式如果是5版本的antd只需要导入组件就有对应的样式。下面是用antd4版本案例
import React, { Component } from react
import { Button} from antd
import {WechatOutlined
} from ant-design/icons;
import antd/dist/antd.cssexport default class App extends Component {render() {return (divButton typeprimary icon{WechatOutlined /}点我/Button/div)}
}3.按需引入样式
import ‘antd/dist/antd.css’ 代表将所有antd的css样式都引入来了。3.x版本的教程会更加详细4.x省略了一些 4.x按需引入样式教程https://3x.ant.design/docs/react/use-with-create-react-app-cn 在5.x版本不需要再手工引入样式框架自己处理了。
4.自定义主题
antd默认主题色是支付宝的蓝色下面是修改默认主题色注意 4.x版本使用了less和css变量需要安装包5.x版本使用了CSS-in-JS不需要另外安装包 下面是4.x版本的教程
4.1 安装包
npm add react-app-rewired customize-cranpm add craco-lessnpm add babel-plugin-import 教程来源于3.x
4.2 修改package.json
需改里面scripts
4.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: orange },}}),
);八、redux
文档 英文https://redux.js.org/中文文档https://redux.org.cn
1.理解
1.1 redux是什么
redux是一个专门用于做状态管理的JS库不是react插件库。它可以用在react,angular,vue等项目中,但基本与react配合使用。作用集中式管理 react 应用中多个组件共享的状态。
1.2 使用场景
某个组件的状态需要让其他组件可以随时拿到(共享)。一个组件需要改变另一个组件的状态(通信)。总体原则:能不用就不用,如果不用比较吃力才考虑使用。
2.redux的核心API
注意在reducer中不要使用在某种情况下不会更改state的逻辑代码。例如某个数为偶数的时候才加1。 逻辑代码需要在业务层控制好reducer只负责加工state数据。
2.1 安装redux
下面案例都是使用redux4进行使用
# 如果需要安装redux4版本npm add redux4
npm add redux2.2 redux工作流程
流程 React Components需要修改state通过Action对象告诉StoreAction对象里面有type和data。 action一个动作一个 动作的对象它可以是两种类型 Object同步actionfunction异步action 包含两个属性 type标识属性值为字符串唯一必要属性data数据属性值类型任意可选属性 例子{type:‘ADD_STUDENT’,data:{name:‘tom’,age:18}} reducer每个组件都有一个 用于初始化状态加工状态加工时根据旧的state和action产生新的state的纯函数 store一个项目就一个 存放公共state的地方
2.3 使用案例
2.3.1 求和案例-纯react版本
import React, { Component } from reactexport default class Count extends Component {state {count:0}// 加法increment (){const {value} this.selectNumberconst {count} this.statethis.setState({count:countvalue*1})}// 减法decrement (){const {value} this.selectNumberconst {count} this.statethis.setState({count:count-value*1})}// 奇数再加incrementIfOdd (){const {value} this.selectNumberconst {count} this.stateif(count%2 ! 0){this.setState({count:countvalue*1})}}// 异步加incrementAsync (){const {value} this.selectNumberconst {count} this.statesetTimeout((){this.setState({count:countvalue*1})},500)}render() {return (divh1当前求和为{this.state.count}/h1select ref{c this.selectNumber c}option value11/optionoption value22/optionoption value33/option/selectnbsp;button onClick{this.increment}/buttonnbsp;button onClick{this.decrement}-/buttonnbsp;button onClick{this.incrementIfOdd}当前求和为奇数再加/buttonnbsp;button onClick{this.incrementAsync}异步加/button/div)}
}
2.3.2 求和案例-redux精简版
将组件组件注册到store数据更改了会被通知主要是重新调用组件render。两种方式 1.在每个组件的componentDidMount函数中注册2.在index.js中注册 步骤 1.编写reducer2.编写store将reducer注册3.在代码中使用store.getState获取状态4.在代码中使用store.dispatch({type:‘increment’,data:value*1}) action让store告诉reducer加工数据。5.在组件订阅store。如果在index.js订阅了就需要这一步骤 count_reducer.js
/* 1.该文件是用于创建一个为count组件服务的reducerreducer的本质就是一个函数2.reducer函数会接到两个参数分别为之前的状态(preState)动作对象(action)
*/// 如果没有传入值或者传入的preState是undefined那么默认为0
export default function countReducer(preState111,action){//从action对象中获取type、dataconst {type,data} action//根据type决定如何加工数据switch (type){case increment:// 加法return preState datacase decrement:// 减法return preState - datadefault:return preState}
}store.js
/* 该文件专门用于暴露一个store对象整个应用只有一个store对象
*/
//引入createStore,专门用于创建redux中最为核心的store对象
import { createStore } from redux
//引入为Count组件服务的reducer
import countReducer from ./count_reducer
//暴露store
export default createStore(countReducer)
index.js
import ReactDOM from react-dom;
import App from ./App;
import store from ./redux/store;ReactDOM.render(App/,document.getElementById(root))store.subscribe((){ReactDOM.render(App/,document.getElementById(root))
})
coponents/Count/index.jsx
import React, { Component } from react
//引入store用户获取redux获取的状态
import store from ../../redux/store
import {createIncrementAction,createDecrementAction} from ../../redux/count_actionexport default class Count extends Component {// componentDidMount(){// // 检测redux中状态的变化只要变化就调用render// store.subscribe((){// // 只要调用了setState就调用render// this.setState({})// })// }// 加法increment (){const {value} this.selectNumberstore.dispatch(createIncrementAction(value*1))}// 减法decrement (){const {value} this.selectNumberstore.dispatch(createDecrementAction(value*1))}// 奇数再加incrementIfOdd (){const {value} this.selectNumberconst count store.getState()if(count%2 ! 0){store.dispatch(createIncrementAction(value*1))}}// 异步加incrementAsync (){const {value} this.selectNumberconst count store.getState()setTimeout((){store.dispatch(createDecrementAction(value*1))},500)}render() {return (divh1当前求和为{store.getState()}/h1select ref{c this.selectNumber c}option value11/optionoption value22/optionoption value33/option/selectnbsp;button onClick{this.increment}/buttonnbsp;button onClick{this.decrement}-/buttonnbsp;button onClick{this.incrementIfOdd}当前求和为奇数再加/buttonnbsp;button onClick{this.incrementAsync}异步加/button/div)}
}
2.3.3 求和案例-redux完整版
比精简版多了以下内容 新增文件: 1.count_action.js专门用于创建action对象2.constant.js放置容易写错的type值 constant.js
/*该模块是用于定义action对象中type类型的常量值 , 是为了便于管理的同时防止程序员写错单词*/
export const INCREAMENT increment
export const DECREAMENT increment
count_action.js
/* 该文件专门为Count组件生成action对象
*/
import { INCREAMENT,DECREAMENT } from ./constant
export const createIncrementAction data ({type:INCREAMENT,data})
export const createDecrementAction data ({type:DECREAMENT,data})
count_reducer.js
/* 1.该文件是用于创建一个为count组件服务的reducerreducer的本质就是一个函数2.reducer函数会接到两个参数分别为之前的状态(preState)动作对象(action)
*/
import { INCREAMENT,DECREAMENT } from ./constant
// 如果没有传入值或者传入的preState是undefined那么默认为0
export default function countReducer(preState111,action){//从action对象中获取type、dataconst {type,data} action//根据type决定如何加工数据switch (type){case INCREAMENT:// 加法return preState datacase DECREAMENT:// 减法return preState - datadefault:return preState}
}store.js
/* 该文件专门用于暴露一个store对象整个应用只有一个store对象
*/
//引入createStore,专门用于创建redux中最为核心的store对象
import { createStore } from redux
//引入为Count组件服务的reducer
import countReducer from ./count_reducer
//暴露store
export default createStore(countReducer)
index.js
import ReactDOM from react-dom;
import App from ./App;
import store from ./redux/store;ReactDOM.render(App/,document.getElementById(root))store.subscribe((){ReactDOM.render(App/,document.getElementById(root))
})
coponents/Count/index.jsx
2.3.2一样
2.4 异步action
异步action不是一定要用的东西在组件里面通过定时器实现也是可以action返回的是Object那么就是同步action。返回的是一个函数那么就是异步action。 因为函数里面可以开启异步任务
2.4.1 安装
# 如果是rect16安装命令npm add redux-thunk2.3.0
# 如果是react最新版本安装命令npm add redux-thunk2.4.2 注册中间件
store.js
/* 该文件专门用于暴露一个store对象整个应用只有一个store对象
*/
//引入createStore,专门用于创建redux中最为核心的store对象
import { createStore,applyMiddleware } from redux
//引入为Count组件服务的reducer
import countReducer from ./count_reducer// 引入redux-thunk,用于支持异步action
import thunk from redux-thunk//暴露store,注册异步action的异步中间件
export default createStore(countReducer,applyMiddleware(thunk))
2.4.3 异步aciton定义
/* 该文件专门为Count组件生成action对象
*/
import { INCREAMENT,DECREAMENT } from ./constant
import store from ./store//同步action,就是指action的值为object类型的一般对象
export const createIncrementAction data ({type:INCREAMENT,data})
export const createDecrementAction data ({type:DECREAMENT,data})//异步action就是指action的值为函数异步action中一般都会调用同步action
export const createIncrementAsyncAction (data,time) {return (dispatch){setTimeout((){// 通知redux加上datadispatch(createIncrementAction(data))},time)}
}
九、react-redux
目的不让开发者随意的在UI组件里面随意和redux进行交互而是通过UI组件外面那一层容器进行交互。
1.模型图
1.所有的UI组件都应该包裹一个容器组件他们是父子关系。2.容器组件是真正和redux打交道的里面可以随意的使用redux的api。3.Ul组件中不能使用任何redux的api。4.容器组件会传给UI组件(1).redux中所保存的状态。2).用于操作状态的方法5.备注容器给Ul传递状态、操作状态的方法均通过props传递。
2.安装
# react16安装npm add react-redux7.2.2
# 下面是react最新版安装命令
npm add react-redux3.相关概念
(1)明确两个概念 1)UI组件不能使用任何redux的api只负责页面的呈现、交互等。2)容器组件负责和redux通信将结果交给UI组件。 (2)如何创建一个容器组件-—靠react-redux 的connect函数 connect(mapStateToProps,mapDispatchToProps)(UI组件) mapStateToProps:映射状态返回值是一个对象mapDispatchToProps:映射操作状态的方法返回值是一个对象 (3)备注1容器组件中的store是靠props传进去的而不是在容器组件中直接引入(4)备注2mapDispatchToProps,也可以是一个对象
4.基础操作单组件 4.1 index.js
监控redux状态变化
import ReactDOM from react-dom;
import App from ./App;
import store from ./redux/store;ReactDOM.render(App/,document.getElementById(root))// 监测redux中状态的改变如redux的状态发生了改变那么重新渲染App组件
store.subscribe((){ReactDOM.render(App/,document.getElementById(root))
})
4.2 redux相关
store.js
/* 该文件专门用于暴露一个store对象整个应用只有一个store对象
*/
//引入createStore,专门用于创建redux中最为核心的store对象
import { createStore,applyMiddleware } from redux
//引入为Count组件服务的reducer
import countReducer from ./count_reducer// 引入redux-thunk,用于支持异步action
import thunk from redux-thunk//暴露store,注册异步action的异步中间件
export default createStore(countReducer,applyMiddleware(thunk))
constant.js
/*该模块是用于定义action对象中type类型的常量值 , 是为了便于管理的同时防止程序员写错单词*/
export const INCREAMENT increment
export const DECREAMENT decrement
count_actino.js
/* 该文件专门为Count组件生成action对象
*/
import { INCREAMENT,DECREAMENT } from ./constant//同步action,就是指action的值为object类型的一般对象
export const createIncrementAction data ({type:INCREAMENT,data})
export const createDecrementAction data ({type:DECREAMENT,data})//异步action就是指action的值为函数
export const createIncrementAsyncAction (data,time) {return (dispatch){setTimeout((){// 通知redux加上datadispatch(createIncrementAction(data))},time)}
}
count_reducer.js
/* 1.该文件是用于创建一个为count组件服务的reducerreducer的本质就是一个函数2.reducer函数会接到两个参数分别为之前的状态(preState)动作对象(action)
*/
import { INCREAMENT,DECREAMENT } from ./constant
// 如果没有传入值或者传入的preState是undefined那么默认为0
export default function countReducer(preState111,action){//从action对象中获取type、dataconst {type,data} action//根据type决定如何加工数据switch (type){case INCREAMENT:// 加法return preState datacase DECREAMENT:// 减法return preState - datadefault:return preState}
}4.3 容器
// 引入Count的UI组件
import CountUI from ../../components/Count
// 引入connect用于连接UI组件与redux
import { connect } from react-redux
// 引入action
import {createIncrementAction,createDecrementAction,createIncrementAsyncAction} from ../../redux/count_action/* 映射状态1.mapStateToProps函数返回是一个对象2.返回得对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value3.mapStateToProps用于传递状态
*/
function mapStateToProps(state){// 等于CountUI n{900}/return {count:state}
}/* 映射操作状态得方法1.mapDispatchToProps函数返回是一个对象2.返回得对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value3.mapDispatchToProps用于传递状态
*/
function mapDispatchToProps(dispatch){return {jia:(number){//通知redux执行加法dispatch(createIncrementAction(number))},jian:(number){//通知redux执行减法dispatch(createDecrementAction(number))},jiaAsync:(number,time){//通知redux执行异步加dispatch(createIncrementAsyncAction(number,time))},}
}// 创建并暴露一个Count的容器组件
export default connect(mapStateToProps,mapDispatchToProps)(CountUI)
4.4 UI组件
import React, { Component } from reactexport default class Count extends Component {// 加法increment (){const {value} this.selectNumberthis.props.jia(value*1)}// 减法decrement (){const {value} this.selectNumberthis.props.jian(value*1)}// 奇数再加incrementIfOdd (){const {value} this.selectNumberif(this.props.count % 2 ! 0){this.props.jia(value*1)}}// 异步加incrementAsync (){const {value} this.selectNumberthis.props.jiaAsync(value*1,500)}render() {return (divh1当前求和为{this.props.count}/h1select ref{c this.selectNumber c}option value11/optionoption value22/optionoption value33/option/selectnbsp;button onClick{this.increment}/buttonnbsp;button onClick{this.decrement}-/buttonnbsp;button onClick{this.incrementIfOdd}当前求和为奇数再加/buttonnbsp;button onClick{this.incrementAsync}异步加/button/div)}
}
5.基础操作代码优化
优化内容 容器组件和UI组件整合成一个文件无需自己给容器组件传递store给App/包裹一个Provider store{store}即可。使用了react-redux后也不用自己检测redux中状态的改变了容器组件可以自动完成这个工作。mapDispatchToProps也可以简单的写成一个对象一个组件要和redux“打交道”要经过那几步 1.定义好UI组件不暴露2.引入connect生成一个容器组件并暴露。3.在UI组件中通过this.props.xxx读取和操作状态
5.1 容器优化 5.2 redux状态变化机制
由于使用了react-redux当状态变化了会自动重新渲染组件 原理由于我们使用connectreact-redux在里面做了状态监听渲染
5.3 容器传入store优化-Provider Provider会分析所有的容器组件将store传入所有的store组件
5.4 整合UI组件和容器组件
将UI组件和容器组件整合到一个文件里面整合的内容还是叫容器
import React, { Component } from react
// 引入connect用于连接UI组件与redux
import { connect } from react-redux
// 引入action
import {createIncrementAction,createDecrementAction,createIncrementAsyncAction} from ../../redux/count_action// 定义UI组件
class Count extends Component {// 加法increment (){const {value} this.selectNumberthis.props.jia(value*1)}// 减法decrement (){const {value} this.selectNumberthis.props.jian(value*1)}// 奇数再加incrementIfOdd (){const {value} this.selectNumberif(this.props.count % 2 ! 0){this.props.jia(value*1)}}// 异步加incrementAsync (){const {value} this.selectNumberthis.props.jiaAsync(value*1,500)}render() {return (divh1当前求和为{this.props.count}/h1select ref{c this.selectNumber c}option value11/optionoption value22/optionoption value33/option/selectnbsp;button onClick{this.increment}/buttonnbsp;button onClick{this.decrement}-/buttonnbsp;button onClick{this.incrementIfOdd}当前求和为奇数再加/buttonnbsp;button onClick{this.incrementAsync}异步加/button/div)}
}// 创建并暴露一个Count的容器组件
export default connect(state ({count:state}),{jia:createIncrementAction,jian:createDecrementAction,jiaAsync:createIncrementAsyncAction,}
)(Count)
6.基础操作多组件数据共享
6.1 实现效果 6.2 redux目录结构 6.3 相关代码
总结 定义一个Pserson组件和Count组件通过redux共享数据。为Person组件编写: reducer、action配置constant常量。重点Person的reducer和Count的Reducer要使用combineReducers进行合并合并后的总状态是一个对象交给store的是总reducer最后注意在组件中取出状态的时候记得“取到位”。
App.jsx
import React, { Component } from react
import Count from ./containers/Count
import Person from ./containers/Personexport default class App extends Component {render() {return (divCount/hr/Person//div)}
}
Person/index.jsx
import React, { Component } from react
// 引入connect用于连接UI组件与redux
import { connect } from react-redux
import { nanoid } from nanoid
import { createAddPersonAction } from ../../redux/action/personclass Person extends Component {addPerson (){const name this.nameNode.valueconst age this.ageNode.valueconst personObj {id:nanoid(),name:name,age:age}console.log(personObj)this.props.addPerson(personObj)this.nameNode.value this.ageNode.value }render() {return (divh2我是Person组件上方组件汇总为{this.props.count}/h2input ref{cthis.nameNode c} typetext placeholder输入名字/input ref{cthis.ageNode c} typetext placeholder输入年龄/button onClick{this.addPerson}添加/buttonul{this.props.persons.map((p){return li key{p.id}{p.name}---{p.age}/li})}/ul/div)}
}// 创建并暴露一个的容器组件
export default connect(state ({persons:state.person_array,count:state.count}),{addPerson:createAddPersonAction}
)(Person)
Count/index.jsx
import React, { Component } from react
// 引入connect用于连接UI组件与redux
import { connect } from react-redux
// 引入action
import {createIncrementAction,createDecrementAction,createIncrementAsyncAction} from ../../redux/action/count// 定义UI组件
class Count extends Component {// 加法increment (){const {value} this.selectNumberthis.props.jia(value*1)}// 减法decrement (){const {value} this.selectNumberthis.props.jian(value*1)}// 奇数再加incrementIfOdd (){const {value} this.selectNumberif(this.props.count % 2 ! 0){this.props.jia(value*1)}}// 异步加incrementAsync (){const {value} this.selectNumberthis.props.jiaAsync(value*1,500)}render() {return (divh2我是Count组件/h2h4当前求和为{this.props.count}下方组件总人数为{this.props.person_count}/h4select ref{c this.selectNumber c}option value11/optionoption value22/optionoption value33/option/selectnbsp;button onClick{this.increment}/buttonnbsp;button onClick{this.decrement}-/buttonnbsp;button onClick{this.incrementIfOdd}当前求和为奇数再加/buttonnbsp;button onClick{this.incrementAsync}异步加/button/div)}
}// 创建并暴露一个Count的容器组件
export default connect(state ({count:state.count,person_count:state.person_array.length}),{jia:createIncrementAction,jian:createDecrementAction,jiaAsync:createIncrementAsyncAction,}
)(Count)
redux/store.js
/* 该文件专门用于暴露一个store对象整个应用只有一个store对象
*/
//引入createStore,专门用于创建redux中最为核心的store对象
import { createStore,applyMiddleware,combineReducers } from redux
//引入为Count组件服务的reducer
import countReducer from ./reducer/count// 引入redux-thunk,用于支持异步action
import thunk from redux-thunk
import personReducer from ./reducer/person//汇总所有的reducer变为一个总的reducer
const allReducer combineReducers({count:countReducer,person_array:personReducer,
})//暴露store,注册异步action的异步中间件
export default createStore(allReducer,applyMiddleware(thunk))
redux/constant.js
/*该模块是用于定义action对象中type类型的常量值 , 是为了便于管理的同时防止程序员写错单词*/
export const INCREAMENT increment
export const DECREAMENT decrement
export const ADDPPERSON add_person
redux/action/count.js
/* 该文件专门为Count组件生成action对象
*/
import { INCREAMENT,DECREAMENT } from ../constant//同步action,就是指action的值为object类型的一般对象
export const createIncrementAction data ({type:INCREAMENT,data})
export const createDecrementAction data ({type:DECREAMENT,data})//异步action就是指action的值为函数
export const createIncrementAsyncAction (data,time) {return (dispatch){setTimeout((){// 通知redux加上datadispatch(createIncrementAction(data))},time)}
}
redux/action/person.js
import { ADDPPERSON } from ../constant// 创建增加一个人的action动作对象
export const createAddPersonAction personObj ({type:ADDPPERSON,data:personObj})
redux/reducer/person.js
import {ADDPPERSON} from ../constant// 初始化人的列表
const initState [{id:001,name:tom,age:18}]export default function personReducer(preStateinitState,action){const {type,data} actionswitch(type){case ADDPPERSON:return [data,...preState]default:return preState}
}
redux/reducer/count.js
/* 1.该文件是用于创建一个为count组件服务的reducerreducer的本质就是一个函数2.reducer函数会接到两个参数分别为之前的状态(preState)动作对象(action)
*/
import { INCREAMENT,DECREAMENT } from ../constant
// 如果没有传入值或者传入的preState是undefined那么默认为0
export default function countReducer(preState111,action){//从action对象中获取type、dataconst {type,data} action//根据type决定如何加工数据switch (type){case INCREAMENT:// 加法return preState datacase DECREAMENT:// 减法return preState - datadefault:return preState}
}7.纯函数
7.1 reducer注意事项
如果使用了unshift不是纯函数不得改写参数数据
import {ADDPPERSON} from ../constant// 初始化人的列表
const initState [{id:001,name:tom,age:18}]export default function personReducer(preStateinitState,action){const {type,data} actionswitch(type){case ADDPPERSON:// 注意redux对比的preState的对象内存地址如果相同那么就不会做更新。// 错误案例不会更新return preState.unshift(data)return [data,...preState]default:return preState}
}
7.2 纯函数
一类特别的函数:只要是同样的输入(实参)必定得到同样的输出(返回)必须遵守以下一些约束 1)不得改写参数数据2)不会产生任何副作用例如网络请求输入和输出设备3)不能调用 Date.now()或者Math.random()等不纯的方法 redux的reducer 函数必须是一个纯函数
// 不是纯函数只要是同样的输入(实参)必定得到同样的输出(返回)
function demo(a){return Math.random() a
}
// 不是纯函数不得改写参数数据
function demo(a){a 9return a
}// 纯函数
function demo(a){return a * 2
}
8.redux开发者工具
8.1 安装扩展程序 8.2 安装库
npm add redux-devtools-extension8.3 store.js注册插件
composeWithDevTools
/* 该文件专门用于暴露一个store对象整个应用只有一个store对象
*/
//引入createStore,专门用于创建redux中最为核心的store对象
import { createStore,applyMiddleware,combineReducers } from redux
//引入为Count组件服务的reducer
import countReducer from ./reducer/count// 引入redux-devtools-extension
import { composeWithDevTools } from redux-devtools-extension// 引入redux-thunk,用于支持异步action
import thunk from redux-thunk
import personReducer from ./reducer/person//汇总所有的reducer变为一个总的reducer
const allReducer combineReducers({count:countReducer,person_array:personReducer,
})//暴露store,注册异步action的异步中间件
export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))
十、react打包
将react代码转换为js代码可以供浏览器识别
npm run build十一、react扩展
1.setState
setState式异步的调用之后不会马上更新值总结 1.对象式的setState是函数式的setState的简写方式(语法糖)2.使用原则: (1).如果新状态不依赖于原状态使用对象方式(2).如果新状态依赖于原状态使用函数方式(3).如果需要在setState()执行后获取最新的状态数据要在第二个callback函数中读取
1.1 对象式
setState(stateChange, [callback])------对象式的setState 1.statechange为状态改变对象该对象可以体现出状态的更改2.callback是可选的回调函数它在状态更新完毕、界面也更新后render调用后才被调用
import React, { Component } from reactexport default class Demo extends Component {state {count:0}add () {const {count} this.statethis.setState({count:count1},(){//改完状态render之后的值是加1之后的值console.log(callback: this.state.count)})// 输出0console.log(输出 this.state.count)}render() {return (divh1当前求和为{this.state.count}/h1button onClick{this.add}点我1/button/div)}
}
1.2 函数式
setState(updater, [callback])------函数式的setState 1.updater为返回statechange对象的函数。2.updater可以接收到state和props。3.callback是可选的回调函数它在状态更新、界面也更新后render调用后才被调用。
import React, { Component } from reactexport default class Demo extends Component {state {count:0}add () {this.setState((state,props){return {count:state.count1}},(){//改完状态render之后的值是加1之后的值console.log(callback: this.state.count)})}render() {return (divh1当前求和为{this.state.count}/h1button onClick{this.add}点我1/button/div)}
}
2.路由懒加载lazyLoad
2.1 无懒加载
import React, { Component } from react
import { NavLink,Route } from react-router-dom
import About from ./About
import Home from ./Homeexport default class Demo extends Component {render() {return (divdivNavLink to/homeHome/NavLinkhr/NavLink to/aboutAbout/NavLink/divdivRoute path/about component{About}/Route path/home component{Home}//div/div)}
}
2.2 懒加载
需要使用Suspense并在callback中执行正在加载路由资源的时候显示的页面
import React, { Component, lazy,Suspense } from react
import { NavLink,Route } from react-router-domconst Home lazy(() import(./Home))
const About lazy(() import(./About))export default class Demo extends Component {render() {return (divdivNavLink to/homeHome/NavLinkhr/NavLink to/aboutAbout/NavLink/divdivSuspense fallback{h1Loadind..../h1}Route path/about component{About}/Route path/home component{Home}//Suspense/div/div)}
}
3.Hooks
3.1 是什么
Hook是React 16.8.0版本增加的新特性/新语法可以让你在函数组件中使用state 以及其他的React 特性
3.2 三个常用的Hook
State Hook: React.useState() 状态钩子 Effect Hook: React.useEffect() 生命周期钩子 Ref Hook: React.useRef()
3.3 State Hook
state Hook让函数组件也可以有state状态并进行状态数据的读写操作语法const [xxx,setXxx]React.useState(initValue)useState()说明 参数第一次初始化指定的值在内部作缓存返回值包含2个元素的数组第1个为内部当前状态值第2个为更新状态值的函数 setXxx()2种写法 setxxx(newvalue)参数为非函数值直接指定新的状态值内部用其覆盖原来的状态值setxxx(value newvalue)参数为函数接收原本的状态值返回新的状态值内部用其覆盖原来的状态值
import React from react// 类式组件
// export default class Demo extends React.Component {// state {count:0} // add (){
// this.setState({count:this.state.count1})
// }// render() {
// return (
// div
// h2当前求和状态为{this.state.count}/h2
// button onClick{this.add}点我1/button
// /div
// )
// }
// }export default function Demo(){const [count,setCount] React.useState(0)function add(){// 第一种写法// setCount(count1)// 第二种写法setCount((count){return count1})}return (divh2当前求和状态为{count}/h2button onClick{add}点我1/button/div)
}
3.4 Effect Hook
Effect Hook 可以让你在函数组件中执行副作用操作用于模拟类组件中的生命周期钩子)React中的副作用操作 发ajax请求数据获取设置订阅/启动定时器手动更改真实DOM 语法和说明
useEffect(() {//在此可以执行任何带副作用操作return){//在组件卸载前执行//在此做一些收尾工作比如清除定时器/取消订阅等}}[statevalue]//如果指定的是[]回调函数只会在第一次render后执行
可以把 useEffect Hook看做如下三个函数的组合 componentDidMount()componentDidUpdate()componentWillUnmount()
import React from react
import ReactDOM from react-dom// 类式组件
// export default class Demo extends React.Component {// state {count:0} // add (){
// this.setState({count:this.state.count1})
// }// unmount (){
// ReactDOM.unmountComponentAtNode(document.getElementById(root))
// }// componentDidMount(){
// this.timer setInterval((){
// this.setState({count:this.state.count1})
// },1000)
// }// componentWillUnmount(){
// clearInterval(this.timer)
// }// render() {
// return (
// div
// h2当前求和状态为{this.state.count}/h2
// button onClick{this.add}点我1/button
// button onClick{this.unmount}卸载组件/button
// /div
// )
// }
// }export default function Demo(){const [count,setCount] React.useState(0)React.useEffect((){let timer setInterval((){setCount(count count1)},1000)return (){clearInterval(timer)}},[])function add(){setCount(count1)}function unmount(){ReactDOM.unmountComponentAtNode(document.getElementById(root))}return (divh2当前求和状态为{count}/h2button onClick{add}点我1/buttonbutton onClick{unmount}卸载组件/button/div)
}
3.5 Ref Hook
Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据语法const refcontainer useRef()作用保存标签对象功能与React.createRef()一样
import React from react
import ReactDOM from react-dom// 类式组件
// export default class Demo extends React.Component {// myRef React.createRef()// show (){
// alert(this.myRef.current.value)
// }// render() {
// return (
// div
// input typetext ref{this.myRef}/
// button onClick{this.show}点击提示数据/button
// /div
// )
// }
// }export default function Demo(){const myRef React.useRef()function show(){alert(myRef.current.value)}function unmount(){ReactDOM.unmountComponentAtNode(document.getElementById(root))}return (divinput typetext ref{myRef}/button onClick{show}点击提示数据/button/div)
}
4.Fragment
作用可以不用必须有一个真实的DOM根标签了 下面两种都可以 / import React from react
import ReactDOM from react-domexport default class Demo extends React.Component {myRef React.createRef()show (){alert(this.myRef.current.value)}render() {return (input typetext ref{this.myRef}/button onClick{this.show}点击提示数据/button/)}
}
5.Context
一种组件间通信方式常用于【祖组件】与【后代组件】间通信
5.1 使用
// 1.创建Context容器对象
const xxxContext React.createContext()// 2.渲染子组的时候外面包裹一层xxxContext.Provider通过value属性给后代组件传递数据
xxxContext.provider value{数据}
子组件
/xxxContext.Provider// 3.后代组件读取数据
// 3.1 第一种方式仅适用于类组件
static contextType xxxContext //声明接收context
this.context // 读取context中value的数据
// 3.2 第二种方式函数组件与类组件都可以
xxxContext.Consumer
{value ( //value就是context中的value数据要显示的内容)
}
/xxxContext.Consumer5.2 类组件代码示例
import React, { Component } from react// 创建Context对象
const MyContext React.createContext()export default class A extends Component {state {username:tom,age:18}render() {const {username,age} this.statereturn (divh3我是A组件/h3h4我的用户名是{username}/h4MyContext.Provider value{{username,age}}B//MyContext.Provider/div)}
}class B extends Component {static contextType MyContextrender() {return (divh3我是B组件/h3h4我的从A组件接收到的用户名是{this.context.username}/h4C//div)}}class C extends Component {static contextType MyContextrender() {return (divh3我是C组件/h3h4我的从A组件接收到的用户名是{this.context.username},年龄是{this.context.age}/h4/div)}}5.3 函数组件代码示例
import React, { Component } from react// 创建Context对象
const MyContext React.createContext()export default class A extends Component {state {username:tom,age:18}render() {const {username,age} this.statereturn (divh3我是A组件/h3h4我的用户名是{username}/h4MyContext.Provider value{{username,age}}B//MyContext.Provider/div)}
}class B extends Component {static contextType MyContextrender() {return (divh3我是B组件/h3h4我的从A组件接收到的用户名是{this.context.username}/h4C//div)}}function C(){return (divh3我是C组件/h3h4我的从A组件接收到的用户名是MyContext.Consumer{value {return ${value.username},年龄是${value.age}}}/MyContext.Consumer/h4/div)
}6.组件优化
Component的两个问题 只要执行setState(),即使不改变状态数据this.setState({})组件也会重新render()只当前组件重新render(),就会自动重新render子组件尽管没有传任何东西给子组件子组件也会重新render效率低 效率高的做法只有当组件的state或props数据发生改变时才重新render()原因Component中的shouldComponentUpdate()总是返回true
6.1 解决办法1
重写shouldComponentUpdate()方法比较新旧state或props数据如果有变化才返回true如果没有返回false
import React, { Component } from reactexport default class Parent extends Component {state {carName:奔驰c36}changeCar (){this.setState({carName:迈巴赫})}shouldComponentUpdate(nextProps,nextState){if(this.state.carName nextState.carName) return falseelse return true}render() {console.log(Parent)const {carName} this.statereturn (divh3我是Parent组件/h3span我的车的名字是{carName}/spanbutton onClick{this.changeCar}点击换车/buttonChild carName奥拓//div)}
}class Child extends Component {shouldComponentUpdate(nextProps,nextState){if(this.props.carName nextProps.carName) return falseelse return true}render() {console.log(Child)return (divh3我是Child组件/h3span接收到的车{this.props.carName}/span/div)}
}
6.2 解决办法2推荐
使用PureComponentPureComponent重写了shouldcomponentUpdate(),只有state或props数据有变化才返回true注意 只是进行state和props数据的浅比较如果只是数据对象内部数据变了返回false不要直接修改state数据而是要产生新数据
import React, { PureComponent } from reactexport default class Parent extends PureComponent {state {carName:奔驰c36}changeCar (){this.setState({carName:迈巴赫})}render() {console.log(Parent)const {carName} this.statereturn (divh3我是Parent组件/h3span我的车的名字是{carName}/spanbutton onClick{this.changeCar}点击换车/buttonChild carName奥拓//div)}
}class Child extends PureComponent {render() {console.log(Child)return (divh3我是Child组件/h3span接收到的车{this.props.carName}/span/div)}
}
7.render props
7.1 如何向组件内部动态传入带内容的结构标签
Vue中使用slot技术也就是通过组件标签体传入结构AB//A
React中使用children props通过组件标签体传入结构使用render props通过组件标签属性传入结构一般用render函数属性7.2 children props
ABxxxx/B
/A
问题如果B组件需要A组件内的数据做不到import React, { Component } from reactexport default class Parent extends Component {render() {return (divh3我是Parent组件/h3AB//A /div)}
}class A extends Component {state {name:tom}render() {return (divh3我是A组件/h3{this.props.children}/div)}
}class B extends Component {render() {return (divh3我是B组件/h3/div)}
}7.3 render props
A render{(data) C data{data}/C}/A
A组件{this.props.render(内部state数据)}
C组件读取A组件传入的数据显示{this.props.data}import React, { Component } from reactexport default class Parent extends Component {render() {return (divh3我是Parent组件/h3{/* // 下面的写法满足不了A传递参数给BAB//A */}A render{(name)B name{name}/}//div)}
}class A extends Component {state {name:tom}render() {return (divh3我是A组件/h3{this.props.render(this.state.name)}/div)}
}class B extends Component {render() {return (divh3我是B组件/h3/div)}
}8.错误边界
理解错误边界(Error boundary)用来捕获后代组件错误渲染出备用页面。如果前端代码报错了避免显示以下界面 特点只能捕获后代组件生命周期产生的错误不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误使用方式getDerivedStateFromError配合componentDidCatch
Parent.jsx
import React, { Component } from react
import Child from ./Childexport default class Parent extends Component {state {hasError:}// 当Parent的子组件出现报错时候会触发getDerivedStateFromError调用并携带错误信息static getDerivedStateFromError(error){console.log(error)return {hasError:error}}// 子组件渲染时出错就会调用该方法。componentDidCatch(){// 可以统计此处的错误然后发送给后台记录用于通知编码人员进行bug解决}render() {return (divh2我是Parent组件/h2{this.state.hasError ? h2当前网络不稳定稍后再试/h2 : Child/}/div)}
}
Child.jsx
子组件
import React, { Component } from reactexport default class Child extends Component {state render() {return (divh2我是Child件/h2{this.state.users.map((user){return h4 key{user.id}{user.name}---{user.age}/h4})}/div)}
}
9.组件通信方式总结
组件间关系 父子组件兄弟组件非嵌套组件祖孙组件跨级组件 通信方式 1.props children propsrender props 2.消息订阅-发布 pubs-sub、event等等 3.集中式管理 redux、dva等等 4.conText 生产者-消费者模式 比较好的搭配方式 父子组件props兄弟组件消息订阅-发布、集中式管理祖孙组件跨级组件消息订阅-发布、集中式管理、conText开发用的少封装插件用的多
十二、React Router6
1.概述
1.React Router以三个不同的包发布到 npm上它们分别为: 1.1 react-router路由的核心库提供了很多的组件、钩子。1.2 react-router-dom包含react-router所有内容并添加一些专门用于DOM的组件例如BrowserRouter\等1.3 react-router-native包括react-router所有内容并添加一些专门用于ReactNative的API,例如NativeRouter等。 2.与React Router 5.x版本相比改变了什么 2.1 内置组件的变化移除switch/新增Routes/等。2.2 语法的变化: component{About}变为element{About/}等。2.3 新增了多个hookuseParams、useNavigate、useMatch等2.4 官方推荐函数式组件
2.component
2.1 BrowerRouter
改组件用于包裹整个应用
import React from react;
import ReactDOM from react-dom;
import { BrowserRouter } from react-router-dom;ReactDOM.render(BrowserRouter{/* 整体结构通常为App组件 */}/BrowserRouter,root
);2.2 HashRouter
说明作用与BrowserRouter一样但HashRouter修改的是地址栏的hash值。备注6.x版本中HashRouter、BrowserRouter的用法与5.x相同。
2.3 Routes与Route
v6版本中移出了先前的switch引入了新的替代者RoutesRoute和Routes要配合使用且必须要用Routes包裹Route。Route相当于一个if语句如果其路径与当前URL匹配则呈现其对应的组件。Route caseSensitive属性用于指定匹配时是否区分大小写(默认为false)。当URL发生变化时Routes都会查看其所有子Route元素以找到最佳匹配并呈现组件。Route也可以嵌套使用且可配合 useRoutes()配置“路由表”但需要通过Outlet组件来渲染其子路由。
Routes/*path属性用于定义路径element属性用于定义当前路径所对应的组件*/Route path/login element{Login /}/Route/*用于定义嵌套路由home是一级路由对应的路径/home*/Route pathhome element{Home /}/*test1 和 test2 是二级路由,对应的路径是/home/test1 或 /home/test2*/Route pathtest1 element{Test/}/RouteRoute pathtest2 element{Test2/}/Route/Route//Route也可以不写element属性, 这时就是用于展示嵌套的路由 .所对应的路径是/users/xxxRoute pathusersRoute pathxxx element{Demo /} //Route
/Routes2.4 Link
作用:修改URL且不发送网络请求(路由链接)。注意:外侧需要用BrowserRouter或HashRouter包裹。
import { Link } from react-router-dom;function Test() {return (divLink to/路径按钮/Link/div);
}2.5 NavLink
作用与Link组件类似且可实现导航的“高亮”效果。
// 注意: NavLink默认类名是active下面是指定自定义的class//自定义样式
NavLinktologinclassName{({ isActive }) {console.log(home, isActive)return isActive ? base one : base}}
login/NavLink/*默认情况下当Home的子组件匹配成功Home的导航也会高亮当NavLink上添加了end属性后若Home的子组件匹配成功则Home的导航没有高亮效果。
*/
NavLink tohome end home/NavLink2.6 Navigate
作用只要Navigate组件被渲染就会修改路径切换视图。replace属性用于控制跳转模式push 或 replace默认是push。
import React,{useState} from react
import {Navigate} from react-router-domexport default function Home() {const [sum,setSum] useState(1)return (divh3我是Home的内容/h3{/* 根据sum的值决定是否切换视图 */}{sum 1 ? h4sum的值为{sum}/h4 : Navigate to/about replace{true}/}button onClick{()setSum(2)}点我将sum变为2/button/div)
}2.7 Outlet
当Route产生嵌套时渲染其对应的后续子路由。
//根据路由表生成对应的路由规则
const element useRoutes([{path:/about,element:About/},{path:/home,element:Home/,children:[{path:news,element:News/},{path:message,element:Message/,}]}
])//Home.js
import React from react
import {NavLink,Outlet} from react-router-domexport default function Home() {return (divh2Home组件内容/h2divul classNamenav nav-tabsliNavLink classNamelist-group-item tonewsNews/NavLink/liliNavLink classNamelist-group-item tomessageMessage/NavLink/li/ul{/* 指定路由组件呈现的位置 */}Outlet //div/div)
}
3.Hooks
3.1 useRoutes()
作用根据路由表动态创建Routes和Route。
//路由表配置src/routes/index.js
import About from ../pages/About
import Home from ../pages/Home
import {Navigate} from react-router-domexport default [{path:/about,element:About/},{path:/home,element:Home/},{path:/,element:Navigate to/about/}
]//App.jsx
import React from react
import {NavLink,useRoutes} from react-router-dom
import routes from ./routesexport default function App() {//根据路由表生成对应的路由规则const element useRoutes(routes)return (div......{/* 注册路由 */}{element}....../div)
}
3.2 useNavigate()
作用返回一个函数用来实现编程式导航。
import React from react
import {useNavigate} from react-router-domexport default function Demo() {const navigate useNavigate()const handle () {//第一种使用方式指定具体的路径navigate(/login, {replace: false,state: {a:1, b:2}}) //第二种使用方式传入数值进行前进或后退类似于5.x中的 history.go()方法navigate(-1)}return (divbutton onClick{handle}按钮/button/div)
}3.3 useParams()
作用回当前匹配路由的params参数类似于5.x中的match.params。
import React from react;
import { Routes, Route, useParams } from react-router-dom;
import User from ./pages/User.jsxfunction ProfilePage() {// 获取URL中携带过来的params参数let { id } useParams();
}function App() {return (RoutesRoute pathusers/:id element{User /}//Routes);
}3.4 useSearchParams()
作用用于读取和修改当前位置的 URL 中的查询字符串。返回一个包含两个值的数组内容分别为当前的seaech参数、更新search的函数。
import React from react
import {useSearchParams} from react-router-domexport default function Detail() {const [search,setSearch] useSearchParams()const id search.get(id)const title search.get(title)const content search.get(content)return (ullibutton onClick{()setSearch(id008title哈哈content嘻嘻)}点我更新一下收到的search参数/button/lili消息编号{id}/lili消息标题{title}/lili消息内容{content}/li/ul)
}
3.5 useLocation()
作用获取当前 location 信息对标5.x中的路由组件的location属性。
import React from react
import {useLocation} from react-router-domexport default function Detail() {const x useLocation()console.log(,x)// x就是location对象: /*{hash: ,key: ah9nv6sz,pathname: /login,search: ?namezsage18,state: {a: 1, b: 2}}*/return (ulli消息编号{id}/lili消息标题{title}/lili消息内容{content}/li/ul)
}
3.6 useMatch()
作用返回当前匹配信息对标5.x中的路由组件的match属性。
Route path/login/:page/:pageSize element{Login /}/
NavLink to/login/1/10登录/NavLinkexport default function Login() {const match useMatch(/login/:x/:y)console.log(match) //输出match对象//match对象内容如下/*{params: {x: 1, y: 10}pathname: /LoGin/1/10 pathnameBase: /LoGin/1/10pattern: {path: /login/:x/:y, caseSensitive: false, end: false}}*/return (divh1Login/h1/div)
}3.7 useInRouterContext()
作用如果组件在 Router 的上下文中呈现则 useInRouterContext 钩子返回 true否则返回 false。场景有判断使用人是不是在路由组件使用了我封装的组件
3.8 useNavigationType()
作用返回当前的导航类型用户是如何来到当前页面的。返回值POP、PUSH、REPLACE。备注POP是指在浏览器中直接打开了这个路由组件刷新页面。
3.9 useOutlet()
作用用来呈现当前组件中渲染的嵌套路由。
const result useOutlet()
console.log(result)
// 如果嵌套路由没有挂载,则result为null
// 如果嵌套路由已经挂载,则展示嵌套的路由对象3.10 useResolvedPath()
作用给定一个 URL值解析其中的path、search、hash值。
import {useResolvedPath} from react-router-dom
const pathObj useResolvedPath(/user?id001nametom#qwe)
// 解析出来是{pathname:/user, search: ?id001nametom, hash: #qwe}4.综合案例
4.1 useRouters路由表 一般来说路由都会放到src/routes里面
4.2 嵌套路由
路由表 一级路由 二级路由
4.3 路由传参
Link参数定义 接收参数
params传参 search传参 state参数
4.4 编程式路由导航 文章转载自: http://www.morning.bhwz.cn.gov.cn.bhwz.cn http://www.morning.krkwp.cn.gov.cn.krkwp.cn http://www.morning.ftmzy.cn.gov.cn.ftmzy.cn http://www.morning.xjkfb.cn.gov.cn.xjkfb.cn http://www.morning.hxbjt.cn.gov.cn.hxbjt.cn http://www.morning.dxpzt.cn.gov.cn.dxpzt.cn http://www.morning.sdkaiyu.com.gov.cn.sdkaiyu.com http://www.morning.kdjtt.cn.gov.cn.kdjtt.cn http://www.morning.lbgfz.cn.gov.cn.lbgfz.cn http://www.morning.yknsr.cn.gov.cn.yknsr.cn http://www.morning.ncqzb.cn.gov.cn.ncqzb.cn http://www.morning.mingjiangds.com.gov.cn.mingjiangds.com http://www.morning.mbpzw.cn.gov.cn.mbpzw.cn http://www.morning.qpmmg.cn.gov.cn.qpmmg.cn http://www.morning.zlkps.cn.gov.cn.zlkps.cn http://www.morning.zhiheliuxue.com.gov.cn.zhiheliuxue.com http://www.morning.hxmqb.cn.gov.cn.hxmqb.cn http://www.morning.rdzlh.cn.gov.cn.rdzlh.cn http://www.morning.rnqrl.cn.gov.cn.rnqrl.cn http://www.morning.jqzns.cn.gov.cn.jqzns.cn http://www.morning.qkbwd.cn.gov.cn.qkbwd.cn http://www.morning.wtcd.cn.gov.cn.wtcd.cn http://www.morning.tzzxs.cn.gov.cn.tzzxs.cn http://www.morning.mlwpr.cn.gov.cn.mlwpr.cn http://www.morning.hmfxl.cn.gov.cn.hmfxl.cn http://www.morning.jyyw.cn.gov.cn.jyyw.cn http://www.morning.rgxn.cn.gov.cn.rgxn.cn http://www.morning.jydhl.cn.gov.cn.jydhl.cn http://www.morning.bflws.cn.gov.cn.bflws.cn http://www.morning.wqbhx.cn.gov.cn.wqbhx.cn http://www.morning.bpmdh.cn.gov.cn.bpmdh.cn http://www.morning.xqffq.cn.gov.cn.xqffq.cn http://www.morning.gbrdx.cn.gov.cn.gbrdx.cn http://www.morning.mmkrd.cn.gov.cn.mmkrd.cn http://www.morning.bpmmq.cn.gov.cn.bpmmq.cn http://www.morning.yqrfn.cn.gov.cn.yqrfn.cn http://www.morning.xflzm.cn.gov.cn.xflzm.cn http://www.morning.lwdzt.cn.gov.cn.lwdzt.cn http://www.morning.3dcb8231.cn.gov.cn.3dcb8231.cn http://www.morning.nnjq.cn.gov.cn.nnjq.cn http://www.morning.brwp.cn.gov.cn.brwp.cn http://www.morning.bzjpn.cn.gov.cn.bzjpn.cn http://www.morning.fcxt.cn.gov.cn.fcxt.cn http://www.morning.taojava.cn.gov.cn.taojava.cn http://www.morning.sqxr.cn.gov.cn.sqxr.cn http://www.morning.gtbjf.cn.gov.cn.gtbjf.cn http://www.morning.xbrxk.cn.gov.cn.xbrxk.cn http://www.morning.jrlgz.cn.gov.cn.jrlgz.cn http://www.morning.sryyt.cn.gov.cn.sryyt.cn http://www.morning.amonr.com.gov.cn.amonr.com http://www.morning.zlbjx.cn.gov.cn.zlbjx.cn http://www.morning.tmnyj.cn.gov.cn.tmnyj.cn http://www.morning.ygwyt.cn.gov.cn.ygwyt.cn http://www.morning.tkqzr.cn.gov.cn.tkqzr.cn http://www.morning.kndst.cn.gov.cn.kndst.cn http://www.morning.gcbhh.cn.gov.cn.gcbhh.cn http://www.morning.jtmql.cn.gov.cn.jtmql.cn http://www.morning.txfxy.cn.gov.cn.txfxy.cn http://www.morning.dppfh.cn.gov.cn.dppfh.cn http://www.morning.fnmgr.cn.gov.cn.fnmgr.cn http://www.morning.qbwmz.cn.gov.cn.qbwmz.cn http://www.morning.kzdwt.cn.gov.cn.kzdwt.cn http://www.morning.rhwty.cn.gov.cn.rhwty.cn http://www.morning.hwnnh.cn.gov.cn.hwnnh.cn http://www.morning.nzkc.cn.gov.cn.nzkc.cn http://www.morning.tjqcfw.cn.gov.cn.tjqcfw.cn http://www.morning.trmpj.cn.gov.cn.trmpj.cn http://www.morning.rjznm.cn.gov.cn.rjznm.cn http://www.morning.kqzt.cn.gov.cn.kqzt.cn http://www.morning.xblrq.cn.gov.cn.xblrq.cn http://www.morning.ltbwq.cn.gov.cn.ltbwq.cn http://www.morning.zxcny.cn.gov.cn.zxcny.cn http://www.morning.ndhxn.cn.gov.cn.ndhxn.cn http://www.morning.xlmpj.cn.gov.cn.xlmpj.cn http://www.morning.wjhnx.cn.gov.cn.wjhnx.cn http://www.morning.yhxhq.cn.gov.cn.yhxhq.cn http://www.morning.sqlh.cn.gov.cn.sqlh.cn http://www.morning.mytmx.cn.gov.cn.mytmx.cn http://www.morning.fosfox.com.gov.cn.fosfox.com http://www.morning.wzwyz.cn.gov.cn.wzwyz.cn