HTML字符串模板是在寫頁面模板時會用到的一個渲染引擎,本質是字符串替換和代碼執行。格式是下面這樣子的:
輸出值:
<html><body> <%= name %> </body></html>
執行代碼
<html><body><% for(var i = 0; i < 2; i++) {%><div><%= i %></div><% } %></body></html>
就是使用<% 和<%=分別表示執行代碼和輸出變量的值,用%>表示自定義的結束。語法很簡單,不考慮復雜情況下,可以自己動手實現一個。
1. 替換思路
把整個HTML模板讀取成字符串后,將HTML部分當作是字符串,將<% %>和<%= %>中間部分取出進行執行。由於<% %>只是執行代碼,不直接輸出HTML模板,不用處理返回結果。而<%= %>需要把表示式的返回值保存起來,加到輸出結果中。
要根據輸入對象,直接讀取值,可以用with方法。雖然該方法已經不被推薦,但在這種情況下應用還是很方便。將模板解析后,使用new Function生成函數,代入輸入對象,即可實現替換。
2. 實現
module.exports = function regTemplate (str, obj) { let pre = 'with(obj){var _str = "";_str +=\'' let evalStr = str .replace(/[\r\n\t]/g, ' ') .replace(/<%=([\s\S]*?)%>/g, "';_str +=$1;_str +='") .replace(/<%/g, "';") .replace(/%>/g, "_str+='") evalStr = pre + evalStr + "';return _str;}" var func = new Function ('obj', evalStr) return func(obj) }
代碼如上。核心是在with時建立一個空白字符串,用str+=‘ 將第一個html片斷加入到結果字符串中,然后遇到<%= %>時, html字符串結束,添加內部表達式的值,再開始加一下個字符串。遇到<%時,html字符串結束,里面的內容不用處理,直接遇到%>表示應該開始下一字符串,再進行相加HTML字符串。最后添加結尾單引號。
使用new Function生成新函數,代入輸入的對象,返回執行結果。
當然,光這樣還是不夠的,由於HTML中代碼中雖然只推薦使用雙引號,但還是有可能出現單引號,這時模板就會失效。改進一下:
module.exports = function regTemplate (str, obj) { let pre = 'with(obj){var _str = "";_str +=\'' // replace last single quote let arr = str.split('%>') arr[arr.length - 1] = arr[arr.length - 1].replace(/'/g, '\\\'') str = arr.join('%>') let evalStr = str // replace first single quote .replace(/([\s\S]*?)<%/m, function (str, $1) { return $1.replace(/'/g, '\\\'') + '<%' }) // replace middle single quote .replace(/%>([\s\S]*?)<%/g, function (str, $1) { return '%>' + $1.replace(/'/g, '\\\'') + '<%' }) .replace(/[\r\n\t]/g, ' ') .replace(/<%=([\s\S]*?)%>/g, "';_str +=$1;_str +='") .replace(/<%/g, "';") .replace(/%>/g, "_str+='") evalStr = pre + evalStr + "';return _str;}" var func = new Function ('obj', evalStr) return func(obj) }
使用正則替換掉中間和第一段中的單引號。但最后一段的正則我不會寫,嘗試了好久也沒成功,沒辦法只能用字符串處理了,導致程序代碼一下子就多了起來,看起來很不舒服。先這樣了。
3. 總結
寫起來很費勁,想了好久也沒想出方案,最后還是按https://www.cnblogs.com/dolphinX/p/3489269.html上面實現的。主要問題還是思路沒有理清,尤其是關於<% %>這種不處理成字符串的,浪費了很多時間。再者正則使用是一知半解,一遇到復雜的就很難寫。
附上練習源碼地址:https://github.com/wenlonghuo/code-test/tree/master/002_htmlTemplate