既严肃认真,又生动活泼

Vue自定义指令获取DOM元素

我们知道,Vue.js的核心是数据驱动,当数据有所变化时,DOM就会被更新,比如:

<span v-text="msg"></span>
export default {
  data(){
  return{
    msg : 'oldMsg'  
  }
 }
 methods : {
  changeMsg : function(){
    this.msg = 'newMsg'
  }
 }  
}

当调用了changeMsg方法,msg被修改为 ' newMsg ' ,我们可以把这次修改理解为数据发生了变化,此时数据的变化就要驱动DOM变化,我们可以看到<span>oldMsg</span>变成了<span>newMsg</span>,Vue 实现这种响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新,这里涉及到Vue.nextTick 和 vm.$nextTick 等等另外一大堆知识点,我们这里不作深入讨论,我们只需要知道,是数据的变化驱动了DOM的变化,在这个过程中,我们发现我们并没有去操作具体的DOM元素,按照以前的做法,我们可能需要getElementByAnyWays(不论用什么方式获取元素,原生Js或者jQuery.....)获取这个span元素,然后在适当的时候修改它的innerHTML,但我们没有这样做,我们只修改了数据,Vue帮我们实现了DOM的修改与更新,这就是Vue最大的特性之一:数据驱动。

 

明白了数据驱动这回事,在使用Vue的过程中,我们发现大部分时候不用去操作具体的每个DOM元素,但是,就怕那剩下的小部分时间……我们还是需要获取具体的DOM并且对它做一些事情,当你有这个需求并且去做的时候,你会发现在Vue中获取DOM元素是一件令人头疼的事情。

 

首先,我们使用一种比较普遍的方法来实现,在需要获取DOM的元素上绑定ref属性,然后通过 this.$refs [ 属性值 ] 来获取。

<p ref="sentence">I'm waiting for ref</p>
export default{
created(){
    console.log(this.$refs.sentence);    // undefined  
   }
   mounted(){
    console.log(this.$refs.sentence)    // VueComponent
   }
}

这里有个小的注意点,就是mounted时期之前,包括beforeMount、created时期,this.$refs都是一个空对象,无法获取到想要的sentence对象,详细解说请戳 官方生命周期图 ,所以,我们要在正确的时间做正确的事情嘛,学习时间就好好看我的文章O(∩_∩)O 。

到了mounted时期,元素已经挂载完成,我们可以获取到sentence对象了,拿到后迫不及待的想要修改它的innerHTML,于是又报错了,this.$refs.sentence.innerHTML是undefined,相当不爽,打断点,一查究竟,发现this.$refs.sentence是奇奇怪怪的VueComponent,并不是我们传统的DOM对象,那怎么获取到我们想要的innerHTML、className等等呢?通过断点仔细查看该VueComponent对象后,发现我们想要的东西都藏在更深一层的属性$el里,如下图:

 

知道了里面的玄机,我们就可以通过 this.$refs.sentence.$el.innerHTML = "whatever you want" 来修改DOM的内容了。

 

 

作了如此多的铺垫,我们是不是可以开始今天高大上的主题了?使用Vue的自定义指令来解决这个问题。

HTML:

<span v-getdom="regist('span1')"></span>
<p v-getdom="regist('p1')"></p>

JavaScript:

const vm = new Vue({
  el:'#app',
  data : {
    domEles: {}
  },
  directives : {
    getdom(el, binding) {
      if (typeof binding.value == 'function')
        binding.value(el);
    }
  },
  methods : {
    regist (flag) {
      return (el)=>{
        this.domEles[flag] = el;
      }
    }
  },
  mounted () {
    console.log(this.domEles.span1);  //=> the span DOM Element
    console.log(this.domEles.p1);   //=> the p DOM Element
  }
}) 

 

首先,我们通过官方提供的directives开发一个局部(局部是指该directives只属于该new Vue()对象,不可被其他Vue对象使用)自定义指令getdom,然后你就可以在模板任何元素上使用v-getdom指令做一些事。

getdom( )传入了两个参数,el 和 binding ,这是官方特定参数,有具体的寓意,如下:

 

 

 

接着,建立一个名为 regist 的方法,接收一个 flag 参数,并根据这个参数返回一个用于将传入参数注册到 this.domEles对象中的闭包函数

将getdom指令与regist方法搭配使用,就可以将想要的元素注册进this.domEles对象中,不论在mounted还是methods中使用都非常方便,只需要this.domEles [ 注册名 ]访问即可,这里我们得到的DOM对象,可以直接获取innerHTML等属性,不需要再深入一层,这与原生DOM是相同的,让我们感到熟悉又舒服。

posted @ 2017-11-24 17:35  大宝章  阅读(12311)  评论(0编辑  收藏  举报