Dva那些事儿(一)

一、知识点的补充:

1、Webpack 是一个前端资源加载/打包工具,只需要相对简单的配置就可以提供前端工程化需要的各种功能。

2、webpack的模块热替换(HMR):如果js引入的css等发生改变的时候,不用刷新, 一些变量发生变化,但是这些变量还没有被使用的时候,是允许不刷新的,但是当修改积累到一定程度,webpack-dev-server会刷新页面的。

3、dva-cli创建新项目:

步骤:接到需求之后推荐的做法不是立刻编码,而是先以上帝模式做整体设计。

step1:先设计 model
step2:再设计 component

step3:最后连接 model 和 component

4、基于dva-cli&antd的react项目实战:http://que01.github.io/2016/11/20/dva-react/

5、keymaster库的下载地址:https://github.com/madrobby/keymaster

6、Keymaster.js快速绑定键盘操作的JavaScript库:http://www.uedsc.com/keymaster-js.html

Keymaster.js是一个JavaScript的库,用户绑定键盘的快捷方式操作,他是一个轻量级的插件,压缩版本不到100K,不依赖任何第三方插件库,能支持多种键盘按钮和组合按键。

二、项目场景描述

转自:https://github.com/dvajs/dva-docs/blob/master/v1/zh-cn/getting-started.md

最终效果:

这是一个测试鼠标点击速度的 App,记录 1 秒内用户能最多点几次。顶部的 Highest Record 纪录最高速度;中间的是当前速度,给予即时反馈,让用户更有参与感;下方是供点击的按钮。

看到这个需求,我们可能会想:

  1. 该如何创建应用?
  2. 创建完后,该如何一步步组织代码?
  3. 开发完后,该如何构建、部署和发布?

在代码组织部分,可能会想:

  1. 如何写 Component ?
  2. 如何写样式?
  3. 如何写 Model ?
  4. 如何 connect Model 和 Component ?
  5. 用户操作后,如何更新数据到 State ?
  6. 如何处理异步逻辑? (点击之后 +1,然后延迟一秒 -1)
  7. 如何处理路由?

以及:

  1. 不想每次刷新 Highest Record 清 0,想通过 localStorage 记录,这样刷新之后还能保留 Highest Record。该如何处理?
  2. 希望同时支持键盘的点击测速,又该如何处理?

我们可以带着这些问题来看这篇文章,但不必担心有多复杂,因为全部 JavaScript 代码只有 70 多行。

具体描述和讲解可以看链接。

安装 dva-cli

你应该会更希望关注逻辑本身,而不是手动敲入一行行代码来构建初始的项目结构,以及配置开发环境。

那么,首先需要安装的是 dva-cli 。dva-cli 是 dva 的命令行工具,包含 init、new、generate 等功能,目前最重要的功能是可以快速生成项目以及你所需要的代码片段。

$ npm install -g dva-cli

安装完成后,可以通过 dva -v 查看版本,以及 dva -h 查看帮助信息。

创建新应用

安装完 dva-cli 后,我们用他来创建一个新应用,取名 myApp

$ dva new myApp --demo

注意:--demo 用于创建简单的 demo 级项目,正常项目初始化不加要这个参数。

然后进入项目目录,并启动。

$ cd myApp
$ npm start

几秒之后,会看到这样的输出:

          proxy: listened on 8989
     livereload: listening on 35729

===================================================================================================下面是我对代码的进一步布局和梳理==================

接到需求之后推荐的做法不是立刻编码,而是先以上帝模式做整体设计。

先设计 model
再设计 component

最后连接 model 和 component

项目目录结构展示:



圈出来的是自己新建的。。。。

第一步:设计model

在models中countAppModel.js文件如下:

