dva的系统学习
Dva JS 的组成
因为工作中用到了dva的思想,所以才有了这一篇文章。这篇文章仅代表我学习后的知识输出,加强学习印象。
dva的组成,mock、public、src、router(路由管理)、index(入口文件)文件组成。其中src包含assets(静态资源文件)、components(公共组件)、models(相当于redux)、routes(相当于view或page)、services(api的集成式管理)、utils(公共方法)。
路由
10.11 路由学习记录
dva启动文件
import dva from 'dva';
import './index.css';
// 1. Initialize
const app = dva();
// 2. Plugins
// app.use({});
// 3. Model
app.model(require('./models/listModel').default);
app.model(require('./models/homeModel').default);
// 4. Router
app.router(require('./router').default);
// 5. Start
app.start('#root');
-
router.js 负责管理静态路由的管理,相当于一个页面管理工具,并且是以jsx语法写的,通过引用
'dva/router',把需的解构出来。由<Route>标签包裹<Route path = "/" component = {IndexPage} />。这样才能显示在页面上。 -
动态路由需要写在组件内部,当一个组件需要包含不同组件的话,那么在组件内部用
<Switch>标签包裹路由,充当插槽。 -
跳转的时候,需要获取到history,那么就需要withRouter高阶组件包裹 ,首先引用
*import* {withRouter} *from* 'dva/router',第二导出*export* default withRouter(siderbar)跳转:props.history.push(跳转路径)
10.12 路由学习记录
重定向 redirect
路由拦截,三目,但是login要在前面
history模式:在index中 Initalize中
const app = dva({
history:require('history').createBrowserHistory()
})
models
每个models就是一个仓库,与redux不同的是,redux是一个store(公共状态管理)中包含许多个reducer(状态处理文件)和action(view和redux连接的桥梁)。而models中每一个models都是一个仓库,里面包含了namespace(命名空间:多个仓库的冲突用namesoace区分),state(状态),reduce(处理方式)。
同步
-
model.js中要导出的是一个对象
export default { namespace: '仓库名称', state : [], reducers:{ 'action名称'(state,{payload:xx}){ return 对数据的处理 } } } -
在index.jd里的Model中写 app.model(require('./models/地址').default);
-
使用connect高阶函数将models 和组件连接起来,会得到dispatch属性
import {connect} from 'dva'; function faSongDispatch(value){ type:"路径/名称", payload:'value' } //这样就把值传过去了 -
使用reducers
reducers:{ 名称(newState,payload){ return{ ...newState, 改变的值} } }
异步
按照经验,异步的逻辑是,发送再接收再显示
services文件 管理api
import request from '../utils/request'
export function 函数名(){
return request("请求地址"),{
herders:{请求头}
}
}
effects :{
*函数名(action,{call,put}){
//action,ui层传过来的值,call和put是对service的使用。call是发起请求,put是发送dispatch给reducer,让reducer进行处理。
}
}
subscriptions 的用法
订阅函数,可以用于加载之前,初始化的时候传递的数据,可以用于加载前进度条之类的
Mockjs的使用
- 语法规范:'name|rule' : value name(属性名),rule(生成规则),value(属性值)
- 占位符:@
运行方式
cd mock
node test.js
使用总结
UI层与model层的连接
使用 connect 连接两层,当UI层需要使用数据的时候,利用props.data data 为数据层引进来的数据
通过声明mapStateToProps 的函数(建议用箭头),参数是state,可以获取到redux中的公共状态。返回值是一个对象,可以将公共状态的数据提取出来。传入connect高阶函数中。
当UI层想要获取redux中的数据或者想要改变redux的数据需要发送dispatch 。可以将发送一个返回值是dispatch方法的函数,写函数外部(可以写在函数外部也可以写内部,建议直接写在函数外部。dispatch函数不需要jsx函数包裹)。
dispatch方法:dispatch方法是connect给予的,所以,当需要用dispatch方法的时候,需要引入props.dispatch 。如果是在外部引用一个返回值是dispatch方法的函数,如果写在函数内部则不需要传dispatch方法,只需要传payload所需要的值。
model层对数据的处理
model中的js文件,导出的都是一个对象,可以将对象分为5个部分
- namespace 命名空间,也就是仓库的名字,发送dispatch的时候需要根据命名空间来找到对应的type。
- state : 数据。 此命名空间所属仓库下的所有数据
- reducers 修改仓库数据的唯一途径:ui层或异步请求来的数据,想要对仓库数据进行修改,都需要经过reducer。
- effects 请求数据的方法,里面默认使用saga作为中间件,所以里面使用生成器函数
*和yield来j发起请求。 - subscriptions 当页面加载前需要一串默认的数据时,比如加载进度条,可以使用。
其中的重点在于 reducers 和 effects 。
当处理同步数据的时候,页面通过命名空间和type名称来发送dispatch,reducer通过type名称来进行匹配。其中有两个参数。state和payload ,state是仓库中的数据,payload是传过来的值。
function save(dispatch,value){
dispatch({
type:'list/add',
payload:value
})
}
注意:不能直接修改state的值,需要先深拷贝再对其进行数据处理 这里可以写成习惯性的写法
reducers: {
add(state, { payload: value }) {
//深拷贝
const newState = JSON.parse(JSON.stringify(state))
//将value添加到数组里
newState.listData.push(value)
return newState
}}
当处理异步的时候,使用effects ,首先ui层要发送dispatch,匹配到effects后,通过call发起请求,之后通过put再次发送一个dispatch,给到reducer,进行数据处理。effects传入两个值一个是payload(没有可以不写)利用saga中间件,用生成器函数来,第一用call获取请求过来的数据,第二用put发送给reducer
//导入service
import { oneApi } from '../services/myService'
//effects
effects:{
*getList(payload,{call,put}){
const res = yield call(oneApi);
const list = res.data.data
yield put({
type: 'SAVE_LIST',
payload:list
})
}
}
//reducers
reducers:{
SAVE_LIST(state,{payload:list}){
const newState = JSON.parse(JSON.stringify(state))
newState.list = list
return newState;
}
}
//注意,第二个参数一定要用大括号包裹,第二个参数返回的是一个对象,如果没有包裹需要解两层,很麻烦。
使用dvajs的 redux方法
import {connect} from 'dva'
function test(props){
useEffect((props.dispatch){
props.dispatch({
type:'SAVE_LIST',
//不需要传入数据的话可以不要payload
//如果是需要传值的post请求,需要写payload
})
},[props.dispatch])
}
const mapStateToProps = (state) => {
return {
list : state.list.list
//第一个state是公共状态,第一个list是命名空间,第二个list是list空间下的state仓库的list数组
}
}
export default connect (mapStateToProps)(test)
工作框架于dva的异同
工作同样使用models,router,sevrices。
但更像dva和cra的结合。
工作框架的启动文件和dva几乎一致,但做了更多的封装,所以可以把工作框架作为以dva为核心。
工作框架的文件架构
使用pages文件夹来存放页面,一个页面是即一个功能模块。所以可以把工作框架以page文件夹,分为外部文件和内部文件。外部文件,models,routes,services,components,均是放公共组件,公共请求,公共仓库。
内部文件,具有一个又一个的单独的文件夹如home文件夹,这就代表了一个home页面。在这个home页面中,又有五个部分:入口文件(这里一般是两个可以复制粘贴),公共组件,models,routers,services。
routes存放页面(可以把页面拆成page和view,view仅存放ui部分,page存放逻辑部分)
models进行状态管理。通过connect与页面连接。
services存放接口服务。
状态管理
models存放所有状态,其实也可以不使用models,也可以使用state。
但是作为数据量前端框架,还是推荐使用工作框架封装的redux,工作框架-core。
这个工作框架-core和dva有什么关系呢。
在我看来就是对dva多封装了一层(或不止一层)。经过我的使用,上手后它确实更加好用。
看看它都有什么吧
export default{
namespace: 'myStore', //仓库命名空间
state:{
num:null, //仓库数据部分
},
reducer:{
add(state,{payload}){
return {
...state,
num:payload
}
}
},
thunk:{
getlist({payload},{put}){
return require(payload).then(res=>{
put({
type:'myStore/add',
payload
})
})
}
}
}
在thunk中主要处理异步问题,但是异步问题不能直接修改state中的数据,只能当获取数据后再次发送dispatch。
//发送dispatch的方式
import {connect} from '@cmbc/工作框架' //和其他redux差不多,只是引用的不一样
function App({}){
return (<div> </div>)
}
const mapStateToProps = (myStore)=> ({
return ...myState
})
const mapDispatchToProps = dispatch =>({
})
export default connect(mapStateToProps,mapDispatchToProps)(App)
//和其他redux一样都需要connect连接,但是高阶函数中,State和dispatch都是对象形式
这种方式是将state和dispatch统一管理,但是写一个函数就需要写多个方法名。
要在 thunk、reducer、dispatch、dispatchToProps、触发的函数这五个阶段。
很麻烦,所以工作框架3推出了hooks,快速获取state和dispatch。
import {connect,useSelector,useDispatch} from '@cmbc/工作框架'
function App({}){
const {num} = useSelector(store=>store.myStore)
const dispatch = useDispatch()
return (<div> </div>)
}
export default connect()(App)
这时就可以不使用高阶函数,直接在组件内部发送dispatch和使用state。
发送dispatch就和dva一模一样了。可以看前面的部分。
组件
根据文件结构和状态管理可以推出在工作框架中存在三种组件。
- 纯ui组件,这种也可以属于layout组件,没有任何交互,所有动态渲染状态通过上层逻辑组件赋予
- 逻辑组件,控制连接models,并包括组件内所有的逻辑部分
- 公共组件,这里的公共组件也可以是其他抽离开的组件,这里有可以连接仓库,可以存在ui,可以存在逻辑,但是组件足够小。
可以理解为
function App(){
useEffect(()=>{},[])
const [state] = useModal('myStore')
const dispatch = useDispatch()
return <View props={...state}/>
}
//逻辑部分
function View(props){
return (<div>
<Title />
</div>)
}
//子组件
function Title(){
const [state] = useModal('myStore')
const dispatch = useDispatch()
return (<div>标题<div>)
}