前言
筆者上一篇文章 TS核心知識點總結及項目實戰案例分析 主要寫了typescript的用法和核心知識點總結, 這篇文章將經過一個實際的前端案例來教你們如何在項目中使用typescript.javascript
你將收穫
-
如何使用umi快速搭建一個基於React + antd + typescript的前端項目 -
中後臺前端項目的目錄和ts文件劃分 -
在React組件中使用typescript -
在工具庫中使用typescript -
互聯網黑白牆 案例分析
正文
該項目是一個響應式網站, 針對PC端和H5均作了必定的適配, 接下來咱們將正對該網站作一次typescript剖析.css
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.ts和global.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文件
-
components 該頁面專有的組件目錄 -
index.tsx 關於頁面的主文件 -
index.less 關於頁面的樣式文件 -
type.ts 關於頁面的類型和接口聲明文件
3. 在React組件中使用typescript
這裏筆者將會拿該項目的自定義上傳組件以及白名單頁面做爲例子, 文件上傳組件筆者將採用SFC(即函數組件), 白名單頁面將採用類組件, 這樣能夠方便你們對這兩中組件開發模式下的typescript開發有個全面的認知.css3
3.1 自定義上傳組件開發
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遊戲, webpack,node,gulp,css3,javascript,nodeJS,canvas數據可視化等前端知識和實戰,歡迎在公號《趣談前端》加入咱們的技術羣一塊兒學習討論,共同探索前端的邊界。
本文分享自微信公衆號 - 趣談前端(beautifulFront)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。