zf-6-dva + roadhog(34天)

大兔子大兔子 提交于 2020-03-17 01:12:28
dva

create-react-app dva-app
cd dva-app
cnpm i dva keymaster -S

文件结构

├── /mock/ # 数据mock的接口文件
├── /src/ # 项目源码目录
│ ├── /components/ # 项目组件
│ ├── /routes/ # 路由组件(页面维度)
│ ├── /models/ # 数据模型
│ ├── /services/ # 数据接口
│ ├── /utils/ # 工具函数
│ ├── route.js # 路由配置
│ ├── index.js # 入口文件
│ ├── index.less
│ └── index.html
├── package.json # 定义依赖的pkg文件
└── proxy.config.js # 数据mock配置文件

计数器

用法 说明
app = dva(opts) 创建应用,返回 dva 实例
app.use(hooks) 配置 hooks 或者注册插件
app.model(model) 注册 model
app.router(({ history, app }) => RouterConfig) 注册路由表
app.start(selector?) 启动应用。selector 可选

import { Dispatch } from 'redux';
import dva, { connect } from 'dva';
import keymaster from 'keymaster';
import { RouterAPI } from 'dva';
import { Router, Route } from 'dva/router';
interface Counter1State {
    number: 0
}
interface Counter2State {
    number: 0
}
interface CombinedState {
    counter1: Counter1State;
    counter2: Counter2State;
}
const app = dva();
const delay = (millseconds: number) => {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve();
        }, millseconds);
    });
}
app.model({
    namespace: 'counter1',
    state: { number: 0 },
    reducers: {//接收老状态,返回新状态
        add(state) { //dispatch({type:'add'});
            return { number: state.number + 1 };
        },
        minus(state) {//dispatch({type:'minus'})
            return { number: state.number - 1 };
        }
    },
    // 延时操作 调用接口  等待
    effects: {
        *asyncAdd(action, { put, call }) { //redux-saga/effects {put,call}
            yield call(delay, 1000);//把100传给delay并调用,yield会等待promise完成
            yield put({ type: 'add' });
        }
    },
    subscriptions: {
        keyboard({ dispatch }) {
            keymaster('space', () => {
                dispatch({ type: 'add' });
            });
        },
        changeTitle({ history }) {
            setTimeout(function () {
                history.listen(({ pathname }) => {
                    document.title = pathname;
                });
            }, 1000);

        }
    }
});
app.model({
    namespace: 'counter2',
    state: { number: 0 },
    reducers: {//接收老状态,返回新状态
        add(state) { //dispatch({type:'add'});
            return { number: state.number + 1 };
        },
        minus(state) {//dispatch({type:'minus'})
            return { number: state.number - 1 };
        }
    }
});
type Counter1Props = Counter1State & { dispatch: Dispatch };
const Counter1 = (props: Counter1Props) => {
    return (
        <div>
            <p>{props.number}</p>
            <button onClick={() => props.dispatch({ type: 'counter1/add' })}>add</button>
            <button onClick={() => props.dispatch({ type: 'counter1/asyncAdd' })}>asyncAdd</button>
            <button onClick={() => props.dispatch({ type: 'counter1/minus' })}>-</button>
        </div>
    )
}
type Counter2Props = Counter2State & { dispatch: Dispatch };
const Counter2 = (props: Counter2Props) => {
    return (
        <div>
            <p>{props.number}</p>
            <button onClick={() => props.dispatch({ type: 'counter2/add' })}>+</button>
            <button onClick={() => props.dispatch({ type: 'counter2/minus' })}>-</button>
        </div>
    )
}

const mapStateToProps1 = (state: CombinedState): Counter1State => state.counter1;
const ConnectedCounter = connect(
    mapStateToProps1
)(Counter1);
const mapStateToProps2 = (state: CombinedState): Counter2State => state.counter2;
const ConnectedCounter2 = connect(
    mapStateToProps2
)(Counter2);
app.router(
    (api?: RouterAPI) => {
        let { history } = api!;
        return (
            (
                <Router history={history}>
                    <>
                        <Route path="/counter1" component={ConnectedCounter} />
                        <Route path="/counter2" component={ConnectedCounter2} />
                    </>
                </Router>
            )
        )
    }
);
app.start('#root');

