typescript实战总结之实现一个互联网黑白墙

前言

笔者上一篇文章 TS核心知识点总结及项目实战案例分析 主要写了typescript的用法和核心知识点总结, 这篇文章将经过一个实际的前端案例来教你们如何在项目中使用typescript.javascript

你将收获

  • 如何使用umi快速搭建一个基于React + antd + typescript的前端项目
  • 中后台前端项目的目录和ts文件划分
  • 在React组件中使用typescript
  • 在工具库中使用typescript
  • 互联网黑白墙 案例分析

正文

在开始文章以前, 咱们先看一下企业黑白墙项目的演示:
(注: 本文仅针对项目剖析和学习使用, 不作任何商业用途)

该项目是一个响应式网站, 针对PC端和H5均作了必定的适配, 接下来咱们将正对该网站作一次typescript剖析.css

由上面的gif能够看出网站的信息结构图大体以下:
接下来进入咱们的正文.

1. 使用umi快速搭建一个基于React + antd + typescript的前端项目

umi是一个功能强大且开箱即用的企业级项目脚手架, 这里笔者直接采用umi来建立一个ts项目, 具体方式以下:html

   
// 1.建立项目空目录
$ mkdir ts-react && cd ts-react

// 2.建立项目
yarn create @umijs/umi-app

// 3.安装项目依赖
yarn

复制代码

用umi开发只须要简单的3个命令便可, 值得注意的是, 在执行步骤2时会在命令行出现以下交互选项:前端

主要是让咱们选择建立的项目类型的, 这里咱们选typescript和antd便可, 有关如何建立可交互的命令行工具, 在笔者的 基于react/vue生态的前端集成解决方案探索与总结 中有介绍, 感兴趣的能够学习交流.vue

通过以上的步骤咱们就初步搭建了一个支持react + typescript + antd技术栈的项目骨架.java

2. 中后台前端项目的目录和ts文件划分

咱们先看看本次研究的项目的目录划分:
   
ts-react
├─ src
│ ├─ assets
│ │ └─ yay.jpg
│ ├─ components
│ │ └─ PublicModal
│ │ ├─ index.css
│ │ ├─ index.tsx
│ │ └─ type.ts
│ ├─ layouts
│ │ ├─ __tests__
│ │ │ └─ index.test.tsx
│ │ ├─ index.css
│ │ └─ index.tsx
│ ├─ locales
│ │ └─ en-US.ts
│ ├─ models
│ ├─ pages
│ │ ├─ __tests__
│ │ │ ├─ __mocks__
│ │ │ │ └─ umi-plugin-locale.ts
│ │ │ └─ index.test.tsx
│ │ ├─ about
│ │ │ ├─ components
│ │ │ ├─ index.d.ts
│ │ │ ├─ index.less
│ │ │ └─ index.tsx
│ │ ├─ index.css
│ │ ├─ index.tsx
│ │ ├─ innerRec.tsx
│ │ └─ list.tsx
│ ├─ utils
│ │ ├─ tool.ts
│ │ └─ type.ts
│ ├─ app.ts
│ └─ global.css
├─ global.d.ts
├─ package.json
├─ readme.md
├─ tsconfig.json
└─ typings.d.ts

复制代码

咱们从外往里看, 在项目根目录下有typings.d.tsglobal.d.ts这两个文件, 前者咱们能够放置一些全局的导出模块,好比css,less, 图片的导出声明, 这样咱们就不用一个个的在页面代码里再从新声明了, 以下:node

   
// typings.d.ts
declare module '*.css';
declare module '*.less';
declare module "*.png";
declare module "*.jpeg";

复制代码

这样作咱们就能避免在页面中导入css或者图片文件时ts报错的问题了. 对于global.d.ts, 笔者建议放一些全局声明的变量, 接口等, 好比说Jquery这种第三方库的声明, window下全局变量的声明等.react

