手把手教你寫一個 Javascript 框架:項目結構

過去幾個月中,RisingStack 的 JavaScript 工程師 Bertalan Miklos 編寫了新一代客戶端框架 NX 。Bertalan 將通過 編寫 JavaScript 框架 系列文章與我們分享他在編寫框架過程中的收獲:

  本章將展示 NX 的項目結構,并講述如何解決可擴展性、依賴注入以及私有變量等方面的一些困難。

  本系列章節如下:

  項目結構(正是本文)

  執行調度(Execution timing)

  沙箱求值

  數據綁定簡介

  ES6 Proxy 實現數據綁定

  自定義元素

  客戶端路由

  項目結構

  沒有放之四海而皆準的項目結構,但有一些基本準則。感興趣的同學可以看下我們的 Node Hero 系列中的《 Node.js 項目結構教程 》這一章。

  NX 框架概覽

  NX 的目標是成為一個開源社區驅動的易于擴展的項目。項目特點如下:

  包含現代客戶端框架必須的所有特性;

  除 polyfill 外,沒有任何外部依賴;

  代碼總量 3000 行;

  沒有代碼多于 300 行的???

  單個特性??橐覽擋懷?3 個。

  項目各??橐覽倒叵等縵巒妓荊?/p>

  這種結構為典型框架開發難題提供了一種解決方案。

  擴展性

  依賴注入

  私有變量

  可擴展性實現

  社區驅動項目必須易于擴展。故項目的核心部分應當小巧,并擁有一個預定義的依賴處理系統。前者確保項目易于理解,后者則保證框架穩定。

  本節先聚焦于實現小巧的內核。

  現代框架應當擁有的主要特性就是創建自定義元素并將其應用于 DOM 的能力。NX 的核心只有一個 component 函數,它的工作正在于此這個函數允許用戶配置、注冊一個新類型的元素。

  component(config)

  .register('comp-name')

  注冊的 comp-name 是空組件類型,可以按照預期在 DOM 中實例化。

  

  下一步是保證能使用新特性擴展組件。為保持簡潔、可擴展,這些新特性不應該污染核心部分。這時候使用依賴注入就很方便了。

  利用中間件實現依賴注入(DI)

  如果你對依賴注入不太熟悉,建立先閱讀這篇文章:《 Dependency Injection in Node.js 》。

  依賴注入是一種設計模式,在這種模式中,一個或多個依賴或服務被注入到或引用傳遞給一個獨立對象。

  DI 解決了硬性依賴,卻引入了新問題。使用者需要知道如何配置、注入依賴。大多客戶端框架都將這些工作交給 DI 容器,幫助開發者完成。

  DI 容器指的是知道如何實例化、配置對象的對象。

  另外一種方式則是中間件 DI 模式,這在服務端得到廣泛應用(如 Express、Koa 等)。其中的奧秘在于,所有可注入的依賴(中間件)擁有相同的接口,以相同方式注入。這種方法則無需 DI 容器。

  為保持簡潔,我決定采用中間件模式。若你曾使用過 Express,以下代碼自然不會陌生:

  component()

  .use(paint) // inject paint middleware

  .use(resize) // inject resize middleware

  .register('comp-name')

  function paint (elem, state, next) {

  // elem is the component instance, set it up or extend it here

  elem.style.color = 'red'

  // then call next to run the next middleware (resize)

  next()

  }

  function resize (elem, state, next) {

  elem.style.width = '100 px'

  next()

  }

  中間件在新的組件實例插入 DOM 時執行,通?;岣道┱掛恍┬綠匭?。如若不同庫擴展相同對象,則將導致名稱沖突。暴露私有變量會加劇問題,并可能被其他人意外利用。

  公開 API 小巧玲瓏,其余部分隱身不見,正是避免問題的優秀方案。

  處理私有變量

  JavaScript 中需要通過函數作用域來實現私有變量。需要使用跨作用域私有變量時,人們習慣使用 _ 前綴來標志,并將其公開暴露。這可以避免意外使用,但無法解決命名沖突。更好的辦法是使用 ES6 的 Symbol 基本數據類型。

  Symbol 是一種唯一的、不可變的數據類型,可用作對象屬性標識符。

  下面的代碼展示了 symbol 的實際使用:

  const color = Symbol()

  // a middleware

  function colorize (elem, state, next) {

  elem[color] = 'red'

  next()

  }

  這樣一來,通過 color symbol (以及元素 elem)就能獲取 red 。 red 的私有程度,可由 color symbol 暴露的不同程度控制。合理數量的私有變量,通過中心存儲讀取,是一種優雅的解決方案。

  // symbols module

  exports.private = {

  color: Symbol('color from colorize')

  }

  exports.public = {}

  index.js 如下:

  // main module

  const symbols = require('./symbols')

  exports.symbols = symbols.public

  在項目內部,所有??槎伎煞夢?symbol 存儲對象,但私有部分不會對外暴露。公有部分則可用于對外部開發者暴露一些低層次特性。這就避免了意外使用,因為開發者需要明確引入需要使用的 symbol。此外,symbol 引用也不會像字符串一樣產出沖突,是故不會產生命名沖突。

  以下幾點概括了不同場景下的用法:

  1. 公有變量

  正常使用.

  function (elem, state, next) {

  elem.publicText = 'Hello World!'

  next()

  }

  2. 私有變量

  項目私有的跨作用域變量,應當在私有 symbol 對象中加入一個 symbol key。

  // symbols module

  exports.private = {

  text: Symbol('private text')

  }

  exports.public = {}

  并在需要的地方引入。

  const private = require('symbols').private

  function (elem, state, next) {

  elem[private.text] = 'Hello World!'

  next()

  }

  3. 半私有變量

  低層次 API 的變量,應當在公有 symbol 對象中加入一個 symbol key。

  // symbols module

  exports.private = {

  text: Symbol('private text')

  }

  exports.public = {

  text: Symbol('exposed text')

  }

  并在需要的地方引入。

  const exposed = require('symbols').public

  function (elem, state, next) {

  elem[exposed.text] = 'Hello World!'

  next()

  }


來源:眾成翻譯

上一篇: 移動直播技術的極限優化與高效研發

下一篇: HTML6 展望

分享到: 更多
砸开没东西的假色子 秒时时彩计划软件下载 彩票双色球开奖结果 pk10千里马全天计划 11选5走势图安徽时时网 英冠 新加坡28有什么技巧 最准的平特一肖图好跑狗图 一肖中特免费资料公开选料 原创美女六肖图原版 投注比例与赛果分析 精准六肖 免费资料2019 福运网五分快三 手机上打金花的技巧 香港36码特围 公开我的必胜法