中文字幕一区二区人妻电影,亚洲av无码一区二区乱子伦as ,亚洲精品无码永久在线观看,亚洲成aⅴ人片久青草影院按摩,亚洲黑人巨大videos

維護(hù)大型JavaScript應(yīng)用程序

發(fā)布于:2021-02-05 14:50:20

0

330

0

javascript React 應(yīng)用程序

我們從長期維護(hù)大型JavaScript應(yīng)用程序中學(xué)到的經(jīng)驗(yàn)教訓(xùn)。

在我們公司,客戶項(xiàng)目通常持續(xù)幾個(gè)月。從第一次與客戶接觸到設(shè)計(jì)階段,再到實(shí)施和初始啟動,一個(gè)項(xiàng)目大約需要半年的時(shí)間。但有時(shí)我們在幾年的時(shí)間里開發(fā)和維護(hù)一個(gè)特定的軟件。

例如,我們在2012年為基金會啟動了GED-VIZ,2013年發(fā)布了GED-VIZ,并每隔幾年添加新的功能和數(shù)據(jù)。2016年,我們將核心可視化轉(zhuǎn)變?yōu)榭芍赜脦?,對其進(jìn)行了重大重構(gòu)。如今,央行仍在使用流量數(shù)據(jù)可視化引擎。另一個(gè)長期項(xiàng)目是OECD數(shù)據(jù)門戶前端:我們于2014年開始實(shí)施,目前仍在擴(kuò)展代碼庫。

在主開發(fā)階段之后,我們將應(yīng)用修復(fù)程序并添加新特性。通常情況下,沒有預(yù)算進(jìn)行重大的重構(gòu)甚至重寫。因此,在一些項(xiàng)目中,我堅(jiān)持使用4-6年前編寫的代碼和當(dāng)時(shí)流行的庫堆棧。

小的改進(jìn)而不是大的重寫

上面提到的兩個(gè)項(xiàng)目都是相當(dāng)大的客戶端JavaScript應(yīng)用程序?,F(xiàn)在,你只能找到幾篇關(guān)于多年來維護(hù)現(xiàn)有JavaScript代碼庫的博客文章。你會發(fā)現(xiàn)很多關(guān)于用現(xiàn)在流行的JavaScript框架重寫前端的帖子。

遷移到一組新的庫和工具是一項(xiàng)巨大的投資,可能很快就會有回報(bào)。這樣可以方便維修。它可以降低變革的成本。它允許更快地迭代并更快地實(shí)現(xiàn)新特性。它可以減少誤差,提高魯棒性和性能。最終,這種投資可能會降低總體擁有成本。

但是當(dāng)一個(gè)客戶無法進(jìn)行這種投資時(shí),我們會尋找方法來逐步改進(jìn)現(xiàn)有的代碼庫。

從長期項(xiàng)目中學(xué)習(xí)

對于一些web開發(fā)人員來說,使用現(xiàn)有的代碼庫是一場噩夢。他們以貶義的方式使用“遺留”一詞來表示他們最近沒有編寫的代碼。

對我來說,恰恰相反。在幾年的時(shí)間里維護(hù)一個(gè)項(xiàng)目的代碼讓我學(xué)到了更多關(guān)于軟件開發(fā)的知識,而不是多個(gè)短命的、一勞永逸的項(xiàng)目。

最重要的是,它讓我不得不面對多年前編寫的代碼。我?guī)啄昵白龅臎Q定對今天的整個(gè)系統(tǒng)都有影響。我今天所做的決定決定了這個(gè)系統(tǒng)長期的命運(yùn)。

我常常在想:今天我會做什么不同的事?需要改進(jìn)什么?像每一個(gè)開發(fā)人員一樣,我有時(shí)也會有一種沖動,那就是銷毀一切,從頭開始構(gòu)建。

但大多數(shù)時(shí)候,我在現(xiàn)有代碼中遇到的問題更加微妙:今天,我將用不同的結(jié)構(gòu)編寫相同的邏輯。讓我向您展示我在JavaScript代碼中發(fā)現(xiàn)的主要結(jié)構(gòu)問題。

避免復(fù)雜結(jié)構(gòu)

我所說的“復(fù)雜”不僅僅是指大。每個(gè)不平凡的項(xiàng)目都有很多邏輯。有很多案例需要考慮和測試。要處理的數(shù)據(jù)不同。

復(fù)雜性來自不同關(guān)注點(diǎn)的交織。人們無法完全避免這一點(diǎn),但我已經(jīng)學(xué)會了先分離關(guān)注點(diǎn),然后以可控的方式將它們帶回來。

