这一章,我们将介绍 pastate 如何构建大规模应用,以及如何在原生 redux 项目中使用等。
路由
当我们的应用日益复杂时,需要用前端路由的模式来管理多个界面。
无参数路由
首先安装 react-router 的网页版 react-router-dom:
$ npm install react-router-dom --save
或
$ yarn add react-router-dom
使用 Router 作为根容器
接着我们用路由 Router 组件作为新的根容器来包装我们的原来的根容器 Navigator,代码如下:
src/index.js
...
import { BrowserRouter as Router } from 'react-router-dom';
ReactDOM.render(
makeApp(
<Router>
<Navigator.view />
</Router>,
storeTree
),
document.getElementById('root')
);
使用 Route 组件来定义路由
接下来,我们来看看导航模块 Navigator 的视图如何修改:
Navigator.view.js
import { Route, Redirect, Switch, withRouter, NavLink } from 'react-router-dom'
class Navigator extends React.PureComponent{
render(){
/** @type {initState} */
const state = this.props.state;
return (
<div>
<div className="nav">
<div className="nav-title">班级信息管理系统</div>
<div className="nav-bar">
<NavLink
className="nav-item"
activeClassName="nav-item-active"
to="/student"
>
学生({this.props.count.student})
</NavLink>
<NavLink
className="nav-item"
activeClassName="nav-item-active"
to="/class"
>
课程({this.props.count.class})
</NavLink>
</div>
</div>
<div className="main-panel">
<Switch>
<Redirect exact from='/' to='/student'/>
<Route path="/student" component={StudentPanel.view}/>
<Route path="/class" component={ClassPanel.view}/>
</Switch>
</div>
</div>
)
}
}
export default withRouter(makeContainer(Navigator, state => ({...})))
我们对该文件进行了 3 处修改
- 使用路由组件
Switch
+Route
来代替之前手动判断渲染子模块的代码,让路由自动根据当前 url 选择对应的子组件来显示; - 使用路由组件
NavLink
来代替之前的导航栏按钮,把每个 tab 绑定到一个特定的 url; - 使用路由的 withRouter 函数包装我们原来生成的 container,这使得当路由变化时,container 会收到通知并重新渲染
完成!这时我们就可以看到,当我们切换导航标签时,url 和显示的子组件同时改变:
路由生效当我们在 /class
路径下刷新或进入时,应用可以显示为课程板块,这是路由组件为我们提供的一个在原来的无路由模式中没有的功能。
// index.js
import { HashRouter as Router } from 'react-router-dom';
这是就会自动启用 #
(hash路由)来分割前端和后端路由:
有参数路由
我们在上面的路由不涉及参数,如果我们需要使用类似 \student\1
、\student\2
的方式来表示目前显示哪一个 index 或 id 的学生,我们可以在定义路由时使用参数:
// Navigator.view.js
<Route path="/student/:id" component={StudentPanel.view}/>
并在被路由的组件的 view 中这样获取参数:
// StudentPanel.view.js
let selected = this.props.match.params.id;
在 actions 中使用路由
如果你要在 actions 中简单地获取当前网址的路径信息,你可以直接使用 window.location
获取:
// StudentPanel.model.js
const actions = {
handleClick(){
console.log(window.location)
console.log(window.location.pathname) // 当使用 BrowserRouter 时
console.log(window.location.hash) // 当使用 HashRouter 时
}
}
// StudentPanel.model.js
const actions = {
selectStudent(history, index){
console.log(history)
history.push(index+'')
// history.push('/student/' + index)
// history.goBack() // or .go(-1)
}
}
经过基础的测试,pastate 兼容 react-router 的路由参数、history 等功能,如果你发现问题,请提交 issue 告诉我们。
嵌入 redux 应用
Pastate 内部使用 redux 作为默认的多模块引擎,这意味着,你可以在原生的 redux 项目中使用 pastate 模块, 只需两步:
- 使用 Pastate 模块的 store 中提供的 getReduxReducer 获取该模块的 redux reducer,并把它挂载到 redux 的 reducerTree 中;
- 把 redux 的 store 实例的 dispatch 注入 astate 模块的 store 的 dispatch 属性 。
import { createStore, combineReducers } from 'redux';
import { makeApp, combineStores } from 'pastate';
...
import * as StudentPanel from './StudentPanel';
const reducerTree = combineReducers({
panel1: oldReducer1,
panel2: oldReducer2,
panel3: StudentPanel.store.getReduxReducer() // 1. 获取 Pastate 模块的 reducer 并挂载到你想要的位置
})
let reduxStore = createStore(reducerTree)
StudentPanel.store.dispatch = reduxStore.dispatch // 2. 把 redux 的 dispatch 注入 Pastate 模块中
开发调试工具
redux devtools使用 redux devtools 不要求 你把 pastate 嵌入到原生 redux 应用中,你不需要懂得什么是 redux,在纯 pastate 项目中就可以使用 redux devtools!
中间件
内置中间件
Pastate 目前实现了基于 actions 的中间件系统,可用于对 actions 的调用进行前置或后置处理。Pastate 内置了几个实用的中间件生成器:
- logActions(time: boolean = true, spend: boolean = true, args: boolean = true): 把 actions 的调用情况 log 到控制台
- syncActions(onlyMutations: boolean = false): 把每个 action 都转化为同步 action, 方便互相调用时的调试观察每个 action 对数据的改变情况
- dispalyActionNamesInReduxTool(onlyMutations: boolean = false): 把 actions 名称显示在 redux devtools 中,方便调试
自定义中间件
const myLogMiddleware = function (ctx, next) {
let before = Date.now();
next();
console.log(ctx.name + ': '+ (Date.now() - before) + 'ms');
}
-
ctx
参数是上下文(context)对象,包括如下属性:
type MiddlewareContext = {
name: string, // action 的名称
agrs?: IArguments, // action 的调用参数
return: any, // action 的返回值
store: XStore // action 绑定的 store
}
-
next
参数是下一个中间件或已做参数绑定的 action 实体, 你可以实现自己的中间件逻辑,决定要不要调用或在什么时候调用 next。
编译与部署
在 pastate 应用中,你可以简单的使用默认的 build 指令来编译应用:
$ npm run build
或
$ yarn build
Pastate 在编译之后仅为 ~28kb, gzip 之后仅为 ~9kb,非常轻便。Pastate 包的整体结构如下(图中显示的是未 gzip 的大小):
pastate 包结构
下一章,我们来详细介绍 pastate 的原理。