React创建组件的方式
1. 无状态函数式组件(推荐)
特点:
你无法使用State,展示性组件,接收Props,渲染DOM,而不关注其他逻辑
代码:
export default function Button(props){
let {name} = props
const sayHi = () => {
alert(`Hi ${name}`);
}
return (
<div>
<h1>Hello, {name}</h1>
<button onClick ={sayHi}>Say Hi</button>
</div>
)
}
2. ES5原生方式(React.createClass)
解释:
React.createClass 是react刚开始推荐的创建组件的方式,这是ES5的原生的JavaScript来实现的React组件
代码:
const Button = React.createClass({
getInitialState
getDefaultProps
render() {return ()}
});
3).React.Component(ES6形式)
解释:
React.Component 是以ES6的形式来创建react的组件的,是React推荐和常用的创建有状态组件的方式
4).React.PureComponent (高性能组件)
特点:
1.继承PureComponent时,不能再重写shouldComponentUpdate,否则会引发警告
2.继承PureComponent时,进行的是浅比较,也就是说,如果是引用类型的数据,只会比较是不是同一个地址
3.浅比较会忽略属性或状态突变的情况,其实也就是,数据引用指针没变而数据被改变的时候,
也不新渲染组件。但其实很大程度上,我们是希望重新渲染的。
所以,这就需要开发者自己保证避免数据突变。如果想使按钮被点击后可以正确渲染也很简单,
将 `const words = this.state.words;` 改为`const words = this.state.words.slice(0)`;
(在原来state的基础上复制出来一个新数组,所以引用地址当然变啦)
jsx是什么
JSX是看起来很像XML的JavasSript语法扩展
React遇到<就会当成HTML语法解析,遇到{}就会当做JS解析
为什么要使用jsx
1. 编译成js的时候优化过了,运行速度会更快
2. 对语法有严格要求,编译时不正确会报错
3. 语法比模板字符串更简洁
使用jsx的要求
只能有一个根节点
props类型检测
import propType from 'prop-types';
class Login extends React.Component{
render(){return (<h1>{this.props.title}</h1>)}
}
Login.defaultProps={
title:'默认标题'
}
Login.propTypes={
title:propType.string
}
react事件的this绑定
1. bind方法
<div onClick = {this.methodfn.bind(this)}></div>
2. 构造函数内绑定
constructor(props) {
super(props);
this.state = {};
this.methodfn = this.methodfn.bind(this)
}
<div onClick = {this.methodfn}></div>
3. 箭头函数绑定
<div onClick = {() => {this.methodfn}}></div>
4. ::绑定(不能传参)
<div onClick = {::this.methodfn}></div>
ref的使用
1. String ref
<input ref="myInput" />
2. callback ref
<input ref="{(ele)=>{ this.myRef=ele }}" />
3. createRef()
constructor(props){
super(props)
this.myRef=React.createRef()
}
<input ref={this.myRef} />
组件通讯
父传子:
props传递
子传父:(传递回调)
class Parent extends React.Component{
say(str){
alert(str)
}
render(){
return (<Child say={this.say}></Child>)
}
}
class Child extends React.Component{
render(){
return (<button onClick={this.props.say('hello')}></button>)
}
}
创建context createContext({})
祖先生产 <Context.Provider value={}>
后代使用 <Context.Consumer></Context.Consumer>
1. redux/mobx/flux
2. context
3. 使用事件发布订阅
React路由
1、BrowserRouter:
浏览器的路由方式,也就是在开发中最常使用的路由方式
2、HashRouter:
在路径前加入#号成为一个哈希值,Hash模式的好处是,再也不会因为我们刷新而找不到我们的对应路径
3、MemoryRouter:
不存储history,所有路由过程保存在内存里,不能进行前进后退,因为地址栏没有发生任何变化
4、NativeRouter:
经常配合ReactNative使用,多用于移动端
5、StaticRouter:
设置静态路由,需要和后台服务器配合设置,比如设置服务端渲染时使用
1.Link/NavLink标签的to属性
2.this.props.history.push()
1、query方式
传参:this.props.history.push({pathname:'/list',query:{color:'red'}})
接收:this.props.location.query
优势 : 刷新地址栏,参数依然存在
缺点:只能传字符串,并且,如果传的值太多的话,url会变得长而丑陋。
2、state方式
传参:this.props.history.push({pathname:'/list',state:{color:'red'}})
接收:this.props.location.state
优缺点与query方式相同
3、params方式(类似vue动态路由)
定义:<Route path="list/:id"></Route>
传参:this.props.history.push({pathname:'/list/001'})
接收:this.props.match.params
优势:传参优雅,传递参数可传对象;
缺点:刷新地址栏,参数丢失
4、search方式
传参:<Link to="/list?id=001'"></Link>
接收:this.props.location.search
优缺点与params方式相同
路由标签和属性
Route标签 定义路由
switch标签 只匹配第一个
redirect标签 重定向
exact属性 完全匹配
生命周期执行过程
componentWillMount() 组件初始化时只调用,以后组件更新不调用,整个生命周期只调用一次,此时可以修改state。
render() react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行。此时就不能更改state了。
componentDidMount() 组件渲染之后调用,只调用一次。
componentWillReceiveProps(nextProps,nextState):当组件接收到新的 props 时,会触发该函数。在改函数中,通常可以调用 this.setState 方法来完成对 state 的修改
shouldComponentUpdate:该方法用来拦截新的 props 或 state,然后根据事先设定好的判断逻辑,做出最后要不要更新组件的决定。
componentWillUpdate:当上面的方法拦截返回 true 的时候,就可以在该方法中做一些更新之前的操作
render:根据一系列的 diff 算法,生成需要更新的虚拟 DOM 数据。(注意:在 render 中最好只做数据和模板的组合,不应进行 state 等逻辑的修改,这样组件结构更加清晰
componentDidUpdate:该方法在组件的更新已经同步到 DOM 中去后触发,我们常在该方法中做一 DOM 操作。
componentWillUnmount:我们通常会做一些取消事件绑定、移除虚拟 DOM 中对应的组件数据结构、销毁一些无效的定时器等工作
React16.3改动生命周期
react v16.3终于出来了,最大的变动莫过于生命周期去掉了以下三个
componentWillMount
componentWillReceiveProps
componentWillUpdate
同时为了弥补失去上面三个周期的不足又加了两个
getDerivedStateFromProps
getSnapshotBeforeUpdate
getDerivedStateFromProps(props,state)
触发时间:在组件构建之后(虚拟dom之后,实际dom挂载之前) ,以及每次获取新的props之后。
每次接收新的props之后都会返回一个对象作为新的state,返回null则说明不需要更新state.
配合componentDidUpdate,可以覆盖componentWillReceiveProps的所有用法
getSnapshotBeforeUpdate
触发时间: update发生的时候,在render之后,在组件dom渲染之前。·
返回一个值,作为componentDidUpdate的第三个参数。
配合componentDidUpdate, 可以覆盖componentWillUpdate的所有用法。
React VirtualDOM
Virtual DOM是一个映射真实DOM的JavaScript对象,
如果需要改变任何元素的状态, 那么是先在Virtual DOM上进行改变
当有变化产生时,一个新的Virtual DOM对象会被创建并计算新旧Virtual DOM之间的差别,
之后这些差别会应用在真实的DOM上
React Diff算法
diff算法作为Virtual DOM的加速器,其算法的改进优化是React整个界面渲染的基础和性能的保障
同时也是React源码中最神秘的,最不可思议的部分 react diff算法的3个策略 Web UI 中DOM节点跨层级的移动操作特别少,
可以忽略不计 拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构。
对于同一层级的一组子节点,它们可以通过唯一id进行区分。
对于以上三个策略,react分别对tree diff,component diff,element diff进行算法优化
react的合成事件
优势:
1. 防止绑定事件过多绑定或事件滥用造成内存占用和性能问题;
2. 屏蔽浏览器底层差异
原理:
当用户在为onClick添加函数时,React并没有将Click时间绑定在DOM上面。
而是在document处监听所有支持的事件,当事件发生并冒泡至document处时,
React将事件内容封装交给中间层SyntheticEvent(负责所有事件合成)
所以当事件触发的时候,对使用统一的分发函数dispatchEvent将指定函数执行
React对于css的模块化解决方案 (CSS Modules)
1. 所有样式都是 local 的,解决了命名冲突和全局污染问题
2. class 名生成规则配置灵活,可以此来压缩 class 名
3. 只需引用组件的 JS 就能搞定组件所有的 JS 和 CSS
4. 依然是 CSS,几乎 0 学习成本 使用了 CSS Modules 后,就相当于给每个 class 名外加加了一个 :local,
5. 以此来实现样式的局部化,如果你想切换到全局模式,使用对应的 :global。
React.createClass与React.Component区别
React.createClass
创建的组件,其每一个成员函数的this都有React自动绑定,任何时候使用,直接使用this.method即可,函数中的this会被正确设置。
React.Component
创建的组件,其成员函数不会自动绑定this,需要开发者手动绑定,否则this不能获取当前组件实例对象。
- 组件属性类型propTypes及其默认props属性defaultProps配置不同
React.createClass
在创建组件时,有关组件props的属性类型及组件默认的属性会作为组件实例的属性来配置,其中defaultProps是使用getDefaultProps的方法来获取默认组件属性的
React.Component
在创建组件时配置这两个对应信息时,他们是作为组件类的属性,不是组件实例的属性,也就是所谓的类的静态属性来配置的
React.createClass
创建的组件,其状态state是通过getInitialState方法来配置组件相关的状态;
React.Component
创建的组件,其状态state是在constructor中像初始化组件属性一样声明的
什么是受控组件
比如input写成:value和onInput的形式,value根据onInput事件的返回值再setState流向组件的value;
值是可控制的,或其他表单类组件
setState有几个参数
参数1:对象,要改变的值
参数2:执行完修改的回调函数
装饰器
简单来说,装饰器就是用一个代码包装另一个代码的简单方式,就是简单的将一个函数包装成为另一个函数
JavaScript中装饰器使用特殊的语法,使用@作为标识符,且放置在被装饰代码之前
React行内样式
<div style={{color:'#fff',fontSize:'16px'}}></div>
解释withRouter
当一个非路由组件也想访问到当前路由的match,location,history对象,那么withRouter将是一个非常好的选择
import { withRouter } from 'react-router'
const FirstTest = withRouter(MyComponent)
redux的使用
1.定义action-type.js文件 (用于声明操作的TYPE类型)
2.定义actions.js文件 (用于声明发起dispatch(action)的操作函数)
3.定义reducers.js文件 (用于声明reducer函数,此函数接收来自dispatch的action操作)
4.定义store.js文件 (用于创建和抛出仓库,根index.js的Provider挂载store对象实现自顶向下的数据流)
5.页面引入react-redux库的connct包装页面使用
connect(mapStateToProps,mapDispatchToProps)(Index)
applyMiddleware
它是 Redux 的原生方法,作用是将所有中间件组成一个数组,依次执行。
redux-logger & redux-thunk & redux-promise & redu-saga
redux-logger
createLogger(applyMiddleware(【某其他插件】,logger))
日志中间件 (必须放最后,仅且在开发模式下使用)
redux-thunk
createLogger(applyMiddleware(thunk))
异步解决方案 , 改造store.dispatch 使得其可以接受函数作为参数
redux-promise
createLogger(applyMiddleware(promiseMiddleware))
异步解决方案 , 改造store.dispatch 使得其可以接受Promise作为参数
redu-saga
1.统一action的形式
2.集中处理异步等存在副作用的逻辑
3.通过转化effects函数,可以方便进行单元测试
4.完善和严谨的流程控制,可以较为清晰的控制复杂的逻辑
中间件的意义
对某些功能进行转化改造封装,满足某些需要的场景
DvaJs的使用
定义:
1. 轻量级的应用框架,
2. 整合简化了redux和redux-saga 的数据流方案,
3. 还额外内置了 react-router 和 fetch
特性:
1. 易学易用,仅有 6 个 api,对 redux 用户尤其友好,配合 umi 使用后更是降低为 0 API
2. elm 概念,通过 reducers, effects 和 subscriptions 组织 model
3. 插件机制,比如dva-loading可以自动处理loading状态,不用一遍遍地写showLoading和hideLoading
4. 支持 HMR,基于 babel-plugin-dva-hmr 实现 components、routes 和 models 的 HMR
DvaJs中models的组成
state: 仓库存储的基础状态
Subscription: 订阅
Effect: 副作用方法
Reducer: 纯函数 可预测返回值
React Hooks
概念由来:
1. 因为纯函数式组件没有状态 + 没有this + 没有生命周期
2. 类组件功能齐全却很重,需要继承React.Component
概念定义:
1. React Hooks就是加强版的函数组件,我们可以完全不使用 class,就能写出一个全功能的组件
2. React Hooks的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来
常用钩子:
1. useState()
import {useState} from 'react';
const [ count, setCount ] = useState(0)
2. userContext()
import { useContext } from 'react';
const AppContext = React.createContext({})
const { name } = useContext(AppContext)
return (
<AppContext.Provider value={{name: 'hook测试'}}>
<A/>
<B/>
</AppContext.Provider>
)
3. userReducer()
import {useReducer} from 'react'
const AddCount = () => {
const reducer = (state, action) => {
if(action.type === 'add'){
return {...state,count: state.count +1,}
}else {return state}
}
const addcount = () => {
dispatch({type: 'add'})
}
const [state, dispatch] = useReducer(reducer, {count: 0})
return (<><p>{state.count}</p><button onClick={addcount}>count++</button></>)
}
export default AddCount
4. useEffect()
import React, { useState, useEffect } from 'react'
const AsyncPage = ({name}) => {
const [loading, setLoading] = useState(true)
const [person, setPerson] = useState({})
useEffect(() => {
setLoading(true)
setTimeout(()=> {
setLoading(false)
setPerson({name})
},2000)
},[name])
return (<>{loading?<p>Loading...</p>:<p>{person.name}</p>}</>)
}
const PersonPage = () =>{
const [state, setState] = useState('')
const changeName = (name) => {setState(name)}
return (<><AsyncPage name={state}/>
<button onClick={() => {changeName('名字1')}}>名字1</button>
<button onClick={() => {changeName('名字2')}}>名字2</button>
</>)
}
export default PersonPage
概念特点:
1. 完全可选的。 你无需重写任何已有代码就可以在一些组件中尝试 Hook。
但是如果你不想,你不必现在就去学习或使用 Hook。
2. 100% 向后兼容的。 Hook 不包含任何破坏性改动。
3. 现在可用。 Hook 已发布于 v16.8.0。
4. 没有计划从 React 中移除 class。
5. Hook不会影响你对React概念的理解。恰恰相反,Hook为已知的React概念
提供了更直接的 API:props, state,context,refs 以及生命周期。
高阶函数
形式:传入函数,返回一个新的函数
功能:增强函数功能
高阶组件
使用:传入组件,返回一个新的组件
目的:增强组件功能
高阶组件的种类
侵入式组件:依赖组件传入的状态和参数
非侵入式组件:不依赖状态和参数
React常用高阶组件
1. connect
2. withRouter
3. From.create()
路由按需加载
1. react-loadable
2. Loadable:loader,loading
3. dva中的dynamic