namespace model 的命名空间,同时也是他在全局 state 上的属性,只能用字符串
state 初始值
reducers 以 key/value 格式定义 reducer。用于处理同步操作,唯一可以修改 state 的地方。由 action 触发。
effects 以 key/value 格式定义 effect。用于处理异步操作和业务逻辑,不直接修改 state。由 action 触发,可以触发 action,可以和服务器交互,可以获取全局 state 的数据等等。
subscriptions 以 key/value 格式定义 subscription。subscription 是订阅,用于订阅一个数据源,然后根据需要 dispatch 相应的 action。在 app.start() 时被执行,数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。

roadhog

roadhog 是基于 webpack 的封装工具,目的是简化 webpack 的配置
提供 server、 build 和 test 三个命令,分别用于本地调试和构建
提供了特别易用的 mock 功能
命令行体验和 create-react-app 一致,配置略有不同,比如默认开启 css modules
还提供了 JSON 格式的配置方式。

安装

npm i roadhog -g
mkdir zhufeng_roadhog
cd zhufeng_roadhog
cnpm init -y
cnpm i react react-dom -S

三个命令

roadhog server
roadhog build
roadhog test

.roadhogrc

{
“entry”: “src/index.js”,
“disableCSSModules”: false,
“publicPath”: “/”,
“outputPath”: “./dist”,
“extraBabelPlugins”: [],
“extraPostCSSPlugins”: [],
“autoprefixer”: null,
“proxy”: null,
“externals”: null,
“library”: null,
“libraryTarget”: “var”,
“multipage”: false,
“define”: null,
“env”: null,
“theme”: null,
}
参数 含义
entry 指定 webpack 入口文件,支持 glob 格式
outputPath 配置输出路径,默认是 ./dist
disableCSSModules 禁用 CSS Modules
publicPath 配置生产环境的 publicPath,开发环境下永远为 /
extraBabelPlugins 配置额外的 babel plugin。babel plugin 只能添加,不允许覆盖和删除
extraPostCSSPlugins 配置额外的 postcss 插件
autoprefixer 配置 autoprefixer 参数
proxy 配置代理
externals 配置 webpack 的 externals 属性
library 配置 webpack 的 library 属性
libraryTarget 配置 webpack 的 libraryTarget 属性
multipage 配置是否多页应用。多页应用会自动提取公共部分为 common.js 和 common.css
define 配置 webpack 的 DefinePlugin 插件,define 的值会自动做 JSON.stringify 处理
env 针对特定的环境进行配置。server 的环境变量是 development,build 的环境变量是 production
theme 配置主题,实际上是配 less 的 modifyVars

.roadhogrc.mock.js

export default {
// Support type as Object and Array
‘GET /api/users’: { users: [1,2] },

// Method like GET or POST can be omitted
'GET /api/users/1': { id: 1 },

// Support for custom functions, the API is the same as express@4
'POST /api/users/create': (req, res) => { res.end('OK'); },

};

去实现一个roadhog
package.json
#!/usr/bin/env node

const spawn = require('cross-spawn');
const chalk = require('chalk');
const script = process.argv[2];
//node bin/roadhog.js -v
switch (script) {
    case "-v":
    case "--version":
        console.log(require("../package.json").version);
        break;
    case "build":
    case "server":
        result = spawn.sync(
            "node",
            [require.resolve(`../lib/${script}`)],
            { stdio: "inherit" }
        );
        process.exit(result.status);
        break;
    default:
        console.log(`Unknown script ${chalk.cyan(script)}.`);
        break;
}
build.js
const webpack = require("webpack");
const chalk = require("chalk");
function doneHandler(err, stats) {
    console.log(stats.toJson().assets);
}
function build() {
    let config = require("./config/webpack.config.prod");
    var compiler = webpack(config);
    compiler.run(doneHandler);
}
build()
webpack.config.prod
const path = require("path");
module.exports = {
    entry: "./src/index.js",
    output: {
        path: path.resolve('dist'),
        filename: "[name].js"
    }
};
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!