開箱即用的輕量級前端框架:dagger.js

語言: CN / TW / HK

推薦一個輕量完備的開源前端框架:dagger.js:https://daggerjs.org

什麼是 dagger.js

dagger.js 是一個基於 html 的描述式單頁應用開發框架,通過在頁面 DOM 元素上新增語義化的指令來驅動業務邏輯。從語法特性的角度來說,dagger.js 模板 + 指令的工作方式與 Angular/Vue 比較接近。

dagger.js 採用去元件去 api 設計,沒有對第三方程式碼或工具的依賴,模型簡單,易於理解。與當前的主流前端框架相比,使用者的整體學習和使用成本更低。

使用 dagger.js,開發者將無須依賴於:

專案構建過程

dagger.js 工作在瀏覽器執行時當中。只需要通過 script 標籤引入框架原始碼(release 版本 gzip 壓縮後約 20KB)即可輕鬆建立單頁應用程式。dagger.js 對專案程式碼結構是非侵入式的,您可以以極小成本對歷史專案(即使是非單頁應用程式)進行漸進升級。

包管理工具

dagger.js 內部實現了一個執行時模組管理器,根據路由配置按需動態載入和解析各類模組,為您的應用程式進行極限瘦身。

第三方路由管理工具

dagger.js 內建了基於 hash 的路由管理器。開發者無需引入額外的路由管理類庫。

資料狀態管理工具

dagger.js 倡導資料即狀態的技術理念。有別於 React/Vue 等框架單向資料流模型中對於 state/prop 物件的區分,dagger.js 的作用域資料由框架本身進行維護,並具有全域檢視響應性,開發者不必為不同元件間的資料狀態同步而勞心費神。

開發正規化

不同於 React Hooks 和 Vue 的組合式 API 方案,dagger.js 提供了更加接近原生 javaScript 開發體驗的心智模型。dagger.js 沒有設計任何框架相關的 “語法糖”,開發者編寫的指令碼程式碼只是普通的原生 javaScript 函式。函式定義本身是上下文無關的,其觸發時機、呼叫引數以及副作用完全由呼叫者(指令)所決定。

dagger.js 中不存在元件(Component)實體的概念,指令是串接作用域資料(Model)和頁面檢視(View)的橋樑。換句話說,在 dagger.js 中,一切業務邏輯都以指令作為呼叫入口。某些特定指令可以在執行上下文建立作用域資料,而所有指令都可以作為作用域資料的消費者。指令讀取或者修改作用域資料,進而觸發依賴收集或者頁面檢視的響應式更新。

下文中我們將通過示例程式碼來體驗 dagger.js 的工作模式。

建立作用域

在 dagger.js 中,我們可以通過生命週期指令 +loading 來建立作用域資料:

<div +loading="{ message: 'Hello dagger!' }">
     <!-- 可以在其他指令中訪問或者修改message -->
     ...
 </div>

+loading 指令在宿主元素(div)初始化時由框架觸發呼叫。它的副作用是,當表示式的執行結果是一個平凡物件時,框架將依據此物件在當前上下文創建出新的作用域資料。技術上說,這個作用域資料是指令返回平凡物件的代理物件。

作用域資料在宿主元素被銷燬時由框架自動移除,無需開發者手動管理。

指令

在 dagger.js 中,除去生命週期指令之外,還有兩種重要的指令型別:控制指令和事件處理指令。

兩種指令都可以自由讀寫在其宣告位置可見的作用域資料,但是行為有所不同。

控制指令

控制指令在執行過程中會收集參與計算的作用域變數欄位,並在指令的依賴項發生變化時動態觸發指令重新執行。我們來看一個例子:

<div dg-cloak     +loading="{ a: 1, b: 2, c: 0, d: 0 }"
     $watch#1="c = a + b"
     $watch#2="d = 2 * c"
 >

     <button +click="a = 1, b = 2">reset</button>
     <input type="number" $value#input="a">
     <input type="number" $value#input="b">
     <br>
     span>${ c } = ${ a } + ${ b }</span>
     <br>
     <span>${ d } = 2 * (${ c })</span>
 </div>

