dva/docs/GettingStarted.md

简介: 进入目录安装依赖: npm i 或者 yarn install开发: npm run dev npm install 太慢,试试yarn吧。建议用npm install yarn -g进行安装。

进入目录安装依赖:

npm i 或者 yarn install
开发:

npm run dev

npm install 太慢,试试yarn吧。建议用npm install yarn -g进行安装。

 

Container Component
Container Component 一般指的是具有监听数据行为的组件,一般来说它们的职责是绑定相关联的 model 数据,以数据容器的角色包含其它子组件,通常在项目中表现出来的类型为:Layouts、Router Components 以及普通 Containers 组件。
 
通常的书写形式为:
import React, { Component, PropTypes } from 'react';
// dva 的 connect 方法可以将组件和数据关联在一起
import { connect } from 'dva';
 
// 组件本身
const MyComponent = (props)=>{};
MyComponent.propTypes = {};
// 监听属性,建立组件和数据的映射关系
function mapStateToProps(state) {
return {...state.data};
}
 
// 关联 model
export default connect(mapStateToProps)(MyComponent);
Presentational Component
Presentational Component 的名称已经说明了它的职责,展示形组件,一般也称作:Dumb Component,它不会关联订阅 model 上的数据,而所需数据的传递则是通过 props 传递到组件内部。
 
通常的书写形式:
import React, { Component, PropTypes } from 'react';
// 组件本身
// 所需要的数据通过 Container Component 通过 props 传递下来
const MyComponent = (props)=>{}
MyComponent.propTypes = {};
// 并不会监听数据
export default MyComponent;
https://github.com/dvajs/dva-docs/blob/master/v1/zh-cn/tutorial/04-%E7%BB%84%E4%BB%B6%E8%AE%BE%E8%AE%A1%E6%96%B9%E6%B3%95.md#container-component
 
 
 
 
Route Components
Route Components 是指 ./src/routes/ 目录下的文件,他们是 ./src/router.js 里匹配的 Component。
通过 connect 绑定数据
比如:
import { connect } from 'dva';
function App() {}
function mapStateToProps(state, ownProps) {
return {
users: state.users,
};
}
export default connect(mapStateToProps)(App);
然后在 App 里就有了 dispatch 和 users 两个属性。
Injected Props (e.g. location)
Route Component 会有额外的 props 用以获取路由信息。
location
params
children
 

 

 

五、Generator函数的概念
Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。

function* gen(x){
var y = yield x + 2;
return y;
}
上面代码就是一个 Generator 函数。它不同于普通函数,是可以暂停执行的,所以函数名之前要加星号,以示区别。
整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用 yield 语句注明。Generator 函数的执行方法如下。

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 函数是否执行完毕,即是否还有下一个阶段。

http://www.ruanyifeng.com/blog/2015/04/generator.html

 

变量声明

const 和 let

不要用 var,而是用 const 和 let,分别表示常量和变量。不同于 var 的函数作用域,const 和 let 都是块级作用域。

const DELAY = 1000;

let count = 0; count = count + 1;

箭头函数

函数的快捷写法,不需要通过 function 关键字创建函数,并且还可以省略 return 关键字。

同时,箭头函数还会继承当前上下文的 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 用于引入模块,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

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

 

 

Getting Started

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.

  1. How to create app?
  2. How to organize code after created app?
  3. How to build, deploy and publish after development?

And somethings about code organization.

  1. How to write Component?
  2. How to write CSS?
  3. How to write Model?
  4. How to connect Model and Component?
  5. How to update State after user interaction?
  6. How to handle async logic?
  7. How to config router?

And.

  1. If I want to use localStorage to save Highest Record, what to do?
  2. If we want to support keyboard click rate test, what to do?

We can takes these questions to read this article. But don't worry, all the code we need is about 70 lines.

Install dva-cli

dva-cli is the cli tool for dva, include initnew.

$ npm install -g dva-cli

After installed, you can check version with dva -v, and view help info with dva -h.

Create new App

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".

Define models

When get the task, you should not write code immediately. But recommend to do state design in god mode.

  1. design models
  2. design components
  3. connect models and components

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.

Write components

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:

  1. import styles from './index.less';, and then use styles.xxx to define css classname is the solution of css-modules
  2. passes in two props,count and dispatchcount is the state in model, bind with connect. And dispatch is used to trigger an action
  3. dispatch({type: 'count/add'}) means trigger an action {type: 'count/add'}. View Actions@redux.js.org on what's an action.

Update state

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 and minus don't need to add namespace prefix in count 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:

  1. Confused with ... operator? It's used for extend Object, similar to Object.extend
  2. add(state) {} is equal to add: function(state) {}

Bind Data

Remember count and dispatch 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

Define Router

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:

  1. 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.

