Init.js:全棧 JavaScript 的原因和方法指南
已發表: 2022-03-11故事
所以,你和你的聯合創始人有這個偉大的創業點子,對吧?
您一直在腦海中添加功能。
通常,您會詢問潛在客戶的意見,他們都很喜歡。
好的,所以人們想要它。 甚至還有一些錢可以賺。 他們不能擁有它的唯一原因是因為您還沒有實現它。
所以最後,有一天你坐下來說:“讓我們開始吧!” 很快,您將嘗試弄清楚如何實現應用程序的業務邏輯,這是推動產品向前發展的殺手級功能:您知道如何去做,並且知道自己可以做到。
“完畢! 有用!” 你說。 您的概念證明是成功的! 剩下的就是將其打包成一個 Web 應用程序。
“好吧,讓我們創建網站”,你說。
然後,您意識到一個事實:您需要選擇一種編程語言; 您需要選擇一個(現代)平台; 您需要選擇一些(現代)框架; 您需要配置(和購買)存儲、數據庫和託管服務提供商; 你需要一個管理界面; 你需要一個權限系統; 你需要一個內容管理器。
您需要做出數十個架構決策。 你想做出正確的:你想使用允許快速開發、不斷迭代、最大效率、速度、穩健性等的技術。 你想要精益,你想要敏捷。 您希望使用能夠幫助您在短期和長期內取得成功的技術。 而且它們並不總是容易挑選出來的。
“我不知所措,”你說,因為你感到不知所措。 你的能量和以前不一樣了。 你試圖把事情拼湊起來,但工作量太大了。
你的概念證明慢慢枯萎和死亡。
提案
在以這種方式自己放棄了大量的想法之後,我決定設計一個解決方案。 我將其稱為“Init”項目(或 init.js)。
這個想法的核心是有一個單一的項目來啟動它們,讓開發人員或技術創始人一次做出所有這些重要的決定,並根據這些決定獲得一個合適的啟動模板。 我知道批評者會說什麼,“一個解決方案不能適用於所有問題”(仇恨者會討厭)。 他們可能是對的。 但是我們可以盡最大努力創建一個近似的解決方案,我認為 Init 非常接近。
為了最好地實現這一目標,我們必須牢記一些關鍵思想。 在開發 Init 時,我考慮過:
成分
組件化是任何系統的一個關鍵特徵,因為它允許您在不同的項目中重用軟件組件——這是 Init 的主要目標。 但是組件化也帶來了一個副產品,“可替換性”,這將是我們用“幾乎”相同的解決方案解決幾個不同問題的最佳盟友。
易於開發
有些問題,在某個地方有一個最好用 Brainf*ck 編寫的解決方案。 但是實現該解決方案(在 Brainfuck 中)幾乎不可能編寫,更不用說閱讀了。 這將花費您的時間和大量的精力。 一般來說,您應該使用使開發更容易的語言和平台,而不是讓您(或以後可能從事開發工作的任何人)更難。
社區
無論您選擇什麼平台,請確保它有一個龐大的社區,並且可以幫助您解決最常見和不常見的問題。 記住:jQuery 可能不是最快、最乾淨或最優雅的庫——但僅僅因為它的社區而成為贏家。
牢記這些目標,接下來我將向您展示我是如何在創建 Init 時做出自己的決定的。
在其核心,Init 利用了“全棧 JavaScript”範式(有些人將它或它的一個子集稱為 MEAN Stack)。 通過使用這樣的堆棧,Init 能夠只使用一種語言,同時為開發 Web 應用程序創建一個極其靈活且功能齊全的環境。 簡而言之,Init 讓您不僅可以將 JavaScript 用於客戶端和服務器開發,還可以用於構建、測試、模板等。
但是讓我們放慢腳步,問問自己:使用 JavaScript 真的是個好主意嗎?
為什麼我選擇 JavaScript
自 1998 年以來,我一直是一名 Web 開發人員。當時我們使用 Perl 進行大部分的服務器端開發,但即使從那時起,我們也已經在客戶端使用了 JavaScript。 從那時起,Web 服務器技術發生了巨大的變化:我們經歷了一波又一波的語言和技術,例如 PHP、AP、JSP、.NET、Ruby、Python 等等。 開發人員開始意識到在客戶端和服務器環境中使用兩種不同的語言會使事情變得複雜。 在單一語言下統一的最初嘗試試圖在服務器上創建客戶端組件並將它們編譯為 JavaScript。 這並沒有按預期工作,並且大多數項目都失敗了(例如:ASP MVC 替換 ASP.NET Web 表單,並且 GWT 可能會在不久的將來被 Polymer 替換)。 但它本質上是一個好主意:客戶端和服務器上的單一語言,允許我們重用組件和資源(這就是關鍵字:資源)。
答案很簡單:將 JavaScript 放在服務器上。
JavaScript 實際上是在 Netscape Enterprise Server 中與 JavaScript 服務器端一起誕生的,但當時這種語言還沒有準備好。 經過多年的反複試驗,Node.js 終於出現了,它不僅將 JavaScript 放在了服務器上,還推動了非阻塞編程的思想,永遠改變了我們編寫“fread”(I/O)的方式(閱讀這裡更多)。
但這些想法並不新鮮——那麼為什麼它們會在 Node.js 中如此流行呢? 可以通過多種方式實現簡單的非阻塞編程。 也許最簡單的是使用回調和事件循環。 在大多數語言中,這不是一件容易的事:雖然“回調”是其他一些語言的常見功能,但事件循環不是,而且您經常發現自己在與外部庫(例如:Python、Tornado)搏鬥。 但是在 JavaScript 中,回調和事件循環一樣內置在語言中,幾乎每個涉足 JavaScript 的程序員都熟悉它們(或者至少使用過它們,即使他們不太了解事件是什麼)循環是)。 突然間,地球上的每家初創公司都可以在客戶端和服務器端重用開發人員(即資源),從而解決“需要 Python Guru”的職位發布問題。
所以現在我們有了一個非常快的平台(感謝非阻塞編程)和一種非常容易使用的編程語言(感謝 JavaScript)。 但夠了嗎? 它會持續嗎? 我確信 JavaScript 將在未來佔有重要的位置。 讓我來告訴你為什麼:
函數式編程
JavaScript 是第一個將函數式範式推向大眾的編程語言(當然,Lisp 最先出現,但大多數程序員從未使用 Lisp 構建過可用於生產的應用程序)。 Javascript 的主要影響 Lisp 和 Self 充滿了創新的想法。 這些想法可以解放我們的思想來探索新技術、模式和範式。 它們都延續到 JavaScript。 看看 monads、Church numbers,甚至(舉個更實際的例子)Underscore.js 的集合函數,它可以為你節省一行又一行的代碼。
動態對象和原型繼承
沒有類的面向對象編程(並且沒有無窮無盡的類層次結構)允許快速開發(創建對象、添加方法和使用它們),但最重要的是,通過允許程序員修改對象實例來減少維護任務期間的重構時間的類。 這種速度和靈活性為快速發展鋪平了道路。
JavaScript 是互聯網
JavaScript 是為 Internet 設計的,它從一開始就存在,並且不會消失。 所有破壞它的嘗試都失敗了:例如,Java Applets 的衰落,VBScript 被微軟的 TypeScript(編譯成 JavaScript)取代,以及 Flash 在移動市場和 HTML5 手中的消亡。 在不破壞數百萬網頁的情況下更換 Javascript 是不可能的,因此我們的目標應該是改進它。 沒有人比 ECMA 的 Technical Committee 39 更適合這項工作了。
好的,JavaScript 的替代品每天都在誕生,比如 CoffeeScript、TypeScript 和數百萬種編譯成 JavaScript 的語言。 這些替代方案可能對開發階段有用(通過源映射),但從長遠來看,它們將無法取代 JavaScript,原因有兩個:它們的社區永遠不會變得更大,它們的最佳功能將被 ECMA 腳本採用(即 JavaScript )。 JavaScript 不是一種彙編語言:它是一種高級編程語言,具有您可以理解的源代碼——所以您應該理解它。
端到端 JavaScript:Node.js 和 MongoDB
因此,這些就是使用 JavaScript 的原因。 現在,我將使用 JavaScript 作為使用 Node.js 和 MongoDB 的理由。
節點.js
Node.js 是一個用於構建快速且可擴展的網絡應用程序的平台——這幾乎就是 Node.js 網站所說的。 但 Node.js 不僅如此:它是任何具有 I/O 訪問權限的 JavaScript 應用程序的首選運行時環境。 即使您不打算使用 Node.js 編寫主服務器應用程序,您也可以使用構建在 Node.js 之上的工具來改進您的開發過程。 例如:用於單元測試的 Mocha.js,用於自動構建任務的 Grunt.js,甚至用於全文代碼編輯的括號。
因此,如果您要為服務器或客戶端編寫 JavaScript 應用程序,您應該查看一些 Node.js 示例,因為您每天都需要並使用它。 有一些有趣的替代方案,但它們都沒有占到 Node.js 社區的 10%。
MongoDB
MongoDB 是一個基於 NoSQL 文檔的數據庫,它使用 JavaScript 作為其查詢語言,讓我能夠完成端到端的 JavaScript 平台。 但這甚至不是選擇這個數據庫的主要原因。
MongoDB 是一個無模式數據庫,它允許您以靈活的方式持久化對象,從而更快地適應需求的變化。 此外,它具有高度可擴展性和基於 map-reduce 的特性,使其適用於大數據應用程序。 MongoDB 非常靈活,它可以用作無模式文檔數據庫、關係數據存儲(儘管它缺少事務),甚至可以用作緩存響應的鍵值存儲。
使用 Express.js 進行服務器組件化
服務器端組件化絕非易事。 但是隨著 Express.js(和 Connect.js)的出現,“中間件”的概念出現了。 在我看來,中間件是在服務器上定義組件的最佳方式。 如果您想將其與已知模式進行比較,它非常接近管道和過濾器。

