0
点赞
收藏
分享

微信扫一扫

vue 自定义指令/对象和数组改变属性

有时候我们想要封装自己的指令(用于对DOM执行某些操作),vue 提供了自定义指令接口满足了我们这种需要。定义方式同样分为全局和局部。

1. 全局自定义指令

语法:Vue.directive('指令名字', 指令配置对象)

Vue.directive('color', {
    // 绑定到元素上时触发的函数
    bind(el, binding, vnode) {
        el.style.color = "#f00";
    }
})

使用方式如下:

<!-- 直接在元素上添加指令即可 -->
<p v-color>两个黄鹂鸣翠柳</p>

上方的代码定义了一个v-color指令,当它被添加到元素上时,元素的文字颜色会变成红色。

当我们想要知道它怎么实现时,我们需要先了解指令配置对象包含什么?

1.1 指令配置对象上的钩子函数

钩子函数可以简单理解为是一种系统在不同的阶断自动执行、用户无须干预的函数。很多开发语言中都有这个称呼,这里你可以简单的理解为钩子函数就是监听过程触发的函数。我们前面讲到的window.onload就是页面加载的钩子函数,AJAX中的onloadstartonprogressonloadend等都是钩子函数。

  • bind:只调用一次,指令第一次绑定到元素时调用(这个时候还没插入DOM)。在这里可以进行一次性的初始化设置。一般在函数内执行样式类名等操作。
  • inserted:当指令绑定的元素插入到父节点时执行的钩子函数
  • update:所在组件的Vnode更新时调用,但是可能发生在其子元素的Vnode更新之前
  • componentUpdate:指令所在组件的Vnode及其子Vnode全部更新时调用
  • unbind:只调用一次,指令与元素解绑时

1.2 钩子函数的参数

指令钩子函数会被传入以下参数:

  • el:指代的是绑定自定义指令的那个元素
  • binding:当前自定义指令对象,包含跟指令相关的信息属性
  • name:指令名,不包括 v- 前缀
  • value:指令的绑定值,例如:v-my-directive="2"中,绑定值为 2
  • expression:字符串形式的指令表达式。例如v-my-directive="1 + 1"中,表达式为 “1 + 1”。
  • arg:传给指令的参数,例如:v-my-directive:fooarg的值为 ‘foo’
  • modifiers:一个包含修饰符的对象,例如:v-my-directive.a.bmodifiers的值为{'a':true,'b':true}
  • oldValue:指令绑定的前一个值,仅在updatecomponentUpdated钩子中可用。无论值是否改变都可用。
  • vnode:Vue编译的生成虚拟节点

用一个示例理解binding中各个参数的含义:

<div id="app">
    <!-- 可以和 vu e中的指令做类比 -->
    <button v-on:click.stop="clickHandler"></button>
    <!-- 自定义一个指令 -->
    <div v-demo:foo.a.b="message">我是自定义的指令</div>
</div>
<script type="text/javascript">
    Vue.directive('demo', {
        bind: function (el, binding, vnode) {
            var s = JSON.stringify
            el.innerHTML =
                '指令名: ' + binding.name + '<br>' +
                '绑定的值: ' + s(binding.value) + '<br>' +
                '完整的指令: ' + s(binding.expression) + '<br>' +
                '指令的参数: ' + binding.arg + '<br>' +
                '指令的修饰符: ' + s(binding.modifiers)
        }
    })

    var vm = new Vue({
        methods: {
            clickHandler(){}
        }
    }).$mount('#app')
</script>

2. 局部自定义指令

如果要注册一个局部指令,实例可以接收一个directives选项,用于添加自定义指令:

<div id="app">
    <input v-focus type="text" />
</div>
<script type="text/javascript">
    vm = new Vue({
        // 局部自定义指令
        directives: {
            // 指令名:{ 钩子函数 }
            focus: {
                // 插入到父节点的生命周期钩子
                inserted: function (el) {
                    el.focus()
                }
            }
        }
    }).$mount('#app')
</script>

3. 自定义指令的简写

在很多时候,我们只关注在bindupdate时触发相同行为,而不关心其它的钩子,这样我们可以可以把指令简写为一个函数,表示仅在bindupdate时触发。

<div id="app">
    <!-- 自定义指令,修改元素的背景颜色为绿色 -->
    <span v-color="green">文字的背景颜色是绿色</span>
</div>
<script type="text/javascript">
    /* 全局的简写,第二个参数直接传递一个函数 */
    Vue.directive('color', function (el, binding) {
        el.style.backgroundColor = binding.value
    })

    var vm = new Vue({
        data(){
            return {
                green: '#0f0'
            }
        },
        // 局部的简写
        directives: {
            // 直接接受一个函数作为属性值
            color: function(el, binding) {
                el.style.backgroundColor = binding.value
            }
        }
    }).$mount('#app')
</script>

1. 对象问题

由于 vue 会在初始化实例时对对象的 property 执行了 getter/setter 转化,所以 property 必须在 data 中声明的对象上存在才能具有响应式。

示例:

<div id="app">
    <p> obj ------- {{obj}}</p>
    <button @click="changeObj">修改obj的内容</button>
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data(){
            return {
                obj: {
                    name: '李白'
                }
            }
        },
        methods: {
            changeObj(){
                // 添加在data中声明的obj内没有的属性,不会引起视图更新
                this.obj.age = '24'
            }
        }
    })
</script>

对于已经创建的实例,vue 不允许动态添加根级别的响应式 property。但是,我们可以使用Vue.set(object, propertyName, value)方法向嵌套对象添加响应式 property。修改上例:

let vm = new Vue({
        el: '#app',
        ...
        methods: {
            changeObj(){
                // 添加在data中声明的obj内没有的属性,不会引起视图更新
                // this.obj.age = '24'

                // 通过该方法,可以让 age 属性具有响应式
                Vue.set(this.obj, 'age', 24)
            }
        }
    })

还可以使用vm.$set实例方法,这也是全局Vue.set方法的别名:

this.$set(this.obj, 'age', 24)

有时你可能需要为已有对象赋值多个新 property,比如使用 Object.assign() 或 _.extend()。但是,这样添加到对象上的新 property 不会触发更新。在这种情况下,你应该用原对象与要混合进去的对象的 property 一起创建一个新的对象。

// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`

this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })

2. 数组问题

vue 不能检测以下数组的变动,比如利用索引直接设置一个数组项时,或直接修改数组的长度时,举个例子:

<div id="app">
    <p> arr ------- {{arr}}</p>
    <button @click="changeArr">修改arr的内容</button>
</div>

<script>
    var vm = new Vue({
        data: {
            items: ['a', 'b', 'c']
        },
        changeArr(){
            this.items[1] = 'x' // 不是响应性的
            this.items.length = 2 // 不是响应性的
        }
    })
</script>

我们同样可以使用Vue.setthis.$set来处理这些问题,当然调用数组的方法也可以实现相同的效果:

Vue.set(this.items, 1, 'x')
this.$set(this.items, 1, 'x')

this.items.splice(1, 1, 'x')
this.items.splice(2) // 修改长度

举报

相关推荐

0 条评论