Add StyleSheet

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.

Async Logic

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:

  1. *add() {} is equal to add: function*(){}
  2. 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 selecttakeforkcancel, and so on. View more on redux-saga documatation

Refresh you browser, if success, it should have all the effects of beginning gif.

Subscribe Keyboard Event

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.

All Code Together

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); }); }

Build

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.

What's Next

After complete this app, do you have answer of all the questions in the beginning? Do you understand this concepts in dva, like modelrouterreducerseffects and subscriptions ?

Next, you can view dva official library for more information.

https://github.com/dvajs/dva/blob/master/docs/GettingStarted.md

 

相关文章
|
运维 网络安全
运维实用神器-clustershell(实现多台服务器同时控制)
运维实用神器-clustershell(实现多台服务器同时控制)
771 0
运维实用神器-clustershell(实现多台服务器同时控制)
|
移动开发 程序员 Android开发
寒冬之下,移动开发没人要了, iOS 开发者该 何去何从?
前言: 作者 | 梅梅    文章来源 CSDN 对于移动互联网而言,2018 年像是球场上的一声裁判哨。哨声响起,高潮迭起的上半场结束。本该再创辉煌的下半场,还没开赛却被告之:规则改变、场地收缩、教练下课、冷板凳无限加长。
|
8月前
|
机器学习/深度学习 人工智能 大数据
销售易CRM:技术革新助力客户关系管理智能化
销售易CRM是国内领先的客户关系管理系统,通过人工智能、大数据、云计算和低代码开发等技术,助力企业实现智能化转型。其AI与机器学习功能可深度挖掘客户数据价值,提供精准商机洞察;大数据分析赋能企业决策智能化,提升预测能力和运营效率;基于云计算的移动办公功能打破协作边界,提高团队效率;低代码平台支持快速定制化开发,灵活响应业务需求。销售易CRM以技术创新和服务升级,推动企业在数字化浪潮中取得竞争优势,实现可持续发展。
|
机器学习/深度学习 人工智能 自然语言处理
软件测试中的人工智能革命:现状与未来展望
【10月更文挑战第2天】 本文深入探讨了人工智能在软件测试领域的应用现状、面临的挑战以及未来的发展方向。通过分析AI技术如何提高测试效率、准确性和自动化水平,文章揭示了AI在改变传统软件测试模式中的关键作用。同时,指出了当前AI测试工具的局限性,并对未来AI与软件测试深度融合的前景进行了展望,强调了技术创新对于提升软件质量的重要性。
436 4
|
8月前
|
存储 Android开发 索引
鸿蒙特效教程10-卡片展开/收起效果
本教程将详细讲解如何在HarmonyOS中实现卡片的展开/收起效果,通过这个实例,你将掌握ArkUI中状态管理和动画实现的核心技巧。
318 6
鸿蒙特效教程10-卡片展开/收起效果
|
数据库 数据安全/隐私保护
共享锁和排他锁在实际应用中的优缺点
【10月更文挑战第16天】共享锁和排他锁是多进程和多线程环境中常用的同步机制,它们各自具有优点和缺点。在实际应用中,需要根据具体的场景和需求选择合适的锁类型。在选择锁时,需要考虑读写比例、数据一致性要求、系统性能、死锁风险等因素,并结合实际情况进行优化和调整。通过合理使用锁,可以提高系统的并发性、数据一致性和性能。
|
数据可视化 项目管理
什么是关键工作?如何识别和管理项目中的关键工作?
项目管理中的关键工作是指那些一旦延迟便会影响整个项目进度的任务。本文从实战角度探讨了关键工作的定义、识别方法及高效管理策略,强调了资源优先配置、预警机制、应急方案及频繁沟通的重要性,并介绍了几款有助于关键任务管理的项目管理工具。
667 1
|
应用服务中间件 Linux nginx
Centos7安装Nginx详细安装步骤
Centos7安装Nginx详细安装步骤
1064 1
Centos7安装Nginx详细安装步骤
|
存储 C语言 Perl
为什么要使用交叉引用?西门子S7-200 SMART的交叉引用表、字节使用表、位使用表如何操作?
本篇我们来学习西门子S7-200 SMART的交叉引用表、字节使用表、位使用表如何操作。首先我们先来看为什么要使用交叉引用:通过交叉引用窗口可以查看程序中参数赋值和存储器使用情况,避免重复赋值。
为什么要使用交叉引用?西门子S7-200 SMART的交叉引用表、字节使用表、位使用表如何操作?
|
存储 缓存 安全
基于GitHub/七牛云 + PicGo 搭建属于Typora的图床
基于GitHub/七牛云 + PicGo 搭建属于Typora的图床
基于GitHub/七牛云 + PicGo 搭建属于Typora的图床