讓我們看看JavaScript中的簡單和復(fù)雜結(jié)構(gòu)。

功能

最簡單的可重用JavaScript代碼是函數(shù)。特別是一個(gè)純函數(shù),它獲取一些輸入并生成一個(gè)結(jié)果(返回值)。函數(shù)以參數(shù)形式顯式獲取所有必需的數(shù)據(jù)。它不會更改輸入數(shù)據(jù)或其他上下文數(shù)據(jù)。這樣一個(gè)函數(shù)易于編寫、易于測試、易于記錄和推理。

編寫好的JavaScript并不一定需要高級設(shè)計(jì)模式。首先也是最重要的,它需要以一種聰明和有益的方式使用最基本的技術(shù):用做一件事正確的函數(shù)構(gòu)造程序。然后將低級函數(shù)組合成高級函數(shù)。

JavaScript中的函數(shù)是成熟的值,也稱為一級對象。作為一種多范例語言,JavaScript允許強(qiáng)大的函數(shù)式編程模式。在我的職業(yè)生涯中,我只接觸過JavaScript函數(shù)式編程的皮毛,但了解基礎(chǔ)知識已經(jīng)有助于編寫更簡單的程序。

對象

下一個(gè)復(fù)雜結(jié)構(gòu)是對象。在其最簡單的形式中,對象將字符串映射為任意值,而沒有邏輯。但它也可以包含邏輯:函數(shù)在附加到對象時(shí)成為方法。

const cat = {
 name: 'Maru',
 meow() {
   window.alert(`${this.name} says MEOW`);
 }
};
cat.meow();


JavaScript中的對象無處不在,用途廣泛。一個(gè)對象可以作為一個(gè)附加了多個(gè)處理函數(shù)的參數(shù)包。對象可以對相關(guān)值進(jìn)行分組,但也可以構(gòu)造程序。例如,您可以在一個(gè)對象上放置幾個(gè)類似的函數(shù),并讓它們對相同的數(shù)據(jù)進(jìn)行操作。

JavaScript中最復(fù)雜的結(jié)構(gòu)是類。它是對象的藍(lán)圖,同時(shí)也是這些對象的工廠。它將原型繼承與對象的創(chuàng)建相結(jié)合。它將邏輯(函數(shù))與數(shù)據(jù)(實(shí)例屬性)交織在一起。有時(shí)構(gòu)造函數(shù)上有一些屬性,稱為“靜態(tài)”屬性。像“singleton”這樣的模式用更多的邏輯重載了一個(gè)類。

類是面向?qū)ο笳Z言中的常見工具,但它們需要設(shè)計(jì)模式的知識和對象建模的經(jīng)驗(yàn)。尤其是在JavaScript中,它們很難管理:構(gòu)建繼承鏈、對象組合、應(yīng)用mixin、超級調(diào)用、處理實(shí)例屬性、getter和setter、方法綁定、封裝,ECMAScript既沒有為常見的OOP概念提供標(biāo)準(zhǔn)的解決方案,也沒有為社區(qū)提供關(guān)于類使用的最佳實(shí)踐。

如果類有一個(gè)明確的目的,那么它們是合適的。我學(xué)會了避免在課堂上增加更多的關(guān)注點(diǎn)。例如,有狀態(tài)的React組件通常被聲明為類。這對于特定的問題域是有意義的。它們有一個(gè)明確的目的:將道具、狀態(tài)和對兩者都起作用的幾個(gè)函數(shù)分組。類的中心是render函數(shù)。

我不再用更多松散相關(guān)的邏輯來豐富這些類。值得注意的是,React團(tuán)隊(duì)正在慢慢地從類轉(zhuǎn)向有狀態(tài)的功能組件。

同樣,Angular中的組件類是幾個(gè)關(guān)注點(diǎn)的交叉點(diǎn):使用@Component()裝飾器應(yīng)用的元數(shù)據(jù)字段?;跇?gòu)造函數(shù)的依賴注入。狀態(tài)為實(shí)例屬性(輸入、輸出以及自定義公共和私有屬性)。這類課程根本不是簡單或單一的目的。它們是可管理的,只要它們只包含所需的特定于角度的邏輯。

選擇結(jié)構(gòu)

多年來,我一直遵循以下準(zhǔn)則:

  1. 使用最直接、最靈活、最通用的結(jié)構(gòu):函數(shù)。如果可能,讓它成為純函數(shù)。

  2. 如果可能,避免在對象中混合數(shù)據(jù)和邏輯。

  3. 如果可能,避免使用類。如果你使用它們,讓它們做一件事。