基本思想是您的組件是管道的一部分。 管道處理請求(輸入)並生成響應(輸出),但您的組件不負責整個響應。 相反,它只修改它需要的內容,然後委託給管道的下一部分。 當管道的最後一段完成處理時,將響應發送回客戶端。
我們將這些“管道片段”稱為“中間件”。 顯然,我們可以創建兩種中間件:
中間件:處理請求和響應的那些,但不完全負責響應本身,因此他們委託給下一個中間件。
決賽:對最終反應負有全部責任的人。 他們處理和修改請求和響應,但不需要委託給下一個中間件。 在實踐中,建議您委託給下一個中間件以實現架構靈活性(即,稍後添加更多中間件),即使該中間件不存在(在這種情況下,響應將直接發送到客戶端)。
作為一個具體的例子,考慮服務器上的“用戶管理器”組件。 就中間件而言,我們會有決賽和中間件。 對於我們的決賽,我們將具有創建用戶和列出用戶等功能。 但在我們可以執行這些操作之前,我們需要我們的中間體進行身份驗證(因為我們不希望未經身份驗證的請求進入並創建用戶)。 一旦我們創建了這些身份驗證中間體,我們就可以將它們插入任何我們想要將以前未經身份驗證的功能轉換為經過身份驗證的功能的地方。
單頁應用程序
Init 項目專注於創建單頁應用程序 (SPA)。 大多數 Web 開發人員都不止一次地嘗試過 SPA。 我已經構建了幾個(大部分是專有的),我可以自信地說它們只是 Web 應用程序的未來。 您是否曾經將 SPA 與移動連接上的常規 Web 應用程序進行比較? 響應能力的差異在幾十秒的數量級上。
SPA 是 Web 的未來——那麼您為什麼要以傳統形式構建您的產品呢? 我聽到的一個常見論點是人們擔心 SEO。 但如果你處理得當,這應該不是問題:谷歌本身有一個非常好的教程,關於如何做到這一點,這裡也有一些很好的評論。
帶有 Backbone.js、Marionette.js 和 Twitter Bootstrap 的客戶端 MV*
關於 SPA 的 MVC* 框架已經談了很多。 這是一個艱難的選擇,但我想說前三名是 Backbone.js、Ember.js 和 Angular.js。
這三個都非常受歡迎。 但是哪一個最適合你?
不幸的是,我必須承認我對 Angular.js 的經驗非常有限,所以我將把它排除在討論之外(有關更多信息,請參閱 Angular.js 教程)。 現在,Ember.js 和 Backbone.js 代表了解決同一問題的兩種不同方式。
Backbone.js 是最小的、簡單的,並且為您提供了足夠創建一個簡單的 SPA。 另一方面,Ember.js 是一個用於創建 SPA 的完整且專業的框架。 它有更多的花里胡哨,但也有更大的學習曲線。
根據您的應用程序的大小,決定可以像查看 featuresUsed/featuresAvailable 比率一樣簡單,這會給您一個很大的提示。
在 Init 的情況下,我想涵蓋大多數場景,所以我選擇 Backbone.js 來輕鬆創建 SPA,而 Backbone.Marionette.View 用於組件化。 這樣,每個組件都是一個簡單的應用程序,最終的應用程序可以像我們想要的那樣複雜。
造型也是一個挑戰,但我們可以再次依靠框架來拯救我們。 對於 CSS,沒有比 Twitter Bootstrap 更好的了,它提供了一整套開箱即用且易於定制的樣式。
Bootstrap 是使用 LESS 語言創建的,它是開源的,因此我們可以根據需要對其進行修改。 它帶有大量的 UX 控件,這些控件在 Bootstrap 站點上有詳細記錄。 此外,還有一個自定義模型,可讓您創建自己的模型。 這絕對是這份工作的人選。
最佳實踐:Grunt.js、Mocha.js、Chai.js、RequireJS 和 CoverJS
最後,我們應該定義一些我們的最佳實踐,並看看 Init 如何幫助您實現和維護它們。 我們的解決方案以幾個基於 Node.js 本身的工具為中心。
Mocha.js和Chai.js :
這些工具允許您通過應用 TDD 或 BDD 來改進開發過程,提供組織單元測試的基礎設施和自動運行它們的運行器。
有數以千計的 JavaScript 單元測試框架。 那麼為什麼要使用 Mocha.js? 簡短的回答:它靈活且完整。
長答案:它有兩個重要的特性(界面、報告器)和一個重要的缺席(斷言)。 讓我解釋。
接口:也許您習慣於套件和單元測試的 TDD 概念,或者您更喜歡帶有“描述”和“應該”的行為規範的 BDD 理念。 Mocha.js 允許您使用這兩種方法。
Reporters :運行您的測試將生成結果報告,您可以使用各種報告器格式化這些結果。 例如,如果您需要為持續集成服務器提供數據,您可以找一個報告員來做這件事。
缺少斷言庫:Mocha.js 遠非問題,旨在讓您使用您選擇的斷言庫,為您提供更大的靈活性。 有很多選擇,但這就是 Chai.js 發揮作用的地方。
Chai.js 是一個靈活的斷言庫,可讓您使用三種主要斷言樣式中的任何一種:
Assert :來自 TDD 老派的經典斷言風格。 例如:
assert.equal(variable, ”value”);
Expect :可鏈接的斷言樣式,最常用於 BDD。 例如:
expect(variable).to.equal(“value”);
應該:也用在 BDD 中,但我更喜歡期望,因為應該與行為規範“它(“應該做某事..”)'聽起來重複。 例如:
variable.should.equal(“value”);
Chai.js 與 Mocha.js 完美結合。 僅使用這兩個庫,您就可以使用 TDD、BDD 或任何可以想像的樣式編寫測試。
咕嚕聲:
Grunt.js 允許您自動執行構建任務,從簡單的複制粘貼和文件連接到模板預編譯、樣式語言(即 SASS 和 LESS)編譯、單元測試(使用 mocha.js)、linting 和代碼縮小(例如,使用 UglifyJS 或 Closure Compiler)。 您可以將自己的自動化任務添加到 Grunt,或者搜索 Grunt 註冊表,那裡有成百上千的插件可用(同樣,使用具有強大社區的工具是值得的)。 Grunt 還可以監視您的文件並在修改文件時觸發操作。
要求:
RequireJS 可能聽起來像是用 AMD 加載模塊的另一種方式,但我可以向您保證,它遠不止於此。 要理解為什麼,我們首先需要提到模塊命名空間的概念(例如,demo.views.hello),它通過將每個模塊包裝在自己的命名空間中來避免污染全局命名空間。 問題是,這些模塊是不可重用的:如果你修改一個“實例”的命名空間,你正在修改所有“實例”的命名空間。 與此相反,RequireJS 允許您從一開始就定義可重用的模塊。 (此外,它將幫助您採用依賴注入以避免讓您的模塊訪問全局變量。)
封面JS :
代碼覆蓋率是評估測試的指標。 顧名思義,它告訴您當前的測試套件覆蓋了多少代碼。 CoverJS 通過在代碼中檢測語句(而不是像 JSCoverage 之類的代碼行)並生成代碼的檢測版本來衡量測試的代碼覆蓋率。 它還可以生成報告以提供給您的持續集成服務器。
使用分支切換功能
當我啟動 Init 時,我需要一種方法讓用戶激活和停用他們可能在項目中需要的各種功能。 我決定對 git 的分支系統採取激進的方法來實現這個功能。
本質上,每個分支都代表用戶可能想要包含的特性或功能。 如果您從頭開始一個項目,請從您需要的最小分支開始,然後通過與所需的分支合併來添加其他技術。 例如,假設您想使用 Backbone.js 和 Marionette.js 開始您的項目。 好吧,您可以從 Backbone.js 分支開始,並將其與 Marionette 分支合併,繼續添加您想要添加的每一個功能。
目前,這種合併以添加功能的想法只能用於技術模板(例如,Backbone、Node、Express)。 但在未來,您將能夠在後端(例如,從 MongoDB 到 Postgres)和客戶端實現之間切換。
使用 Init 啟動項目並立即部署到 Heroku
從未有過更簡單的方式來啟動項目。 只需前往 GitHub 存儲庫,檢查具有最新提交的分支(現在是用戶管理器,儘管將來可能會改變)然後:
- 為您的項目創建目錄(或使用現有目錄)。
- 使用“git init”創建您的存儲庫(或使用現有存儲庫)。
使用 init 添加遙控器
git remote add init git://github.com/picanteverde/init.git
獲取你想要的分支
git pull init usermanager
獲取 Heroku 進程文件
git pull init heroku-webprocess
安裝 Heroku Toolbelt 後,創建一個 Heroku 應用程序
heroku create
將你的 master 分支推送到 Heroku
git push heroku master
- 訪問您的應用,在 Heroku 上啟動並運行!
現在您只需幾行代碼即可開始開發您的殺手級功能。 不僅如此,您還將在盡可能自動化的開發套件中使用最新、最高效的技術進行開發。
我希望您可以使用 Init 來啟動您的下一個大創意。 請記住檢查 Init 存儲庫以獲取新的修復和功能 - 它的開發非常活躍,我期待聽到您的反饋。