您的当前位置:首页正文

【教程】Pastate.js 响应式 react 框架(七)规模

来源:花图问答

这一章,我们将介绍 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 模块, 只需两步:

  1. 使用 Pastate 模块的 store 中提供的 getReduxReducer 获取该模块的 redux reducer,并把它挂载到 redux 的 reducerTree 中;
  2. 把 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 的原理。