一.为什么要学习react
声明式设计
高效灵活
组件化
单向数据流
虚拟dom
二.包含内容
React基础
React Hooks
React路由
Redux
组件库
Immutable
Mobx
React+TS
dva+umin
三.React介绍
四.create-react-app
(1)删除node_modules,打包剩余的其他文件。拿到打包后的文件后,使用命令npm i 下载项目所需的依赖
(2)npm i -g nrm(npm仓库管理)
nrm -V(查看版本)
nrm ls(查看目录)
nrm use npm(切换到npm)
nrm use taobao(切换到taobao镜像)
五.编写第一个react应用程序
jsx===js+xml
六.JSX语法与组件
JSX语法
类组件
注意:(1)组件首字母必须大写
(2)只能含有一个根组件
(3)return要想回车必须加上小括号
函数式组件
16.8之前:无状态组件
16.8之后,react hooks
组件的嵌套
组件的样式
(1)react的js中不加上引号会被认为是变量,加上则为字符串
(2)属性得用驼峰写法
(3)样式属性得用className,label标签中的得用htmlFor
事件处理
this指向:谁调用就执行谁
Ref的应用
七. 组件的数挂载
状态
第一种方式:
第二种方式:
列表渲染
todolist案例
let a=[1,2,3]
let b=[...a],深度复制,生成b这一个新数组
let c=a.slice(),不传参数的话等于把a复制给b,contact()也一样
不要直接修改状态,造成不可修改的错误
条件渲染
根据条件判断是渲染还是删除
dangerouslySetInnerHTML
卖座影院案例
(1)发起请求
(2)模糊搜索:使用filter进行过滤,不改变原数组,可以使用备份,使得数组不会被覆盖。
setState状态
(1)在定时器中,setState是同步更新的,调用一次执行一次;
(2)在同步环境中,setState是异步更新的。首先会设置合并开关,把所有相同的过程合并,所以此时的状态没有改变,合并完之后执行,状态才会发生改变。
(3)setState的第二个参数可以得知setState状态的改变
外面的盒子得固定高度(className="kerwinwrapper"),可以获取视口高度。
props属性
(1)props简单用法
(2)用大括号表示js地盘:传变量,表达式,js数字,布尔值。
(3)props验证属性
默认属性·:
(4)类属性和对象属性
对象属性需要new之后才能访问得到,直接定义在类上面的可以直接访问(类属性)
类属性可以加上static之后移到类中。
(5)函数式组件props属性:通过形参实现
属性合并,传过来的属性在左边
函数式组件props验证,直接挂载到函数身上
总结
八.表单中的受控组件和非受控组件
react中的onInput和onChange事件是一样的,原生js则不然。
非受控组件
(1)React要编写一个非受控组件,可以 使用 ref 来从 DOM 节点中获取表单数据,就是非受控组件。
(2)因为非受控组件将真实数据储存在 DOM 节点中,所以在使用非受控组件时,有时候反而更容易同时集成 React 和非 React 代码。如果你不介意代码美观性,并且希望快速编写代码,使用非受控组件往往可以减少你的代码量。否则,你应该使用受控组件。
(3)默认值
在 React 渲染生命周期时,表单元素上的 value 将会覆盖 DOM 节点中的值,在非受控组件中,你经
常希望 React 能赋予组件一个初始值,但是不去控制后续的更新。 在这种情况下, 你可以指定一个
defaultValue 属性,而不是 value 。
同样, <input type="checkbox"> 和 <input type="radio"> 支持 defaultChecked , <select>
和 <textarea> 支持 defaultValue 。
受控组件
九.组件的通信方式
状态提升
(1)test.json(模拟后端接口数据)
(2)app父组件
(3)电影项目
(4)电影细节
发布订阅模式实现
(1)原生基本模式
(2)
context状态树传参
父组件为生产者,子组件为消费者
注意:GlobalContext.Consumer内必须是回调函数,通过context方法改变根组件状态
context优缺点:
优点:跨组件访问数据
缺点:react组件树种某个上级组件shouldComponetUpdate 返回false,当context更新时,不
会引起下级组件更新
插槽
子组件
父组件
优点:(1)为了复用
(2)一定程度上减少父子通信,在谁的的组件中,就可以使用它的状态
十.生命周期
初始化阶段
(1)componentWillMount:第一次上树前的最后一次修改状态机会,初始化状态的作用,不能获取真实dom
(2)render只能访问this.props和this.state,不允许修改状态(会出现死循环)和DOM输出,会执行多次
(3)componentDidMount:成功render并渲染完成真实DOM之后触发,可以修改DOM,只会执行一次
a.数据请求axios
b.订阅函数的调用
c.setInterval
d.基于创建完的dom进行初始化
运行阶段
(1)componetWillupdate:不能修改属性和状态
(2)render:只能访问this.props和this.state,不允许修改状态(会出现死循环)和DOM输出
(3)componentDidUpdate:可以修改DOM,获取DOM节点,会运行多次
(4)shouldComponentUpdate:return false会阻止render更新,使得组件得以优化
render只能更新两次,旧的状态(上一个)与新的状态(下一个)
(5)componentWillReceiveProps:在父组件中更新才有效,只要父组件更新就会执行(多次)。子组件中,this.props.text是旧的状态。
销毁阶段
componentWillUnmount:在删除组件之前进行清理操作,比如计时器和事件监听器
老生命周期的问题
(1) componentWillMount ,在ssr中 这个方法将会被多次调用, 所以会重复触发多遍,同时在这里如果绑定事件,将无法解绑,导致内存泄漏 , 变得不够安全高效逐步废弃。
(2) componentWillReceiveProps 外部组件多次频繁更新传入多次不同的 props,会导致不必要的异步请求
(3) componetWillupdate, 更新前记录 DOM 状态, 可能会做一些处理,与componentDidUpdate相隔时间如果过长, 会导致状态不太行
新的生命周期
(1)getDerivedStateFromProps:第一次的初始化组件以及后续的更新过程中(包括自身状态更新以及父传子) ,返回一个对象作为新的state,返回null则说明不需要在这里更新state
具体格式:
初始化:修改状态,可能会被写固定了
substring:(1)substring是用来截取字符串的,根据参数的个数不同,方法含义也不同;
(2)substring(0,2)这个只含开头不含结尾,因此截取是截取两个字符,从第一个到第二个字符,不包含第三个。
(3)substring(2)这个表示截掉前两个,得到后边的新字符串。
在子组件中
(2)getSnapshotBeforeUpdate :取代了 componetWillUpdate ,触发时间为update发生的时候,在render之后dom渲染之前返回一个值,作为componentDidUpdate的第三个参数
(render之前,didUpdate之前)
value=100
react中性能优化的方案
(1)shouldComponentUpdate
控制组件自身或者子组件是否需要更新,尤其在子组件非常多的情况下, 需要进行优化。
(2)PureComponent
PureComponent会帮你 比较新props 跟 旧的props, 新的state和老的state(值相等,或者
对象含有相同的属性、且属性值相等 ),决定shouldcomponentUpdate 返回true 或者
false, 从而决定要不要呼叫 render function。
注意:
如果你的 state 或 props 『永远都会变』,那 PureComponent 并不会比较快,因为
shallowEqual 也需要花时间。
十一.React Hooks
使用hooks理由
(1) 高阶组件为了复用,导致代码层级复杂
(2) 生命周期的复杂
(3)写成functional组件,无状态组件 ,因为需要状态,又改成了class,成本高
useState(保存组件状态)
useEffect,类似于componentDidMount
(1)不要对 Dependencies 撒谎, 如果你明明使用了某个变量,却没有申明在依赖中,你等于向 React 撒了谎,后果就是,当依赖的变量改变时,useEffect 也不会再次执行, eslint会报警告
(2)组件销毁
(3)useEffect和useLayoutEffect(存放到内存中)有什么区别?
简单来说就是调用时机不同, useLayoutEffect 和原来 componentDidMount & componentDidUpdate 一致,在react完成DOM更新后马上同步调用的代码,会阻塞页面渲染。而 useEffect 是会在整个页面渲染完才会调用的代码。
官方建议优先使用 useEffect
在实际使用时如果想避免页面抖动(在 useEffect 里修改DOM很有可能出现)的话,可以把需要操作DOM的代码放在 useLayoutEffect 里。在这里做点dom操作,这些dom修改会和 react 做出的更改一起被一次性渲染到屏幕上,只有一次回流、重绘的代价。
(4)useCallback(记忆函数)
防止因为组件重新渲染,导致方法被重新创建 ,起到缓存作用; 只有第二个参数 变化了,才重新声明一次(组件自身的状态发生变化方法才会重新渲染,要是与组件自身状态无关则不会重新创建,只会拿取先前缓冲的方法)
a.只有name改变后,这个函数才会重新声明一次
b.如果传入空数组,那么就是第一次创建后就被缓存,如果name后期改变了,拿到的还是老的name。
c.如果传入第二个参数,每次都会重新声明一次,拿到的就是最新的name
(5)useMemo 记忆组件
a.useCallback 的功能完全可以由 useMemo 所取代,如果你想通过使用 useMemo 返回一个记忆函数也是完全可以的。
useCallback(fn, inputs) is equivalent to useMemo(() => fn, inputs)
b.唯一的区别是:useCallback 不会执行第一个参数函数,而是将它(把创建的方法)返回给你,而 useMemo 会执行第一个函数并且将函数执行结果返回给你。所以在前面的例子中,可以返回handleClick 来达到存储函数的目的。
所以 useCallback 常用记忆事件函数,生成记忆后的事件函数并传递给子组件使用。而 useMemo 更适合经过函数计算得到一个确定的值,比如记忆组件。
(6)useRef(保存引用值)
使用useRef可以存放先前变量的值
(7)useContext(减少组件层级,跨级通信)
var GlobalContext= React.createContext()
(8)useReducer(状态放在外面)
(9)useReducer和useContext联合使用
(10)自定义hooks
当我们想在两个函数之间共享逻辑时,我们会把它提取到第三个函数中。
必须以“use”开头吗?必须如此。这个约定非常重要。不遵循的话,由于无法判断某个函数是否包含对其内部 Hook的调用,React 将无法自动检查你的 Hook 是否违反了 Hook 的规则
十二.React路由
1. 什么是路由?
路由是根据不同的 url 地址展示不同的内容或页面。
一个针对React而设计的路由解决方案、可以友好的帮你解决React components 到URl之间的同步映射关系。
2. 路由安装
https://reacttraining.com/react-router/web/guides/quick-start
3. 路由使用(记得加上<Switch><Switch/>)
(1) 路由方法导入(可以定义一个路由文件,里面存放路由组件)
(2)定义路由以及重定向(记得加上<Switch><Switch/>)
模糊匹配
精确匹配(exact)
a. exact 精确匹配 (Redirect 即使使用了exact, 外面还要嵌套Switch 来用)
b. Warning: Hash history cannot PUSH the same path; a new entry will not be added to the historystack,这个警告只有在hash 模式会出现。
(3)嵌套路由(不要精确匹配)记得加上<Switch><Switch/>
(4) 路由跳转方式
原生方式
a. 声明式导航
<NavLink to="/films" activeClassName="active">films</NavLink>
b. 编程式导航
<Route/>路由中component组件会变成其的孩子,拥有history
类组件:this.props.history.push(`/center`)
函数组件:props.history.push(`/center`)
useHistory
(5)动态路由
a.动态路由传参:冒号是占位符不能省略
b.query传参
c.state传参
(6)路由拦截
(7)路由模式
HashRouter=>#,不能向后端发送数据,只存储在本地
BrowserRouter:可以向后端发送数据
(8) withRouter的应用与原理
不是所有的组件都是Router的孩子,如下
Router路由组件才有history,match,location
withRouter为非路由组件提供路由组件的属性
(9)跨域请求
a.cors,需要后端配合
b.jsonp,需要后端配合
c.反向代理:隐藏服务端(推荐)
https://facebook.github.io/create-react-app/docs/proxying-api-requests-in-development
正向代理:隐藏客户端
在src下创建setupProxy.js,写入如下:
(10)css样式
https://facebook.github.io/create-react-app/docs/adding-a-css-modules-stylesheet
创建css文件名后面必须加上module
class,id选择器(常用)
全局独一不会被影响
十三.Flux与Redux
Flux 是一种架构思想,专门解决软件的结构问题。它跟MVC架构是同一类东西,但是更加简单和
清晰。Flux存在多种实现(至少15种)
https://github.com/voronianski/flux-comparison
Facebook Flux是用来构建客户端Web应用的应用架构。它利用单向数据流的方式来组合React
中的视图组件。它更像一个模式而不是一个正式的框架,开发者不需要太多的新代码就可以快速的上手Flux。
Redux最主要是用作应用状态的管理。简言之,Redux用一个单独的常量状态树(state对象)保存这一整个应用的状态,这个对象不能直接被改变。当一些数据变化了,一个新的对象就会被创建(使用actions和reducers),这样就可以进行数据追踪,实现时光旅行。
与react绑定后使用redux实现案例
redux介绍及设计和使用的三大原则
redux工作流
redux原理解析
Redux-reducer合并
如果不同的action所处理的属性之间没有联系,我们可以把 Reducer 函数拆分。不同的函数
负责处理不同属性,最终把它们合并成一个大的 Reducer 即可。
redux中间件(异步的时候才需要)
在redux里,action仅仅是携带了数据的普通js对象。action creator返回的值是这个action类型的
对象。然后通过store.dispatch()进行分发。同步的情况下一切都很完美,但是reducer无法处理异
步的情况。
那么我们就需要在action和reducer中间架起一座桥梁来处理异步。这就是middleware。
a.中间件的由来与原理、机制
这段代码的意思是,中间件这个桥梁接受到的参数action,如果不是function则和过去一样直接执
行next方法(下一步处理),相当于中间件没有做任何事。如果action是function,则先执行action,
action的处理结束之后,再在action的内部调用dispatch。
b.中间件-redux-thunk
redux-thunk (store.dispatch参数可以是一个function)
c.中间件redux-promise (store.dispatch参数可以是一个promise对象)
Redux DevTools Extension
https://github.com/zalmoxisus/redux-devtools-extension
配置store.js
十四. react-redux
介绍
https://github.com/reactjs/react-redux
不需要自己dispatch,connect负责包装,订阅,取消订阅
将store存储的内容共享给App组件
将store中的内容通过参数传递给App组件
容器组件与UI组件
(1)UI组件
•只负责 UI 的呈现,不带有任何业务逻辑
•没有状态(即不使用this.state这个变量)
•所有数据都由参数(this.props)提供
•不使用任何 Redux 的 API
(2) 容器组件
•负责管理数据和业务逻辑,不负责 UI 的呈现
•带有内部状态
•使用 Redux 的 API
Provider与connect
(1)React-Redux 提供Provider组件,可以让容器组件拿到state。
(2)React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这
两种组件连起来.
HOC与context通信在react-redux底层中的应用
(1) connect 是HOC, 高阶组件
(2) Provider组件,可以让容器组件拿到state , 使用了context
高阶组件构建与应用
HOC不仅仅是一个方法,确切说应该是一个组件工厂,获取低阶组件,生成高阶组件。
(1)代码复用,代码模块化
(2)增删改props
(3) 渲染劫持
封装成高阶组件
Redux 持久化
白名单:持久化;黑名单:不会持久化。
十五.UI组件库
Ant Design 是一个致力于提升『用户』和『设计者』使用体验的设计语言 ;旨在统一中台项目的前端 UI 设计,屏蔽不必要的设计差异和实现成本,解放设计和前端的研发资源; 包含很多设计原则和配套的组件库。
1. ant-design (PC端)
https://ant-design.gitee.io/index-cn (镜像库,快)
2. antd-mobile (移动端)
十六. Immutable
1.Immutable.js介绍
https://github.com/immutable-js/immutable-js
每次修改一个 Immutable 对象时都会创建一个新的不可变的对象,在新对象上操作并不会影响到原对象的数据。
2. 深拷贝与浅拷贝的关系
(1) var arr = { } ; arr2 = arr ;
(2) Object.assign() 只是一级属性复制,比浅拷贝多拷贝了一层而已。
(3) const obj1 = JSON.parse(JSON.stringify(obj)); 数组,对象都好用的方法(缺点: 不能有undefined)
3. Immutable优化性能的方式
Immutable 实现的原理是 Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable 使用了 Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。
https://upload-images.jianshu.io/upload_images/2165169-cebb05bca02f1772
4.Immutable中常用类型(Map,List)
(1)Map
第一种方法:
第二种方法:
注意:
(2)List
普通数组的方法都可以使用
(3)fromJS
如果取一级属性 直接通过get方法,如果取多级属性 getIn(["a","b","c"]])
updateIn=>更新
5. Immutable+Redux的开发方式
6. 缺点
容易跟原生混淆
文档与调试不方便
十七.Mobx
Mobx介绍
(1) Mobx是一个功能强大,上手非常容易的状态管理工具。
(2) Mobx背后的哲学很简单: 任何源自应用状态的东西都应该自动地获得。
(3) Mobx利用getter和setter来收集组件的数据依赖关系,从而在数据发生变化的时候精确知道哪些组件需要重绘,在界面的规模变大的时候,往往会有很多细粒度更新。(vue类似)
Mobx与redux的区别
Mobx写法上更偏向于OOP
对一份数据直接进行修改操作,不需要始终返回一个新的数据
并非单一store,可以多store。
Redux默认以JavaScript原生对象形式存储数据,而Mobx使用可观察对象
优点:
a. 学习成本小
b. 面向对象编程, 而且对 TS 友好
缺点:
a. 过于自由:Mobx提供的约定及模版代码很少,代码编写很自由,如果不做一些约定,比较容易导致团队代码风格不统一,
b. 相关的中间件很少,逻辑层业务整合是问题。
Mobx的使用
(1)observable 和 autorun
(2) action,runInAction和严格模式
mobx-react的使用
(1)react 组件里使用 @observer
observer 函数/装饰器可以用来将 React 组件转变成响应式组件。
(2) 可观察的局部组件状态
@observable 装饰器在React组件上引入可观察属性。而不需要通过 React 的冗长和强制性的 setState 机制来管理。
(3)Provider 组件
它使用了 React 的上下文(context)机制,可以用来向下传递 stores。 要连接到这些 stores,需要传递一个 stores名称的列表给 inject,这使得 stores 可以作为组件的 props 使用。this.props
5. 支持装饰器
npm i @babel/core @babel/plugin-proposal-decorators @babel/preset-env
创建 .babelrc
创建config-overrides.js
安装依赖
npm i customize-cra react-app-rewired
修改package.json
十八.TS
typescript
https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/
(1) TypeScript 的定位是静态类型语言,在写代码阶段就能检查错误,而非运行阶段
(2) 类型系统是最好的文档,增加了代码的可读性和可维护性。
(3) 有一定的学习成本,需要理解接口(Interfaces)、泛型(Generics)、类(Classes)等
(4) ts最后被编译成js
安装
变量声明
基本数据类型
数组
对象
函数
类
类+接口
函数组件
十九.styled-components
二十.单元测试
二十一.redux-saga
生成器
可执行生成器
异步流程
二十二.react-router 6
概述
(1)官方文档:Home v6.4.1 | React Router
(2)
react-router: 路由的核心库,提供了很多的组件、钩子。
react-router-dom: 包含react-router所有内容,并添加一些专门用于 DOM 的组件,例如 <BrowserRouter>等 。
react-router-native: 包括react-router所有内容,并添加一些专门用于ReactNative的API,例如:<NativeRouter>等。
(3)
a.与React Router 5.x 版本相比,改变了什么?
b.内置组件的变化:移除<Switch/> ,新增 <Routes/>等。
c.语法的变化:component={About} 变为 element={<About/>}等。
d新增多个hook:useParams、useNavigate、useMatch等。
e.官方明确推荐函数式组件了!!!
(4)安装
npm install react-router-dom@6
BrowserRouter和HashRouter
(1)在 React Router 中,最外层的 API 通常就是用 BrowserRouter。BrowserRouter 的内部实现是用了 history 这个库和 React Context 来实现的,所以当你的用户前进后退时,history 这个库会记住用户的历史记录,这样需要跳转时可以直接操作。
(2)BrowserRouter 使用时,通常用来包住其它需要路由的组件,所以通常会需要在你的应用的最外层用它。
(3)HashRouter:作用与<BrowserRouter>一样,但<HashRouter>修改的是地址栏的hash值,
6.x版本中<HashRouter>、<BrowserRouter> 的用法与 5.x 相同。
Routes 与 Route
a.v6版本中移出了先前的<Switch>,引入了新的替代者:<Routes>。
b.<Routes> 和 <Route>要配合使用,且必须要用<Routes>包裹<Route>。
c.<Route> 相当于一个 if 语句,如果其路径与当前 URL 匹配,则呈现其对应的组件。
d.<Route caseSensitive> 属性用于指定:匹配时是否区分大小写(默认为 false)。
e.当URL发生变化时,<Routes> 都会查看其所有子 <Route> 元素以找到最佳匹配并呈现组件 。
f.<Route> 也可以嵌套使用,且可配合useRoutes()配置 “路由表” ,但需要通过 <Outlet> 组件来渲染其子路由。
(1)Route
Route 用来定义一个访问路径与 React 组件之间的关系。比如说,如果你希望用户访问 https://your_site.com/about 的时候加载 <About /> 这个 React 页面,那么你就需要用 Route:
<Route path="/about" element={<About/>}/>
(2)Routes
Routes 是用来包住路由访问路径(Route)的。它决定用户在浏览器中输入的路径到对应加载什么 React 组件,因此绝大多数情况下,Routes 的唯一作用是用来包住一系列的 Route。
(3)例子
设置默认页路径(如 404 页)
(1)在最后加入 path 为 * 的一个路径,意为匹配所有路径。
Link
(1)作用: 修改URL,且不发送网络请求(路由链接)。
(2)注意: 外侧需要用<BrowserRouter>或<HashRouter>包裹。
NavLink
(1)作用: 与<Link>组件类似,且可实现导航的“高亮”效果。
Navigate
(1)作用:只要<Navigate>组件被渲染,就会修改路径,切换视图。
(2)replace属性用于控制跳转模式(push 或 replace,默认是push)。
(3)相当于5版本的Redirect。
(4)跳转模式
使用useRoutes注册路由
(1)src文件夹下新建子文件夹:routes,routes下新建文件:index.js 路由表独立成js文件:src/routes/index.js
routes/index.js
App.js
嵌套路由的实现
(1)routes/index.js,用 children 来嵌套路由。
(2)Home/index.js,<Outlet>
作用:当<Route>产生嵌套时,渲染其对应的后续子路由。
路由链接中的 to 属性值,可以是
to="/home/news",即全路径(推荐这样写,不然直接看不知道是不是子路由)
to="./news",即相对路径
to="news"
获取params参数有两种方式:
(1)使用 useParams
const {id,title,content} = useParams();
(2)使用 useMatch
const {params:{id,title,content}}=useMatch("/home/message/detail/:id/:title/:content");
传递 search 参数
(1).useSearchParams()
作用:用于读取和修改当前位置的 URL 中的查询字符串。 返回一个包含两个值的数组,内容分别为:当前的seaech参数、更新search的函数。
(2).useLocation()
作用:获取当前 location 信息,对标5.x中的路由组件的location属性。
(3).使用useLocation
记得下载安装qs:npm install --save qs。
nodejs官方说明querystring这个模块即将被废弃,推荐我们使用qs模块
传递 state 参数
刷新页面后对路由state参数的影响 :
在以前版本中,BrowserRouter没有任何影响,因为state保存在history对象中;HashRouter刷新后会导致路由state参数的丢失 但在V6版本中,HashRouter在页面刷新后不会导致路由state参数的丢失。
13.编程式路由导航
(1) 编程式导航下,路由传递params参数
(2)编程式导航下,路由传递search参数
(3)编程式导航下,路由传递state参数
(4)withRouter的替换者
我们可以利用 react-router-dom 对象下的 withRouter 函数来对我们导出的 Header 组件进行包装,这样我们就能获得一个拥有 history 对象的一般组件。
withRouter可以加工一般组件(即非路由组件),让一般组件具备路由组件所持有的API。但v6版本中已废除,可以直接用useNavigate实现。
参考链接:
https://github.com/dselegent/Learning-Notes/tree/master/react