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

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

网络异常,图片无法展示
|


前言


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


你将收获


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


正文


在开始文章之前, 我们先看一下企业黑白墙项目的演示:

网络异常,图片无法展示
|


(注: 本文仅针对项目剖析和学习使用, 不做任何商业用途)


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


由上面的gif可以看出网站的信息结构图大致如下:


网络异常,图片无法展示
|


接下来进入我们的正文.


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



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


// 1.创建项目空目录
$ mkdir ts-react && cd ts-react
// 2.创建项目
yarn create @umijs/umi-app
// 3.安装项目依赖
yarn

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


网络异常,图片无法展示
|


网络异常,图片无法展示
|


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


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


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, 图片的导出声明, 这样我们就不用一个个的在页面代码里再重新声明了, 如下:

// typings.d.tsdeclaremodule'*.css';
declaremodule'*.less';
declaremodule"*.png";
declaremodule"*.jpeg";

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


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


  • 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开发有个全面的认知.


3.1 自定义上传组件开发


自定义上传组件我们主要应用在发布模块, 基于antd进行二次封装以便能兼容支持antd的Form模型, 如下图:


网络异常,图片无法展示
|


结合typescript的实现如下:


importReact, { useState, useEffect, SFC, ReactNode } from'react';
import { Upload, message } from'antd';
import { LoadingOutlined, PlusOutlined } from'@ant-design/icons';
importstylesfrom'./index.less';
exportinterfaceBeforeUploadFunc {
  (file:File, fileList:FileList): boolean|Promise<File>;
}
exportinterfaceSuccessBack {
  (url: string): string;
}
exportinterfaceChangeFunc {
  (value: string|Array<string>): void;
}
exportinterfaceIProps {
action: string;
listType?: string;
showUploadList?: boolean;
headers?: object;
beforeUpload?: BeforeUploadFunc;
onSuccess?: SuccessBack;
withCredentials?: boolean;
text?: string|ReactNode;
imgUrl?: string;
onChange?: ChangeFunc;
value?: string;
}
constUploadCp: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    } =propsconst [loading, setLoading] =useState(false)
const [imageUrl, setImageUrl] =useState(imgUrl)
consthandleChange= (info:FileList):void=> {
// 一些操作  }
functionhandleBeforeUpload(file:File):boolean {
constisJpgOrPng=file.type==='image/jpeg'||file.type==='image/png';
if (!isJpgOrPng) {
message.error('You can only upload JPG/PNG file!');
    }
constisLt2M=file.size/1024/1024<2;
if (!isLt2M) {
message.error('Image must smaller than 2MB!');
    }
returnisJpgOrPng&&isLt2M;
  }
useEffect(() => {
!value&&setImageUrl(imgUrl)
  }, [imgUrl, value])
return<Uploadname="file"listType={listType}
className={styles.avatarUploader}
showUploadList={showUploadList}
action={action}
withCredentials={withCredentials}
headers={headers}
beforeUpload={beforeUpload}
onChange={handleChange}
>          {(value||imageUrl) ?<imgsrc={value||imageUrl} alt="avatar"style={{ width: '100%' }} alt={text} /> : text}
</Upload>}
exportdefaultUploadCp

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


3.2 白名单页面开发


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


网络异常,图片无法展示
|


代码如下:


importReactfrom'react';
import { List, Avatar, Button, Skeleton, Tag, Modal } from'antd';
importstylesfrom'./index.less';
importreqfrom'@/utils/req';
exportinterfaceIPropsextendsLocation {
}
interfaceList {
name: string;
img: string;
desc: string;
isLoading?: boolean;
}
interfaceLoadingState {
initLoading: boolean;
loading: boolean;
}
exportinterfaceIStateextendsLoadingState {
data: Array<List>;
list: Array<List>;
}
classLoadMoreListextendsReact.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// 页面实现代码  }
}
exportdefaultLoadMoreList

以上代码实现了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--浅识别 */exportconstisIe= ():boolean=> {
letexplorer=window.navigator.userAgent;
//判断是否为IE浏览器if (explorer.indexOf("MSIE") >=0) {
returntrue;
    }else {
returnfalse    }
}
/** * 颜色转换16进制转rgba * @param {String} hex  * @param {Number} opacity  */exportfunctionhex2Rgba(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标签exportconsthtmlSafeStr= (str:string):string=> {
returnstr.replace(/<[^>]+>/g, "")
}
interfaceparams {
  [propertyName: string]: string|number}
/* 解析url参数 */exportconsttoParams= (params:params):string=> {
if(params){
letquery= [];
for(letkeyinparams){
query.push(`${key}=${params[key]}`)
        }
return`${query.join('&')}`    }else{
return''    }
}

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



目录
相关文章
|
19小时前
|
JavaScript
vite+typescript从入门到实战(二)
vite+typescript从入门到实战
41 0
|
19小时前
|
自然语言处理 前端开发 JavaScript
TypeScript实战——ChatGPT前端自适应手机端,PC端
TypeScript实战——ChatGPT前端自适应手机端,PC端
|
7月前
|
JavaScript 算法
带你读《现代TypeScript高级教程》十八、TS实战之扑克牌排序(1)
带你读《现代TypeScript高级教程》十八、TS实战之扑克牌排序(1)
|
7月前
|
JavaScript
带你读《现代TypeScript高级教程》十八、TS实战之扑克牌排序(2)
带你读《现代TypeScript高级教程》十八、TS实战之扑克牌排序(2)
|
7月前
|
JavaScript
带你读《现代TypeScript高级教程》十八、TS实战之扑克牌排序(3)
带你读《现代TypeScript高级教程》十八、TS实战之扑克牌排序(3)
|
7月前
|
JavaScript
带你读《现代TypeScript高级教程》十八、TS实战之扑克牌排序(4)
带你读《现代TypeScript高级教程》十八、TS实战之扑克牌排序(4)
|
10月前
|
JavaScript 前端开发 API
《现代Typescript高级教程》实战之封装Fetch
TypeScript封装Fetch 1. 安装与配置TypeScript 首先,你需要在你的机器上安装TypeScript。在命令行中输入以下命令:
341 0
|
JavaScript 前端开发 定位技术
JS超集对TypeScript的Map对象以及联合类型的深入实战
JS超集对TypeScript的Map对象以及联合类型的深入实战
JS超集对TypeScript的Map对象以及联合类型的深入实战
|
缓存 JavaScript 前端开发
Vue3 TypeScript 使用教程 - 实战 Vue3 element-plus 开发「待办清单」
Vue3 的源码使用 TypeScript 全部重构,而 TypeScript 是 JS 的一个超集,主要提供对 ES6 的支持以及更棒的代码可读性和高维护性。可以说 Vue3 Typescript 已经成为开发标配。本文带领大家从搭建环境开始,手把手带领大家用 Vue3 Typescript + element-plus 开发一个极简「待办清单」app,在实战中学习 Vue3 TypeScript。如果你正在搭建后台管理工具,又不想处理前端问题,推荐使用卡拉云 ,卡拉云是新一代低代码开发工具,可一键接入常见数据库及 API ,无需懂前端,仅需拖拽即可快速搭建属于你自己的后台管理工具,一周工作
|
弹性计算 监控 JavaScript
Serverless 实战 —— 函数计算 + Typescript 实践
首先介绍下在本文出现的几个比较重要的概念: 函数计算(Function Compute):函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。
Serverless 实战 —— 函数计算 + Typescript 实践