emmmmm。。。。。。。。。。
如果有什么说的不对的地方,欢迎评论告知。
=3333333=
先从最基础的react说起吧,目前框架都属于组件化的,但是更主要的目的是为了让前端围绕着数据逻辑来做页面而并非原先的去操作dom的思想,三大框架目前都是往一个方向去。不过相比其他,react主打是diff算法处理dom,所以掌握他的生命周期去处理好数据才是我们最初应该学习的东西。
react
最常用的就是react.Component
谈一下他的生命周期吧。
假如我页面有一个首页,我们现在要做顶部的导航栏,那么我们现在去写一个顶部导航的组件。
假设这个顶部的导航,每个人权限不同,看到的东西不一样,那么这个导航是通过请求数据来实现的。
来,先熟悉
生命周期。
挂载时期会执行的(可以理解为这个组件丢到页面时候会执行,并且只执行一次的)。
constructor() // 构造函数,一般我会在这里去定当前组件的state初始化
componentWillMount() // 挂载之前,在17之后改名下面一条
UNSAFE_componentWillMount() // 上面挂载前马上要更改的名字。
//这个是唯一会在服务端调用的生命周期钩子,在这里设置一些东西不会导致组件重新渲染,
//所以这里最好不要随意进行一些组件需要数据的请求。
render() // 这个组件需要输出的东西,按道理来说都是一些jsx之类的,
//如果你输出的是字符串和数字,则会变成text node,
//如果输出的是布尔值的false,或者null,就会不进行渲染。
componentDidMount() // 这里就是组件挂载时候最后触发的一个生命周期了。
// 因为这个里面请求数据会让组件重新渲染,所以一般数据放在这里进行请求。
然后这个组件因为数据变化也会重新渲染,一种是props改变的时候(或者父组件重新渲染的时候),一种是自己的state改变的时候。
props改变的时候会执行的生命周期
UNSAFE_componentWillReceiveProps(nextProps) // 这个生命周期在17版之前叫componentWillReceiveProps
// 一般是在父组件更新的时候,或者props改变的时候会触发的,用来根据props更改而改本组件state用的
// 官方目前推荐用下一条去替换他
static getDerivedStateFromProps(nextProps, prevState) // 接收到新的props或者父组件重新渲染的时候,都会调用。
// 如果你只想处理变化,就要比较新旧值
// 作用跟上面一条是一样的,props改变时候要改state就在这里做
shouldComponentUpdate(nextProps, nextState) // 这里可以通过返回false,来阻止重新渲染
// 默认为true,若shouldComponentUpdate()返回false,
//而后UNSAFE_componentWillUpdate(),render(), 和 componentDidUpdate()将不会被调用
UNSAFE_componentWillUpdate() // 17版以前叫componentWillUpdate
// 在收到新的state或者props的时候,渲染前被立即调用
//不要在这里setState,不要在这里setState,不要在这里setState,
render() // 当然少不了输出需要显示的东西啦
getSnapshotBeforeUpdate() // 在最新的渲染输出提交给DOM前将会立即调用
// 为了支持异步渲染,如果在componentWillUpdate处理的一些事情,可能会有延迟滞后
// 这一生命周期返回的任何值将会 作为参数被传递给componentDidUpdate()。
componentDidUpdate(prevProps, prevState) // 终于说到最后一个了,我擦啦
// 更新发生后立即被调用
// 这也是一个适合发送请求的地方,要是你对比了当前属性和之前属性(例如,如果属性没有改变那么请求也就没必要了),不然就循环给你看,嘿嘿嘿嘿嘿嘿
然后就是state更改的时候了,懒得打了,就是上面props更改时候的一些生命周期去掉UNSAFE_componentWillReceiveProps(nextProps)或者static getDerivedStateFromProps(nextProps, prevState),直接从shouldComponentUpdate开始。
最后组件准备被移除的时候
componentWillUnmount()
// 在组件被卸载和销毁之前立刻调用。可以在该方法里处理任何必要的清理工作
// 例如解绑定时器,取消网络请求,清理任何在componentDidMount环节创建的DOM元素。
捕捉错误的生命周期
componentDidCatch(error, info)
// 打印它们之下组件里的错误,不能捕捉它自己内部的错误。
// 在这里是用来处理错误的,不要尝试用这个去处理数据
好了,生命周期大致就是这些,估计看完也困了。
我们假如要做一个顶部导航,那么(敲黑板)重点就在于这个组件怎么请求,请求完了啥时候往里面丢,页面怎么处理之类的。
其实总结起来也比较简单,步骤如下
- constructor() 定初始化的state,里面有一个我们需要循环展示出来的list用来储存导航
- render() 里面写好循环state里面的list等展示效果
- componentDidMount() 根据当前用户的一些信息去请求数据,并且处理完setState塞入list里面
- shouldComponentUpdate() 如果他的父组件会更新,但是他不需要做过多的重复渲染,则在这里判断新旧值,考虑是否要重新渲染
另外有的小伙伴说,React.PureComponent可以帮你处理一些 shouldComponentUpate()新旧值的判断是否要重新渲染,不过这个只会对对象进行浅对比。
如果对象包含复杂的数据结构,那么可能会导致他无法渲染出你要的最新的数据,React.PureComponent 的 shouldComponentUpate() 会忽略整个组件的子级。请确保所有的子级组件也是”Pure”的。
关于redux的数据流
说简单一点,其实就是
- 我往项目根部创建一个store(储存数据的东西)
- 触发action
- action告诉reducer我要改数据了!
- reducer改完了数据页面也同时拿到了
action => reducer => store
不过因为redux不希望有一些副作用的数据产生,所以我们要在这里面请求数据,就需要一些中间件。
目前比较大众的有俩。
redux-saga和redux-thunk
先大致介绍一下
redux-thunk
说简单一些,就是让action中可以请求异步操作,请求到了数据再执行reducer存入store里面。
引入方法
import thunkMiddleware from 'redux-thunk'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux'
import { Reduces } from './store/index.js' // 引入所有的reduces
const store = createStore(
Reduces,
applyMiddleware(
thunkMiddleware // 允许 dispatch() 函数
)
)
<Provider store={store}> // 丢到里面去
<Router>
</Router>
</Provider>
然后在action里面,就可以
/**
* 获取用户订单信息
* @param {String} type 用户订单的请求类型
* @return {dispatch} dispatch 返回调用新的action方法
*/
getUserOrderList(type) {
return dispatch => {
// 这个OrderModel是我自己项目所有订单请求都放在里面的,其实就相当与写一个fetch,然后then的处理他
return OrderModel.orderlist({
status: type
}).then(response => {
// 这里在存入store里面去,这里一般我会写一个公用的dispatch的方法进行丢入
})
}
}
然后用dva的朋友一般用的都是
redux-saga
这个其实我了解的并不深,就拿官网的事例来用一下
import { call, put, takeEvery, takeLatest } from 'redux-saga/effects'
import Api from '...'
// worker Saga : 将在 USER_FETCH_REQUESTED action 被 dispatch 时调用
function* fetchUser(action) {
try {
// call这里进行请求数据,拿到的数据丢到user里面
const user = yield call(Api.fetchUser, action.payload.userId);
// 开始匹配type,进行数据更改在这里
yield put({type: "USER_FETCH_SUCCEEDED", user: user});
} catch (e) {
yield put({type: "USER_FETCH_FAILED", message: e.message});
}
}
这里放上两个文档供参考
bug
一般玩react最容易遇到的坑,就是如何处理好数据,搭配生命周期,刚开始可能会因为没处理好数据,导致组件无内容报错,也有循环不加key等。
感觉报错最多的可能性应该是这个key了。
当循环的时候,或者使用table表单的时候,特别是antd里面的table,要仔细看文档,需要key的时候一定要加上。
route
route目前用的比较多的是两种
BrowserRouter和HashRouter
HashRouter
这个就比较丑了,但是路由都在前端,所有的页面都是用#后面带上一些东西,前端自己做管理就好了。
HashRouter 使用 URL 的 hash (例如:window.location.hash) 来保持 UI 和 URL 的同步。
不过像描点就不要想啦。
import { HashRouter } from 'react-router-dom'
<HashRouter>
<App/>
</HashRouter>
BrowserRouter
这个就是使用 HTML5 提供的 history API 来处理路由,所有的页面都是真实路径,需要后端配置所有请求页面都指向我们的主页面。
import { BrowserRouter } from 'react-router-dom'
<BrowserRouter>
<App/>
</BrowserRouter>
跳转路由直接页面里面跳转,可以用下面的方法
import { Link } from 'react-router-dom'
<Link to="/about">关于</Link>
如果是在js里面想跳转,则
this.props.history.push() // 或者replace,过着go()啥的
就可以进行跳转了,不过组件里面要拿到父组件的props需要继承一下
比如
<子组件 {...this.props}></子组件>
这样就可以把父组件的props全部丢到子组件里面去 =3=
最后把项目的一些需要的文档整理了一下