其次是src目录,咱们具体介绍一下目录的意义:webpack

  • assets 存放静态资源如图片/视频/音频等, 参与webpack的打包过程
  • layouts 存放公共布局
  • components 存放全局共同组件
  • locales 多语言配置目录
  • models dva的models文件夹, 处理redux流
  • pages 存放页面的目录, 内部能够有页面组件components, 结构相似于全局的components
  • utils 存放js工具库, 请求库等公共js文件
在了解了上面的目录和目录的含义以后, 咱们再来看看如何规划其中的ts文件.
对于组件库来讲, 其下面的一个子目录对应一个组件, 里面包含必须的样式文件, 组件tsx文件和组件自有类型文件, 这里命名为type.ts, 专门存放该组件所须要的类型和接口声明.
同理对于页面文件夹来讲, 也应具备相似的结构, 就比如上面的about页面, 包含以下结构:
  • components 该页面专有的组件目录
  • index.tsx 关于页面的主文件
  • index.less 关于页面的样式文件
  • type.ts 关于页面的类型和接口声明文件
还须要说明一点的是, 若是某个页面有私有的类型或者接口声明,咱们能够直接在文件内部去声明, 不必所有都拿到外面去定义和声明.
目录规划这块基本完成, 实际状况仍是须要根据自身项目结构来作更合理的划分, 接下来咱们看看具体的typescript在业务代码中的应用.

3. 在React组件中使用typescript

这里笔者将会拿该项目的自定义上传组件以及白名单页面做为例子, 文件上传组件笔者将采用SFC(即函数组件), 白名单页面将采用类组件, 这样能够方便你们对这两中组件开发模式下的typescript开发有个全面的认知.css3

3.1 自定义上传组件开发

自定义上传组件咱们主要应用在发布模块, 基于antd进行二次封装以便能兼容支持antd的Form模型, 以下图:
结合typescript的实现以下:
   
import React, { useState, useEffect, SFC, ReactNode } from 'react';
import { Upload, message } from 'antd';
import { LoadingOutlined, PlusOutlined } from '@ant-design/icons';
import styles from './index.less';

export interface BeforeUploadFunc {
(file:File, fileList:FileList): boolean | Promise<File>;
}

export interface SuccessBack {
(url: string): string;
}

export interface ChangeFunc {
(value: string | Array<string>): void;
}

export interface IProps {
action: string;
listType?: string;
showUploadList?: boolean;
headers?: object;
beforeUpload?: BeforeUploadFunc;
onSuccess?: SuccessBack;
withCredentials?: boolean;
text?: string | ReactNode;
imgUrl?: string;
onChange?: ChangeFunc;
value?: string;
}

const UploadCp:SFC<IProps> = (props:IProps) => {
const {
listType = 'picture-card',
showUploadList = false,
action = 'http://io.cc.com/api/files/free',
headers,
beforeUpload = handleBeforeUpload,
onSuccess,
withCredentials = true,
text = '上传封面',
imgUrl,
onChange,
value
} = props

const [loading, setLoading] = useState(false)
const [imageUrl, setImageUrl] = useState(imgUrl)

const handleChange = (info:FileList):void => {
// 一些操做
}

function handleBeforeUpload(file:File):boolean {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) {
message.error('You can only upload JPG/PNG file!');
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error('Image must smaller than 2MB!');
}
return isJpgOrPng && isLt2M;
}

useEffect(() => {
!value && setImageUrl(imgUrl)
}, [imgUrl, value])

return <Upload
name="file"
listType={listType}
className={styles.avatarUploader}
showUploadList={showUploadList}
action={action}
withCredentials={withCredentials}
headers={headers}
beforeUpload={beforeUpload}
onChange={handleChange}
>
{(value || imageUrl) ? <img src={value || imageUrl} alt="avatar" style={{ width: '100%' }} alt={text} /> : text}
</Upload>
}

export default UploadCp

复制代码