大多數(shù)JavaScript框架都有自己的代碼結(jié)構(gòu)。在基于組件的UI框架(如React和Angular)中,組件通常是對象或類。選擇組合而不是繼承很容易:只需創(chuàng)建一個(gè)新的輕量級組件類來分離關(guān)注點(diǎn)。

這并不意味著需要堅(jiān)持這些結(jié)構(gòu)來建模業(yè)務(wù)邏輯。最好將這個(gè)邏輯放到函數(shù)中,并將它們從UI框架中分離出來。這允許分別開發(fā)框架代碼和業(yè)務(wù)邏輯。

模塊

管理JavaScript文件和外部庫之間的依賴關(guān)系過去是一團(tuán)糟。在9elements,我們是CommonJS或AMD模塊的早期采用者。后來,社區(qū)決定使用標(biāo)準(zhǔn)的ECMAScript 6模塊。

模塊成為JavaScript中的一種基本代碼結(jié)構(gòu)。這取決于它們的用法是簡單還是復(fù)雜。

隨著時(shí)間的推移,我對模塊的使用也發(fā)生了變化。我以前用多次導(dǎo)出創(chuàng)建相當(dāng)大的文件。或者,單個(gè)導(dǎo)出是一個(gè)巨大的對象,它將一組常量和函數(shù)分組。今天我嘗試用一個(gè)導(dǎo)出或幾個(gè)導(dǎo)出來創(chuàng)建小型的、扁平的模塊。這將導(dǎo)致每個(gè)函數(shù)一個(gè)文件,每個(gè)類一個(gè)文件,以此類推。文件foo.js如下所示:

export default function foo(…) {…}

如果您更喜歡命名導(dǎo)出而不是默認(rèn)導(dǎo)出:

export function foo(…) {…}

這使得單個(gè)函數(shù)更易于引用和重用。以我的經(jīng)驗(yàn),很多小文件不會帶來很大的成本。它們允許更容易地在代碼中導(dǎo)航。此外,特定代碼段的依賴關(guān)系也可以更有效地聲明。

避免創(chuàng)建非類型化對象

JavaScript最好的特性之一是對象文本。它允許您快速創(chuàng)建具有任意屬性的對象。我們已經(jīng)看到了上面的一個(gè)例子:

const cat = {
 name: 'Maru',
 meow() {
   window.alert(`${this.name} says MEOW`);
 }
};

JavaScript對象表示法是如此簡單和富有表現(xiàn)力,以至于它被轉(zhuǎn)換成了一種如今無處不在的獨(dú)立數(shù)據(jù)格式:JSON。但是在ECMAScript版本的過程中,對象文本獲得了越來越多超出其最初用途的特性。新的ECMAScript特性,比如objectrest/Spread,允許更自由地創(chuàng)建和混合對象。

在一個(gè)小的代碼庫中,動態(tài)創(chuàng)建對象是一個(gè)高效的特性。不過,在大型代碼庫中,對象文字成為一種負(fù)擔(dān)。在我看來,具有任意屬性的對象不應(yīng)該存在于這樣的項(xiàng)目中。

問題不在于對象本身。問題是不符合中心類型定義的對象。它們通常是運(yùn)行時(shí)錯(cuò)誤的來源:屬性可能存在或不存在,可能有某種類型或沒有。對象可以具有所有必需的屬性,但也可以具有更多屬性。通過閱讀代碼,您無法判斷對象在運(yùn)行時(shí)將具有哪些屬性。

JavaScript沒有類型定義,但是有幾種方法可以以更受控制的方式創(chuàng)建對象。例如,一個(gè)函數(shù)可以用來創(chuàng)建所有看起來相似的對象。該函數(shù)確保所需的屬性存在且有效,或者具有默認(rèn)值。另一種方法是使用創(chuàng)建死簡單值對象的類。

同樣,函數(shù)可以在運(yùn)行時(shí)檢查參數(shù)是否可用。它可以使用typeof、instanceofNumber.isNaN等顯式檢查類型,也可以隱式使用duck類型。

一個(gè)更徹底的解決方案是用類型定義來豐富JavaScript,比如TypeScript或Flow。例如,在TypeScript中,首先定義重要數(shù)據(jù)模型的接口。函數(shù)聲明其參數(shù)的類型和返回值。TypeScript編譯器確保只傳遞允許的類型,因?yàn)榫幾g器可以訪問所有調(diào)用。