React+ Dva + Atd入门知识(必读)——框架详解

学react是会有很多疑惑,

  • 不知道需要用到ES6的哪些知识点;
  • react component 有三种写法,都要掌握还是
  • ....

JavaScript语言

变量声明

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

const DELAY = 1000;

let  count = 0;
count = count + 1;

模板字符串
模板字符串提供了另一种做字符串组合的方法。

const user = 'world';
console.log(`hello ${user}`);    //hello world

//多行
const content = `
    Hello ${firstName},
    Thanks for ordering ${qty} tickets to ${event}.
`;

默认参数

function  logActivity(activity = 'skiing') {
    console.log(activity);
}

logAcitity();    //skiing

箭头函数
函数的快捷写法,不需要通过function关键字创建函数,并且还可以省略return关键字。同时,箭头函数还会继承当前四昂下文的this关键字。
比如:

[1, 2, 3].map(x => x + 1)

等同于:

[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 './service/github';

//导出默认
export default App;
//部分导出, 需import { App } from './file'; 引入
export class App extend Component {};

ES6对象和数组
析构赋值

析构赋值让我们从Object或Array里取出部分数据存为变量

//对象
const user = { name: 'guanguan', age: 2};
const { name, age } = user;
console.log(`${name} : ${age} `);        //guanguan : 2

//数组
const arr = [1, 2];
const [foo, bar] = arr;
console.log(foo);

我们也可以析构传入的函数参数

const add = (state, { payload } => {
    return state.concat(payload);
});

析构时还可以配alias(别名),让代码更具有语义

const add = (state, { payload: todo }) => {
    return state.concat(todo);
};

对象字面量改进

这是析构的反向操作,用于重新组织一个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 name = 'duoduo';
const age = 8;

const user = { name, age };    //{ name: 'duoduo', age: 8};

 也可用户获取数组的部分项

const arr = ['a', 'b', 'c'];
const [first, ...rest] = arr;
rest;    //['b', 'c']

//with ignore
const [first, , ...rest] = arr;
rest;    //['c']

 代替apply

function foo(x, y, z){}
const args = [1, 2, 3];

//下面两句效果相同
foo.apply(null, args);
foo(...args);

对于Object而言,用于组合成新的Object

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}

 promise

promise用于更优雅地处理异步请求。比如发起异步请求:

fetch('/api/todos')
    .then(res => res.json())
    .then(data => ({ data }))
    .catch(err => ({ err }));
 

定义Promise

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

delay(1000).then(_ => {
    console.log('executed');
});

Generators
dva的effects是通过generator组织的。generator返回的是迭代器,通过yield关键字实现暂停功能
这是一个典型的dva effect,通过yield把异步逻辑通过同步的方式组织起来

app.model({
    namespace: 'todos',
    effects: {
        *addRemote({ payload: todo }, { put, call }) {
            yield call(addTodo, todo);
            yield put({ type: 'add', payload: todo });
        },    
    },
});

React Component

Stateless Functional Component

react Compnent 有三种定义方式,分别是React.createClass, class和Stateless Functional Component。推荐使用最后一种,保持简洁和无状态,这是函数,不是Object,没有this作用域。

比入定义App Component

function App(props){
    function handleClick(){
        props.dispatch({ type: 'app/create' })
    }
    return <div onClick={handleClick}>${props.name}</div>
}

JSX

Component嵌套

类似HTML,JSX里可以给组件添加子组件

<App>
    <Header />
    <MainContent />
    <Footer />
</App>

className

class是保留字,所以添加样式时,需用className替代class

<h1 className = "fancy">hello dva</h1>

JavaScript表达式

javascript表达式需要用{}括起来,会执行并返回结果。

<h1>{this.props.title}</h1>

Mapping Arrays to JSX

可以把数组映射为JSX元素列表

<ul>
    { this.props.todos.map((todo, i) => <li key={i}>{todo}</li>)}
</ul>

注释

尽量别用“//”做单行注释

<h1>
    {/* multiline comment */}
    {/*
        multi
        line
        comment
    */}
    {
        //single line
    }
    Hello
</h1>

Spread Atrribute

这是JSX从ECMAScript6借鉴过来的很有用的特性,用于扩充组件props

别如:

const attrs = {
    href: 'http://example.org',
    target: '_blank',    
};
<a {...attrs}>Hello</a>

等同于

const attrs = {
    href: 'http://example.org',
    target: '_blank',    
}
<a href = {attrs.href} target = {target.target}>Hello</a>

Props

数据处理在React中是非常重要的概念之一,分别可以通过props,state和context来处理数据。而在dva中你只需要关心props

proptypes

JavaScript是弱类型语言,所以请尽量声明propTypes对props进行校验,以减少不必要的问题

function App(props) {
    return <div>{props.name}</div>;
}
App.propTypes = {
    name: React.PropTypes.string.isRequired,
};

内置的proptype有:

  • PropTypes.array
  • PropTypes.bool
  • PropTypes.func
  • PropTypes.number
  • PropTypes.object
  • PropTypes.string

往下传数据:

往上传数据:

CSS Modules

理解css Modules

一张图理解CSS Modules的工作原理

buttom class在构建之后会被重命名为ProductList_button_1FU0u.button是本地名称。而 produc'tList_button_1FU0Ou是global name。你可以用简短的描述性名字,而不需要关心命名冲突问题。

