Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。在React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。此外,React 还需要借助 Key 值来判断元素与本地状态的关联关系,因此我们绝不可忽视转换函数中 Key 的重要性。
在代码中调用 setState 函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面。在React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。
• 初始化阶段:
o getDefaultProps:获取实例的默认属性
o getInitialState:获取每个实例的初始化状态
o componentWillMount:组件即将被装载、渲染到页面上
o render:组件在这里生成虚拟的 DOM 节点
o componentDidMount:组件真正在被装载之后
• 运行中状态:
o componentWillReceiveProps:组件将要接收到属性的时候调用
o shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回 false,接收数据后不更新,阻止 render 调用,后面的函数不会被继续执行了)
o componentWillUpdate:组件即将更新不能修改属性和状态
o render:组件重新描绘
o componentDidUpdate:组件已经更新
• 销毁阶段:
o componentWillUnmount:组件即将销毁shouldComponentUpdate 是做什么的,(react 性能优化是哪个周期函数?)
shouldComponentUpdate 这个方法用来判断是否需要调用 render 方法重新描绘dom。因为 dom 的描绘非常消耗性能,如果我们能在 shouldComponentUpdate 方法中能够写出更优化的 dom diff 算法,可以极大的提高性能。
虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能。用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM树,插到文档当中当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异把 2 所记录的差异应用到步骤 1 所构建的真正的DOM 树上,视图就更新了。
• 把树形结构按照层级分解,只比较同级元素。
• 给列表结构的每个单元添加唯一的 key 属性,方便比较。
• React 只会匹配相同 class 的 component(这里面的 class 指的是组件的名字)
• 合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty.到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制.
• 选择性子树渲染。开发人员可以重写 shouldComponentUpdate 提高 diff 的性能。
在 HTML 中,类似 , 和 这样的表单元素会维护自身的状态,并基于用户的输入来更新。当用户提交表单时,前面提到的元素的值将随表单一起被发送。但在 React 中会有些不同,包含表单元素的组件将会在 state 中追踪输入的值,并且每次调用回调函数时,如 onChange 会更新 state,重新渲染组件。一个输入表单元素,它的值通过 React 的这种方式来控制,这样的元素就被称为"受控元素"。
高阶组件是一个以组件为参数并返回一个新组件的函数。HOC 运行你重用代码、逻辑和引导抽象。最常见的可能是 Redux 的 connect 函数。除了简单分享工具库和简单的组合,HOC 最好的方式是共享 React 组件之间的行为。如果你发现你在不同的地方写了大量代码来做同一件事时,就应该考虑将代码重构为可重用的HOC。
因为 this.props 和 this.state 的更新可能是异步的,不能依赖它们的值去计算下一个state。
你可以使用属性初始值设定项(property initializers)来正确绑定回调,create-reactapp 也是默认支持的。在回调中你可以使用箭头函数,但问题是每次组件渲染时都会创建一个新的回调。
在 super() 被调用之前,子类是不能使用 this 的,在 ES2015 中,子类必须在constructor 中调用 super()。传递 props 给 super() 的原因则是便于(在子类中)能在constructor 访问 this.props。
在 React 组件中,应该在 componentDidMount 中发起网络请求。这个方法会在组件第一次“挂载”(被添加到DOM)时执行,在组件的生命周期中仅会执行一次。更重要的是,你不能保证在组件挂载之前 Ajax 请求已经成,如果是这样,也就意味着你将尝试在一个未挂载的组件上调用 setState,这将不起作用。componentDidMount 中发起网络请求将保证这有一个组件可以更新了。
为了解决跨浏览器兼容性问题,您的 React 中的事件处理程序将传递SyntheticEvent 的实例,它是 React 的浏览器本机事件的跨浏览器包装器。这些 SyntheticEvent 与您习惯的原生事件具有相同的接口,除了它们在所有浏览器中都兼容。有趣的是,React 实际上并没有将事件附加到子节点本身。React 将使用单个事件监听器监听顶层的所有事件。这对于性能是有好处的,这也意味着在更新 DOM 时,React 不需要担心跟踪事件监听器。
• redux 是一个应用数据流框架,主要是解决了组件间状态共享的问题,原理是集中式管理,主要有三个核心方法,action,store,reducer,工作流程是view 调用 store 的 dispatch 接收 action 传入 store,reducer 进行 state 操作,view 通过 store 提供的 getState 获取最新的数据,flux 也是用来进行数据操作的,有四个组成部分 action,dispatch,view,store,工作流程是view 发出一个 action,派发器接收 action,让 store 进行数据更新,更新完成以后 store 发出 change,view 接受 change 更新视图。Redux 和 Flux 很像。主要区别在于 Flux 有多个可以改变应用状态的 store,在 Flux 中dispatcher 被用来传递数据到注册的回调事件,但是在 redux 中只能定义一个可更新状态的 store,redux 把 store 和 Dispatcher 合并,结构更加简单清晰
• 新增 state,对状态的管理更加明确,通过 redux,流程更加规范了,减少手动编码量,提高了编码效率,同时缺点时当数据更新时有时候组件不需要,但是也要重新绘制,有些影响效率。一般情况下,我们在构建多交互,多数据流的复杂项目应用时才会使用它们
• 一个组件所需要的数据,必须由父组件传过来,而不能像 flux 中直接从store 取。
• 当一个组件相关数据更新时,即使父组件不需要用到这个组件,父组件还是会重新 render,可能会有效率影响,或者需要写复杂的shouldComponentUpdate 进行判断。
注意:class定义的组件需要有一个render方法,返回一个HTML标签,表示当前组件展示的HTML内部部分,此方法在数据或者睡属性改变之后都会重复执行,react是单向数据流
class定义组件时,state可以为组件定义局部转台
state的数据改变以后需要使用setState方法实现
当数据改变以后render方法会重新执行
区别:
function定义组件没有this指向问题
class定义的组件有自己的据状态和生命周期函数
function定义的组件又叫做无状态组件,但是16.8之后可以使用hooks模拟组件的局部状态和生命周期
目前官方建议使用function定义组件,写法简单、容易理解
我目前一直在使用function定义组件
父组件向子组件传参,用props属性
子组件向父组件传参,使用方法调用
componentWillMount
componentDidMount
render
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
路由基础使用
//HashRouter哈希路由
//BrowserRouter浏览器历史记录路由
//as 作用重新命名
import { HashRouter as Router, Route, Link, Switch } from 'react-router-dom'
// Router表示最顶层的路由组件对象,一个项目只能有一个
// Route表示每一个地址对应显示的组件内容,可以有多个
// 如果要让地址绝对匹配,需要添加属性exact
// Link生成a标签
// Switch表示路由内容Route只匹配一个就不往后走了
//eg:
【首页】
【电影】//可以拼接
} />
/Route>
//模糊匹配,现有路由匹配不上时选择这一个
路由获取参数的方法:
new URLSearchParams 是原生js内置的方法,用来获取url中传递的参数
const zhuan =new URLSearchParams(props.location.search)
console.log(zhuan.get('id'))
使用路由后,会自动在props中自动的加上以下属性
//location,表示当前路由地址
//history, 表示历史记录信息,里面有跳转方法 push pop go
//metch 可以使用的一些匹配方法
import { useLocation } from "react-router-dom";
// useLocation 可以获取当前props传递的location数据
import { useParams } from "react-router-dom";
// useParams 可以获取当前props传递的id数据
路由传参的方法:
路由传参分为三种
query,参数拼接在url上面,传入的是拼接的字符串,刷新页面时参数还在
params,参数不在url上,传入的是对象,传参刷新后会消失
state,参数不在url上,传入的是对象,传参刷新后会消失
补充:解决params和state刷新后消失,在path后拼接占位符<Route path='/detail/:id' component={Detail} />,在pathname后面拼接id,search:"?id="+item.albumId,(传值个数与占位符数目相同,占位符是传值的属性名)
1.通过?直接传参
{/* {v.name} */}
2.通过query传参
qs 可以用来解析和格式化url中传递的参数
import {stringify,parse}from 'qs'
// const p = { a: 123, b: true, c: "我是小明" };
// console.log(stringify(p));
// console.log(parse(p));
const temQuery={id:v._id ,b:789 }
<Link to={"/detail?" +stringify(temQuery) }>{v.name}</Link>
3.通过params传参
但是一刷新就没有了
4.通过state传参
但是一刷新就没了
<Link
to={{
pathname:"/detail",//表示路由对应的组件的path
params/state:{id:v._id},
}}
>{v.name}
</Link>
5.解决params传参刷新数据丢失问题
通过占位符可以解决
<Route path='/detail/:id' component={Detail}></Route>
<Link
to={{
pathname:"/detail/"+v._id,
search:"?id=" + v._id,
params:{id:v._id},
}}
>{v.name}
</Link>
6.withRouter 是一个高阶组件,可以把路由属性传递到组件的属性中进行使用
高阶组件就是把一个组件当参数传递到一个function进行封装,为其添加新的属性
高阶组件最常见的两个例子:withRouter和connect
高阶组件就是高阶函数,常见的高阶函数有map和foreach
import {withRouter}from 'react-router-dom'
export default withRouter(Gao)
通俗的说:将一个没有被Route路由包裹的组件包裹进Route里面,然后react-router的三个对象history, location, match就会被放进这个组件的props属性中. 就能实现编程式导航跳转
路由中定义组件的方法
1.
<Route path='/detail/:id' component={Detail}></Route>
2.
<Route path='/detail'>
<Detail>
</Route>
3.
<Route path='/detail' render={()=><List/>}></Route>
前两中想要获取属性必须使用withRouter方法,第三种不用,第三种在用私有路由的时候作用大。
私有路由
先自己定义一个组件,判断localstorage中是否有token,有token的话,就展现当前页面,没有token,就跳转到登录页
import React from 'react'
import {Redirect,Route} from 'react-router-dom'
//Redirect重定向
function ProtectedRoute(props) {
console.log(props)
console.group("私有路由")
console.log(props)
console.groupEnd()
//写法一
/* return (
localStorage.getItem('token')? }/>:
) */
//写法二(官方写法)
const {children,...rest} = props//解构赋值,...rest为剩下的项,当成属性直接放在 中
return localStorage.getItem('token')?children: } />
}
export default ProtectedRoute
使用私有路由
import ProtectedRoute from './components/ProtectedRoute'
【我的】
{/* 写法一 */}
{/* */}
{/* 写法二(官方写法) */}
路由跳转
props.history.push("/user/info");
//history可以实现编程式跳转
//push直接跳转,相当于向路由栈中插入一个新纪录
//go(-1)回退
//goback回退
是一个全局状态管理插件,作用是对数据进行管理,热镀锌是针对数据进行操作的,可以结合任何一个js库进行使用。react是一个针对视图层的library,所以一般在做开发的时候都会把react和redux进行结合使用。
redux是单向数据流的,数据是单向流动的,state用来存储数据,所有的数据改变都在reducer中进行,redux中还有一个action,它用来组织数据
单向数据流的概念:数据是单向流动的,view通过dispatch派发一个action改变数据,数据改变之后页面重新渲染。
三部分:state,reducer,action
每一个reducer接收两个参数
参数一 表示初始状态
参数二 表示action 每一个action必须有一个type属性
要通过dispatch派发一个action改变数据,action必须是一个对象。通过switch接收,参数为action.type
store.dispatch({
type:"ADD_COUNT",
payload:{
step:2,
},
});
switch(action.type){
case "ADD_COUNT":
return { ...state, age:state.age + action.payload.step};
//type是action固定的属性
//payload指的是载荷,是一个编程习惯,一般传的数据一般都放在payload里面
default:
return state;
}
}
Provider可以把redux数据和react项目做关联
connect 作用是把redux中的数据和react组件的props属性做关联
组件的props属性中会多出来一个dispatch方法和redux中的数据内容
export default connect((abc)=>abc)(App);
redux 跨组件传参,可以用全局状态共享
通过dispatch派发一个action改变数据,组件中props来接收,connect是关联的桥梁,把属性映射到组件里面
当做异步action时,必须使用中间件实现。
combineReducers可以把多个reducer合并成一个
applyMiddleware允许使用一个中间件,compose是引入一个第三方中间件
引入
import {createStore,combineReducers,compose,applyMiddleware} from 'redux';
compose(
applyMiddleware(...[thunk,x,xxx],//加入我们要使用的中间件,多个的话中间用逗号隔开
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
),
),
thunk的作用,当使用了redux-thunk之后,action可以是一个function
import thunk from 'redux-thunk'//作用是可以在redux中使用异步action
可以为我们自动配置路由
嵌套路由— _layout.tsx tsx是一个ts 文件 ts语法跟js语法一样,如果报错的话,就写成props:any
母版页,是一个嵌套路由
改变路由,histroy改成hash的话,就加一个文件即可
umi的打包:在package.json中scripts下,手动添加 “build”:‘umi build’,cmd端输入npm run dev即可
dva是用来做数据处理的,所有的数据处理都在dva中进行管理的