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源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索