我们知道,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是相同的,让我们感到熟悉又舒服。