然后你要做的全部事情就是在css/less文件里写.button{...},并在组件里通过style.button来引用他。

定义全局CSS

CSS Modules默认是局部作用域的,想要声明一个全局规则,可用:global语法

.title {
    color: red;
}

:global(.title) {
    color: green;
}

然后在引用的时候:

<App className={styles.title} />         //red
<App className="title" /}                //green

className Package

在一些复杂的场景中,一个元素可能对应多个classname,而每个className又基于一些条件来决定是否出现。这时,classname这个库就非常有用。

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

Reducer

reducer是一个函数,接受state和action,返回老的或新的state。即:(state, action)=> state.

增删改

以todos为例。

app.model({
    namespace: 'todos',
    state: [],
    reducer: {
        add(state, { payload: todo}){
            return state.concat(todo);
        },
        remove(state, { payload: id }) {
            return state.filter(todo => todo.id != id)
        },
        update(state, { payload: updateTodo }) {
            return state.map(todo => {
                if(todo.id === updatedTodo.id) {
                    return {...todo, ...updatedTodo};
                } else {
                    return todo;
                }
            });
        },
    },
});

深层嵌套的例子,应尽量避免。

Effect

app.model({
    namespace: 'todos',
    effects: {
        *addRemote({ payload: todo },{ put, call }){
            yield call(addTodo, todo);
            yield put({ type: 'add', payload: todo });
        },
    },
});

effects

put

用于触发action。

yield put({ type: 'todo/add', payload: 'Learn Dva' });

call

用于调用异步逻辑,支持promise

const result = yield call(fetch, '/todos');

select

用于从state里获取数据

const todos = yield select(state => state.todos);

错误处理

全局错误处理

dva里,effects和subscriptions的抛错全部会走onError hook,所以用在onError里统一处理错误。

const app = dva({
    onError(e, dispatch) {
        console.log(e.message);
    },
});

然后effects里的抛错和reject的promise就都会被捕获到了。

本地错误处理

如果需要对某些effects的错误进行特殊处理,需要在effect内部加 try , catch

app.model({
    effect: {
        *addRemote() {
            try{
                //your code here
            } catch(e) {
                console.log(e.message);
            }
        },
    },
});

异步请求

异步请求基于whatwg-fetch

GET和POST

import request from '../util/request';

//GET
request('/api/todos');

//POST
request('/api/todos', {
    method: 'POST',
    body: JSON.stringify({a: 1}),
});

统一错误处理

假如约定后台返回一下格式时, 做统一的错误处理

{
    status: 'error',
    message: '',
}

编辑utils/request.js,加入以下中间件。

function parseErrorMessage({ data }) {
    const { status, message } = data;
    if(status === 'error' ){
        throw new Error(message);
    }
    return { data };
}

然后,这类错误就会走到onError hook里。

Subscription

subscription是订阅,用于订阅一个数据源,然后根据需要dispatch相应的action。数据源是可以是当前的时间/服务器的websocket连接,keyboard输入,geolocation变化,history路由变化等。格式为({ dispatch, history })=> unsubscribe

异步数据初始化

比如:当用户进入/users页面时,触发action user/fetch加载用户数据。

app.model({
    subscriptions: {
        setup({ dispatch, history })=>{
            history.listen(({ pathname }) => {
                if(pathname === '/users') {
                   dispatch({
                        type: 'users/fetch',
                    });
                }
            });
        },
    },
});

path-to-regexp Package

如果url规则比较复杂,比如/users/:userId/search,那么匹配和userId的获取都会比较麻烦这里推荐用path-to-regexp简化这部分逻辑

import pathToRegexp from 'path-to-regexp';

// in subscription
const match = pathToRegexp('/users/:userId/search').exec(pathname);
if (match) {
  const userId = match[1];
  // dispatch action with userId
}

Router

config with JSX Element (router.js)

<Route path = "/" component = {App} />
    <Route path = "accounts" component = {Accounts} />
    <Route path = "statements" compnent = {Statements} />
</Route>

Router Components

Route Components 是指 ./src/routes/目录下的文件,他们是./src/router.js里匹配

通过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 Compotent 会有额外的props用以获得路由信息

  • location
  • params

基于action进行页面跳转

import { routerRedux } from 'dva/router';

//Inside Effects
yield put(routerRedux.push('/logout'));

//Out Effects
dispatch(routerRedux.push('/logout'));

//with query
routerRedux.push({
    pathname: '/logout',
    query: {
        page: 2,
    },
});

除push(location) 外还有更多方法

dva配置

Redux Middleware

比如要添加redux-logger中间件:

import createLogger from 'redux-logger';

const app = dva({
    onAction: createLogger(),
});

注:onAction支持数组,可同时传入多个中间件

history

切换history为browserHistory

import { browserHistory } from 'dva/router';
const app = dva({
    history: browserHistory,
});

去除hashHistory下的_k查询参数

import { useRouterHistory } from 'dva/router';
import { createHashHistory } from 'history';
const app = dva({
    history: userRouterHistory(createHashHistory)({querykey: false}),
});

工具

通过dva-cli创建项目

先安装dva-cli

$ npm install dva-cli -g

然后创建项目

$ dva new myapp

最后,进入目录并启动

$ cd myapp
$ npm start

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值