示例連結:https://codepen.io/dagger8224/pen/JjpJxzq

上例中,我們在 div 元素上聲明瞭控制指令 $watch 的兩個例項。其中編號為 #1” 的指令依賴於作用域變數下欄位  a 和欄位  b ,編號為 #2 的指令依賴於作用域變數下欄位  c 。當用戶修改  a 或  b 的值時,將首先觸發指令  $watch#1 重新計算,更新欄位  c 的值。 c 值發生變化後再觸發指令  $watch#2 重新計算,進而更新欄位  d 的值。

除去 $watch 指令之外,dagger.js 還提供了更多語義化的控制指令,我們簡單列舉下這些控制指令的使用方法:

<input type="checkbox" $checked="checked">

$checked 指令用於繫結宿主元素的選中狀態,適用於型別為  radio 和  checkbox 的  input 元素,以及  option 元素。

<div $class="className">...</div>

$class 指令用於將表示式的內容繫結到宿主元素的 class 屬性上。

<div $each="array">...</div>

$each 指令用於迴圈渲染具有相同檢視模板的陣列,物件,或者其他可迭代變數。

<div $exist="exist">...</div>

$exist 指令用於切換宿主元素及其子級元素的存在狀態。

<div $html="content"></div>

$html 指令用於在宿主元素下動態建立子級元素。

<input type="file" $file="file">
 <input type="file" multiple $file="files">

$file 指令用於繫結使用者選擇的本地檔案資訊。

<select $selected="selected">
     <option value="1">1</option>
     ...
 </select>

$selected 指令用於繫結宿主元素的選中值,適用於型別為  radio 和  checkbox 的  input 元素,以及  select 元素。

<div $style="style">...</div>

$style 指令用於繫結宿主元素的 style 屬性。

<span $text="text"></span>
 <span>${ text }</span>

$text 指令用於在宿主元素下動態建立文字內容。大多數情況下,我們也可以使用原地模板字串(上例第二種方式)來建立動態文字節點,這樣更加簡單靈活。

<input type="text" $value="message">

$value 指令用於將表示式的內容與 input 元素的 value 建立雙向資料繫結。

事件處理指令

與控制指令相比,事件處理指令的用法更加簡單。事件處理指令由使用者操作或系統事件觸發呼叫,在執行過程中不會對作用域資料進行依賴收集。我們來看一個例子:

<button +click="alert(message)">click</button>

上例中,button 元素上的 +click 指令聲明瞭一個滑鼠點選事件處理函式。類似地,您也可以宣告其他的原生事件處理指令(+keyup,+mouseenter 等等),或者自定義事件處理指令(例如:+custom_event)。

接下來我們來看幾個指令的綜合示例:

指令綜合示例 1

