Vue中原生template標簽失效如何解決
目錄
- 前言
- 一、事件未綁定的原因
- 二、如何處理原生template標簽
前言
需要原生Javascript + three.js的數據標注平臺加入Vue框架.
本來挺順利的, 我直接在mounted
周期做了初始化, 然后剩下的操作還是交給JavaScript文件執行, 最后發現里面有很明顯的事件觸發問題.
一、事件未綁定的原因
找了整一天也沒找著這事件為什么觸發不了, 在這中間還把代碼簡化掉只留下事件觸發邏輯執行了好幾次.
第二天意識到原生代碼里的template
可能有問題, 在原生環境中template
標簽內部的東西是不會渲染出來的, 雖然解析器在加載頁面的時候確實會處理這部分代碼片段.
取自MDN:
將模板視為一個可存儲在文檔中以便后續使用的內容片段.
雖然解析器在加載頁面時確實會處理 <template> 元素的內容,
但這樣做只是為了確保這些內容有效, 元素內容不會被渲染.
但是放到vue里(這里特指Vue2), 如果template
標簽在Vue實例綁定的元素內部存在(即不是根元素外的那個template
), 那么在DOM中該template
的子元素是正常存在并顯示的, 我以前經常拿template
做v-for
容器.
然后聯想前面幾次結構簡化demo, 大概不是沒綁定而是綁錯了目標.
這個原生項目的HTML代碼很多, 所以作者做了一些優化, 在需要某個模塊的時候才將其appendChild
加入DOM, 其余的時候這些模塊都被放在template
標簽內, 而vue把這些東西都出來渲染了, 那么初始化的時候事件大概率就已經被綁到了template
里面的那些代碼里, 等到這些模塊被appendChild
的時候, 事件綁定已經結束了, 所以appendChild
是將沒有事件綁定的DOM加到了正確位置.
我在控制臺把視口里的DOM都刪掉之后發現下面還有一層被擠出去的DOM, 那是有事件綁定的DOM.
的確是這樣.
二、如何處理原生template標簽
我是想把他appendChild
這個優化留下來的, 我覺得在原生環境里能有這種封裝的思想挺好, 不過看起來不好辦…
我打算把原來那幾個模塊抽到組件里, 提前把組件寫到后面會插入到的位置, 然后用這種結構控制顯示隱藏:
<template v-if="isShow"> <aaa></aaa> </template>
這樣挺好的其實, 如果這個項目的結構再簡單一點我絕對會用組件方案的, 結果我發現我要傳回調函數, 傳4層干擾到3個很重要的類, 只是為了在合適的時機回調改變組件的狀態, 我覺得很糟糕.
而且, 如果后面會有…或者現在就有我沒有察覺到的需求是增加不定數量個這種模塊, 我把組件直接注冊到這里用就算是寫死了, 恐怕會不好改.
需要這種操作的組件有三個, 我想起來學后端渲染的時候給前端發的html模板, 那…能不能把這些html轉成字符串存到一個單獨的js文件, 然后在需要的地方導入后appendChild
呢? 這樣對源代碼改動最小, 不用改appendChild
, 也讓html文檔那邊更簡潔一些.
export const batchEditorToolsTemplate = ` <div id="batch-editor-tools-wrapper"> <div id="batch-editor-tools"> <div id="exit">退出</div> <div id="prev">上一頁</div> <div id="next">下一頁</div> <div id="trajectory">軌跡</div> <div id="auto-annotate">自動</div> <div id="auto-annotate-translate-only">自動(無旋轉)</div> <div id="interpolate">插值</div> <div id="reload">重新加載</div> <div id="finalize">定稿</div> </div> </div> `
然后用這個工具函數把appendChild
替換掉:
function analyseDomStr(str, target) { // dom字符串, 目標元素 const template = document.createElement("template"); template.innerHTML = str; target.appendChild(template.content); }
這樣性能不如之前好, 不過——事件綁定看起來沒什么問題了.
本來想用Document.createDocumentFragment()
API的, 所以初版就寫成這樣了:
function analyseDomStr(str, target) { // dom字符串, 目標元素 const fragment = document.createDocumentFragment(); const template = document.createElement("template"); template.innerHTML = str; fragment.appendChild(template.content); // 此處還是要按照原生template的那套來的, 這個template不會被vue特殊解析 target.appendChild(fragment); }
很遺憾并不能直接使用innerHTML
向DocumentFragment
內寫入DOM, 仍舊需要appendChild
來完成, 所以完全沒有必要創建DocumentFragment
, 我認為這個API更加適合用于對頻繁DOM操作進行優化, 比如用戶點擊按鈕后就要插入100條tips
, 那就更適合先使用這個API生成一個文檔內容分段, 然后把成品分段加入DOM.
這個初版和舊版也都是回流一次…
因為文檔片段存在于內存中, 并不在 DOM 樹中, 所以將子元素插入到文檔片段時不會引起頁面回流(對元素位置和幾何上的計算).
因此, 使用文檔片段通常會帶來更好的性能.
完全可以把:
const ul = document.querySelector("ul"); const li = document.createElement("li"); for (let i = 0; i < 100; i++) { ul.appendChild("li"); }
這種會引起頁面頻繁回流的寫法
改成
const ul = document.querySelector("ul"); const li = document.createElement("li"); const fragment = document.createDocumentFragment(); for (let i = 0; i < 100; i++) { fragment.appendChild("li"); } ul.appendChild(fragment);
這樣頁面只會在fragment
被appendChild
后回流一次.
到此這篇關于Vue中原生template標簽失效如何解決的文章就介紹到這了,更多相關Vue template標簽失效內容請搜索以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持!
