进入目录安装依赖:css
npm i 或者 yarn install
开发:html
npm run dev node
npm install 太慢,试试yarn吧。建议用npm install yarn -g进行安装。react
5、Generator函数的概念
Generator 函数是协程在 ES6 的实现,最大特色就是能够交出函数的执行权(即暂停执行)。webpack
function* gen(x){
var y = yield x + 2;
return y;
}
上面代码就是一个 Generator 函数。它不一样于普通函数,是能够暂停执行的,因此函数名以前要加星号,以示区别。
整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操做须要暂停的地方,都用 yield 语句注明。Generator 函数的执行方法以下。git
var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }
上面代码中,调用 Generator 函数,会返回一个内部指针(即遍历器 )g 。这是 Generator 函数不一样于普通函数的另外一个地方,即执行它不会返回结果,返回的是指针对象。调用指针 g 的 next 方法,会移动内部指针(即执行异步任务的第一段),指向第一个遇到的 yield 语句,上例是执行到 x + 2 为止。
换言之,next 方法的做用是分阶段执行 Generator 函数。每次调用 next 方法,会返回一个对象,表示当前阶段的信息( value 属性和 done 属性)。value 属性是 yield 语句后面表达式的值,表示当前阶段的值;done 属性是一个布尔值,表示 Generator 函数是否执行完毕,便是否还有下一个阶段。github
http://www.ruanyifeng.com/blog/2015/04/generator.htmlweb
不要用 var
,而是用 const
和 let
,分别表示常量和变量。不一样于 var
的函数做用域,const
和 let
都是块级做用域。shell
const DELAY = 1000; let count = 0; count = count + 1;
函数的快捷写法,不须要经过 function
关键字建立函数,而且还能够省略 return
关键字。npm
同时,箭头函数还会继承当前上下文的 this
关键字。
好比:
[1, 2, 3].map(x => x + 1); // [2, 3, 4]
等同于:
[1, 2, 3].map((function(x) { return x + 1; }).bind(this));
import
用于引入模块,export
用于导出模块。
好比:
// 引入所有 import dva from 'dva'; // 引入部分 import { connect } from 'dva'; import { Link, Route } from 'dva/router'; // 引入所有并做为 github 对象 import * as github from './services/github'; // 导出默认 export default App; // 部分导出,需 import { App } from './file'; 引入 export class App extend Component {};
这是析构的反向操做,用于从新组织一个 Object 。
const name = 'duoduo'; const age = 8; const user = { name, age }; // { name: 'duoduo', age: 8 }
定义对象方法时,还能够省去 function
关键字。
app.model({ reducers: { add() {} // 等同于 add: function() {} }, effects: { *addRemote() {} // 等同于 addRemote: function*() {} }, });
Spread Operator 即 3 个点 ...
,有几种不一样的使用方法。
可用于组装数组。
const todos = ['Learn dva']; [...todos, 'Learn antd']; // ['Learn dva', 'Learn antd']
也可用于获取数组的部分项。
const arr = ['a', 'b', 'c']; const [first, ...rest] = arr; rest; // ['b', 'c'] // With ignore const [first, , ...rest] = arr; rest; // ['c']
还可收集函数参数为数组。
function directions(first, ...rest) { console.log(rest); } directions('a', 'b', 'c'); // ['b', 'c'];
代替 apply。
function foo(x, y, z) {} const args = [1,2,3]; // 下面两句效果相同 foo.apply(null, args); foo(...args);
区分 apply和call就一句话: foo.call(this,arg1,arg2,arg3)==foo.apply(thsi,arguments)==this.foo(arg1,arg2,arg3); 二者区别 call 和 apply都属于Function.prototype的一个方法,它是Javascript引擎内在实现的,
由于属于Function.prototype对象的实例,也就是每一个方法都有call,apply属性,这两个方法很容易混淆,
由于他们的做用同样,只是使用方式不一样(传递的参数不一样)。
不一样点分析 咱们针对上面的foo.call(this,arg1,arg2,arg3)展开分析:<br/> foo 是一个方法,this是方法执行时上下文相关对象,永远指向当前代码所处在的对象中。arg1,arg2,arg3是传给foo方法的参数。 function A(){ var message="a"; return{ getMessage:function(){return this.message} } } function B(){ var message="b"; return{ setMessage:function(message){this.message=message} } } var a=new A(); var b=new B(); b.setMessage.call(a,"a的消息"); alert(a.getMessage()); //这里将输出“a的消息” 这就是动态语言Javascript call的威力所在,简直是无中生有,对象的方法能够任意指派,而对象自己是没有这个方法的。注意,指派通俗地讲就是把方法借给另外一个对象调用,原理上时方法执行时上下文的对象改变了。 因此,b.setMessage.call(a,"a 的消息");就等于用a作执行时上下文对象调用b对象的setMessage()方法,而这个过程与b一点关系都没有。做用等效于a.setMessage() 下面说一下apply的使用场景 function print(a,b,c,d){ console.log(a+b+c+d); } function example(a,b,c,d){ //用call方式借用print,参数显式打散传递 print.call(this,a,b,c,d); //apply方式借用print,参数做为一个数组传递 print.apply(this,arguments); //或者这样写 print.apply(this,[a,b,c,d]); } example('我','不','开','心'); 从上面的例子咱们发现,call和apply方法除了第一个参数相同,call方法的其余参数一次传递给借用的方法作参数,而apply就两个参数,第二个参数是借用方法的参数组成的数组。
总结一下,当参数明确时可用call,当参数不明确时用apply并结合arguments
https://github.com/Jafeney/myBlog/blob/master/JavaScript%E6%A0%B8%E5%BF%83%E2%80%94%E2%80%94call%E5%92%8Capply%E7%9A%84%E5%8C%BA%E5%88%AB.md
对于 Object 而言,用于组合成新的 Object 。(ES2017 stage-2 proposal)
const foo = { a: 1, b: 2, }; const bar = { b: 3, c: 2, }; const d = 4; const ret = { ...foo, ...bar, d }; // { a:1, b:3, c:2, d:4 }
此外,在 JSX 中 Spread Operator 还可用于扩展 props,详见 Spread Attributes。
定义全局 CSS
CSS Modules 默认是局部做用域的,想要声明一个全局规则,可用 :global 语法。
好比:
.title {
color: red;
}
:global(.title) {
color: green;
}
而后在引用的时候:
<App className={styles.title} /> // red
<App className="title" /> // green
classnames Package
在一些复杂的场景中,一个元素可能对应多个 className,而每一个 className 又基于一些条件来决定是否出现。这时,classnames 这个库就很是有用。
import classnames from 'classnames';
const App = (props) => {
const cls = classnames({
btn: true,
btnLarge: props.type === 'submit',
btnSmall: props.type === 'edit',
});
return <div className={ cls } />;
}
这样,传入不一样的 type 给 App 组件,就会返回不一样的 className 组合:
<App type="submit" /> // btn btnLarge
<App type="edit" /> // btn btnSmall
https://github.com/dvajs/dva-knowledgemap
不少同窗在搭建项目的时候,每每忽略项目结构的划分,实际上合理的项目划分每每可以提供规范的项目搭建思路。 在 dva 架构的项目中,咱们推荐的目录基本结构为:
.
├── /mock/ # 数据mock的接口文件 ├── /src/ # 项目源码目录 │ ├── /components/ # 项目组件 │ ├── /routes/ # 路由组件(页面维度) │ ├── /models/ # 数据模型 │ ├── /services/ # 数据接口 │ ├── /utils/ # 工具函数 │ ├── route.js # 路由配置 │ ├── index.js # 入口文件 │ ├── index.less │ └── index.html ├── package.json # 项目信息 └── proxy.config.js # 数据mock配置
你们能够发现,dva 将项目中全部可能出现的功能性都映射到了对应的目录当中,而且对整个项目的功能从目录上也有了清晰的体现,因此咱们建议你的项目也按照这个目录来组织文件,若是你是用的是 dva-cli 工具生成的 dva 的脚手架模板,那么会帮你按照这样目录生成好。
https://github.com/dvajs/dva-docs/blob/master/v1/zh-cn/tutorial/02-%E5%88%92%E5%88%86%E7%BB%93%E6%9E%84.md
This article will lead you to create dva app quickly, and learn all new concepts.
Final App.
This app is used to test click speed, by collecting click count within 1 second.
Some questions you may ask.
And somethings about code organization.
And.
We can takes these questions to read this article. But don't worry, all the code we need is about 70 lines.
dva-cli is the cli tool for dva, include init
, new
.
$ npm install -g dva-cli
After installed, you can check version with dva -v
, and view help info with dva -h
.
After installed dva-cli, we can create a new app with it, called myapp
.
$ dva new myapp --demo
Notice: --demo
option is only used for creating demo level app. If you want to create normal project, don't add this option.
cd
myapp, and start it.
$ cd myapp
$ npm start
After a few seconds, you will get these outputs:
proxy: listened on 8989
livereload: listening on 35729
📦 173/173 build modules
webpack: bundle build is now finished.
(Press Ctrl-C
if you want to close server)
Open http://localhost:8989/ in browser. If success, you will see a page with "Hello Dva".
When get the task, you should not write code immediately. But recommend to do state design in god mode
.
With this task, we define model in this:
app.model({ namespace: 'count', state: { record : 0, current: 0, }, });
namespace
is the key where model state is in global state. state
is the default data for model. Then record presents highest record
,and current
presents current click speed.
After designed model, we start to write component. Recommend to organize Component with stateless functions. Because we don't need state almost in dva architecture.
import styles from './index.less'; 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> ); };
Notice:
import styles from './index.less';
, and then use styles.xxx
to define css classname is the solution of css-modulescount
and dispatch
. count
is the state in model, bind with connect. And dispatch
is used to trigger an actiondispatch({type: 'count/add'})
means trigger an action {type: 'count/add'}
. View Actions@redux.js.org on what's an action.reducer
is the only one which can update state, this make our app stable, all data modification is traceable. reducer
is pure function, accept arguments state
and action
, return new state
.
(state, action) => newState
We need two reducers, add
and minus
. Please notice add
will only be recorded if it's highest.
Notice:
add
andminus
don't need to add namespace prefix incount
model. But if outside the model, action must prefix namespace separated with/
. e.g.count/add
.
app.model({
namespace: 'count',
state: {
record: 0,
current: 0,
},
+ 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}; + }, + }, });
Notice:
...
operator? It's used for extend Object, similar to Object.extend
add(state) {}
is equal to add: function(state) {}
Remember
count
anddispatch
props used in the Component before? Where are them come from?
After define Model and Component, we need to connect them together. After connect, Component can use the data from Model, and Model can receive actions dispatched from Component.
In this task, we only need to bind count
.
function mapStateToProps(state) { return { count: state.count }; } const HomePage = connect(mapStateToProps)(CountApp);
Notice: connect
is from react-redux。
Which Component should be rendered after receiving a url? It's defined by router.
This app has only one page, so we don't need to modify the router part.
app.router(({history}) => <Router history={history}> <Route path="/" component={HomePage} /> </Router> );
Notice:
history
is default hashHistory with _k
params. It can be changed to browserHistory, or remove _k
params with extra configuration.Refresh page in browser, if success, you will see page below.
We define stylesheet in css modules
, which doesn't have many differences from normal css. Because we have already hooked className in Component, at this moment, we only need to replace index.less
with follow content:
.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; } }
Result.
Prior to this, all of our operations are synchronous. When clicking on the + button, the value is incremented by 1.
Now we have to dealing with async logic. dva process side effect( async logic ) with effects on model, which is executed based on redux-saga, with generator syntax.
In this app, when user clicked the + button, value will plus 1, and trigger a side effect, that is, minus 1 after 1 second.
app.model({
namespace: 'count',
+ effects: { + *add(action, { call, put }) { + yield call(delay, 1000); + yield put({ type: 'minus' }); + }, + }, ... +function delay(timeout){ + return new Promise(resolve => { + setTimeout(resolve, timeout); + }); +}
Notice:
*add() {}
is equal to add: function*(){}
call
and put
are effect commands from redux-saga. call
is for async logic, and put
is for dispatching actions. Besides, there are commands like select
, take
, fork
, cancel
, and so on. View more on redux-saga documatationRefresh you browser, if success, it should have all the effects of beginning gif.
After implemented mouse click speed test, how to implement keyboard click speed test?
There is a concept called Subscription
from dva, which is from elm 0.17.
Subscription is used for subscribe a data source, then dispatch action if needed. The data source could be current time, websocket connection from server, keyboard input, geolocation change, history router change, and so on.
Subscription is in model.
+import key from 'keymaster'; ... app.model({ namespace: 'count', + subscriptions: { + keyboardWatcher({ dispatch }) { + key('⌘+up, ctrl+up', () => { dispatch({type:'add'}) }); + }, + }, });
Here, we don't need to install keymaster
dependency manually. When we write import key from 'keymaster';
and save, dva-cli will install keymaster
and save to package.json
. Output like this:
use npm: tnpm
Installing `keymaster`... [keymaster@*] installed at node_modules/.npminstall/keymaster/1.6.2/keymaster (1 packages, use 745ms, speed 24.06kB/s, json 2.98kB, tarball 15.08kB) All packages installed (1 packages installed from npm registry, use 755ms, speed 23.93kB/s, json 1(2.98kB), tarball 15.08kB) 📦 2/2 build modules webpack: bundle build is now finished.
index.js
import dva, { connect } from 'dva'; import { Router, Route } from 'dva/router'; import React from 'react'; import styles from './index.less'; import key from 'keymaster'; const app = dva(); app.model({ namespace: 'count', state: { record: 0, current: 0, }, 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: { *add(action, { call, put }) { yield call(delay, 1000); yield put({ type: 'minus' }); }, }, subscriptions: { keyboardWatcher({ dispatch }) { key('⌘+up, ctrl+up', () => { dispatch({type:'add'}) }); }, }, }); 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> ); }; function mapStateToProps(state) { return { count: state.count }; } const HomePage = connect(mapStateToProps)(CountApp); app.router(({history}) => <Router history={history}> <Route path="/" component={HomePage} /> </Router> ); app.start('#root'); // --------- // Helpers function delay(timeout){ return new Promise(resolve => { setTimeout(resolve, timeout); }); }
Now that we've written our application and verified that it works in development, it's time to get it ready to deploy to our users. To do so, run the following command:
$ npm run build
Output.
> @ build /private/tmp/dva-quickstart
> atool-build Child Time: 6891ms Asset Size Chunks Chunk Names common.js 1.18 kB 0 [emitted] common index.js 281 kB 1, 0 [emitted] index index.css 353 bytes 1, 0 [emitted] index
After build success, you can find compiled files in dist
directory.
After complete this app, do you have answer of all the questions in the beginning? Do you understand this concepts in dva, like model
, router
, reducers
, effects
and subscriptions
?
Next, you can view dva official library for more information.
https://github.com/dvajs/dva/blob/master/docs/GettingStarted.md