下面的示例程式碼演示了生命週期指令 +loading+loaded+unloading+unloaded ,控制指令  $exist 以及事件處理指令 + click` 的使用:

<div dg-cloak +loading="{ status: '', exist: true }">
     <button +click="exist = !exist">toggle exist</button>
     <span class="status-span">The status of dynamic span: ${ status }</span>
     <span class="dynamic-span"
         $exist="exist"
         +loaded="status = 'existing'"
         +unloading="status = 'removing'"
         +unloaded="status = 'removed'">
         Some content
     </span>
 </div>

點選按鈕將切換作用域欄位 exist 的值,進而觸發聲明瞭  $exist 指令的 span 元素載入或移除。

示例連結:https://codepen.io/dagger8224/pen/wvmgRyj

指令綜合示例 2

下面的示例程式碼演示了控制指令 $each 和  $checked 的使用:

<label $each="[1, 2, 3]">
     <input type="checkbox" name="checkbox1" $checked="checked[index]">checkbox${ item }
 </label>

切換複選框的勾選狀態,作用域變數 checked 欄位的內容將隨之發生變化,這是雙向資料繫結指令的典型用法。

示例連結:https://codepen.io/dagger8224/pen/PoRpROq

各種指令更詳細的定義和使用方法參見 官網文件

模組

接下來我們一起了解下 dagger.js 的模組設計。

在 dagger.js 中,我們把 html 模板,指令碼,層疊樣式表等可複用的程式碼片段統稱為模組。

dagger.js 內部維護了一個執行時模組管理器,開發者通過 json 格式的配置項註冊模組,框架將在應用程式首次載入或頁面內路由發生切換時觸發模組資源按需動態載入、解析和執行。

與作用域資料類似,模組僅在其註冊層級及子層級生效,避免對全域性作用域造成汙染。

您可以根據業務需求靈活配置模組的組合方式(每一個組合方案構成一個名空間)來實現程式碼複用。我們來看一個模組配置項的示例:

<script type="dagger/configs">
     {
         "script": "https://codepen.io/dagger8224/pen/NWyVxwx.js",
         "style": "https://codepen.io/dagger8224/pen/NWyVxwx.css",
         "template": {
             "uri": "#template",
             "style": "style"
         }
     }
 </script>

上面的配置項中,我們註冊了一個名為 script 的指令碼模組,一個名為  style 的樣式模組,和一個名為  template 的模板模組,並將樣式模組的作用範圍限定為模板模組內部。

示例連結:https://codepen.io/dagger8224/pen/rNJrzzj

路由

作為一個完備的單頁應用開發框架,dagger.js 內建了基於瀏覽器 hash 的路由管理器。當頁面路由發生切換時,根作用域下 $router 物件的內容將同步變化,進而驅動頁面檢視產生響應式更新(需要配合  $html 控制指令使用)。我們來看一個示例:

上例中,當路由從  root 切換至  parent1 之後,頁面內容將發生切換。

示例連結:https://codepen.io/dagger8224/pen/zYRQrwP

路由物件的欄位內容可以在下面的示例中檢視:

示例連結:https://codepen.io/dagger8224/pen/PoQBovd

渲染效能

dagger.js 並未採用虛擬 DOM 方案,而是通過細粒度的指令執行來實現頁面檢視增量更新。在 js-framework-benchmark 測試場景中,其綜合執行時效能與 React17 版本相當。

未來計劃

dagger.js 目前已經在多家公司的 B 端管理系統當中得到應用。在未來,我們計劃圍繞框架持續建設周邊生態,通過開源方式對框架進行推廣運營,讓更多的前端開發者有機會了解和使用 dagger.js。

總結

dagger.js 是一個輕量級無依賴的描述式前端開發框架,與主流框架相比,具有更低的學習和使用成本。使用者僅需瞭解上文中介紹的指令,模組與路由的相關概念和用法,就可以快速上手使用 dagger.js 構建單頁應用程式。

以上是對 dagger.js 主要功能特性的概要介紹。更多資訊請檢視官方文件:https://daggerjs.org

github 地址:https://github.com/dagger8224/dagger.js

更多示例截圖

three.js

示例連結:https://codepen.io/dagger8224/pen/QWmwaLq

3D Carousel

示例連結:https://codepen.io/dagger8224/pen/JjLRbmz

Tesla

示例連結:https://codepen.io/dagger8224/pen/RwMGvPv?editors=1010

color picker

示例連結:https://codepen.io/dagger8224/pen/vYRmGJp?editors=1010

progress bar

示例連結:https://codepen.io/dagger8224/pen/dympJXz?editors=1010

text animation

示例連結:https://codepen.io/dagger8224/pen/JjLXppg

您可以在 https://codepen.io/dagger8224/pens 網站檢視用 dagger.js 編寫的更多有趣的 demo:

  • empty page: https://codepen.io/dagger8224/pen/ZErWBpB

  • to do list: https://codepen.io/dagger8224/pen/QWQVYGN

  • $scope animation: https://codepen.io/dagger8224/pen/YzeNmRz

  • anime.js: https://codepen.io/dagger8224/pen/eYMpzvB

  • clock: https://codepen.io/dagger8224/pen/RwMWRYd?editors=1000

  • digital clock: https://codepen.io/dagger8224/pen/LYdZEag

  • add tags: https://codepen.io/dagger8224/pen/ZErjzwm