最近公司有一个微信公众号的项目,于是花了一定的时间去搭建关于一个可以用于开发的微信公众号的架子。技术选型使用了阿里的dva。
- 第一步,使用
dva new wechat-public
来初始化脚手架。 - 第二步,完善当前脚手架。搭建一个架子无非就从这几点来操作:
- 网络层(api接口,service服务)。
- 业务层(使用dva基于redux的数据流方案,在models中定义数据源,以及业务操作)
- 路由(使用dva基于react-route搭建页面路由,并且基于react-router搭建一个灵活的可控路由)
- 静态资源库的存放(文件夹assets中)
- 组件库的存放(文件夹components中)
- 页面存放(文件夹pages中)
- 工具类(文件夹utils)
- 网络层的构造。
我们选取axios作为网络层请求的基本库。对其进行封装。在封装后,遇见了一个跨域请求的问题,原来是withCredentials
犯的错误。
经查询,有以下的解释,参考:
XMLHttpRequest.withCredentials 有什么用?
跨域请求是否提供凭据信息(cookie、HTTP认证及客户端SSL证明等)
也可以简单的理解为,当前请求为跨域类型时是否在请求中协带cookie。
XMLHttpRequest.withCredentials 怎么用?
withCredentials属于XMLHttpRequest对象下的属性,可以对其进行查看或配置。
- 数据的流向与页面路由
利用dva自带的redux的封装,我们可以查看models层是mvc中的控制层的作用。首先通过在services中创建一个js文件,将我们的网络请求的服务暴露出来,然后再通过models层中的effects去交互。具体的可以去dva官网学习。
期间遇到的问题:
1.如何全局监听整个页面的路由。
我们可以在react-routes中来操作。首先,我们创建一个app.js,来作为所有界面的父界面,然后,将其注册在route中。为以下的代码
<ConnectedRouter history={history}>
<App>
<Switch>
{/* 默认跳转到 service 页面 */}
<Route exact path="/" render={() => <Redirect to="/service" />} />
{
Routes.map(({ name, path, exact = true,...dynamic_d }) => (
<Route path={path} component={dynamic({app,...dynamic_d})} exact={exact} key={name}></Route>
))
}
</Switch>
</App>
</ConnectedRouter>
其中ConnectedRouter为dva-redux中暴露出来的Router,可以使用redux检测整个路由的变化,若我猜的不错,应该可以使用routerRedux
去跳转路由,随后,使用在Switch外层包裹App,此App即为我们所需要封装的层。首先感谢这篇文章给我启发,随后,我们使用connect来将此组件包裹,connect为dva控制流的一个衔接。并且,我们需要在models中来注册这个model,这里需要注意一个问题,就是WithRouter的使用,由于这个页面是初始化页面,所以router还没有注册进来,我们用WithRouter让其props中的push,pop等方法注册进来,这样我们就可以在其中进行页面控制。随后,我们则可以通过绑定的app控制层来控制当前页面的路由。在models中使用:
subscriptions: {
setup({ dispatch, history }) { // eslint-disable-line
},
setupHistory({ dispatch, history }) {
history.listen((location) => {
//当前监听全局的路由变化
console.log(location)
})
}
},
- 如何通过dva-loading来控制整个项目的loading。
这个问题是一个比较需要关注的问题,因为之前我有用dva来做项目,由于当时对dva不是特别的熟悉,所以还没有将其发挥极致,最后loading是自己写一个界面,然后在每个界面都在request后面来使用,导致每个界面都需要首先注册一个state的isshow(举个例子)来控制loading的显示和隐藏。这极大的让整个项目的代码冗余,并且消耗时间。
直到查询loading的控制后,发现整个组件,并且可以通过整个项目的数据流来控制。在index.js中注册这个后:
const app = dva({
...createLoading({
effects: true,
}),
history: createHistory(),
onError (error) {
Toast.fail(error.message);
},
});
随后,我们可以通过connect将loading注入到组件组件中,并且组件中会有
effes/user
来控制单个的异步请求,这样,我们就可以通过App来全局控制整个loading了,极大的减少了我们的工作量。在其他页面,只要connect就可以获取当前页面的异步请求,是不是特别的简单。另外,我们需要注意一些路由的问题,v3和v4的写法不同,具体看官网。
- 关于dva的动态加载组件的问题。
在写这个架子之前,我有写一个mobx的控制流架子,里面动态组件是通过react-loadable
,但是dva项目中已经有属于他的一个动态组件加载。那就是dynamic。
如何使用? 这边刚好可以通过这个,把我们的按业务加载model也给写下来。
//tabs
const pagingTabs = [
{
name: '就诊服务',
path: '/service',
models: () => [
import('models/service'),
],
LoadingComponent:MyLoadingComponent,
component:()=> import("pages/index")
},
{
name: '个人中心',
path: '/center',
LoadingComponent:MyLoadingComponent,
models: () => [
import('models/center'),
],
component:()=> import("pages/index")
},
{
name: '医院信息',
path: '/hospital',
LoadingComponent:MyLoadingComponent,
models: () => [
import('models/hospital'),
],
component:()=> import("pages/index")
}
]
以上是一个路由的封装,将所有的路由都封装到这个pagingTabs
里面,然后,我们通过上面的代码注册路由即可。
注意:在component中,我们不能够直接导入组件,而是要通过方法来回调出组件,这样才能够实现异步加载。
具体参考:React最佳实践
- 关于dva无法使用二级路由的问题,一直报错:
unexpect token <
此错误困惑我很久,无法明白dva的问题。但也由于粗心的问题,第一我们得分清楚hashhistory和browerhistory的区别,然后,我们不能使用dva自带的history去注册路由,而是需要使用第三方库history
,下载后,通过以下来注册:
import { createHashHistory as createHistory } from 'history';
const app = dva({
...createLoading({
effects: true,
}),
history: createHistory(),
onError (error) {
Toast.fail(error.message);
},
});
browerhistory 页面是无法出来的,会是空白页面或者报错,是因为项目中无法找到这个页面,我们得使用hash来让页面显示出来,记得在前面中加#
。
- 关于dva的
.webpackrc
文件和.roadhogrc
文件
其实这两个文件相同,只是因为dva最新版把roadhogrc改为了webpackrc,其中语法可以参照roadhogrc中,我们在使用这个框架的时候,在github中去寻找这个库,这个只是creact-react-app中的一个扩展,让我们不用通过eject就可以自定义整个webpack。
当然,我们还可以自己创建一个webpack.config.js
来自定义webpack,比如我写了:
module.exports = (webpackConfig, env) => {
// 别名配置
webpackConfig.resolve.alias = {
'pages':`${__dirname}/src/pages`,
'components':`${__dirname}/src/components`,
'utils':`${__dirname}/src/utils`,
'routes':`${__dirname}/src/routes`,
'models':`${__dirname}/src/models`,
'services':`${__dirname}/src/services`,
'models':`${__dirname}/src/models`,
'@':`${__dirname}/src`
}
return webpackConfig
}
来定义alias,让文件容易读取。
- 关于html-plugin插件
我们使用这个插件来定义我们整个项目的入口模板,并且可以通过他自动生成Html,通过filename来指定html生成的路径。此项目中使用entry.ejs
来定义入口。
整个框架:dva + axios + antd-mobile + react-router + roadhog
另感谢这些作者:
React最佳实践系列 —— Dva 进阶之路由和动态加载
DVA框架统一处理所有页面的loading状态
ajax中的withCredentials使用效果
dva