Open sorrycc opened 8 years ago
如果定义了多个model,model之间如何通信?
@jerexyz 通过 action。dva 封装自 redux,通信机制也等同于 redux 。
问个题外话
dva-cli 会为我们安装 keymaster 依赖并保存到 package.json 中
这个怎么做到的?
@sorrycc 源码没看到用到这个依赖
我 fork 了一份的,因为要优先用内部的 tnpm 作为安装工具。
希望文档中能够补充一下。
@ibigbug 都是很好的问题,说下我的理解。
问:onClick 的 dispatch 放在 Component 里面,keyUp 的 dispatch 写在 model 里面: 事件订阅放在两个地方?
keyUp 可以当做是事件,也可以当做是数据源。作为事件,放 Component 比较合适;作为数据源,放 model 的 subscription 则更合适。
Subscription 语义是订阅,用于订阅一个数据源,然后根据条件 dispatch 需要的 action。keyUp 的事件绑定可以把他理解成是订阅了键盘事件的数据源。
而如果把 keyUp 放在 React Component,按我理解,应该是要放在 componentWillMount 之类的生命周期里。我不喜欢这么做,因为:
问:namespace 已经声明了,还要在每个方法见面加前缀么?
这是一个非常纠结的点。
我也希望把前缀去掉,但其实并不止需要去 key 里的前缀,reducers, effects 和 subscriptions 里 dispatch/put/take 的时候也要处理。
考虑一下原因,最终决定不去前缀:
详见:https://github.com/dvajs/dva/pull/10#issuecomment-232247445
问:如果想在页面加载时 load 一组数据该怎么写?
在 subscriptions 里写,因为 component 已经是 stateless 了。可以参考:https://github.com/dvajs/dva/blob/master/examples/user-dashboard/src/models/users.js#L20-L31
问:effect 里面参数如何传递?
通过 action 传递数据,effects 里可以去到 action 对象。
比如:
const effects = {
*['a'](action) {
console.log(action.payload); // 1
},
};
dispatch({type: 'a', payload: 1});
问:将 elm-lang 的 subscription,effect 的概念直接引入,是否有什么特殊考虑? 如果和大家已经熟知的 redux 概念差异较大,是否会增加理解和学习成本?
subscription、effect 和 redux 的 reducer 并不冲突,他们解决的是不同的问题。至于为何要引入,主要是认同 elm 的概念,前端的好多库(包括 redux)都是从 elm 里借的概念;另外,用 redux 我们通常不会仅用 redux 本身,还需要各种插件的配合,而 subscription + effect + reducer 则很好的解决了我目前能想到的所有项目问题。
dva 在设计上会尽量保证 redux 用户的低迁移/使用成本,对 redux 用户友好。比如 api 设计上没有做过度封装。
subscriptions: [
function(dispatch) { // error dispatch 找不到
key('⌘+up, ctrl+up', () => { dispatch({type:'count/add'}) });
},
]
为啥 阿里都喜欢用 less 嘞
@janjon 抱歉,api 变更了,这里换成 function({ dispatch }) {}
。
有几个疑问,希望可以帮忙解答下
关于effect的归属问题
如果有一个effect需要同时修改多个model,这个effect应该归属于哪个model呢?
关于subscription的归属问题
同上,一个数据源修改多个model的情况
subscription如何禁用
假如监听了键盘事件,但在某个特定条件下想禁用这个subscription,相关逻辑是写在subscription内部,还是可以从外部禁用呢?比如当另一个model的某个字段变成true时,停止监听键盘事件,该如何实现?
关于effect的归属问题
effect 只能归属一个 model,修改其他 model 要 dispatch 该 model 的 action
subscription如何禁用
用 model 内部变量,比如:
const isEnable = true;
export default {
namespace: 'keyboard',
subscriptions: {
setup({ dispatch }) {
key('ctrl+up', () => {
if (isEnable) { /* do something with dispatch */ }
});
},
},
effects: {
*enable() {
isEnable = true;
},
*disable() {
isEnable = false;
},
}
};
@sorrycc 感谢~
webpack打包工具怎么加载字体文件就报错。该怎么配置。可以出个教程讲讲你这个webpack跟我们的不一样
blog开头贴的地址404
@Plortinus 感谢提醒,已更新。
如果存在页面上存在一个列表,列表里面每一项都对应于一个相同的model(每一项对应之前定义好的一个组件实例,类似于TodoList里面的每一个Todo,但是这个Todo也是进行了封装了,并且使用了Todo model,不是Plain Object),此时,这样的情况如何处理?
dva new myApp cd myApp npm start 后: err with request :Error: socket hang up / err with request :Error: socket hang up /favicon.ico
访问 http://localhost:8000 呢?
On Wed, Nov 9, 2016 at 9:29 AM, shenxiuqiang notifications@github.com wrote:
dva new myApp cd myApp npm start 后: err with request :Error: socket hang up / err with request :Error: socket hang up /favicon.ico
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sorrycc/blog/issues/8#issuecomment-259312229, or mute the thread https://github.com/notifications/unsubscribe-auth/AACJOGzQcFJLiYBI6_NyOvSFqstHFUn_ks5q8SIHgaJpZM4JU09Y .
@sorrycc 请问,如果我想给.normal添加一个背景图片,直接在样式表里写background:url('xxx.png');报错,请问怎么解决?
@spiroo 报什么错? https://github.com/dvajs/dva/issues 这里提 issue 吧。
最终打包想按需加载模块,不全在一个脚本里,应如何处理才好
按步骤写好了index.less,再index.js里也有写好import和className相关语句,但网页上没有出现样式。用浏览器开发工具查看页面代码,已看到css已出现,但在div里却没出现,求赐教
同上,完成后,css无效~
@yelgogogo .roadhogrc 里的配置要把CSSModules打开...
请问写mock时用post方法如何获取到body传过来的参数
module.exports = {
'POST /getList'(req, res) {
const page = qs.parse(req.query);
console.log(qs.parse(req.query))
console.log(qs.parse(req.body))
},
貌似并不会自动安装 keymaster 啊。。
如何实现嵌套的路由,像/products/edit/1234或者/products/view/1234,
{ path: '/',
component: App,
getIndexRoute(nextState, cb) {
require.ensure([], (require) => {
cb(null, {component: require('./routes/Home/')});
}, 'home');
},
childRoutes: [
{
path: 'products',
getComponent(nextState, cb) {
require.ensure([], (require) => {
cb(null, require('./routes/Product/index'));
}, 'products');
},
childRoutes: [
{
path: 'edit',
getComponent(nextState, cb){
require.ensure([], (require) => {
cb(null, require('./routes/Product/edit'));
}, 'productEdit');
},
},
],
},
],
}
我import key from 'keymaster'后,并没有自动安装啊,运行报错说Module not found: 'keymaster' in ...
监听页面加载完成之后 立刻执行 函数?或者在render之后执行函数,如何做呢?
reducers 和 effects 同名的action 方法,effects的动作会覆盖 reducers 的动作吗?我的本地测试显示只执行了 effects 里面的 add 方法
@joshle 会,先执行reducers里面的方法,然后执行effects,俩个不能同名
其中 “完成 component” 小节中 dispatch 方法是如何传递的
请问,为什么component dispatch type 'count/add' 的actioner, 只执行了effects的add,而reducers的add确没有被执行呢?
@Lifedance dva2.0还是某个版本后就不会同时触发effects和reducers了
作为后端码农,我竟然仔细了阅读了每一个issues,感谢并膜拜~
每次修改完代码后编译构建都很慢,请问如何做优化!
model 什么时候会用到哇,感觉放在页面的state 里面好像就不需要model存放state了
我们将基于 dva 完成一个简单 app,并熟悉他的所有概念。
最终效果:
这是一个测试鼠标点击速度的 App,记录 1 秒内用户能最多点几次。顶部的 Highest Record 纪录最高速度;中间的是当前速度,给予即时反馈,让用户更有参与感;下方是供点击的按钮。
看到这个需求,我们可能会想:
在代码组织部分,可能会想:
以及:
我们可以带着这些问题来看这篇文章,但不必担心有多复杂,因为全部 JavaScript 代码只有 70 多行。
安装 dva-cli
那么,首先需要安装的是 dva-cli 。dva-cli 是 dva 的命令行工具,包含 init、new、generate 等功能,目前最重要的功能是可以快速生成项目以及你所需要的代码片段。
安装完成后,可以通过
dva -v
查看版本,以及dva -h
查看帮助信息。创建新应用
安装完 dva-cli 后,我们用他来创建一个新应用,取名
myApp
。注意:
--demo
用于创建简单的 demo 级项目,正常项目初始化不加要这个参数。然后进入项目目录,并启动。
几秒之后,会看到这样的输出:
(如需关闭 server,请按 Ctrl-C.)
在浏览器里打开 http://localhost:8989/ ,正常情况下,你会看到一个 "Hello Dva" 页面。
定义 model
接到需求之后推荐的做法不是立刻编码,而是先以上帝模式做整体设计。
这个需求里,我们定义 model 如下:
namespace 是 model state 在全局 state 所用的 key,state 是默认数据。然后 state 里的 record 表示
highest record
,current
表示当前速度。完成 component
完成 Model 之后,我们来编写 Component 。推荐尽量通过 stateless functions 的方式组织 Component,在 dva 的架构里我们基本上不需要用到 state 。
注意:
import styles from './index.less';
,再通过styles.xxx
的方式声明 css classname 是基于 css-modules 的方式,后面的样式部分会用上count
和dispatch
,count
对应 model 上的 state,在后面 connect 的时候绑定,dispatch
用于分发 actiondispatch({type: 'count/add'})
表示分发了一个{type: 'count/add'}
的 action,至于什么是 action,详见:Actions@redux.js.org更新 state
更新 state 是通过 reducers 处理的,详见 Reducers@redux.js.org。
reducer 是唯一可以更新 state 的地方,这个唯一性让我们的 App 更具可预测性,所有的数据修改都有据可查。reducer 是 pure function,他接收参数 state 和 action,返回新的 state,通过语句表达即
(state, action) => newState
。这个需求里,我们需要定义两个 reducer,
count/add
和count/minus
,分别用于计数的增和减。值得注意的是count/add
时 record 的逻辑,他只在有更高的记录时才会被记录。注意:
{ ...state }
里的...
是对象扩展运算符,类似Object.extend
,详见:对象的扩展运算符add(state) {}
等同于add: function(state) {}
绑定数据
在定义了 Model 和 Component 之后,我们需要把他们连接起来。这样 Component 里就能使用 Model 里定义的数据,而 Model 中也能接收到 Component 里 dispatch 的 action 。
这个需求里只要用到
count
。这里的 connect 来自 react-redux。
定义路由
这个需求只有一个页面,路由的部分不需要修改。
注意:
history
默认是 hashHistory 并且带有_k
参数,可以换成 browserHistory,也可以通过配置去掉_k
参数。现在刷新浏览器,如果一切正常,应该能看到下面的效果:
添加样式
默认是通过 css modules 的方式来定义样式,这和普通的样式写法并没有太大区别,由于之前已经在 Component 里 hook 了 className,这里只需要在
index.less
里填入以下内容:效果如下:
异步处理
在此之前,我们所有的操作处理都是同步的,用户点击 + 按钮,数值加 1。
现在我们要开始处理异步任务,dva 通过对 model 增加 effects 属性来处理 side effect(异步任务),这是基于 redux-saga 实现的,语法为 generator。(但是,这里不需要我们理解 generator,知道用法就可以了)
在这个需求里,当用户点 + 按钮,数值加 1 之后,会额外触发一个 side effect,即延迟 1 秒之后数值 1 。
注意:
*add() {}
等同于add: function*(){}
takeEvery
),还可以选择takeLatest
,或者完全自定义take
规则刷新浏览器,正常的话,就应该已经实现了最开始需求图里的所有要求。
订阅键盘事件
在 dva 里有个叫 subscriontions 的概念,他来自于 elm。
Subscription 语义是订阅,用于订阅一个数据源,然后根据条件 dispatch 需要的 action。数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。
dva 中的 subscriptions 是和 model 绑定的。
这里我们不需要手动安装 keymaster 依赖,在我们敲入
import key from 'keymaster';
并保存的时候,dva-cli 会为我们安装keymaster
依赖并保存到package.json
中。输出如下:所有代码
index.js
构建应用
我们已在开发环境下进行了验证,现在需要部署给用户使用。敲入以下命令:
输出:
该命令成功执行后,编译产物就在 dist 目录下。
下一步
通过完成这个简单的例子,大家前面的问题是否都已经有了答案? 以及是否熟悉了 dva 包含的概念:model, router, reducers, effects, subscriptions ?
还有其他问题?可以关注 dva repo 了解更多细节。
(完)