问题描述
限时送ChatGPT账号..我想设计一个自定义指令,将 Dom 树中所有 TextNode 的 'cx' 替换为 cx
.
以下是我迄今为止尝试过的:
Vue.config.productionTip = false函数 removeKeywords(el, 关键字){if(!keyword) 返回让 n = 空让基金会 = []walk=document.createTreeWalker(el,NodeFilter.SHOW_TEXT,null,false)while(n=walk.nextNode()) {if(n.textContent.trim().length <1) 继续founds.push(n)}让结果 = []founds.forEach((item) => {if( new RegExp('cx', 'ig').test(item.textContent) ) {让 kNode = document.createElement('span')kNode.innerHTML = item.textContent.replace(new RegExp('(.*?)(cx)(.*?)', 'ig'), '$1$2$3')item.parentNode.insertBefore(kNode, item)item.parentNode.removeChild(item)}})}让我的指令 = {}myDirective.install = 函数安装(Vue){让 timeoutIDs = {}Vue.directive('关键字突出显示', {绑定:函数绑定(el,绑定,vnode){clearTimeout(timeoutIDs[binding.value.id])if(!binding.value) 返回timeoutIDs[binding.value.id] = setTimeout(() => {removeKeywords(el, binding.value.keyword)}, 500)},组件更新:功能组件更新(el,绑定,vnode){clearTimeout(timeoutIDs[binding.value.id])timeoutIDs[binding.value.id] = setTimeout(() => {removeKeywords(el, binding.value.keyword)}, 500)}});};Vue.use(myDirective)应用程序 = 新的 Vue({el: "#app",数据: {关键字:'abc',关键字 1:'xyz'},方法: {}})
.header {背景颜色:红色;}强的 {背景颜色:黄色}
<script src="https://unpkg/vue@2.5.16/dist/vue.js"><;/脚本><div id="应用程序"><input v-model="keyword"><input v-model="keyword1"><h1>测试用例 1:尝试将第二个输入更改为 <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><h1>正在运行的测试用例 2</h1><div :key="keyword+keyword1" v-keyword-highlight="{keyword:keyword, id:2}"><p>Test1<span>Test2</span>Test3<span>{{keyword}}{{keyword1}}</span></p>
第一种情况:应该是相关的VNode已经被替换为引起的,所以不会正确更新数据属性.
第二种情况:它按预期工作.解决方案是添加:key
强制挂载组件,所以当更新被触发时,它会渲染模板和最新的数据属性然后挂载.
但我更喜欢在指令钩子中强制挂载而不是在组件上绑定 :key
,或者根据模板和最新的数据属性获取更新的 Dom($el).所以任何想要使用这个指令的人都不需要区分 :key
.
非常感谢.
解决方案我不确定这是最佳做法,因为有针对修改 vnode
的警告,但这在您的示例中可以动态运行添加密钥
vnode.key = vnode.elm.innerText
奇怪的是,first 指令响应 componentUpdated
但第二个没有响应,即使第二个内部元素更新它们的值但第一个没有 -这与您的预期相反.
请注意,发生更改是因为第二个实例在输入更改时再次调用 bind
,而不是因为 componentUpdated
中的代码.
console.clear()Vue.config.productionTip = false函数 removeKeywords(el, 关键字){console.log(el, 关键字)if(!keyword) 返回让 n = 空让基金会 = []walk=document.createTreeWalker(el,NodeFilter.SHOW_TEXT,null,false)while(n=walk.nextNode()) {if(n.textContent.trim().length <1) 继续founds.push(n)}让结果 = []founds.forEach((item) => {if( new RegExp('cx', 'ig').test(item.textContent) ) {让 kNode = document.createElement('span')kNode.innerHTML = item.textContent.replace(new RegExp('(.*?)(cx)(.*?)', 'ig'), '$1$2$3')item.parentNode.insertBefore(kNode, item)item.parentNode.removeChild(item)}})}让我的指令 = {}myDirective.install = 函数安装(Vue){让 timeoutIDs = {}Vue.directive('关键字突出显示', {绑定:函数绑定(el,绑定,vnode){console.log('bind', binding.value.id)clearTimeout(timeoutIDs[binding.value.id])if(!binding.value) 返回vnode.key = vnode.elm.innerTexttimeoutIDs[binding.value.id] = setTimeout(() => {removeKeywords(el, binding.value.keyword)}, 500)},组件更新:功能组件更新(el,绑定,vnode){//clearTimeout(timeoutIDs[binding.value.id])//timeoutIDs[binding.value.id] = setTimeout(() => {//removeKeywords(el, binding.value.keyword)//}, 500)}});};Vue.use(myDirective)应用程序 = 新的 Vue({el: "#app",数据: {关键字:'abc',关键字 1:'xyz'},方法: {}})
.header {背景颜色:红色;}强的 {背景颜色:黄色}
<script src="https://unpkg/vue@2.5.16/dist/vue.js"><;/脚本><div id="应用程序"><input v-model="keyword"><input v-model="keyword1"><h1>测试用例 1:尝试将第二个输入更改为 <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><h1>正在运行的测试用例 2</h1><div :key="keyword+keyword1" v-keyword-highlight.keyword1="{keyword:keyword, id:2}"><p>Test1<span>Test2</span>Test3<span>{{keyword}}{{keyword1}}</span></p>
I want to design one custom directive to replace 'cx' to <strong>cx</strong>
for all TextNodes in the Dom Tree.
Below is what I had tried so far:
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/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>
First Case: It should be caused by related VNode already been replaced by <span><strong></strong></span>
, so will not get updated with the data properties correctly.
Second Case: It works as expected. The solution is added :key
to force mount the component, so when update is triggered, it will render with the template and latest data properties then mount.
But I prefer to force mount in the directive hook instead of bind :key
at the component, or get the updated Dom($el) based on the template and the latest data properties. so anyone else who want to use this directive doesn't need to case about the :key
.
Many thanks for any.
解决方案I'm not sure this is the best practice since there are warnings against modifying vnode
, but this works in your sample to dynamically add the key
vnode.key = vnode.elm.innerText
The weird thing I notice that the first directive responds to componentUpdated
but the second does not, even though the second inner elements update their values but the first does not - which is contrary to what you would expect.
Note that the change occurs because the second instance calls bind
again when the inputs change, not because of the code in 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/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>
这篇关于Vue 自定义指令使用更新的 Dom(或 $el)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
更多推荐
[db:关键词]
发布评论