jsx 语法
使用一个父节点包裹
jsx 中不能一次性返回零散的多个节点,如果有多个请包涵在一个节点中。
注释
jsx 中用{/* */}
的注释形式
return (
// jsx 外面的注释
<div>
{/* jsx 里面的注释 */}
<p>hello world</p>
</div>
)
样式
对应 html 的两种形式,jsx 的样式可以这样写:
css样式:<p className="class1">hello world</p>
,注意这里是className
,而 html 中是class
内联样式:<p style={{display: 'block', fontSize: '20px'}}>hello world</p>
,注意这里的{{...}}
,还有fontSize
的驼峰式写法
ref 相当于vue中的ref
let myRef = React.createRef()
<input ref={myRef} />
this.myRef.current.value 获取值
事件
拿 click 事件为例,要在标签上绑定 click 事件,可以这样写
class Hello extends React.Component {
render() {
return (
<p onClick={this.clickHandler.bind(this)}>hello world</p>
)
}
clickHandler(e) {
// e 即js中的事件对象,例如 e.preventDefault()
// 函数执行时 this 即组件本身,因为上面的 .bind(this)
console.log(Date.now())
}
}
<button onClick={handler} />
handler = ()=>{
这里也能使用this,和bind(this)是相同的意思
}
传递参数 事件的两种写法
第一种bind
onClick={this.handle.bind(this,index)}
第二种箭头函数
onClick={()=>{this.handle(index)}}
注意,onClick
是驼峰式写法,以及.bind(this)
的作用
循环
在 jsx 中使用循环,一般会用到Array.prototype.map
(来自ES5标准)
class Hello extends React.Component {
render() {
const arr = ['a', 'b', 'c']
return (
<div>
{arr.map((item, index) => {
return <p key={index}>this is {item}</p>
})}
</div>
)
}
}
注意,arr.map
是包裹在{}
中的,key={index}
有助于React的渲染优化,jsx中的{}
可放一个可执行的 js 程序或者变量
操作state中的数组时,切记使用arr.slice() 或者 let newList = [...this.state.list]的方法对数组进行复制一份,避免直接操作原数组,对数组添加和删除也是操作复制出来的对象,最后this.setState({list:newList})
判断
jsx中使用判断一般会用到三元表达式(表达式也是放在{}
中的),例如:
return (
<div>
<p>段落1</p>
{
true
? <p>true</p>
: <p>false</p>
</div>
}
</div>
)
也可以这样使用:
<p style={{display: true ? 'block' : 'none'}}>hello world</p>
条件渲染
{this.state.list.length && <p>111</p>}
className={this.state.list.length>0?'':''}
富文本展示 dangerouslySetInnerhtml
<span dangerouslySetInnerHtml={{__html:item.txt}}</span>
state 状态
只能在当前组件内使用,外部无法获取,state 相当于vue中的data
第一种直接定义
state = {}
第二种通过constructor定义
constructor {
super()
this.state = {}
}
class Hello extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
// 显示当前时间
now: Date.now()
}
}
render() {
return (
<div>
<p onClick={this.clickHandler.bind(this)}>hello world {this.state.now}</p>
</div>
)
}
clickHandler() {
// 设置 state 的值的时候,一定要用 this.setState ,不能直接赋值修改
this.setState({
now: Date.now()
})
}
}
setState扩展
setState处在同步的逻辑中,异步更新状态,更新真实Dom
setState处在异步中 同步更新状态 同步更新Dom
setState接受第二个参数,第二个参数是回调函数,状态和dom更新完就会触发
如果不用生命周期,需要结合回调函数使用
this.setState({list:list},()=>{
new BetterScroll('.div')
})
如果在异步接口中使用,因为是同步的
this.setState({list:res.data.list})
new BetterScroll('.div')
props
如果传递非字符串类型,可以使用花括号,可以传递对象数字等非字符串的类型
render(
<Nav title="xxx" show={true}/>
)
父组件传递参数的时候 可以通过es6展开运算符把整个对象传递下去,如下图
render(
<Nav {...item} />
)
在子组件中获取props,最好通过解构的方式,是一个技巧
let {title,show} = this.props
props属性验证
组件名.propTypes = {title:strig,show:boolean}
推荐下面的写法
static propTypes = {title:strig,show:boolean}
props默认值
组件名.defaultProps = {title:'xxx',show:false}
推荐下面的写法
static defaultProps = {title:'xxx',show:false}
新版的react推荐使用函数式组件,代替类组件
这里没有this,可以通过props获取属性
export default function Nav (props) {}
state与props的区别
state用于组件保存控制修改自己的状态
props是让使用该组件的父组件传入属性配置组件
无状态的组件相当于木偶组件可以通过通信交流
受控组件
组件的数据渲染是否被调用者传递的props完全控制来区别受控组件与非受控组件
表单受控与非受控
在html中表单元素通常自己维护自己的状态,根据用户输入进行更新,而在react中通过组件的state控制取值的表单元素叫做受控组件
<input value={this.state.value} onChange={(evt)=>{
this.setState({value:evt.current.value})
}}
父子组件通信
父组件通过props 子组件通过回调函数
父组件
<Inp val={this.state.value} onChanges={(vl)=>{console.log(vl)}}
子组件
<input value={this.props.val} onChange={(evt)=>{
this.props.onchanges(evt.value)
}}
通过ref通信
const inp = React.createRef()
<Inp ref={this.inp} />
非父组件通信
一、状态提升,借用父组件的状态给兄弟组件传参,类似与父子通信
二、发布订阅,原生js原理后面的Reduce也是基于它实现的
发布就是遍历list将回调函数执行,一定要先订阅再去发布,否则什么也拿不到
let bus = {
list:[]
订阅
subscrbe (callback) {this.list.push(callback)}
发布
publish () {
this.list.forEach(callback=> callback&&callback())
}
}
三、context状态树传参
const GlobalContext = React.createContext() 创建对象
在父组件用 <GlobalContext.provider value={{
info: this.state.info,
changeInfo: (val) => {
this.setState({info:val})
}
}} > 把html包起来 他是供应商
子组件 是消费者 <GlobalContext.consumer>
{
(value) => {
return <div onClick={()=>{
value.changeInfo('xx')
}}>xxxx</div>
}
}
</GlobalContext.consumer>
插槽
对与组件的封装一定要留好插槽,可以使组件更加灵活 复用性更好
父组件
render(
<div>
<child>
<div>111</div>
<div>222</div>
</child>
</div>
)
子组件. 通过数组的形式和父组件的模版相对应,下标可以任意修改
<div>
{this.props.children[2]}
{this.props.children[1]}
</div>
生命周期
分为 初始化 运行中 销毁阶段,函数式组件没有生命周期
初始化
componentWillMount:render之前最后一次修改状态的机会
–
render:只能访问props state 表示正在渲染
–
componentDidMount:挂载完成 可以修改Dom 放请求
运行中
componentWillUpdate:不能修改属性状态
–
render 同上
–
componentDidUpdate (preProps, preState) {} 旧属性 旧状态
–
shouldComponentUpdate:组件是否应该更新,用于优化性能 return false 不能新 true 为更新
shouldComponentUpdate(nextProps,nextState){
旧状态保存在 this.state中 新状态在 nextState
如果多个参数要对比
通过JSON.stringfly(this.state)与 nextState字符串直观比较
}
使用场景
如果一个页面要渲染100个盒子,每次修改其中一个盒子的样式,会发现react会进行所有的渲染对比,这时候就需要SCU只更新当前盒子
shouldComponentUpdate(nextProps,nextState){
if(this.props.current == this.props.index || nextProps.current == nextProps.index){
return true
}
return false
}
componentWillReceviceProps:父组件修改属性触发 最先获取父组件传来的属性,可以利用属性进行ajax,把属性转化为自己的状态或者逻辑处理,在这里获取 this.props.xxx还是旧值
销毁阶段
componentWillUnmount:释放定时器、监听函数
老生命周期的问题
- componentWillMount 在ssr中会多次被调用,在这里绑定事件会无法解绑回内存泄漏,不安全,逐步废弃
- componentWillReceiveProps 对外部频繁更新,传多个不同的props会导致不必要的异步请求
- componentWillUpdate 会导致状态不准,因为它在render之前会被render干扰
新生命周期
getDerivedStateFromProps
第一次初始化及后续更新,返回一个对象作为新的state它是一个类方法
这个return 会和当前组件的state合并
static getDerivedStateFromProps(nextProps,nextState){
return {name:nextState.name.substring(0,1)+'OA'}
}
它需要结合componentDidUpdate使用 可以实现ajax请求,主要用于父传子
componentDidUpdate(prevProps,prevState){
if (this.state.name === prevState.name){return}
else 触发逻辑请求
}
getSnapShotBeforeUpdate
==它取代了componentWillUpdate,它在render后执行,dom渲染之前返回一个值,作为componentDidUpdate的第三个参数
getSnapShotBeforeUpdate(){ return 100}
componDidUpdate(prevProps,prevState,value){ value 的值就是100}
pureComponent
与SUC有相同的效果,如果state或者props永远都变,就不适合用它
import React ,{pureComponent} from 'react'
export default class App extends pureComponent
react hooks 钩子
解决生命周期复杂,无状态组件后期又要改为有状态组件的大量修改
useState 保存组件状态
import React, {useState} from 'react'
const [name,setName] = useState('xx');
通过setName改变name值
useEffect 处理副作用
第一个参数是回调函数,第二个参数是数组,如果是空数组里面的回调函数只执行一次
类似于vue3中的watchEffect自动追踪属性
useEffect(()=>{},[])
使用时先对追踪的属性解构
let {x1,x2} = props
useEffect(() =>{},[x1,x2])
模拟生命周期从创建到销毁
useEffect(()=>{
事件监听,定时器
return()=>{
负责销毁事件监听,定时器
}
},[])
useEffect与useLayoutEffect的区别
- useLayoutEffect 调用机制不同,他和componentDidMount、componentDidUpdata一致,完成dom更新后马上同步调用
- 会阻塞页面渲染,而useEffect会在整个页面渲染完成才调用
- 如果需要操作dom代码就用useLayoutEffect,官方建议优先使用useEffect
useCallback 记忆函数
防止组件更新渲染,导致方法重新创建起到缓冲的作用,只有第二个参数变化了才会重新申明一次
const [text,setText] = useState('');
const handle = useCallback((ev)=>{setText(ev.value)},[])
useMemo 记忆组件
useMemo和useCallback功能相同,区别:useCallback不会执行第一个函数,而是返回给你,而useMemo会立即执行并返回结果,更适合经过函数计算得到一个确定的值,和vue中的计算属性很像
let {list,text} = this.props
需要依赖的属性需要放在数组里
const getList = useMemo(()=>{},[list,text])
useRef
useRef相当于React.createRef()
useContext
能够简化供应商与消费者中消费者的代码
const GlobalContext = React.createContext() 创建对象
在父组件用 <GlobalContext.provider value={{
info: this.state.info,
changeInfo: (val) => {
this.setState({info:val})
}
}} > 把子组件包起来 他是供应商 </GlobalContext>
没有使用useContext
子组件 是消费者 <GlobalContext.consumer>
{
(value) => {
return <div onClick={()=>{
value.changeInfo('xx')
}}>xxxx</div>
}
}
</GlobalContext.consumer>
使用useContext
不用在额外包裹标签,从回调函数中获取value
const value = useContext(GlobalContext);
useReducer
==通常他和useContext一起使用,解决父组件下多个子组件传参的问题,在父组件通过供应商把state和dispatch做为服务对外提供出去,通过useContent获取state和dispatch
import React, {useReducer} from 'react'
const reducer = (prevState,action){
let newValue = {...prevState}
switch(action){
case 'min':
return action.polyload
}
}
const intialState = {
count: 0
}
export default function App () {
const [state, dispatch] = useReducer(reducer,intialState);
return (
<div><button onClick={()=>{ dispatch({type:'min',polyload:'2'})}}></button>
<p>{state.count}</p>
</div>
)
}
自定义hooks
在打函数中提取小函数,把共享的逻辑提取出来,以use开头,相当于vue3的组合式函数
路由
import {HashRouter as Router, Route, switch} from 'react-router-dom'
单独提取一个文件 定义下面的内容
<HashRouter>
防止页面刷新路由, exact精准定位
<switch>
<Route path="/home" component={Home} />
<Redirect from="/" to="/home" exact/>
<Route component={NotFound} />
</swith>
</HashRouter>
嵌套路由
在home组件中定义路由,就是当前组件的子路由
render(
<div>
<switch>
<Route path="" component={} />
<Redirect from="/" to="/home" exact/>
</switch>
</div>
)
路由跳转
- 声明式导航:<navLink to=“” activeClassName=“” //>
- 编程式导航:this.props.history.push(‘/’)
动态路由
const history = useHistory()
<Route path="/detail/:id" component={} />
通过props.match.params.id获取参数
route的源码就是一个组件,把外部的component通过它的props获得,并作为它的子组件,所以route与component是父子关系
路由传参
- 需要设置动态路由 配合 history.push(`/detail/${id}') 通过props.match.params.id获取参数
- query传参 history.push({pathname:‘’,query: {id:id}}) ,使用props.location.query.id获取
- state传参 history.push({pathname:‘’,state: {id:id}}) 使用props.location.state.id获取
2、3刷新参数会丢失,推荐使用1的方法
路由拦截
import {HashRouter as Router, Route, switch} from 'react-router-dom'
<HashRouter>
防止页面刷新路由, exact精准定位
<switch>
<Route path="/home" component={Home} />
<Redirect from="/" to="/home" exact/>
<Route path="/index" component={NotFound} render:{()=>{
return isAuth ? <Center/> : <Redirect to="/login" /
}} />
</swith>
</HashRout
<Route path=“home” <Route path=“path/a” 如果第一个路由不加exact 就永远跳不到第二路由
路由模式
HashRouter,BrowserRouter,但是BrowserRouter朝后端发送请求,会被认为后端路径,会出现404的情况
解决center组件无法拿到location
通过render的props
<Route path="/index" component={NotFound} render:{(props)=>{
return isAuth ? <Center {...props}/> : <Redirect to="/login" /
}} />
通过withRouter
const centers = withRouter(center)
export default centers
反向代理
import http-proxy-middleware
module.exports = function(app){
app.use(
'/api',
createProxyMiddleware({
target: '',
changeOrigin: true
})
)
}
cssmodule
react会自动重命名,防止冲突,多用ID及class 标签选择器无效,如果不想重命名可以使用 :global(.class)
import style from './css/film.module.css'
<p className={style.active} />
flux
单向数据流的方式组合视图组件,他是一个模型,view发布用户的action dispatch收到action让state相应更新,state更新后发出一个change事件,view收到change事件更新界面
Redux
redux是flux的一种实现方案
定义计算规则,即 reducer
function counter(prevState = {
state: 0 这里可以定义默认值
}, action) {
let newState = {...prevState}
switch (action.type) {
case 'INCREMENT':
return newState.state = action.payload
case 'DECREMENT':
return newState.state - 1
default:
return newState.state
}
}
根据计算规则生成 store
let store = createStore(counter)
// 触发数据变化
store.dispatch({type: 'INCREMENT',payload: '23'})
store.dispatch({type: 'DECREMENT'})
获取state
store.getState().xxx
combineReducer
拆分出来的多个reducer可以进行合并
const reducer = combineReducers({cityReducer,tabReducer})
异步支持 redux-thunk(创K) 返回方法
import reduxThunk from 'redux-thunk'
const store = createStore(reducer,applyMiddleWare(reduxThunk))
在redux文件下创建action文件夹
function Action () {
return (dispatch) =>{
axios({}).then((res) =>{
dispatch({type: '', payload: ''})
})
}
}
export default Action
useEffect(()=>{
store.dispatch(Action());
通过订阅
let unSubscribe = store.subscribe(()=>{store.getState().xxx})
return()=>{
unSubscribe() 取消订阅
}
})
异步支持 redux-promise 返回promise
store同时支持thunk和promise
import reduxThunk from 'redux-thunk'
import reduxPromise from 'redux-promise'
既支持返回一个函数,也支持返回一个promise对象
const store = createStore(reducer,applyMiddleWare(reduxThunk,reduxPromise))
在redux文件下创建action文件夹
async function Action () {
let list = await axios({}).then((res) =>{
dispatch({type: '', payload: ''})
})
}
return list
}
export default Action
react-redux
提供一个高阶组件connect生成订阅和取消订阅
indexjs
挂到app上以后子组件就不用引入store了
import {provider} from 'react-deux'
import store from 'xx/store'
render(
<provider store={store}><App/></provider>
)
如何使用呢?
const mapStateToProps = (state) =>{
return {
show: state.show
}
}
const mapDispatchToProps = {
下面都是action
hide,
show
}
export default connect (mapStateToProps,mapDispatchToProps)(组件名)
高阶组件 高阶函数 原理
function Kc (callback){
var value = callback();
return (myComponent) => {
return (props) => {
return (<div style="x">
<{... value} {..props} {...obj} myComponent/></div>)
}
}
}
redux 持久化 redux-persist
原理本地存储
import {persiststore, persistReducer} from 'redux-persist'
import storage from 'xx'
const PersistConfig = {key: 'xx', storage}
const PersistReducer = persistReducer(PersistConfig,reducer)
const store = ({PersistReducer, xxxx})
let persistor = persiststore(store)
export {store, persistor}
再修改indexjs把组件加一层标签<PersistGate>
Immutable
每次修改一个immutable都会创建一个新的不可变的对象,在新对象上操作不会影响原对象数据,避免了deepCopy把所有节点都复制一遍的性能损耗,如果对象一个节点变化只修改这个节点和受它影响的节点