簡易js模板引擎寫法
前面
js 模板引擎有很多很多,我以前常用 art-template ,有時也會拿 vue 來當模板引擎用。
直到......
年初的時候,我還在上個專案群組,那時候程式碼規格是未經允許不能使用【外部程式碼】,囧。
有了需求,那就寫吧,但後來因為一些原因沒用。後來分了產線,自己搭了一套構建,用了幾個月感覺挺爽,把這小段代碼按照比較大眾的規範重寫,跟大家分享下。
https://github.com/shalldie/mini-tpl
語法
首先是選擇模板文法,ejs文法是首選,因為大眾,更不需要去學習指令型模板引擎的那些東西。
如果寫過 jsp 或 asp/asp.net 的可以直接上手。
怎麼用它?
我要這麼用
<body> <p id="root"></p> <script id="tplContent" type="text/html"> <ul> <% for(var i=0; i<data.length; i++){ var item = data[i]; if(item.age < 30){%> <li>我的名字是<%=item.name%>,我的年龄是<%=item.age%></li> <%}else{%> <li>my name is <%=item.name%>,my age is a sercet.</li> <%}%> <% } %> </ul> </script> <script src="../build/mini-tpl.min.js"></script> <script> var data = [{ name: 'tom', age: 12 }, { name: 'lily', age: 24 }, { name: 'lucy', age: 55 }]; var content = document.getElementById('tplContent').innerHTML; var result = miniTpl(content, data); document.getElementById('root').innerHTML = result; </script> </body>
想要這麼用,那就分析一下怎麼才能實現。
new Function
1 const content = 'console.log("hello world");'; 2 3 let func = new Function(content); 4 5 func(); // hello world
new Function ([arg1[, arg2[, ...argN]],] functionBody)
functionBody# 一個含有包含函數定義的JavaScript語句的<strong>字串</strong>
#。
使用Function建構器產生的函數,並不會在建立它們的上下文中建立閉包;它們一般在全域作用域中被建立。
當執行這些函數的時候,它們只能存取自己的本地變數和全域變量,不能存取Function建構器被呼叫產生的上下文的作用域。 (MDN)
也就是說:
可以用 new Function 來動態的建立一個函數,去執行某動態產生的函數定義js語句。
透過 new Function 產生的函數,作用域在全域。
那麼傳參有3種:
、把變數放到全域(扯淡)
、函數傳參有3種:
把變數放到全域(扯淡)、
函數傳參 用call/ apply把值傳給函數的this
const content = 'console.log(data);';
let func = new Function('data', content);
func('hello world'); // hello world
登入後複製
到此為止,雛形有了。下面來拆分。 模板分割const content = 'console.log(data);'; let func = new Function('data', content); func('hello world'); // hello world
先看模板:
<% for(var i=0; i<data.length; i++){ var item = data[i]; if(item.age < 30){%> <li>我的名字是<%=item.name%>,我的年龄是<%=item.age%></li> <%}else{%> <li>my name is <%=item.name%>,my age is a sercet.</li> <%}%> <% } %>
js 邏輯部分,由<%%> 包裹,
js 變數的佔位,由<%= %> 包裹,剩下的是普通的要拼接的html字串
部分。- 也就是說,需要用正規找出的部分有3種:
- 邏輯部分的js內容
- 佔位部分的js內容
其它的 純文本
其中第2項,js佔位的部分,也屬於拼接文字。所以可以放在一起,就是
js部分
拼接部分
。 正規提取當然是選擇正規表示式啊!這裡先跟大家擴充一下關於偽數組方面的內容,以及瀏覽器的控制台如何看待偽陣列:
不扯遠,直接說結論:#只要有 int類型的length屬性
,有function類型的splice
屬性。 那麼瀏覽器就會認為他是一個陣列。如果裡面的其它屬性按照索引來排序,甚至還可以像陣列裡面的項目一樣在控制台展示出來。
這個判斷方式叫做duck typing
,如果一個東西長得像鴨子,而且叫起來像鴨子,,,那麼它就是鴨子 0_o#回到正文,這個需要多次從模板中,把js邏輯部分和文字依序提取出來。對於每一次提取,都要獲取提取出的內容,本次匹配最後的索引項目(用於提起文本內容)。所以我選擇了 RegExp.prototype.exec 。
屬性/索引 | |
---|---|
| [0] |
| [1],...[n] |
| #index |
| input |
通过这样,就可以拿到匹配到的 js 逻辑部分,并通过 index 和本次匹配到的内容,来获取每个js逻辑部分之间的文本内容项。
要注意,在全局匹配模式下,正则表达式会接着上次匹配的结果继续匹配新的字符串。
/** * 从原始模板中提取 文本/js 部分 * * @param {string} content * @returns {Array<{type:number,txt:string}>} */ function transform(content) { var arr = []; //返回的数组,用于保存匹配结果 var reg = /<%(?!=)([\s\S]*?)%>/g; //用于匹配js代码的正则 var match; //当前匹配到的match var nowIndex = 0; //当前匹配到的索引 while (match = reg.exec(content)) { // 保存当前匹配项之前的普通文本/占位 appendTxt(arr, content.substring(nowIndex, match.index)); //保存当前匹配项 arr.push({ type: 1, //js代码 txt: match[1] //匹配到的内容 }); //更新当前匹配索引 nowIndex = match.index + match[0].length; } //保存文本尾部 appendTxt(arr, content.substr(nowIndex)); return arr; } /** * 普通文本添加到数组,对换行部分进行转义 * * @param {Array<{type:number,txt:string}>} list * @param {string} content */ function appendTxt(list, content) { content = content.replace(/\r?\n/g, "\\n"); list.push({ txt: content }); }
得到了js逻辑项 和 文本内容 ,就可以把他们拼在一起,来动态生成一个function。要注意的是,文本内容中,包含 js占位项,这个地方要转换一下。
/** * 模板 + 数据 =》 渲染后的字符串 * * @param {string} content 模板 * @param {any} data 数据 * @returns 渲染后的字符串 */ function render(content, data) { data = data || {}; var list = ['var tpl = "";']; var codeArr = transform(content); // 代码分割项数组 for (var i = 0, len = codeArr.length; i < len; i++) { var item = codeArr[i]; // 当前分割项 // 如果是文本类型,或者js占位项 if (!item.type) { var txt = 'tpl+="' + item.txt.replace(/<%=(.*?)%>/g, function (g0, g1) { return '"+' + g1 + '+"'; }) + '"'; list.push(txt); } else { // 如果是js代码 list.push(item.txt); } } list.push('return tpl;'); return new Function('data', list.join('\n'))(data); }
这样就完成了简易的模板引擎,不要觉得拼字符串慢。
在现代浏览器(IE8开始)中,特地对字符串的操作做了大量的优化,用 += 拼字符串,要比用数组 push 再 join 的方式快很多很多,即使放到IE7(IE6不清楚)中,我这里测试也是拼字符串快。。。
以上是簡易js模板引擎寫法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

用法:在JavaScript中,insertBefore()方法用於在DOM樹中插入一個新的節點。這個方法需要兩個參數:要插入的新節點和參考節點(即新節點將要插入的位置的節點)。

Java回呼函數的基本寫法和使用方法引言:在Java程式設計中,回呼函數是一種常見的程式模式,透過回呼函數,可以將某個方法作為參數傳遞給另一個方法,從而實現方法的間接呼叫。回調函數的使用,在事件驅動、非同步程式設計和介面實作等場景中非常常見。本文將介紹Java回呼函數的基本寫法和使用方法,並提供具體的程式碼範例。一、回呼函數的定義回呼函數是一種特殊的函數,它可以作為參數
