UNPKG

pdquickui

Version:

A web frontend framework that supports conditional and loop rendering, data binding, monitoring data changes, and automatic rendering. Previously known as QuickUI.

827 lines (736 loc) 20.4 kB
<img src="./static/image/logo.png" width=80> # QuickUI *(原名:PDQuickUI,自 `0.6.0` 版本起更名為 QuickUI)* ![tag](https://img.shields.io/badge/tag-JavaScript%20Library-bb4444) ![size](https://img.shields.io/github/size/pardnchiu/QuickUI/dist%2FQuickUI.js) ![license](https://img.shields.io/github/license/pardnchiu/QuickUI)<br> [![npm](https://img.shields.io/npm/v/pdquickui)](https://www.npmjs.com/package/pdquickui) [![download](https://img.shields.io/npm/dm/pdquickui)](https://www.npmjs.com/package/pdquickui) [![jsdeliver](https://img.shields.io/jsdelivr/npm/hm/pdquickui)](https://www.jsdelivr.com/package/npm/pdquickui)<br> [![](https://img.shields.io/badge/read-English%20Version-ffffff)](https://github.com/pardnchiu/QuickUI/blob/main/README.en.md) `QuickUI` 是從 [PDRenderKit](https://github.com/pardnchiu/PDRenderKit) 中獨立出來的前端渲染框架,專注於強化前端框架功能。<br> 透過引入虛擬 DOM 概念重寫渲染邏輯,提升渲染效能,並實現更高效的數據監聽和自動更新。<br> 本專案移除了 `PDRenderKit` 中針對 `prototype` 的擴展,確保兼容性與效能,適合用於複雜的應用場景。<br> 提供 `module` 和非 `module` 版本,授權從 `PDRenderKit` 的 `GPL-3.0` 更改為 `MIT`。<br> ## 特點 - **清晰的架構**:UI 和資料邏輯分離,維護方便。 - **代碼簡潔**:減少重複代碼,提升可讀性。 - **自動渲染**:監控資料變動並自動更新,減少手動操作。 - **輕量化**:使用原生 JS 和內建 API 撰寫,無任何外部依賴。 ## 安裝方式 - 從 npm 安裝 ```bash npm i @pardnchiu/quickui ``` - 從 CDN 引入 - **引入 `QuickUI` 套件** ```html <!-- Version 0.6.0 and above --> <script src="https://cdn.jsdelivr.net/npm/@pardnchiu/quickui@[VERSION]/dist/QuickUI.js"></script> <!-- Version 0.5.4 and below --> <script src="https://cdn.jsdelivr.net/npm/pdquickui@[VERSION]/dist/PDQuickUI.js"></script> ``` - **Module 版本** ```javascript // Version 0.6.0 and above import { QUI } from "https://cdn.jsdelivr.net/npm/@pardnchiu/quickui@[VERSION]/dist/QuickUI.esm.js"; // Version 0.5.4 and below import { QUI } from "https://cdn.jsdelivr.net/npm/pdquickui@[VERSION]/dist/PDQuickUI.module.js"; ``` ## 使用方法 - **初始化 `QUI`** ```Javascript const app = new QUI({ id: "", // 指定渲染元素 data: { // 自訂 DATA }, event: { // 自訂 EVENT }, when: { before_render: function () { // 停止渲染 }, rendered: function () { // 已渲染 }, before_update: function () { // 停止更新 }, updated: function () { // 已更新 }, before_destroy: function () { // 停止銷毀 }, destroyed: function () { // 已銷毀 } } }); ``` ## 功能介紹 自動渲染:加載自動渲染在檢測到資料變更時自動重新渲染。 <details> <summary>屬性概覽</summary> | 屬性 | 描述 | | --- | --- | | `{{value}}` | 將文字插入到 HTML 標籤中,並隨資料變更自動更新。 | | `:path` | 搭配 `temp` 標籤,用於將外部文件中的 HTML 片段加載到當前頁面。 | | `:html` | 使用文本替換元素的 `innerHTML`。 | | `:for` | 支援 `item in items`、`(item, index) in items`、`(key, value) in object` 格式,遍歷資料集合,生成對應的 HTML 元素。 | | `:if`<br>`:else-if`<br>`:elif`<br>`:else` | 根據條件顯示或隱藏元素,實現分支邏輯。 | | `:model` | 將資料綁定到表單元素(如 `input`),當輸入變更時自動更新資料。 | | `:hide` | 根據特定條件隱藏元素。 | | `:effect` | 用於指定元素的過渡效果,如 `fade-in` 或 `expand`,以增強用戶體驗。 | | `:mask` | 控制區塊載入時的動畫效果,支援 `true|false|1|0`,提升載入動態視覺效果。 | | `:[attr]` | 設定元素屬性,例如 `ID`、`class`、圖像來源等。<br>範例:`:id`、`:class`、`:src`、`:alt`、`:href`... | | `:[css]` | 設定元素CSS,例如 `margin`、`padding` 等。<br>範例:`:background-color`、`:opacity`、`:margin`、`:top`、`:position`... | | `@[event]` | 添加事件監聽器,當事件觸發時執行指定操作。<br>範例:`@click`、`@input`、`@mousedown`... | </details> <details> <summary>文字替換</summary> ### `{{value}}` - index.html ```HTML <h1>{{ title }}</h1> <script> const app = new QUI({ id: "app", data: { title: "test" } }); </script> ``` - Result ```HTML <body id="app"> <h1>test</h1> </body> ``` *** ### `:html` - index.html ```HTML <section :html="html"></section> <script> const app = new QUI({ id: "app", data: { html: "<b>innerHtml</b>" } }); </script> ``` - Result ```HTML <body id="app"> <section> <b>innerHtml</b> </section> </body> ``` </details> <details> <summary>插入區塊</summary> > [!NOTE] > 確保測試時已禁用瀏覽器中的本地文件限制或使用實時服務器。 ### `:path` - test.html ```html <h1>path heading</h1> <p>path content</p> ``` - index.html ```html <body id="app"> <temp :path="./test.html"></temp> </body> <script> const app = new QUI({ id: "app" }); </script> ``` - Result ```html <body id="app"> <!-- 直接插入 PATH 內容 --> <h1>path heading</h1> <p>path content</p> </body> ``` </details> <details> <summary>迴圈渲染</summary> ### `:for` - index.html ```html <body id="app"> <ul> <li :for="(item, index) in ary" :id="item" :index="index">{{ item }} {{ CALC(index + 1) }}</li> </ul> </body> <script> const app = new QUI({ id: "app", data: { ary: ["test1", "test2", "test3"] } }); </script> ``` - Result ```html <body id="app"> <li id="test1" index="0">test1 1</li> <li id="test2" index="1">test2 2</li> <li id="test3" index="2">test3 3</li> </body> ``` *** ### 巢狀迴圈 - index.html ```html <body id="app"> <ul> <li :for="(key, val) in obj"> {{ key }}: {{ val.name }} <ul> <li :for="item in val.ary"> {{ item.name }} <ul> <li :for="(item1, index1) in item.ary1"> {{ CALC(index1 + 1) }}. {{ item1.name }} - ${{ item1.price }} </li> </ul> </li> </ul> </li> </ul> </body> <script> const app = new QUI({ id: "app", data: { obj: { food: { name: "Food", ary: [ { name: 'Snacks', ary1: [ { name: 'Potato Chips', price: 10 }, { name: 'Chocolate', price: 8 } ] }, { name: 'Beverages', ary1: [ { name: 'Juice', price: 5 }, { name: 'Tea', price: 3 } ] } ] }, home: { name: 'Home', ary: [ { name: 'Furniture', ary1: [ { name: 'Sofa', price: 300 }, { name: 'Table', price: 150 } ] }, { name: 'Decorations', ary1: [ { name: 'Picture Frame', price: 20 }, { name: 'Vase', price: 15 } ] } ] } } } }); </script> ``` - Result ```html <body id="app"> <ul> <li>food: Food <ul> <li>Snacks <ul> <li>1. Potato Chips - $10</li> <li>2. Chocolate - $8</li> </ul> </li> <li>Beverages <ul> <li>1. Juice - $5</li> <li>2. Tea - $3</li> </ul> </li> </ul> </li> <li>home: Home <ul> <li>Furniture <ul> <li>1. Sofa - $300</li> <li>2. Table - $150</li> </ul> </li> <li>Decorations <ul> <li>1. Picture Frame - $20</li> <li>2. Vase - $15</li> </ul> </li> </ul> </li> </ul> </body> ``` </details> <details> <summary>條件渲染</summary> - index.html ```html <body id="app"> <h1 :if="heading == 1">{{ title }} {{ heading }}</h1> <h2 :else-if="isH2">{{ title }} {{ heading }}</h2> <h3 :else-if="heading == 3">{{ title }} {{ heading }}</h3> <h4 :else>{{ title }} {{ heading }}</h4> </body> <script> const app = new QUI({ id: "app", data: { heading: [Number|null], isH2: [Boolean|null], title: "test" } }); </script> ``` - Result: `heading = 1` ```html <body id="app"> <h1>test 1</h1> </body> ``` - Result: `heading = null && isH2 = true` ```html <body id="app"> <h2>test </h2> </body> ``` - Result: `heading = 3 && isH2 = null` ```html <body id="app"> <h3>test 3</h3> </body> ``` - Result: `heading = null && isH2 = null` ```html <body id="app"> <h4>test </h4> </body> ``` </details> <details> <summary>模板渲染</summary> - index.html ```HTML <body id="app"></body> <script> const test = new QUI({ id: "app", data: { hint: "hint 123", title: "test 123" }, render: () => { return ` "{{ hint }}", h1 { style: "background: red;", children: [ "{{ title }}" ] }` } }) </script> ``` - result ```HTML <body id="app"> hint 123 <h1 style="background: red;">test 123</h1> </body> ``` </details> <details> <summary>雙向綁定</summary> ```html <body id="app"> <input type="password" :model="password"> <button @click="show">test</button> </body> <script> const app = new QUI({ id: "app", data: { password: null, }, event: { show: function(e){ alert("Password:", app.data.password); } } }); </script> ``` </details> <details> <summary>事件偵測</summary> ```html <body id="app"> <button @click="test">test</button> </body> <script> const app = new QUI({ id: "app", event: { test: function(e){ alert(e.target.innerText + " clicked"); } } }); </script> ``` </details> <details> <summary>CSS設置</summary> > [!NOTE] > 支援 `:[CSS屬性]` 的簡易設定方式,直接將資料綁定到樣式屬性。 - index.html ```html <body id="app"> <button :width="width" :backdround-color="color">test</button> </body> <script> const app = new QUI({ id: "app", data: { width: "100px", color: "red" } }); </script> ``` - Result: ```html <body id="app"> <button style="width: 100px; backdround-color: red;">test</button> </body> ``` </details> <details> <summary>可用函式</summary> ### `LENGTH()` - index.html ```HTML <body id="app"> <p>Total: {{ LENGTH(array) }}</p> </body> <script> const app = new QUI({ id: "app", data: { array: [1, 2, 3, 4] } }); </script> ``` - result ```HTML <body id="app"> <p>Total: 4</p> </body> ``` *** ### `CALC()` - index.html ```HTML <body id="app"> <p>calc: {{ CALC(num * 10) }}</p> </body> <script> const app = new QUI({ id: "app", data: { num: 1 } }); </script> ``` - result ```HTML <body id="app"> <p>calc: 10</p> </body> ``` *** ### `UPPER()` / `LOWER()` - index.html ```HTML <body id="app"> <p>{{ UPPER(test1) }} {{ LOWER(test2) }}</p> </body> <script> const app = new QUI({ id: "app", data: { test1: "upper", test2: "LOWER" } }); </script> ``` - result ```HTML <body id="app"> <p>UPPER lower</p> </body> ``` *** ### `DATE(num, format)` - index.html ```HTML <body id="app"> <p>{{ DATE(now, YYYY-MM-DD hh:mm:ss) }}</p> </body> <script> const app = new QUI({ id: "app", data: { now: Math.floor(Date.now() / 1000) } }); </script> ``` - result ```HTML <body id="app"> <p>2024-08-17 03:40:47</p> </body> ``` </details> <details> <summary>懶加載</summary> ### `:lazyload` - index.html ```html <body id="app"> <img :lazyload="image"> </body> <script> const app = new QUI({ id: "app", data: { image: "test.jpg" }, option: { lazyload: true, // 圖片延遲加載: true|false (預設: true) } }); </script> ``` - result ```html <body id="app"> <img src="test.jpg"> </body> ``` *** ### `SVG` 替換 - test.svg ```XML <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <line x1="18" y1="6" x2="6" y2="18" stroke="black" stroke-width="2" stroke-linecap="round"/> <line x1="6" y1="6" x2="18" y2="18" stroke="black" stroke-width="2" stroke-linecap="round"/> </svg> ``` - index.html ```html <body id="app"> <temp-svg :src="svg"></temp-svg> </body> <script> const app = new QUI({ id: "app", data: { svg: "test.svg", }, option: { svg: true // SVG 檔案轉換: true|false (預設: true) } }); </script> ``` - result ```html <body id="app"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <line x1="18" y1="6" x2="6" y2="18" stroke="black" stroke-width="2" stroke-linecap="round"> <line x1="6" y1="6" x2="18" y2="18" stroke="black" stroke-width="2" stroke-linecap="round"> </svg> </body> ``` </details> <details> <summary>多國語言</summary> > [!NOTE] > 若為物件格式,直接配置多語言內容。 > 若為字串格式,會透過 `fetch` 動態載入語言檔案。 - en.json ```JSON { "greeting": "Hello", "username": "Username" } ``` - index.html ```html <body id="app"> <h1>{{ i18n.greeting }}, {{ i18n.username }}: {{ username }}</h1> <button @click="change" data-lang="zh">切換至中文</button> <button @click="change" data-lang="en">Switch to English</button> </body> <script> const app = new QUI({ id: "app", data: { username: "帕登" }, i18n: { zh: { greeting: "你好", username: "用戶名" }, en: "en.json", }, i18nLang: "zh | en", // 選擇顯示語言 event: { change: e => { const _this = e.target; const lang = _this.dataset.lang; app.lang(lang); }, } }); </script> ``` - result `i18nLang = zh` ```html <body id="app"> <h1>你好, 用戶名: 帕登</h1> <button data-lang="zh">切換至中文</button> <button data-lang="en">Switch to English</button> </body> ``` - result `i18nLang = en` ```html <body id="app"> <h1>Hello, Username: 帕登</h1> <button data-lang="zh">切換至中文</button> <button data-lang="en">Switch to English</button> </body> ``` </details> <details> <summary>生命週期</summary> ```html <body id="app"></body> <script> const app = new QUI({ id: "app", when: { before_render: function () { // 停止渲染 // retuen false }, rendered: function () { // 已掛載 }, before_update: function () { // 停止更新 // retuen false }, updated: function () { // 已更新 }, before_destroy: function () { // 停止銷毀 // retuen false }, destroyed: function () { // 已銷毀 } } }); </script> ``` </details> <details> <summary>資料獲取</summary> ```html <body id="app"> <input type="text" :model="test"> <button @click="get">測試</button> </body> <script> const app = new QUI({ id: "app", data: { // 給 input 綁定的值 test: 123 }, event: { get: _ => { // 點擊時彈出內容為 test 值的通知 alert(app.data.test); }, set: _ => { let dom = document.createElement("button"); // 按鈕點按事件設置為 get 函式 dom.onclick = app.event.get; app.body.append(dom); } } }); </script> ``` </details> ## 開發者 <img src="https://avatars.githubusercontent.com/u/25631760" align="left" width="96" height="96" style="margin-right: 0.5rem;" /> <h4 style="padding-top: 0">邱敬幃 Pardn Chiu</h4> [![](https://pardn.io/image/mail.svg)](mailto:dev@pardn.io) [![](https://skillicons.dev/icons?i=linkedin)](https://linkedin.com/in/pardnchiu) ## 授權條款 本專案依據 [MIT](https://github.com/pardnchiu/PDMarkdownKit/blob/main/LICENSE) 授權使用。 ## 獲取完整原始碼 [聯絡我](mailto:dev@pardn.io) 獲取完整未混淆源碼<br> 可隨意修改、商業使用,根據需求選擇授權版本: - 需保留 `Powered by @pardnchiu/quickui` 的版權聲明:$7,500 - 完全自主,無需添加版權聲明:$10,000 *** ©️ 2024 [邱敬幃 Pardn Chiu](https://www.linkedin.com/in/pardnchiu)