指令 - directive

指令是 针对DOM节点 的功能增强。

注意:所有指令都只能在DOM节点上使用, 无法在组件上使用

1. 使用指令

当 Regular 解析到一个属性时,例如 <div r-class={ {'z-active': isActive} } ></div>,会根据属性名去检查它是否是一个指令(通过Component.directive定义)

  • 如果是: 则完全交由指令处理
  • 如果否: 则视为普通属性
<h2 r-html={title} title={title} ></h2>

上例中,r-html 就是一个指令, title则是一个普通属性插值。

2. 定义指令

每一个组件可以通过Component.directive来扩展指令,其中最关键的就是 link 方法,它用来处理指令的启动

以下面的html的插值指令为例(实际上这也是内部r-html的实现), innerHTML会相应数据的变更


Regular.directive('html', {
    // elem指向当前节点,value即传入属性值
    link( elem, value ){
        this.$watch(value, val => {
            elem.innerHTML = val;
        })
    }
})

参考

使用

<div html={content} ></div>

注意这里 Regular 指令的命名是没有要求的,但建议工程中有一个统一前缀来区分于普通属性

指令属性值说明

这里需要说明的是,属性值即 指令link 的第二个参数,有三种可能

  • 属性不是插值,r-html='blog',则入参即为对应字符串如'blog.title'
  • 属性是插值,即类似r-html={blog.title},value是一个 Expression对象
  • 当没有值,如 <div r-html></div>则传入'' (空字符)

你可以根据自己的需要作对应的判断

2.2. 更新逻辑:directive.update(elem, value, oldValue, extra)

由于绝大部分场景都如上例 类似,只需要处理数据变更的情况,此时可以选择更简洁的update方法


Regular.directive('html', {
    update(elem, value){
        elem.innerHTML = value
    }
})

是不是简便了许多?

说明

  • 如果传入值是表达式(如{blog.title}),update会在 初始化值变更时 触发
  • 如果是空或文本,update会只在初始化触发一次,不会产生绑定

⚠️ 在0.6.0版本以前, Regular有一个已知Bug —— update 函数必须在link函数存在时才会生效,所以至少需要定义一个空的link函数

2.3. 指令参数: directive.param

  • param: 指令的参数属性名的配置

有些场景,指令可能需要辅助的参数,Regular 支持指令声明式依赖其他属性作为参数

例如,用html指令实现一个超出指定字符就显示...省略符号的需求

Regular.directive('html', {
    param: ['limit'],
    update(elem, value, oldvalue, extra){
        const limit = parseInt( extra.param.limit, 10);
        const htmlStr = value.length > limit? htmlStr.slice(0, limit) + '...': value;
        elem.innerHTML = htmlStr;
    }
})

extra.param即我们接受的参数集合的对象。

说明

  1. 指令参数目前没有变更的监听函数,所以无论是否传入的是表达式,都只有初始值
  2. 指令参数属性将不会进入正常属性的流程
  3. link 和 update 都接受extra参数

3. 指令销毁

如果你的指令需要销毁逻辑,比如在指定启动时,引入了全局状态,只需要在link函数中返回一个Function即可

Regular.directive("lazy-load", function(){
    document.addEventListener('scroll', onScroll);
    return function (){
        document.removeEventListener('scroll', onScroll);
    } 
})

上例中我们直接传入了函数,这是一种简写方式,此函数会成为指令定义的link函数。

Question: 为什么要返回销毁函数,而不是通过监听 $destroy事件来完成?

Answer: 因为指令的销毁并不一定伴随着组件销毁,指令的生命周期更短,一些语法元素会导致它在组件销毁之前被重复创建和销毁:

  1. if / else / elseif
  2. list
  3. include

不过值得庆幸的是,一切关于数据监听的事务,都无需回收,比如你在link函数内部通过this.$watch进行了数据绑定,Regular 会进行自动收集。

4. 属性解析的流程示意图

参考

5. 内建指令

移步索引:内建指令

6. 指令优于DOM操作的原因

因为 组件概念 以及语句的存在,Regular 中指令的作用被大大弱化,但是 对于针对具体节点的功能性增强 ,仍然推荐使用指令来解决,而不是手动的操作DOM。原因如下:

  1. 指令是一种可复用的抽象, 亦如Regular中的r-model指令,如果手动实现,你仍然需要大量的代码
  2. 声明式的指令理解起来更加直观

results matching ""

    No results matching ""