新聞中心
對此,有人提出了純凈代碼的概念。它是一種旨在提高軟件代碼的質量和可維護性,以讀者為中心的一致性編程風格。我們常說,任何人都可以編寫出能讓計算機理解的代碼,但只有優(yōu)秀的開發(fā)人員才能夠通過清晰簡潔的設計模式,編寫出易于人類閱讀、理解、修改和維護的純凈代碼,以降低軟件的開發(fā)成本,并消除技術債務。

10年積累的成都網站設計、網站建設經驗,可以快速應對客戶對網站的新想法和需求。提供各種問題對應的解決方案。讓選擇我們的客戶得到更好、更有力的網絡服務。我雖然不認識你,你也不認識我。但先建設網站后付款的網站建設流程,更有赤峰林西免費網站建設讓你可以放心的選擇與我們合作。
下面,我們將介紹十一種在使用React和TypeScript編寫純凈代碼時,必備且實用的模式。
1. 使用默認方式導入React
請導入如下代碼:
import * as React from "react";
{具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/c95fa549af8a958fd074324fcfb6f73f }
上述代碼既簡單又粗暴。如果我們不想使用React的所有內容的話,就沒有必要如此,而應當采用如下更好的默認導入模式:
import React, {useContext, useState} from "react";{具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/c6de59538119ee33d9c2e71c64620a56 }
使用這種方法,我們可以從模塊中按需解構出React內容,而不必導入所有的組件。當然,值得注意的是:在使用該導入方式之前,我們需要按照如下方式配置tsconfig.json文件:
{
"compilerOptions": {
"esModuleInterop": true
}
}{具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/d704140bbe003cd05be31b6ae7120468 }
在上述配置代碼段中,我們通過將esModuleInterop設置true,以啟用??[allowSyntheticDefaultImports]??。它會讓TypeScript能夠支持我們的語法。
2. 在運行時的實現(xiàn)之前聲明類型
請看如下的代碼:
import React, {Component} from "react";
const initialState = { count: 1 }
const defaultProps = { name: "John Doe" }
type State = typeof initialState;
type Props = { count?: number } & typeof defaultProps
class Counter extends Component {
static defaultProps = defaultProps;
state = initialState;
// ...
}{具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/f7b5daad358c0e9f3623baa83474ab01 }
由于我們將運行時與編譯時(compile-time)的聲明區(qū)分開來,因此上面的代碼段看上去更加清晰、易讀。此類聲明類型被稱為——編譯類型的優(yōu)先聲明。
讓我們再來看如下的代碼:
import React, {Component} from "react";
type State = typeof initialState;
type Props = { count?: number } & typeof defaultProps
const initialState = { count: 1 }
const defaultProps = { name: "John Doe" }
class Counter extends Component {
static defaultProps = defaultProps;
state = initialState;
// ...
}{具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/317170a0b580b92738a94fce7197be17 }
我們從代碼的第一行就能夠清晰地看到,開發(fā)人員已經知曉了對應的API。接著,我們就將編譯時與運行時的聲明區(qū)分開來了。
3. 始終為Children Prop(子屬性)提供顯式類型
在React.d.ts中,TypeScript需要將函數(shù)組件和類組件的Children Prop予以注解,以展示React是如何處理Children Prop的。對此,我們有必要為Children Prop顯式地提供一個類型,以便將“children”用于內容映射的場景中。當然,如果我們的組件無需使用內容映射的話,則可以簡單地用never類型予以注釋。請參考如下代碼段:
import React, {Component} from "react";
// Card.tsx
type Props = {
children: import('react').ReactNode
}
class Card extends Component {
render() {
const {children} = this.props;
return {children};
}
} {具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/45672ae71f63904fa7f6c88ec5a90e75 }
下面是一些用于注釋Children Prop的其他有效類型:
- ReactNode | ReactChild | ReactElement
- 對于原語,我們可以使用string | number | boolean
- 對象和數(shù)組也是有效的類型
- never | null | undefined (注意:我們并不推薦使用null和undefined)
4. 使用類型推斷來定義組件狀態(tài)或DefaultProps
請參考如下代碼段:
import React, {Component} from "react";
type State = { count: number };
type Props = {
someProps: string & DefaultProps;
}
type DefaultProps = {
name: string
}
class Counter extends Component {
static defaultProps: DefaultProps = {name: "John Doe"}
state = {count: 0}
// ...
} {具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/05c4c6b8ff5930776a0febab60ce53f8 }
雖然上述代碼可以被順利執(zhí)行,但我們有必要對其進行重構和改進,以便TypeScript的類型系統(tǒng)能夠正確地推斷出readonly類型(如DefaultProps和initialState),進而防止開發(fā)人員意外地設置狀態(tài):this.state = {},而引起錯誤。請參見如下代碼:
import React, {Component} from "react";
const initialState = Object.freeze({ count: 0 })
const defaultProps = Object.freeze({name: "John Doe"})
type State = typeof initialState;
type Props = { someProps: string } & typeof defaultProps;
class Counter extends Component {
static readonly defaultProps = defaultProps;
readonly state = {count: 0}
// ...
} {具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/6e4ea2442091e86b3903738d21f0b613 }
在上述代碼中,通過凍結DefaultProps和initialState,TypeScript類型系統(tǒng)可以將它們推斷為Readonly類型??梢姡ㄟ^對靜態(tài)DefaultProps和其類中的Readonly狀態(tài)予以標記,我們消除了上文提到的,由設置狀態(tài)可能引起的運行時錯誤。
5. 使用類型別名而不是接口(interface)來聲明屬性和狀態(tài)
雖然我們可以使用interface,但是為了確保清晰的一致性,并應對無法使用interface的場景,我們應當使用類型別名。例如,在前面的示例中,我們通過重構代碼,使得TypeScript的類型系統(tǒng),能夠從實現(xiàn)中定義狀態(tài)類型,進而正確地推斷出只讀類型。而在如下代碼中,我們就無法針對其模式使用interface:
// works
type State = typeof initialState;
type Props = { someProps: string } & typeof defaultProps;
// throws error
interface State = typeof initialState;
interface Props = { someProps: string } & typeof defaultProps;
{具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/74b89d92471cdadbba52065a87334549 }
此外,在不能使用由unions和intersection創(chuàng)建的types,去擴展interface時,我們也必須使用type的別名。
6. 不要在接口/類型別名中使用方法聲明
通常,只有所有的類型和推理要素都以相同的方式被聲明時,我們才能確保代碼中的模式一致性。然而,--strictFunctionTypes參數(shù)只能有效地比較兩個函數(shù),而非方法。對此,你可以通過鏈接--https://github.com/Microsoft/TypeScript/issues/25296#issuecomment-401517062,來進一步了解有關Typescript相關問題的解釋。當然,您也可以參考如下代碼段:
// Don't do this
interface Counter {
start(count:number) : string
reset(): void
}
// Do
interface Counter {
start: (count:number) => string
reset: () => string
}
{具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/63cc5145646f5f02c4e28e36bd8af926 }
7. 不要使用FunctionComponent
請不要使用FunctionComponent (簡寫 FC ),來定義某個函數(shù)組件。通常,我們在將TypeScript與React一起使用時,對應的函數(shù)式組件可以被寫成如下兩種方式:
(1)常規(guī)性功能代碼:
type Props = { message: string };
const Greeting = ({ message }: Props) => {message};{具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/276112f9e5ed21c69ba02ffec755a7e1 }
(2)使用React.FC或React.FunctionComponent的代碼段(如下所示):
import React, {FC} from "react";
type Props = { message: string };
const Greeting: FC = (props) => {props}; {具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/310dd40107547a3d3ed08ae782f767cf }
可見,使用FC的優(yōu)點包括:針對displayName、propTypes和DefaultProps等靜態(tài)屬性,提供了類型檢查和自動完成。不過,根據(jù)經驗,它對propTypes、contextTypes、以及displayName的defaultProps,可能會造成問題。此外,F(xiàn)C為Children Prop提供的隱式類型,也存在著一些已知的問題。當然,如前所述,組件API本來就應該是顯式的,因此我們沒有必要將Children Prop設置為隱式類型。
8. 不要將構造函數(shù)用于類組件
請使用新的類字段建議(請參考--https://github.com/tc39/proposal-class-fields#consensus-in-tc39),而不必在JavaScript類中使用構造函數(shù)。畢竟,使用構造函數(shù)會涉及調用super()和傳遞props,這些都會引入不必要的板模式(plate)和復雜性。
我們可以使用如下代碼段中的類字段,來編寫更簡潔、更易于維護的React類組件:
// Don't do
type State = {count: number}
type Props = {}
class Counter extends Component{
constructor(props:Props){
super(props);
this.state = {count: 0}
}
}
// Do
type State = {count: number}
type Props = {}
class Counter extends Component{
state = {count: 0}
}
{具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/ddd9bb947f736794db1d85d8b560f1f0 }
由上述代碼可知,我們在使用類字段時,涉及到的boilerplate(鍋爐板模式)越少,待處理的this變量也就越少。
9. 不要在類中使用公共訪問器(Public Accessor)
讓我們來看如下代碼:
import { Component } from "react"
class Friends extends Component {
public fetchFriends () {}
public render () {
return // jsx blob
}
}{具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/96e8c072ad43426b7306e77f1a462e4d }
在上述類中,由于public的所有元素的運行時都是默認的,因此我們無需通過顯式使用public關鍵字,來添加額外的boilerplate文件,只需如下模式即可:
import { Component } from "react"
class Friends extends Component {
fetchFriends () {}
render () {
return // jsx blob
}
}{具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/87da70d3cef765b652e9532b98b52921 }
10. 不要在組件類中使用私有訪問器
讓我們再來看如下代碼:
import {Component} from "react"
class Friends extends Component {
private fetchProfileByID () {}
render () {
return // jsx blob
}
}{具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/8e31307e752e5f6b93c901556bd1dfc1 }
在上面的代碼中,私有訪問器僅在編譯時,將fetchProfileByID方法私有化(private)。而在運行時,fetchProfileByID方法仍然是公共的。
目前,我們有多種方法可以將JavaScript類中的屬性和方法設定為私有。下面的代碼段展示了其中的一種--使用下劃線 (_) 的命名規(guī)則:
import {Component} from "react"
class Friends extends Component {
_fetchProfileByID () {}
render () {
return // jsx blob
}
}{具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/d69d76d7c0c3c10c6e95af5354b0da2b }
雖然上述方法并沒有真正使得fetchProfileByID成為私有方法,但它很好地向其他開發(fā)人員傳達了我們的意圖,即:任何指定的方法應該被視為私有方法。弱映射(weakmap)、符號和作用域變量(scoped variable)都是如此。正如下面的代碼段所示,我們可以通過新的??ECMAScript類字段???的“建議”,使用各種??私有字段??,來輕松地達到此目的:
import {Component} from "react"
class Friends extends Component {
#fetchProfileByID () {}
render () {
return // jsx blob
}
}{具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/594c426e763afe26c12badf60432831e }
當然,值得注意的是,您需要使用TypeScript3.8及更高版本,才能支持私有字段的相關語法。
11. 不要使用枚舉(enum)
雖然enum是JavaScript中的保留字,但是使用enum并非標準化的JavaScript慣用模式。鑒于枚舉已在C#和Java之類的編程語言中被廣泛使用,您可以在此按照如下代碼方式,使用編譯類型的表述方式:
// Don't do this
enum Response {
Successful,
Failed,
Pending
}
function fetchData (status: Response): void => {
// some code.
}
// Do this
type Response = Sucessful | Failed | Pending
function fetchData (status: Response): void => {
// some code.
}
{具體請參見GitHub的Gist鏈接--https://gist.github.com/lawrenceagles/2dd0ecbd8d54ceae715feedc81d445cb }
小結
毫無疑問,使用TypeScript會為您的代碼添加許多額外的boilerplate,不過總體說來仍然是利大于弊的。希望上述介紹的十一種有關React和TypeScript應用的最佳實踐和JavaScript慣用模式,能夠讓您的代碼更加清晰、更加易被維護。
原文標題:10 Must-Know Patterns for Writing Clean Code With React and Typescript,作者: Alex Omeyer
本文名稱:用React和Typescript編寫純凈代碼的十一種必備模式
標題鏈接:http://www.dlmjj.cn/article/dpocdge.html


咨詢
建站咨詢