import key from 'keymaster';//?????为什么dva-cli 没有自动下载该依赖
/**2、
	Store 收到 Action 以后,必须给出一个新的 State对象(注意:不是改变值,是生产新的对象。),这样 View 才会发生变化。
	这种 State 的计算过程就叫做 Reducer。
   
	 在Redux中,reducer就是获得这个应用的当前状态和事件然后返回一个新状态的函数。
    通过获取当前state和一个action作为参数,再返回下一个state:
	
	store.dispatch方法会触发 Reducer 的自动执行。
	
	Reducer 函数最重要的特征是,它是一个纯函数。
	也就是说,只要是同样的输入,必定得到同样的输出。
	
	Reducer 函数负责生成 State。由于整个应用只有一个 State 对象,包含所有数据,
	对于大型应用来说,这个 State 必然十分庞大,导致 Reducer 函数也十分庞大。
	
	*add() {} 等同于 add: function*(){}
	call 和 put 都是 redux-saga 的 effects,call 表示调用异步函数,put 表示 dispatch action,其他的还有 select, take, fork, cancel 等,详见 redux-saga 文档
	默认的 effect 触发规则是每次都触发(takeEvery),还可以选择 takeLatest,或者完全自定义 take 规则
	*/
	
	
/**
	2. Model
	count        表示 model state 在全局 state 所用的 key,state 是默认数据
	record       表示 highest record
	current      表示 当前速度
	add(state)   表示 add: function(state) {}
	{ ...state } 表示 "..." 是对象扩展运算符,类似 Object.extend
*/

function delay(timeout){
  return new Promise(resolve => {
	setTimeout(resolve, timeout);
	});
}

/**
	数据的改变发生通常是通过用户交互行为或者浏览器行为(如路由跳转等)触发的,
	当此类行为会改变数据的时候可以通过 dispatch 发起一个 action
*/
export default {
	namespace:'count',
	state:{
		record:0,
		current:0,
	},
	
	/**
		同步行为会直接通过 Reducers 改变 State.
		Reducer 则是描述如何改变数据的
		Reducer 必须是纯函数,所以同样的输入必然得到同样的输出,
	*/
	reducers: {
		add(state) {
			 const newCurrent = state.current + 1;
			 return { ...state,
			   record: newCurrent > state.record ? newCurrent : state.record,
			   current: newCurrent,
			 };
	   },
		minus(state) {
			return { ...state, current: state.current - 1};
	   },
	},
	
	/**
	   异步行为(副作用)会先触发 Effects 然后流向 Reducers 最终改变 State.
	   它使得我们的函数变得不纯,同样的输入不一定获得同样的输出。
	   
	   dva 为了控制副作用的操作,底层引入了redux-sagas做异步流程控制,
	   由于采用了generator的相关概念,所以将异步转成同步写法,从而将effects转为纯函数。
	*/
	effects: {
		*add(action, { call, put }) 
			{
			  yield call(delay, 1000);
			  yield put({ type: 'minus' });
			},
	},
	
	/**
		Subscriptions 是一种从 源 获取数据的方法,它来自于 elm。
		
		Subscription 语义是订阅,用于订阅一个数据源,然后根据条件 dispatch 需要的 action。
		数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。
	*/
	subscriptions: {
	   keyboardWatcher({ dispatch }) {
		   
		 /*multiple shortcuts that do the same thing*/
		 key('⌘+up, ctrl+up',()=>{dispatch({type:'add'})});
	   },
	},
};
第二步,设计component

在components文件夹中CountAppComponent.js:

import styles from '../index.less';//styles是个变量;代表index.less文件对象
/**
Action 是一个普通 javascript 对象,它是改变 State 的唯一途径。
无论是从 UI 事件、网络回调,还是 WebSocket 等数据源所获得的数据,最终都会通过 dispatch 函数调用一个 action,从而改变对应的数据。
action 必须带有 type 属性指明具体的行为,其它字段可以自定义,如果要发起一个 action 需要使用 dispatch 函数;需要注意的是 dispatch 是在组件 connect Models以后,通过 props 传入的。

dispatching function 是一个用于触发 action 的函数,action 是改变 State 的唯一途径,
但是它只描述了一个行为,而 dipatch 可以看作是触发这个行为的方式.而 Reducer 则是描述如何改变数据的。

在 dva 中,connect Model 的组件通过 props 可以访问到 dispatch,可以调用 Model 中的 Reducer 或者 Effects,
dispatch({type:'count/add'});如果在 model 外调用,需要添加 namespace,即:count

*/
const CountApp = ({count,dispatch})=>{
	return (
		<div className={styles.normal}>
			<div className={styles.record}>Highest Record:{count.record}</div>
			<div className={styles.current}>{count.current}</div>
			<div className={styles.button}>
				<button onClick={
					()=>{
					dispatch({type:'count/add'});
					}
				}>+</button>
			</div>
		</div>
	);
};
export default CountApp;
第三步:连接component和model
import CountApp from '../components/CountAppComponent';
import { connect } from 'dva';
/**
	在组件设计方法中,我们提到过 Container Components,
	在 dva 中我们通常将其约束为 Route Components,因为在 dva 中我们通常以页面维度来设计 Container Components。

	所以在 dva 中,通常需要 connect Model的组件都是 Route Components,
	组织在/routes/目录下,而/components/目录下则是纯组件(Presentational Components)
*/
function mapStateToProps(state){
	return {count:state.count};
}

