過去流行的是 Angular,然后是 React,現在是 Vue.js……其他的像 Ember、Backbone 或 Knockout 什么的幾乎都快消失了。一些標準,例如 Web Components,則很少被使用。似乎每年都會發布一些新框架,比如 Svelte、Aurelia,而且每個框架在服務器端都有對應的對象(開頭那些框架對應的 NestJS、NextJS 或 Nuxt,Svelte 對應的 Sapper,等等)。非 JavaScript Web 框架(如 Django、Spring、Laravel、Rails 等)就更不用說了。甚至還有框架之上的框架(Quasar、SolidJS)、為框架生成組件代碼的框架(Stencil、Mitosis),以及 NCDP(無代碼開發平臺,No-Code Development Platform)。
這種多樣性讓想知道哪種技術值得學習的開發人員和技術選型決策者感到困惑。
網絡上經常會出現一些比較這些框架的文章,好像是在幫助我們解開這種困惑。但大多數作者通常是帶有偏見的,因為他們可能“用過這個框架”,但“只嘗試了一些其他的框架”。偏見程度較低的作者總是得出“這取決于具體情況”的結論(取決于性能、工具、支持、社區等),這實際上是一種非結論性的結論。
即使一些基準測試基于同一個應用程序對不同的框架進行了比較,也很難獲得真實的結果,因為這種基準測試受限于被測試的應用程序(比如待辦事項應用程序)。
框架看起來就像是宗教(或者說是):每一個框架都假裝自己為開發者提供了解決方案,但每一個又都不一樣。它們每一個都聲稱可以為應用程序提供最好的前景,但關于哪一個真正名副其實的爭論又不絕于耳。每一個框架都要求你遵循特定的規則,它們之間可能有相似之處,但要從一個框架轉換到另一個框架總是很難。
現在,讓我們來看看框架的“無神論”方法:不使用框架。
從哪里講起?
我有超過 25 年的專業軟件開發經驗,除此之外,本文還將以構建真實純 JS Web 應用程序(前端和后端)的經驗為基礎。
為什么不使用框架?
實際上,這個想法還很新。早在 2017 年,Django Web 框架聯合創始人 Adrian Holovaty 就談到了他的框架“疲勞”,以及他為什么離開 Django 去構建自己的純 JS 項目。
有人可能會問,為什么會有人想要在不使用框架的情況下開發 Web 應用程序?為什么不在其他人花了數年時間和精力的成果的基礎上做開發?或者是因為 NIH(Not Invented Here)綜合癥導致人人都想構建定制的框架?
開發人員并不會比一般人更傾向于自找麻煩,實際上,他們可能比任何人都懶:他們只會想寫更少的代碼(這樣他們就可以更少犯錯),想要自動化(以避免人為錯誤)……
但他們又想要敏捷,也就是能夠輕松、快速地解決問題。
雖然“快速”似乎是框架承諾的東西(為你搭建腳手架,并增加可靠性),但它不是免費的:它們想讓你簽署合同,同意支付“稅”費,并將你的代碼放入“孤井”(“稅和孤井”的說法來自 IBM Carbon 系統設計團隊負責人 Akira Sudhttps://github.com/carbon-design-system/carbon-web-components#readme)。
使用框架是需要付出成本的:
遵循它們的 API 規則,這樣它們就可以向你提供服務。這就是框架的工作方式:你的代碼必須遵守某些規則,包括或多或少的樣板代碼。你每天要想的不是“如何做這件事”,而是“如何讓框架做(或不做)這件事”。如果你規避了這些約束,風險就由你自己承擔:如果你通過直接調用底層 API 來繞過框架,就不要指望它們能理解你的意圖,也不要指望它們的行為能保持一致。所以,框架會讓你“專注于業務”是一個虛假的承諾:實際上,框架的事情你也沒少操心。
如果你想要以下這些東西,就不得不強制進行升級:
1) 想要一個新功能(即使你不需要所有功能,也必須升級所有東西);
2) 想要修復一個 bug;
3) 不想失去框架的支持(隨著新版本的發布,你的應用程序所依賴的版本將會被棄用)。
外交部回應美議員敦促運動員在北京冬奧會上不用中國新數字貨幣:無知:7月20日消息,在2021年7月20日外交部發言人趙立堅主持例行記者會上,路透社記者提問三名美國共和黨參議員敦促美奧組委禁止美方運動員在參加2022年北京冬奧會期間使用中國的新數字貨幣。中方對此有何評論?趙立堅回答,美方有關政客的行徑只會反映出他們的無知。建議他們去搞搞清楚什么是數字貨幣。至于說間諜活動和數字安全威脅,美國才是公認的全球“桂冠”。美方通過網絡竊密、電話監聽等各種手段,不僅監聽監視競爭對手,連自身盟友也不放過。這些事實世人皆知。數字貨幣是數字技術發展的必然產物,順應了當前全球數字經濟發展的歷史潮流。中國法律也對保障數據安全作出了明確規定。美方政客應該尊重奧林匹克憲章精神,停止將體育運動化的做法,不要拿中國新數字貨幣說事兒。[2021/7/20 1:05:29]
如果框架出現了 bug,但沒有明確的計劃修復日期,這會讓你感到非常沮喪(可能還會讓項目面臨風險)。第三方提供的框架庫(如小部件)或插件也不例外,如果你一直使用舊版本,它們與你的應用程序的兼容性會越來越差。對于框架維護者來說,維護向后兼容性已經成為一件非常麻煩的事情。他們發現,開發自動升級代碼的工具(Angular 的 ng-update、React 的原生升級助手、Facebook 的 jscodesshift 等)會更有利可圖。
需要學習如何使用它們(它們能做或不能做什么、它們的概念、API、生態系統、工具),包括了解在新版本中可能發生的變化。如果你選擇的是當前最流行的框架,這可能會容易些,但你不可能了解一個框架的方方面面。此外,炒作也從來不會消停:如果你決定在一個新應用程序中使用另一個框架(或者更糟的是,從一個框架遷移到另一個框架),那么你在舊框架上所有的投入都將歸零。這就是為什么很多企業項目會缺乏活力,即使每個項目都可能與前一個項目不一樣。已故的 David Wheeler 曾經說過:“保持兼容性意味著有意重復別人的錯誤”。
將控制權委托給框架,這是對框架缺陷的妥協:你可能無法做任何你想做的事(或防止框架做你不希望它們做的事情)或者你也許不能獲得你想要的性能(因為額外的分層、普適性、更大的代碼體積或向后兼容性需求)。
技能零散化。很多開發人員要么不太了解底層 API(因為他們總是使用框架提供的東西),要么活在過去(只知道過時的知識,不知道最新的改進和功能)。“工具法則”常常導致過度設計,為簡單的問題構建復雜的解決方案,而構建簡單解決方案的知識逐漸零散化。在指南的指導下,我們失去了(或者沒有獲得)好的軟件設計(原則、模式)文化,并失去(或者沒有獲得)構建重要工程的經驗。就像 CSS 框架(Bootstrap、Tailwind 等)的用戶缺乏 CSS 技能一樣,Web 框架的用戶也注定缺乏現代 Web API 和軟件設計經驗。
一旦你把錢放入框架,就很難把它拿出來。
除了必須支付“稅”費來獲得框架的好處之外,如果框架沒有標準化,它們還會帶來另一個問題。
因為它們強制要求你遵循框架規則——而且每一條規則都不一樣——這意味著你的應用程序將與一個專有的生態系統綁定在一起,也就是用專有 API(及其升級過程)鎖定你的應用程序代碼。這對于你的項目來說是一個冒險的賭注,正如它們所暗示的那樣:
沒有可移植性:將代碼遷移到另一個框架(或者一個有重大變化的新版本,甚至是不使用框架)將是非常昂貴的,包括可能需要進行重新培訓的成本;
你的代碼與其他框架運行時或你想要使用的其他框架組件庫沒有互操作性:由于規則不同,大多數框架彼此之間很難實現互操作。
當然,在項目剛開始時,你可以選擇最流行的框架。對于一個短期的項目來說,這可能是可以接受的,但對于長期項目來說則不然。
框架來來去去。從 2018 年開始,每年都有 1 到 3 個新框架取代舊框架。
不過,標準框架并不存在孤井。在 Web 平臺(即瀏覽器框架)上,使用標準 Web API 可以降低你的投入風險,因為它們可以在大多數瀏覽器上運行。即使不是所有的瀏覽器都支持,仍然可以通過 polyfill 來彌補。
例如,現在的 Web 組件既可移植(幾乎可以在所有瀏覽器中使用),又可互操作(可以被任何代碼使用,包括專有框架),因為它們可以被封裝成任意的 HTML 元素。不僅具備更好的性能,它們的運行時(自定義元素、陰影 DOM、HTML 模板)還作為瀏覽器的一部分運行,所以它們已經在那里(不需要下載),并且是原生的。
ECN以太坊中文社區Esther:以太坊2.0不用等3-5年:2020年12月30日下午3點,火幣尖峰對話《12.30火幣尖峰對話:為什么我們如此需要以太坊2.0以及它現在到底怎么樣?》主題線上活動在社群舉行。ECN以太坊中文社區負責人Esther表示,以太坊2.0的新路線圖是“可執行PoS信標鏈+數據分片+layer2”的架構,可能加速ETH2.0的步伐。根據Vitalik的說法,輕客戶端支持在2021年實現沒有問題;合并和分片可能在2021年末迎來成熟的測試網版本,因為我們需要等待PoS信標鏈在產品環境中平穩安全地運行較長一段時間,同時不斷進行合并測試。
以太坊社區一向是穩中求進,比起更新速度更加注重整個網絡的穩定性和安全性。總的來說,新路線圖縮短了ETH的鎖定周期,提前為以太坊帶來數千倍的吞吐量,加速了以太坊2.0完全可用的步伐,很可能將不再需要等待3—5年。[2020/12/30 16:05:52]
很少會有開發者試圖逃離框架孤井。
那么框架本質上就是不好的嗎?
如果是為實現應用程序邏輯而創建自己的框架,那就不能說框架是不好的:任何應用程序都需要實現自己的業務規則。
如果符合以下這些情況,框架就是好的:
是應用程序特有的:任何應用程序最終都會設計自己的“業務”框架。
成為標準,例如,Web 平臺就是一個標準的 Web 框架,而 Web 組件框架(lit、stencil、skatejs 等)最終構建的組件都符合這個標準。
添加一些其他解決方案(包括其他框架)所缺少的獨特價值。對于這種情況,你幾乎沒有選擇,這些附加價值證明了隱含的鎖定成本是合理的。例如,一個特定于操作系統的框架遵循了操作系統的標準,除此之外沒有其他方式可以獲得能夠滿足需求的應用程序或擴展。
用于構建非關鍵(短期、低質量預期,并且可以接受“稅費”和“孤井”)應用程序。例如,使用 Bootstrap 構建原型、MVP 或內部工具。
簡單地說,避免使用框架來構建應用程序的目標是:
通過避免框架的“一刀切”約束來最大化靈活性。此外,去掉規則的約束,提升應用程序的創造力。大多數使用 Bootstrap 開發的 Web 應用程序都屬于此類,因為它們很難擺脫預定義組件和樣式,最終將很難從其他角度思考問題。
盡量減少對炒作過度的框架的依賴。不被框架鎖定,才能夠避免可移植性和互操作性方面的問題。
只在需要時進行最細粒度的操作(例如,不依賴框架的刷新周期),并減少依賴項,只使用一些必需的輕量級庫,以此來最大化性能。
當然,我們的目標也不能是“重新發明輪子”。我們來看看該怎么做。
那么,如何在沒有框架的情況下開發應用程序呢?
首先,我們必須明確一個反目標:不要將“不使用框架構建應用程序”與“取代框架”混淆起來了。框架是一種用于托管任意應用程序的通用技術解決方案,所以它們的目標并非你的應用程序,而是所有的應用程序。相反,脫離框架才有可能讓你更專注于你的應用程序。
不使用框架開發應用程序并不意味著要重新實現框架。
要評估在不使用框架的情況下構建應用程序的難度,我們要明白:它不像構建框架那么困難,因為以下這些不是我們的目標:
構建專有的組件模型(實現特定組件生命周期的容器);
構建專有的插件或擴展系統;
構建一個奇特的模板語法(JSX、Angular HTML 等);
實現通用的優化(變更檢測、虛擬 DOM);
特定于框架的工具(調試擴展、UI 構建器、版本遷移工具)。
因此,構建一個普通的應用程序并不是一項艱巨的“重新發明輪子”的任務,因為這個“輪子”主要是關于 API/ 合約、實現、通用引擎和相關的優化、調試能力等。放棄通用目標,專注于應用程序的目標,這意味著你可以擺脫大部分目標,而這才是真正的“專注于你的應用程序”。
那么,我們該如何設計和實現一個普通的應用程序?因為大多數應用程序都是使用框架構建的,所以如果沒有這些熟悉的工具,確實很難設計出一種方法來實現類似的結果。你必須:
聲音 | 陳偉星:不用在戰略上擔心政府打壓區塊鏈:今日,陳偉星發微博對區塊鏈所出現的欺詐行為表達了自己的觀點。他認為,金融的核心在于制定估值模型,不是喊單、拉盤。但喊單行為是所有二級權益市場的通病,并不局限于幣圈。任何投機市場都有欺詐性投資、傳銷等犯罪問題的出現,區塊鏈也不例外。此外他表示:“我們中國共產黨是唯物主義政黨,也是布爾什維克主義政黨,也是熱衷于與“邪惡”斗爭的政黨,只會“過度”尊重科學、“過度”追求人民福利,所以不用在戰略上擔心政府打壓區塊鏈,需要擔心的是騙子混淆視聽,讓政府錯殺無辜。”[2019/1/4]
改變你的想法:不要使用特定于框架的服務。對于一個普通的應用程序來說,你可能不需要這些服務。不需要變更檢測,直接更新 DOM 即可……
用其他技術替代方案來執行原先使用框架執行的常見任務(更新 DOM、延遲加載等)。
一些作者,如 Jeremy Likness 或 Chris Ferdinandi(被稱為“JS 極客”)也提到過這個話題。但是,根據定義,任何一個普通的應用程序都可以選擇(或不選擇)使用其中的一種技術,具體視需求而定。例如,MeetSpace 的作者只需要使用標準 API 就足以。
接下來,讓我們來看看一些常見的“解法”。
標準 API 屬于“好的框架”,因為它們:
具備可移植性:它們在任何地方都可用,如果不可用,可以通過 polyfill 的方式實現。
具備互操作性:它們可以與其他標準交互,并被用在專有代碼中。
長期存在:由多個行業參與者設計,而不只是一個。它們被設計得很好,一旦發布就會一直存在,使用它們的風險較小。
在大多數情況下在瀏覽器中都是立即可用的,避免了下載過程。在某些情況下,你可能需要下載 polyfill。但是,與專有框架(注定會越來越不流行)不一樣的是,它們的可用性會越來越高(逐漸降低下載的必要性)。
在選擇編程語言時,我們要著重考慮標準。JavaScript 經過多年的發展,現在也包含了在其他編程語言中出現的特性,比如 class 關鍵字和通過 JSDoc 注釋(如 @type)提供有限的類型檢查支持。
很多編程語言可以被編譯成 JavaScript:TypeScript、CoffeeScript、Elm、Kotlin、Scala.js、Haxe、Dart、Rust、Flow 等。它們都為你的代碼添加了不同的價值(類型檢查、額外的抽象、語法糖)。普通的應用出現應該使用它們嗎?為了回答這個問題,讓我們來看看它們是否隱含了與框架相同的缺點:
遵循語法:大多數編程語言都強制要求這么做(CoffeeScript、Elm、Kotlin 等)。但需要注意的是,它們是 JavaScript 的超集(TypeScript、Flow),你仍然可以用純 JavaScript 編寫你選擇的某些部分。
如果你使用的是非常舊的編程語言(包括 JavaScript)版本,就需要升級,但升級頻率比框架低很多。
需要學習它們的語法。不過,你可以循序漸進地學習超集編程語言,因為你的代碼的某些部分可以繼續使用傳統 JS。
對于非超集編程語言來說,離散化技能確實是一個風險。因為它們的編譯具有普適性,可能不是最優的,而你可能沒有意識到這一點。也許你可以使用更簡單和高效的 JS 代碼來完成同樣的操作。
需要對缺點做出妥協,因為我們無法改變轉譯成 JS(或者使用 tsconfig.json 做一點定制)或編譯成 WebAssembly 的過程。有些語言可能還會忽略 JS 的一些概念。
具備可移植性,因為通常代碼可以轉譯到 ES5(但有時你不得不妥協,即使你想要轉譯到 ES6)。WebAssembly 很新,所有現代瀏覽器都支持它。
提供與其他 JS 代碼的互操作性。例如,Typescript 可以被配置為支持 JS。
在一個普通的應用程序中,我們要小心謹慎地使用非超集語言,因為它們或多或少都隱含了一些約束。超集語言(TypeScript、Flow)通過避免“要么全有要么全無”來最小化這些約束,我們應該在它們可以帶來價值的地方使用它們。
需要注意的是,在 JavaScript 之上構建的語言層意味著我們的工具鏈中又增加了一層復雜性,可能會因為某些原因招致失敗(見下文)。此外,在經過編譯或轉譯之后,開發階段的好處也會消失(通常在運行時不會強制執行類型或可見性約束檢查)。
聲音 | 陳軍:聯盟鏈可以不用發行代幣進行激勵:本期金色相對論中,對于無幣區塊鏈的共識及其技術如何應用在場景中,杭州量子大學區塊鏈研究院聯合執行院長陳軍表示,無幣不是沒有Token,它是區塊鏈記賬的一部分,只是當沒有資產上鏈的時候,Token就是代幣,當資產上鏈時Token就是對資產的標記,這個時候如果再發行一個用來為這個資產定價的Token,那就是代幣。
無幣區塊鏈應用在聯盟鏈、私有鏈環境下,私有鏈肯定不需要共識,聯盟鏈的也比較容易實現,通常是輪流記賬,如果聯盟太大,就需隨機排隊加上輪流記賬,這個可以不用激勵,只是解決公正公平問題。如果一定要激勵,直接用收取的法幣交易手續費來激勵就可以。
所以在聯盟鏈環境下一般都可以用法幣做支付,不需要發行代幣,但會有代表交易標的Token,方便查詢和追溯。[2018/9/29]
基于不“重寫框架”的假設,就會得出普通的 JS 應用程序不應該使用開發庫的結論。這是完全錯誤的。“重新發明輪子”,即從頭開始重寫一切,并不是一個明智的目標。我們的目標是消除框架(而不是開發庫)中隱含的約束,請不要將其與“自己編寫一切”的教條混淆在一起。
因此,如果你自己不能編寫某些代碼(可能是因為沒有時間,或者因為需要太多的專業知識),使用開發庫并沒有什么錯。你只需要關心:
模塊化:如果你只需要一小部分功能,就要避免依賴整個大開發庫;
避免冗余:在沒有標準的情況下才使用開發庫,并優先選擇實現了標準的開發庫;
避免鎖定:不要直接使用開發庫的 API,而是把它們包裝在應用程序 API 中。
需要注意的是,不要被那些聲稱它們不是框架的文檔或文章所迷惑(因為它們“沒有被明確定義”成框架,或者沒有定義一個“完整的應用程序”):只要隱含了約束,它們就是框架。
Holovaty 說,只是應用模式(不使用框架)來構建軟件是不夠的。
模式是眾所周知的東西,不特定于某種開發過程。它們本身是自我文檔化的,因為它們可以被有經驗的開發人員快速識別出來。
這里僅舉幾個例子:
模型、視圖和控制器模式(MVC);
根據配置創建對象的工廠模式;
簡化反應式編程的觀察者模式;
用于遍歷集合的迭代器模式;
用于延遲加載、安全檢查的代理模式;
用于封裝操作(可能基于上下文被觸發)的命令模式。
這樣的模式有很多:你可以自由地用它們來滿足你的需求。如果一個模式為你的應用程序的一個典型問題提供了典型的解決方案,你一定要用它。更寬泛地說,任何符合 SOLID 原則和具有良好內聚力的東西都有利于應用程序的靈活性和可維護性。
在面試開發者時,當被問及在構建一個普通應用程序時他們主要會擔心哪些東西時,他們大多數會回答:實現復雜的模型變化檢測和后續的“視圖”更新。這是典型的“工具法則”效應,它會讓你按照框架的思路思考問題,但實際上你的一些簡單的需求根本不需要用到框架:
“視圖”只是 DOM 元素。你當然可以對它們進行抽象(你也應該這樣做),但最終它們也只是抽象而已。
更新它們只是調用 viewElement.replaceChild(newContent) 的問題,不需要更新更大范圍的 DOM,也不需要重畫或滾動。更新 DOM 的方法有好多種,可以插入文本,也可以操作實際的 DOM 對象,只要選一個適合你的就行了。
在普通應用程序中,“檢測”什么時候需要更新視圖通常是沒有必要的。因為在大多數情況下,你只知道在一個事件之后需要更新什么,然后你直接執行這個命令就可以了。當然,在某些情況下,你可能需要通過反轉依賴和通知觀察者(見下文)來進行一般性的更新。
開發人員不希望缺失的另一個特性是編寫帶有動態部分或監聽器的 HTML 片段。
首先,DOM API(如 document.createElement("button"))并不是那么難,而且實際上比任何模板語言都更強大,因為你可以全面訪問這些 API。編寫很長的 HTML 片段可能很乏味,如果它們真的很長,可以將它們拆分成更細粒度的組件。
不過,將這些元素視為模板確實可以提高可讀性。那么該如何管理它們呢?這里有多種方法:
現在可以在瀏覽器中使用HTML模板了(實際上從2017年就可以了)。它們提供了構建可重用的HTML <template>片段的能力。這實際上是Web組件的一部分。
Binance首席執行官趙長鵬:最近加密貨幣價格下滑不用擔心:據btcmanager消息,Binance創始人兼首席執行官趙長鵬在推特上表示,此次加密貨幣價格下跌是正常現象。加密貨幣市場幾乎每年都在經歷同樣的模式,幣價下跌隨之而來的是幣價急速上升。不用太過于擔心。Binance整體對加密貨幣市場抱有信心。[2018/6/18]
JavaScript從ES6(2015)開始支持模板字面量,你可以很輕松地將值嵌入到字符串中。你可以嵌入原始類型(數字、字符串,包括其他HTML代碼等),但不能嵌入更復雜的元素,例如注冊了監聽器的DOM元素。
我們可以借助標記模板字面量函數將復雜的值(如DOM節點)嵌入到模板中。ObservableHQ已經設計了一個非常方便的工具,可以用它編寫html`<header>${stringOrNode}</header>這樣的代碼,或者實現更復雜的模板,比如html`<ul>${items.map(item => `<li>${item.title}</li>}</ul>。
模板中的條件或循環語句該怎么辦?且不說這可能從來都不是一個好主意(UI 中不應該包含邏輯),你可以(也應該)只用 JS 來實現邏輯,然后使用上面的技術將結果插入到模板中。
現在,我們有了基本的模板,那么該如何將事件綁定到 DOM 節點呢?這里也有幾種選擇:
HTML事件處理器代碼(<button onclick="myClickHandler(event)">)可以被插入到HTML源代碼中,但這并非最好的辦法,因為指定的處理器只在指定的范圍內可用。
事件處理器API(button.addEventListener("click", myClickHandler))可用于所有通過DOM API或HTML標記模板字面量函數創建的節點。
那么定制或業務事件該怎么辦?如果我需要對應用程序的某個組件觸發的一些事件作出反應該怎么辦?這里也有多種處理方式:
自定義事件:你可以通過擴展 EventTarget 來創建自己的事件類,并派發或監聽它們,就像“標準”事件一樣。
理論上說,使用 EventEmitter 也是一種辦法(存在于 Node 中,在瀏覽器中作為庫存在),但它很少被使用。
觀察者模式:你可以構建自己的觀察者,也可以考慮使用 RxJs,它是這方面的標準。你只需要構建一個 Subject,并在發生事件時通知所有訂閱者,讓訂閱者對事件做出反應。
雖說開發普通的應用程序不同于開發復雜的基礎設施(也就是用于托管組件的容器),但如果一些東西在系統中會多次出現,那么將它們設計成可重用組件(與上下文無關)仍然是一個好主意。無論你使用何種技術,也無論是業務還是技術,一定程度粒度的抽象仍然是有用的:將與同一業務概念相關的數據和規則封裝成一個可重用的對象,或者構建可以在應用程序多個地方進行實例化的小部件,總歸是個好主意。
創建組件的方法有很多,具體視自己的需求而定。早在 2017 年,Mev-Rael 就提出了很多技巧,用于處理 JavaScript 組件的狀態、自定義屬性和視圖。當然,我們不要拘囿于別人推薦的技術,而是要先考慮自己的需求,然后再選擇合適的技術。
除了標準的小部件組件(通常是標準的 Web 組件),任何一個組件都應該能夠:
將邏輯和視圖拆分開(通常會使用 MVC 模式)。把它們混合在一起通常會導致代碼不易于維護,還會降低靈活性(例如,如果你想同時以詳情或表格的形式顯示一條記錄,你的 RecordComponent 只需要使用 DetailRecordView 或 RowRecordView)。
參數化組件的行為或視圖。
通過觸發事件的形式通知訂閱者組件中發生了某些事件(通常是在發生用戶交互之后)。
同步:如果發生一些事件,組件應該能夠進行重繪。這個使用反應式開發庫(如 RxJS)可以很容易實現。
在任何情況下,無論你選擇了什么樣的設計策略,你的組件(或者更具體地說,它的相關“視圖”)都必須能夠提供一些 HTML 渲染結果。你可以使用包含 HTML 代碼的字符串,但 HTMLElement(或 Element)通常是更好的選擇(可讀性高,直接更新,可以綁定事件處理器),而且性能更好(不需要解析)。
此外,你可能希望使用來自第三方的外部組件。由于專有框架的流行程度較高,它們可以更大程度地利用社區開發的庫和組。它們中的大多數實際上與純 JS 實現的特性(比如 JQuery)并沒有太大不同,但問題是,它們缺乏互操作性,所以到最后你會發現自己需要的其實是純 JS 或 Web 組件。
所幸的是,這樣的庫確實存在,比如 Vanilla JS Toolkit,盡管可能不太常見。在 Web 組件方面,webcomponents.org 列出了 2000 多個元素。甚至還有普通的 Web 組件,只是它們與我們要討論的不太相關(更多的是關注輕量級實現,而不是互操作性)。
在 SPA 中管理路由需要使用 Web History API。雖然這并不復雜,但你仍然可能希望將其委托給簡單的路由器庫,如 Navigo。
你所要做的就是在路由時用一個 DOM 元素替換另一個 DOM 元素(使用 replaceChildren() 或 replaceWith() 方法)。
按需加載 JavaScript 代碼是任何一個 Web 應用程序都需要考慮的問題。你一定不希望為了顯示一個登錄界面而加載全部的應用程序代碼。
早在 2009 年,在 Web 框架出現之前,James Burke(Dojo 開發者)就發布了 RequireJS(最開始叫“RunJS”)來解決這個問題。從那時起,隨著模塊化的出現,出現了更多的技術。從 ES6(2015)開始,我們可以動態加載代碼。在 Node 中可以,在瀏覽器中也可以:
那么如何將模塊分拆到單獨的文件中?打包器(如 Webpack)可以為你做這些工作。
需要注意的是,在導入路徑里你應該只使用常量,否則打包器就無法猜到你想要加載什么,就會將所有可能的文件都打包在一個文件中。例如,await import(./welcome/${moduleName}) 將把所有東西都打包到指定的目錄中,因為打包器不知道變量 moduleName 在運行時會是什么。
越來越多的框架為原生平臺(如 React Native)提供了運行、遷移或編譯應用程序的方法,以便將它們作為獨立應用程序部署到 Android 或 iOS 移動系統上。
除了考慮開發真正的原生應用程序之外,更普遍的解決方案是將 Web 應用程序嵌入到原生容器中,比如之前的 PhoneGap(現已停止維護)或 Apache Cordova,現在的 NativeScript(它支持框架,如 Angular,也支持普通的應用程序),或者像 Electron 這樣的原生 Web 應用程序包裝器,或者 Electron 的輕量級后繼者 Tauri。
很多框架在前端和后端運行的代碼是相似的,這樣更容易實現對 SEO 友好的服務器端渲染(SSR)。
這可能是一個又酷又便利的特性,但需要注意的是,它也可能導致服務器鎖定。因此,在向應用程序引入框架鎖定之前,你需要考慮它對項目、基礎設施、客戶端技術等方面的影響。
所幸的是,你也可以在不使用框架的情況下實現這個特性。
從服務器端渲染
采用普通的實現方案在一開始看起來很簡單:不就是返回 HTML 嗎?是的,你已經有現成的組件了,但是:
你還需要一個服務器端 DOM API,因為默認情況下,服務器端不提供 DOM API(由 Domenic Denicola 負責維護的 JSDOM 或經過優化的 Happy DOM 就是很好的選擇)。
你的渲染組件不能假設是 DOM 是在客戶端或服務器端,也就是說,不要使用全局 DOM,因為在服務器端,每個請求都需要一個 DOM。要做到這一點,你需要從(客戶端或服務器)應用程序上下文中選擇 DOM 對象(windowdocument 和類型,如 Node、HTMLElement、NodeFilter),而不是直接獲取。
在客戶端和服務器應用程序之間共享渲染組件有多種辦法,比如將其發布在包存儲庫中,但最靈活的應該是讓應用程序包引用 monorepo 中的模塊。
添加交互性
然而,一旦 HTML 元素被轉換成字符串,在這些元素上設置的所有事件處理器都丟失了。為了恢復交互性,你需要一些“補水”步驟,也就是注入腳本,讓它們在客戶端執行。框架因其普適性很難做到這一點。就拿影子 DOM 來說,它們不斷嘗試改進算法,希望能夠以最聰明的方式做到這一點,但如果我們把問題縮小到應用程序層面,就會變得簡單很多。
當然,在普通的服務器應用程序中做到這一點也意味著需要將 JS 腳本注入到響應消息中(通過引用或內聯,具體取決于你想要怎樣的“漸進”程度,比如將 Web 組件所需的代碼嵌入到 HTML 響應中,讓它們在客戶端執行)。
普通的解決方案讓你可以控制在哪里、什么時候以及附加哪些東西:你可以先只發送 HTML,再加載基本的交互性 JavaScript,然后加載更多(取決于用戶的操作),等等。
這比本文中提到的任何一個東西都簡單,因為它們是應用程序代碼,而不是通用的框架代碼。
多年來,國際化問題都是通過庫來處理的(最終也被集成到框架中)。要自己集成這些庫也很容易,但你也可以選擇自己實現一個,因為與通用庫相比,自己的實現可以支持更簡單、更有效的消息類型。
這里為你提供了:
類型檢查:每個消息都有一個靜態類型(和幾個翻譯實現),所以 IDE 可以檢查你是否使用了有效的消息屬性,并為你提供自動補全功能。
翻譯完整性檢查:在為所有消息鍵提供所有語言的翻譯之前,無法通過編譯。
你所需要做的就是(加載和)實例化與用戶語言環境相關的消息類。通用庫不會提供這種特定于業務的消息類型。
如果你想要擺脫對強約束性軟件技術棧的依賴,那你很可能也想擺脫對工具的依賴:你不希望只有靠著它們(它們的局限性、性能、錯誤、版本)才能向前走。你不希望被一個你無法解決的構建問題(或者需要數小時或數天才能解決)所困擾(特別是如果你使用的是最近構建的版本,而它們還沒有經過充分的實戰測試)。
話雖如此,你仍然很難避免使用這些工具。大多數情況下,你的產品代碼必須以某種方式打成包,包括縮小體積、混淆、代碼拆分、搖樹優化、延遲加載、包含樣式等。毫無疑問,現有的打包工具如 Webpack、Parcel、ESBuild 或 Vite 會做得比你更好。
你所能做的是:
盡可能少用轉譯。例如,使用 TypeScript 可能是件好事,但它會帶來額外的復雜性,你的工具鏈中必須有相應的工具來處理這種復雜性。CSS 也一樣,特別是最新版本,不值得你用預處理器(如 Sass)來處理它們。
盡可能少用工具。你用的工具越多,就越有可能出問題或無法滿足你的需求。
如果確實需要使用工具,請選擇最流行的工具,因為它們經過實戰測試,更有可能滿足你的需求(這樣你就不會陷入“改變需求或更換工具”的困境)。過早使用最新的打包工具可能會為你節省幾秒鐘的構建時間,但這些時間很可能都不夠用來理解工具文檔、處理 bug 或處理因缺乏支持而導致的問題。
說到底,最大的挑戰不是技術上的,而是關于人的:
你要走出舒適區。希望你終將能夠明白,使用普通的解決方案并不是那么困難,框架的復雜性比它們帶來的好處要大得多。此外,你可能會看到更多新的 API(WebComponents、ES6 模塊、代理、MutationObserver……),而且 Web 比你想象的更現代、更強大。
至于其他人,你可以嘗試說服他們。他們可能不愿意這么做,因為任何人都不愿意開啟自己從未嘗試過的旅程。
其他人可能會跟你說:
“你要開發自己的框架”:不,我們要開發的是應用程序,而不是框架。
“你要寫更多的代碼”:也許,但也許不會太多(取決于用了多少開發庫),因為這需要與框架的樣板代碼進行比較。但不管怎樣,需要加載的代碼都會更少。
“你將不斷地重新發明輪子”:當然不是。不使用框架是為了不遵循它們預定義的規則(配置、生命周期管理、刷新機制等),但我們并沒有忘記 DRY 原則,我們仍然可以(并且應該)使用經過實戰測試的第三方庫。
“你需要為每一個功能寫更多的代碼”:不,你可以遵循自己的規則,而不是使用框架樣板代碼。
“沒有文檔可看”:肯定不會有框架文檔(因為根本就沒有框架),但你仍然需要寫應用程序文檔。值得一提的是,使用模式有助于你自動文檔化你的軟件設計。你只需要關心應用程序的代碼文檔,而如果你多使用一個框架,就需要多看一份文檔。
“不會有約束或模式來指導開發人員”:不,如果你確實需要約束,沒有什么能阻止你(你只需要定義契約就行了)。
“你會錯過性能提升”,比如曾經被大肆炒作的虛擬 Dom(如今受到了挑戰,包括來自 Svelte 或 Aurelia 框架的挑戰):不,因為需要這些“性能提升”的是框架本身(為了通用性),而不是應用程序。相反,通用框架更有可能錯過一些可以通過自定義代碼實現的性能提升。
你遇到這個問題是因為你沒有使用框架。每一個問題(包括漏洞、延遲、招募等)都會被歸咎于因為沒有使用框架。因為大多數開發人員的經驗是,所有正常運行的東西都使用了框架,默認情況下,不使用它們將被認為是有風險的。一旦出現問題,無論是否與不使用框架有關,這個假設都會被認為是正確的。他們忘記了在使用框架時也會遇到類似的問題。
“我們找不到開發者”:他們會說很難找到能夠寫純 JS 代碼的開發者。這句話是對的,也是錯的。因為很多開發者(且不說管理者)會發現自己更習慣于使用框架。如果他們從來沒有使用過或不了解基本的 Web API,那么他們可能會對從零開始構建一個 Web 應用程序感到害怕。但是,如果你想要開發高質量的應用程序,就不應該去找這種類型的開發者。當然,現在找 React 開發者很容易,但你需要的不只是 React 開發者,而是優秀的開發者。
“你無法獲得與框架相同的代碼質量”。當然,框架或開發庫通常是由行業里有經驗的開發者編寫的。但是,框架的代碼主要與框架特定的活動相關(組件生命周期、通用的刷新機制和優化、工具,等等),與你的應用程序無關。此外,即使使用了框架,你仍然可能做出糟糕的設計,寫出糟糕的代碼。應用程序的質量總是更多地取決于團隊的質量,而不是因為缺少框架。
“你無法獲得與框架相同的性能”:不,我們可以獲得更好的性能。行業里關于框架采用了可以“提升性能”的復雜技術的說法就不在這里討論了,因為它們可能主要被用來解決框架通用解決方案的性能缺陷(比如虛擬 DOM)。
毫無疑問,性能最好的框架是那些在普通代碼之上添加層數較少的框架。框架的“優化”更多的是為了彌補框架本身的開銷。
結? ? 論
不使用框架構建 Web 應用程序并非意味著要自己構建框架,它是關于在不使用通用引擎的情況下開發應用程序,目的是:
避免散失控制和被隱含約束(鎖定、升級成本等);
可以進行優化(性能、體積、設計)。
也就是只編寫特定于應用程序的代碼(業務和技術),包括使用開發庫。你真正應該關注的框架是你自己的框架,也就是那個特定于應用程序的框架。這是真正的“專注于業務”,也是最有效的。
這并沒有你想象的那么難,特別是有了現代標準的加持(在必要時主流瀏覽器可以通過 polyfill 來支持新特性)。
Tags:WEBDOMAPIHTMMetaWeb3PadDeFi KingdomblockchaincapitalHTML價格
4 月 2 日,Terraform Labs 成員 Ezaan 于社區提出新提案,建議引入穩定幣流動性的新“黃金標準”4pool.
1900/1/1 0:00:001.DeFi代幣總市值:1349.91億美元 DeFi總市值 數據來源:coingecko2.過去24小時去中心化交易所的交易量:624.
1900/1/1 0:00:00我最近采訪了 Solana 頭號錢包 Phantom 的 CEO Brandon Millman.
1900/1/1 0:00:001.DeFi代幣總市值:1158.75億美元 DeFi總市值 數據來源:coingecko2.過去24小時去中心化交易所的交易量:51.
1900/1/1 0:00:00前些天在國外某網絡社交平臺上,一位網友控訴自己在某元宇宙虛擬空間中進行“VR睡眠”的時候慘遭“VR強奸”.
1900/1/1 0:00:00頭條 ▌馬斯克發布關于狗狗幣的視頻金色財經報道,Ark Invest研究總監Brett Winton發布了一條有關老派人士不了解比特幣礦工的推文.
1900/1/1 0:00:00