javascript - Vue 自定义指令使用更新后的 Dom(或 $el)
问题描述
<strong>cx</strong>
我想为Dom 树中的所有 TextNode设计一个自定义指令来替换“cx” 。
以下是我迄今为止尝试过的:
Vue.config.productionTip = false
function removeKeywords(el, keyword){
if(!keyword) return
let n = null
let founds = []
walk=document.createTreeWalker(el,NodeFilter.SHOW_TEXT,null,false)
while(n=walk.nextNode()) {
if(n.textContent.trim().length < 1) continue
founds.push(n)
}
let result = []
founds.forEach((item) => {
if( new RegExp('cx', 'ig').test(item.textContent) ) {
let kNode = document.createElement('span')
kNode.innerHTML = item.textContent.replace(new RegExp('(.*?)(cx)(.*?)', 'ig'), '$1<strong>$2</strong>$3')
item.parentNode.insertBefore(kNode, item)
item.parentNode.removeChild(item)
}
})
}
let myDirective = {}
myDirective.install = function install(Vue) {
let timeoutIDs = {}
Vue.directive('keyword-highlight', {
bind: function bind(el, binding, vnode) {
clearTimeout(timeoutIDs[binding.value.id])
if(!binding.value) return
timeoutIDs[binding.value.id] = setTimeout(() => {
removeKeywords(el, binding.value.keyword)
}, 500)
},
componentUpdated: function componentUpdated(el, binding, vnode) {
clearTimeout(timeoutIDs[binding.value.id])
timeoutIDs[binding.value.id] = setTimeout(() => {
removeKeywords(el, binding.value.keyword)
}, 500)
}
});
};
Vue.use(myDirective)
app = new Vue({
el: "#app",
data: {
keyword: 'abc',
keyword1: 'xyz'
},
methods: {
}
})
.header {
background-color:red;
}
strong {
background-color:yellow
}
<script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>
<div id="app">
<input v-model="keyword">
<input v-model="keyword1">
<h1>Test Case 1: try to change 2nd input to <span class="header">anything</span></h1>
<div v-keyword-highlight="{keyword:keyword, id:1}">
<p>Test1<span>Test2</span>Test3<span>{{keyword}}{{keyword1}}</span></p>
</div>
<h1>Test Case 2 which is working</h1>
<div :key="keyword+keyword1" v-keyword-highlight="{keyword:keyword, id:2}">
<p>Test1<span>Test2</span>Test3<span>{{keyword}}{{keyword1}}</span></p>
</div>
</div>
第一种情况:应该是相关的 VNode 已经被 替换了<span><strong></strong></span>
,所以无法正确更新数据属性。
第二种情况:它按预期工作。添加解决方案:key
以强制挂载组件,因此当触发更新时,它将使用模板和最新数据属性呈现然后挂载。
但是我更喜欢在指令钩子中强制挂载而不是:key
在组件上绑定,或者根据模板和最新的数据属性获取更新的 Dom($el)。所以任何想要使用这个指令的人都不需要考虑:key
.
非常感谢。
解决方案
我不确定这是最佳做法,因为有警告不要修改vnode
,但这在您的示例中可以动态添加密钥
vnode.key = vnode.elm.innerText
我注意到第一个指令响应但第二个没有响应的奇怪之处,componentUpdated
即使第二个内部元素更新了它们的值但第一个没有 - 这与您的预期相反。
请注意,发生更改是因为第二个实例bind
在输入更改时再次调用,而不是因为componentUpdated
.
console.clear()
Vue.config.productionTip = false
function removeKeywords(el, keyword){
console.log(el, keyword)
if(!keyword) return
let n = null
let founds = []
walk=document.createTreeWalker(el,NodeFilter.SHOW_TEXT,null,false)
while(n=walk.nextNode()) {
if(n.textContent.trim().length < 1) continue
founds.push(n)
}
let result = []
founds.forEach((item) => {
if( new RegExp('cx', 'ig').test(item.textContent) ) {
let kNode = document.createElement('span')
kNode.innerHTML = item.textContent.replace(new RegExp('(.*?)(cx)(.*?)', 'ig'), '$1<strong>$2</strong>$3')
item.parentNode.insertBefore(kNode, item)
item.parentNode.removeChild(item)
}
})
}
let myDirective = {}
myDirective.install = function install(Vue) {
let timeoutIDs = {}
Vue.directive('keyword-highlight', {
bind: function bind(el, binding, vnode) {
console.log('bind', binding.value.id)
clearTimeout(timeoutIDs[binding.value.id])
if(!binding.value) return
vnode.key = vnode.elm.innerText
timeoutIDs[binding.value.id] = setTimeout(() => {
removeKeywords(el, binding.value.keyword)
}, 500)
},
componentUpdated: function componentUpdated(el, binding, vnode) {
//clearTimeout(timeoutIDs[binding.value.id])
//timeoutIDs[binding.value.id] = setTimeout(() => {
//removeKeywords(el, binding.value.keyword)
//}, 500)
}
});
};
Vue.use(myDirective)
app = new Vue({
el: "#app",
data: {
keyword: 'abc',
keyword1: 'xyz'
},
methods: {
}
})
.header {
background-color:red;
}
strong {
background-color:yellow
}
<script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>
<div id="app">
<input v-model="keyword">
<input v-model="keyword1">
<h1>Test Case 1: try to change 2nd input to <span class="header">anything</span></h1>
<div v-keyword-highlight="{keyword:keyword, id:1}">
<p>Test1<span>Test2</span>Test3<span>{{keyword}}{{keyword1}}</span></p>
</div>
<h1>Test Case 2 which is working</h1>
<div :key="keyword+keyword1" v-keyword-highlight.keyword1="{keyword:keyword, id:2}">
<p>Test1<span>Test2</span>Test3<span>{{keyword}}{{keyword1}}</span></p>
</div>
</div>
推荐阅读
- erlang - 如何:使用来自不同项目的特定应用程序作为 dep
- javascript - 计算器:变量在“if else”语句中以某种方式未定义
- c# - 停止为解决方案中的每个项目复制图像
- npm - 安装 Bugsnag 时出现错误“错误:spawn npm ENOENT”
- python - 在numpy数组列中查找某个值的最小和最大索引
- javascript - 单击任何子级时如何获取父级div的类名
- java - Firebase onMessageReceived() 不返回 getNotification
- python - 递归的中间结果
- reactjs - 如何使用 Hooks 转换基于类的 React-Redux 代码
- laravel - 条件形式 Laravel/Livewire