前端開發系列054-基礎篇之文字插值
title: 前端開發系列054-基礎篇之文字插值
tags:
categories: []
date: 2017-12-24 00:00:00
本文討論前端框架\模板中文字插值的實現方案,本文將會主要以[Vue]()框架作為參考討論文字插值語法的具體實現和推導方案,並補充相關的技術細節。
文字插值
在Vue官網文件的第一部分( 宣告式渲染 )我們可以看到下面一段描述。
Vue.js 的核心是一個允許採用簡潔的模板語法來宣告式地將資料渲染進 DOM 的系統: <div id="app"> {{ message }} </div> var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } }) 我們已經成功建立了第一個 Vue 應用! 看起來這跟渲染一個字串模板非常類似,但是 Vue 在背後做了大量工作。 現在資料和 DOM 已經被建立了關聯,所有東西都是響應式的。我們要怎麼確認呢? 開啟你的瀏覽器的JavaScript控制檯,並修改 app.message 的值,你將看到上例相應地更新。
在Vue官網的另一部分(模板語法-插值)說明了“ Vue.js 使用了基於 HTML 的模板語法,允許開發者宣告式地將 DOM 繫結至底層 Vue 例項的資料。所有 Vue.js 的模板都是合法的 HTML ,所以能被遵循規範的瀏覽器和 HTML 解析器解析 ”。
我們知道,在Vue框架中資料繫結的插值語法使用的是Mustache語法 (雙大括號)
,而這篇短小的文章將簡單討論其內部的實現機制。
Class-例項的構建初步
# 標籤部分 <div id="app"> {{ message }} </div> # 引入框架檔案 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> # 建立Vue例項 var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } })
在Vue框架中我們總是會通過上面的方式來建立並得到一個例項物件,在呼叫的時候我們傳遞了一個物件作為建構函式(class)的引數,在該物件中我們設定了掛載的標籤(el屬性)
、例項資料(data屬性)
等資訊。
這裡,我們先提供一個 建構函式 或者是 Class 來模擬這個整體的結構。
/* Class的寫法 */ class Manager { constructor(o) { /* 根據傳入的el來獲取頁面中掛載的標籤 */ this.el = document.querySelector(o.el); /* 把物件引數中的data成員(資料)新增到例項物件 */ /* 在訪問的時候可以直接通過(new Manager()).xx訪問 */ for (let key in o.data) { this[key] = o.data[key]; } } } /* 初始化:傳入配置物件建立例項物件 */ let app = new Manager({ el: "#app", data: { message: "Hello 文頂頂!" } })
在開始的時候,[ 建構函式 \ Class ]的樣子可能可能是像上面這樣的,先嚐試獲取引數物件中el
的值以獲取例項在頁面中掛載的標籤,然後通過一個迴圈結構來把data
中的資料都直接新增到例項物件,這種處理將允許我們直接以app.message
的方式來操作資料。
資料和標籤的渲染關係
設計出基本結構後,現在我們可以開始考慮如果需要把data
中的資料渲染(繫結)到頁面的標籤,那該如何實現? 簡單思考一秒鐘後,我們似乎可以嘗試以下的實踐策略:
(1) 在初始化的操作中先獲取掛載標籤的屬性節點(這很容易辦到,使用innerHTML就可以)。
(2) 在innerHTML中尋找類類似於{{message}}的結構,如果找到那麼摳出雙括號中的欄位-message
(3) 在例項物件中獲取-message欄位對應的value值,使用該值來替換{{message}}部分。
(!) 因為標籤中可能存在多個插值程式碼,因此可能需要迴圈處理,在尋找插值程式碼的時候使用正則匹配或許會比較合適。
下面試著給出用正則來匹配標籤內容並進行替換的核心程式碼,正則表示式的結果可以參考下面的註釋,用於匹配 {{ xxx }} 的特定結構,\s*
表示可以允許存在空格,\\s
表示對\
進行轉義處理,引數g
用以表示應用全域性匹配。
let reg = new RegExp(`{{2}\\s*msg\\s*}{2}`, "g"); // /{{2}\s*msg\s*}{2}/g
this.el.innerHTML = this.el.innerHTML.replace(reg, "文頂頂");
考慮到在引數物件的data
中可能會有多個數據(鍵值對
),且執行文字插值的時候某個資料可能會出現在標籤的多個位置,因此需要通過迴圈的方式來檢查 innerTTML 欄位中每個資料的情況。我們可以通過 Object.keys()
方法來獲取所有的屬性名(key的集合
),然後遍歷該陣列並執行正則替換操作。
<!-- 標籤部分 -->
<div id="app">{{ message }}
<span>{{message}}</span>
<span>{{msg}}</span>
</div>
<!-- JS程式碼部分 -->
<script>
/* Class的寫法 */
class Manager {
constructor(o) {
/* 根據傳入的el來獲取頁面中掛載的標籤 */
this.el = document.querySelector(o.el);
/* 把物件引數中的data成員(資料)新增到例項物件 */
/* 在訪問的時候可以直接通過(new Manager()).xx訪問 */
for (let key in o.data) {
this[key] = o.data[key];
}
/* 獲取data資料中所有的key */
/* 根據data中的屬性集合來遍歷渲染頁面中指定的內容 */
Object.keys(o.data).forEach(ele => {
let reg = new RegExp(`{{2}\\s*${ele}\\s*}{2}`, "g");
/* /{{2}\s*message\s*}{2}/g */
/* /{{2}\s*msg\s*}{2}/g */
console.log(this.el.innerHTML, reg);
this.el.innerHTML = this.el.innerHTML.replace(reg, this[ele]);
})
}
}
/* 初始化:傳入配置物件建立例項物件 */
let app = new Manager({
el: "#app",
data: {
message: "文頂頂",
msg: "米桃兒"
}
})
</script>
當代碼執行的時候,可以看到下面的效果。
至此,便簡單了實現了資料-標籤渲染的功能。如果資料發生變化後標籤中對應的內容也要隨之變化,這種資料驅動UI的結構最核心之處在於監聽資料的變化並通知給UI檢視
,具體實現可以參考下一篇文章。