以上代码咱们使用了React的函数组件, React提供了函数组件的类型SFC, 内置了children因此咱们不用显示的再声明一次. 其余的好比函数声明, 泛型接口, 可选类型的设置等笔者在上一篇文章TS核心知识点总结及项目实战案例分析有详细介绍.不懂的能够在评论区与我交流.

3.2 白名单页面开发

在了解完函数式组件如何与typescript搭配使用以后, 咱们再来看看类组件. 咱们那拿搜索列表页做为例子来说解:

代码以下:
   
import React from 'react';
import { List, Avatar, Button, Skeleton, Tag, Modal } from 'antd';
import styles from './index.less';
import req from '@/utils/req';

export interface IProps extends Location {

}

interface List {
name: string;
img: string;
desc: string;
isLoading?: boolean;
}

interface LoadingState {
initLoading: boolean;
loading: boolean;
}

export interface IState extends LoadingState {
data: Array<List>;
list: Array<List>;
}

class LoadMoreList extends React.Component<IProps, IState> {
state:IState = {
initLoading: true,
loading: false,
data: [],
list: [],
};

componentDidMount() {
this.getData();
}

getData = () => {
req.get(`/blackwhite/get?type=${this.props.location.query.type}`).then((res:List) => {
this.setState({
initLoading: false,
data: res,
list: res.slice(0, pageNum)
});
})
};

render() {
const { initLoading, loading, list, data } = this.state;
return // 页面实现代码
}
}

export default LoadMoreList

复制代码

以上代码实现了class组件的typescript应用, 对于interface类型声明用到了继承, 固然也能够不用继承直接写类型声明, 这里主要为了学习方便. 你们也能够把公用的页面类型放到单独的type.ts目录下复用.

4. 在工具库中使用typescript

在掌握了类组件和函数组件的typescript写法以后, 咱们来讲说工具类的typescript编写方式, 这块比较简单, 笔者简单举几个经常使用工具函数, 将其改形成typescript的模式. 代码以下:

   
// utils/tool.ts
/*
* @Author: Mr Jiang.Xu
* @Date: 2019-06-06 11:23:05
* @Last Modified by: Mr Jiang.Xu
* @Last Modified time: 2019-06-29 22:33:52
*/


/**
* 识别ie--浅识别
*/

export const isIe = ():boolean => {
let explorer = window.navigator.userAgent;
//判断是否为IE浏览器
if (explorer.indexOf("MSIE") >= 0) {
return true;
}else {
return false
}
}

/**
* 颜色转换16进制转rgba
* @param {String} hex
* @param {Number} opacity
*/

export function hex2Rgba(hex:string, opacity:number):string {
if(!hex) hex = "#2c4dae";
return "rgba(" + parseInt("0x" + hex.slice(1, 3)) + "," + parseInt("0x" + hex.slice(3, 5)) + "," + parseInt("0x" + hex.slice(5, 7)) + "," + (opacity || "1") + ")";
}

// 去除html标签
export const htmlSafeStr = (str:string):string => {
return str.replace(/<[^>]+>/g, "")
}


interface params {
[propertyName: string]: string | number
}
/* 解析url参数 */
export const toParams = (params:params):string => {
if(params){
let query = [];
for(let key in params){
query.push(`${key}=${params[key]}`)
}
return `${query.join('&')}`
}else{
return ''
}
}

复制代码

以上是几个比较简单的案例, 方便你们入门和理解, 实际工做中场景会更复杂, 可是掌握了基本声明和定义模式, 基本能够解决大部分ts声明问题. 做为一名前端工程师typescript的意义很大,虽然它增长了编程的复杂度和学习成本, 可是长远来讲, 对于团队的编码规范, 问题定位, 项目维护和代码管理的角度确实有很多积极做用, 因此学习typescript刻不容缓.

最后

若是想学习更多H5游戏webpacknodegulpcss3javascriptnodeJScanvas数据可视化等前端知识和实战,欢迎在公号《趣谈前端》加入咱们的技术群一块儿学习讨论,共同探索前端的边界。

本文分享自微信公众号 - 趣谈前端(beautifulFront)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索