Vue 自定义指令使用更新的 Dom(或 $el)

编程入门 行业动态 更新时间:2024-10-13 16:17:59
本文介绍了Vue 自定义指令使用更新的 Dom(或 $el)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

限时送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:关键词]

本文发布于:2023-04-27 01:19:58,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1145607.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:自定义   指令   Vue   Dom   el

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!