基于最新版本 dva+react+typescript 的简单Demo
前言: 本文写给想学 ts
却无从下手, 学了 ts
想融入到 react
开发中却找不到练手demo的新手, 一个 测试鼠标和键盘点击速度的App
, 本项目虽然有很多相似版本, 但是不符合各种依赖的版本更替
,并且没有 typescript
的实现版本, 所以崩崩觉得还是有必要实现以下这个小东西, 给新手一些参考...... 具体效果如下图:
Ps: 这是一个测试鼠标点击速度的 App,记录 1 秒内用户能最多点几次。顶部的 Highest Record 纪录最高速度;中间的是当前速度,给予即时反馈,让用户更有参与感;下方是供点击的按钮。
下面和我一起完成这个小demo吧
1. 项目目录结构
2. 安装依赖
npm install -g typescript
npm install --g dva-cli@next // 最新版本 dva脚手架, 目前为 1.0.0-beta.4
npm install --save-dev @types/react @types/react-dom
npm install --save-dev ts-loader tslint-react
npm install --save antd
npm install --save keymaster // 键盘事件依赖, 后面会用到
3. 创建项目
dva new 07-dva_calculator_count_example & cd 07-dva_...
4. 配置 typescript, tslint
具体相关配置详见 崩崩 的第一篇文章: 链接描述
5. 定义项目路由
由于 dva与umi 通过 umi-plugin-dva 插件相结合, 使用起来非常方便
而我们的项目路由则约定在
./src/pages
目录下, 所以新建 example 文件夹./src/pages/example
接着测试我们的路由, 在./src/pages/example
下新建page.tsx
(./src/pages/example/page.tsx
),
6. 测试路由
在./src/pages/example
新建page.tsx
, 写入测试的react
组件
import * as React from 'react';
export interface ICounterProps {
};
const Counter: React.SFC<ICounterProps> = (): JSX.Element => {
return (
<h1>This is example page.</h1>
);
};
export default Counter;
接着, 命令行键入npm start
, 在Chrome
地址栏接上example
, 可以看到结果了, 路由成功!
7. 策划model
- 首先新建
./src/utils/delay.tsx
, 这是model中的延迟函数, 比较简单
// 定义 Promise 接口
interface IDelayPromise {
Promise: (resolve?: () => {}, reject?: () => {}) => void;
};
const delay = (time: number): IDelayPromise => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, time);
});
};
export default delay;
- 现在正式开始书写我们的
model
,model
是一个项目的灵魂,- 所以我放在
第一步
来做...... 新建./src/pages/example/models/counter.tsx
, 代码如下:
import key from 'keymaster'; // 键盘相关事件
import delay from '../../../utils/delay'; // 延迟函数
export default {
namespace: 'counter',
state: {
current: 0, // 即时显示的数字
record: 0, // 最高纪录
},
/*
回想我们的需求
1. 鼠标点击, 数字增加, 纪录一秒内最高次数
2. 一秒后, 数字递减, 变为初始数字
而 reducers 是唯一可以改变 state 的地方,
这个需求里,我们需要定义两个 reducer,
count/add 和 count/minus ,分别用于计数的增和减。要注意的是 count/add 时 record 的
逻辑,他只在有更高的记录时才会被记录, 这个用一句 三元判断即可
*/
reducers: {
add (state) {
const newCurrent = state.current + 1;
return {
...state,
record: newCurrent > state.record ? newCurrent: state.record,
current: newCurrent,
};
},
minus (state) {
const newCurrent = state.current - 1;
return {
...state,
current: newCurrent,
};
},
},
/*
接着, 由于我们要实现, 延迟并返回初始状态,
所以, 我们在 effects 中定义 addRemote 方法
注: 这里的 call, put, 是大佬封装好的方法, 直接使用即可
call: 用于调用异步处理函数
put: 调用 reducers
*/
effects: {
*addRemote ({ payload }, { call, put }) {
yield put({ type: 'add' });
yield call({ delay, 1000 }); // delay 函数,
yield put({ type: 'minus' });
},
},
/*
最后, 我们可以再加一点功能, 通过订阅键盘来获取键盘敲击次数
当然, 按键也是随意更改的, 通过 subscriptions
*/
subscriptions: {
keyboardWather ({ dispatch }) {
key('space', () => {
dispatch({ type: 'addRemote' });
});
},
},
};
OK, 到这里, 我们的model
部分已经完成, 这是一个项目最重要的部分, 对类似put
,call
,subscriptions
这些api
不太熟悉的可以看一下这里: 链接描述
8. 书写 components
- 现在开始项目组件的编写, 因为所有的状态都被存到了
dva
中,- 所以书写
简单
而美观
的函数组件(SFC)
是不二之选
1. 在 ./src/pages/example/page.tsx
,这是大的路由
组件, 里面包含类似 Header
, Nav
, Footer
, 等类似的 容器组件
, 在其内写入如下代码:
import * as React from 'react';
import Counter from './components/Counter'; // 导入 Counter 组件, 这是整个项目的容器
const styles: NodeRequire = require('./index.less'); // 整个项目的样式文件, 等会儿会一一列出
export interface IAppProps {};
const App: React.SFC<IAppProps> = (): JSX.Element => {
return (
<div className={styles['app-container']}>
<div className={styles['app-content']}>
<Counter />
</div>
</div>
);
};
export default App;
2. 新建 ./src/pages/example/index.less
,写入样式
.app-container {}
.app-content {
box-sizing: border-box;
overflow: hidden;
width: 400px;
height: 400px;
margin: 50px auto;
border: 1px solid #ccc;
box-shadow: 0 0 4px 4px #ddd;
}
.counterbox {
display: flex;
flex-direction: column;
box-sizing: border-box;
height: 100%;
padding: 10px;
}
.counterbox .counter-show {
flex: 1;
line-height: 1.5;
font-size: 20px;
color: #666;
}
.counterbox .counter-currentcount {
line-height: 100px;
font-size: 30px;
}
.counterbox .counter-button {
flex: 3;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
margin-top: 10px;
}
3. 新建 ./src/pages/example/components/Counter.tsx
, 这是整个项目的工作地
import * as React from 'react';
import { connect } from 'dva'; // 将dva中的 state 转化为组件的 props
import { Dispatch } from '../../../node_modules/redux'; // 类型限制,
const styles: NodeRequire = require('../index.less');
import CounterButton from './CounterButton'; // 这是点击的按钮, 我将它分成了一个单独的组件,
// 定义接口
export interface ICounterProps {
current: number; // 即时数字
record?: number; // 最高纪录
dispatch: Dispatch<{type: string, payload?: any}>; // tip: 这里类型定义Dispatch, vscode 会自动帮我引入 Dispatch, 很智能
};
const Counter: React.SFC<ICounterProps> = (props: ICounterProps): JSX.Element => {
const { current, record, dispatch } = props;
// 按钮点击事件, 处理函数
const handleClick: React.ReactEventHandler<HTMLButtonElement> = (event: React.MouseEvent<HTMLButtonElement>): void => {
console.log(event.currentTarget); // button Element
dispatch({
type: 'counter/addRemote', // 触发 action
});
};
return (
<div className={styles['counterbox']}>
<p className={styles['counter-show']}>The highest count is: { record }</p>
<div className={styles['counter-currentcount']}>
Current count is: { current }
</div>
<div className={styles['counter-button']}>
<CounterButton onBtnClick={handleClick} />
</div>
</div>
);
};
// 将 state => props
const mapStateToProps = (state): {current: number, record?: number} => {
const { current, record } = state.counter;
return {
current,
record,
};
};
export default connect(mapStateToProps)(Counter);
3. 书写 Button按钮
组件
import * as React from 'react';
import { Button } from 'antd';
export interface ICounterButtonProps {
onBtnClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
};
const CounterButton: React.SFC<ICounterButtonProps> = (props: ICounterButtonProps): JSX.Element => {
return (
<Button
type = "primary"
onClick = {props.onBtnClick}
>
Please Click Me
</Button>
);
};
export default CounterButton;
9. 大功告成
保存, 刷新浏览器, 应该就OK啦
崩崩结语
项目不难, 但是对于
新手
来说, 如何使用ts
进行类型定义
? ,以及懂得如何去梳理脉络
, 我觉得是至关重要的.对代码有不懂的同学可以
下方评论
,或者加扣: 1766083035, 深入讨论自学不易,
100% of the effort to change back to the tears of graduation.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。