export default connect(mapStateToProps)(CountApp);
第四步:设计浏览器url路径

在src文件夹router.js文件如下:

import React from 'react';
import { Router, Route, Switch } from 'dva/router';
import IndexPage from './routes/IndexPage';
import mapStateToProps from './routes/MapStateToProps';
/**
	3. Router
	history 默认是 hashHistory 并且带有 _k 参数,
	可以换成 browserHistory,也可以通过配置去掉 _k 参数。
	 
	这里的路由通常指的是前端路由,由于我们的应用现在通常是单页应用,所以需要前端代码来控制路由逻辑,
	通过浏览器提供的 History API 可以监听浏览器url的变化,从而控制路由相关操作。
 */
function RouterConfig({ history }) {
  return (
    <Router history={history}>
      <Switch>
        <Route path="/" exact component={IndexPage} />
		<Route path="/mapStateToProps" exact component={mapStateToProps} />
      </Switch>
    </Router>
  );
}

export default RouterConfig;
第五步:修改项目入口文件index.js

在src目录下index.js文件如下:

import dva from 'dva';


// 1. Initialize
const app = dva();
console.log(app._store);//可以看到顶部的state数据;

//2. Model
app.model(require('./models/countAppModel').default);


/**
	3.Router
	dva 实例提供了 router 方法来控制路由,使用的是react-router
*/
app.router(require('./router').default);


// 4. Start
app.start('#root');
第六步:浏览器页面渲染载体页面index.html

在根项目文件夹public的index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Dva Demo</title>
  <link rel="stylesheet" href="index.css" />
</head>
<body>

<div id="root"></div>
<script src="index.js"></script>
<script src="keymaster.js"></script>
</body>
</html>

其他:routes文件夹中的IndexPage.js页面:

import React from 'react';
import { connect } from 'dva';


function IndexPage() {
  return (
    <div>Hello Dva.</div>
  );
}

IndexPage.propTypes = {
};

export default connect()(IndexPage);
src目录下的index.less:
.normal{
	width:200px;
	margin:100px auto;
	padding:20px;
	border:1px solid #ccc;
	box-shadow:0 0 20px #ccc;
}
.record {
  border-bottom: 1px solid #ccc;
  padding-bottom: 8px;
  color: #ccc;
}

.current {
  text-align: center;
  font-size: 40px;
  padding: 40px 0;
}

.button {
  text-align: center;
  button {
    width: 100px;
    height: 40px;
    background: #aaa;
    color: #fff;
  }
}
运行项目:



Hightest Record:22是按Ctrl+↑快捷键实现的1秒内按键最对的次数。

如果步骤顺序不对,欢迎指点☺

改正上面的顺序:

参考GitHub指南:

https://github.com/dvajs/dva-docs/blob/master/v1/zh-cn/tutorial/05-%E7%BB%84%E4%BB%B6%E8%AE%BE%E8%AE%A1%E5%AE%9E%E8%B7%B5.md

step1:先准备好dva基本框架;

step2:再为项目配置路由;

step3:然后设计页面维度的容器routes文件夹的内容

step4:设计component组件的内容;

step5:给 Model 添加 Reducers、Effects;

step6:将请求相关(与后台系统的交互)从models中抽离出来,单独放到  /services/中

step7:在 dva 中,配套的工具能够很方便的模拟数据,可以脱离服务器复杂的环境进行模拟的本地调试开发,mock 数据。(用roadhog,支持类似的mock方案)(dora-plugin-proxy本库已久未维护。)

step8:在 dva 中,所有的页面都是基于组件的。希望样式依附于组件,不同组件的样式相互之间不会造成污染。添加样式

step9:设计布局。

最后欢迎看官们浏览,记得点赞或评赞哦~

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值