directives

在讲解视图层指令时,我们讲到ref特性,使用它我们可以获取当前DOM元素对象,以便执行相关操作。

<div id="app">
<input ref="ipt" type="text" v-model="value" />
</div>
new Vue({
el: "#app",
data: {
value: ''
},
methods: {
handleEle() {
let ele = this.$refs.ipt
// do somthing
}
}
})

如果某个DOM操作在不同组件的元素或组件内多个元素都需要执行,像这样在每个组件里都重复写一遍处理逻辑代码肯定不是好办法。此时我们可以自定义一个指令,在指令钩子函数的回调里复用逻辑代码。

其时,自定义指令的思想还是代码复用的想法,同组件、混入、函数思想一样。

基本使用

根据指令使用范围的不同,你可以将指令定义在全局作用域、实例作用域或单个组件作用域内。

// 在vue全局作用域
vue.directive('name', {
bind: function (el, binding,vnode){/* do something */},
inserted: function (el, binding,vnode){/* do something */},
update: function (el, binding,vnode, oldVnode){/* do something */},
componentUpdated: function (el, binding,vnode, oldVnode){/* do something */},
unbind: function (el, binding,vnode){/* do something */},
}) // 在实例作用域中
var vm = new Vue({
directives: {
'name': {
bind: function (el, binding,vnode){/* do something */},
inserted: function (el, binding,vnode){/* do something */},
update: function (el, binding,vnode, oldVnode){/* do something */},
componentUpdated: function (el, binding,vnode, oldVnode){/* do something */},
unbind: function (el, binding,vnode){/* do something */},
}
}
}) // 在组件作用域中
export default {
directives: {
'name': {
bind: function (el, binding,vnode){/* do something */},
inserted: function (el, binding,vnode){/* do something */},
update: function (el, binding,vnode, oldVnode){/* do something */},
componentUpdated: function (el, binding,vnode, oldVnode){/* do something */},
unbind: function (el, binding,vnode){/* do something */},
}
}
}

指令钩子函数

其中,指令有多个钩子函数,正如vue实例和组件有生命周期钩子函数一样。它们有不同的命名,和不同触发时机。

  • bind: 钩子函数会在指令绑定到元素时调用。只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  • inserted:钩子函数会在绑定的元素被添加到父节点时调用。但是和mounted一样,此进还不能保证元素已经被添加到父节点上(仅保证父节点存在)。可以使用this.$nextTick来保证这一点。
  • update:钩子函数在绑定该指令的组件vnode节点被更新时调用,但是该组件的子组件的vnode可能此时还未更新。但是你可以通过比较更新前后的值(vnode, oldVnode)来忽略不必要的模板更新 (详细的钩子函数参数见下)
  • componentUpdated:钩子函数会在组件的 VNode 及其子组件的 VNode 全部更新后调用。
  • unbind:钩子函数用于指令的拆除,只调用一次,当指令从元素上解绑时调用。

但不是每次自定义指令的时候需要调用所有钩子,事实上,它们都是可选的,你可以根据自定义指令的需求确定指令回调函数执行的时机,来绑定特定的时机的钩子上。基本最常用的就是bindupdate。针对最常用的有简写的方式,直接传入一函数,但不建议,代码中最好明确指令绑定的钩子。

钩子函数的参数

五个钩子的回调函数都包含3个参数elbindingvnode。其中updatecomponentUpdate钩子函数包含第4个参数oldVnode

vue.directive('name', {
bind: function (el, binding, vnode){/* do something */},
inserted: function (el, binding, vnode){/* do something */},
update: function (el, binding, vnode, oldVnode){/* do something */},
componentUpdated: function (el, binding, vnode, oldVnode){/* do something */},
unbind: function (el, binding, vnode){/* do something */},
})

我们通过一个自定义指令例子来理解每个参数具体的含义:

指令的使用都需要加上v,并且以-连字符连接。如v-namev-my-name

<div v-my-directive:example.one.two = 'someExpression'>自定义指令示例<div>
 // 全局注册
vue.directive('my-directive', {
bind: function (el, binding, vnode){/* do something */},
})
  • el:指令所绑定的DOM元素,可以用来直接操作DOM对象。
  • vnode: Vue编译生成的虚拟节点,具体API见下文。
    • 可以使用vnode.context来获取当前指令所处的上下文对象this,这个属性很有用
    • vnode.ele来获取该节点元素,但必须在vnode.context.$nextTick(() => console.log(vnode.elm.parentNode))得到保证。
  • oldVnode:上一个虚拟节点,仅在 updatecomponentUpdated 钩子中可用。
  • binding:对象,包含以下该指令的相关属性。具体如下:
    • name:指令的名称,但不包含v-部分,在上例中值为my-directive
    • arg:指令的参数,即example
    • modifiers: 对象,指令的修饰符对象。即{one:true, two:true}
    • expression: 指令表达式的字符串形式。即"someExpression"
    • value: 指令表达式计算的结果,即value = someExpression。如果data对象中。{someExpression: (3/4).toFixed(2),则value = o.75
    • oldVlue:指令上一次的value值。它只在updatecomponentUpdated 钩子中可用。

文件的组织

一般我们使用指令,也就是因为有频繁操作,所以在项目中一般都是全局注册。在项目src文件中定义一个directives文件夹,新建一个index.js

import Vue from 'vue'

var hasPerm = {
bind(el, binding, vnode){
let permissionList = vnode.context.$route.meta.permission if(!permissionList){
console.error(`权限判断不生效。因路由中不包含permission字段,请检查路由表设置。当前路由地址:${vnode.context.$route.path}`)
return
}
if(typeof (permissionList) != "object" || !permissionList.length){
console.error(`权限判断不生效。因路由中permission字段非数组类型或内容为空,请检查路由表设置。当前路由地址:${vnode.context.$route.path}`)
return
}
if(!permissionList.includes(binding.value)){
el.parentNode.removeChild(el)
}
}
} var directives = {
hasPerm
} for (item in directives) {
Vue.directive(item, directives[item])
} export default Vue
// 在main.js文件导入
import './filters/install'

或者将自定义指令写成插件形式,注册到Vue

const hasPermission = {
install: function (Vue){
Vue.directive('hasPermission', {
bind(el, binding, vnode){
let permissionList = vnode.context.$route.meta.permission
if(!permissionList){
console.error(`权限判断不生效。因路由中不包含permission字段,请检查路由表设置。当前路由地址:${vnode.context.$route.path}`)
return
}
if(typeof (permissionList) != "object" || !permissionList.length){
console.error(`权限判断不生效。因路由中permission字段非数组类型或内容为空,请检查路由表设置。当前路由地址:${vnode.context.$route.path}`)
return
}
if(!permissionList.includes(binding.value)){
el.parentNode.removeChild(el)
}
}
})
}
} export default hasPermission
// 所有自定义的插件都在plugins文件夹的install.js完成注册
import Vue from 'vue'
import hasPermission from './hasPermission' const Plugins = [
hasPermission
] // 全局注册插件
Plugins.map((plugin) => {
Vue.use(plugin)
}) export default Vue
// 在main.js文件导入
import './plugins/install'

扩展知识:vnode API

vnode参数

// 源码路径: vue/scr/vnode/vnode.js
export default class VNode {
// outer
tag: string | void;
data: VNodeData | void;
children: ?Array<VNode>;
text: string | void;
elm: Node | void;
ns: string | void;
context: Component | void; // rendered in this component's scope
key: string | number | void;
componentOptions: VNodeComponentOptions | void;
componentInstance: Component | void; // component instance
parent: VNode | void; // component placeholder node // strictly internal
raw: boolean; // contains raw HTML? (server only)
isStatic: boolean; // hoisted static node
isRootInsert: boolean; // necessary for enter transition check
isComment: boolean; // empty comment placeholder?
isCloned: boolean; // is a cloned node?
isOnce: boolean; // is a v-once node?
asyncFactory: Function | void; // async component factory function
asyncMeta: Object | void;
isAsyncPlaceholder: boolean;
ssrContext: Object | void;
fnContext: Component | void; // real context vm for functional nodes
fnOptions: ?ComponentOptions; // for SSR caching
devtoolsMeta: ?Object; // used to store functional render context for devtools
fnScopeId: ?string; // functional scope id support // ... 其它代码
}

vue-learning:22 - js - directives的更多相关文章

  1. 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 十五 ║Vue基础:JS面向对象&字面量& this字

    缘起 书接上文<从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 十四 ║ VUE 计划书 & 我的前后端开发简史>,昨天咱们说到了以我的经历说明的web开发经历的 ...

  2. vue中config/index.js:配置的详细理解

    当我们需要和后台分离部署的时候,必须配置config/index.js: 用vue-cli 自动构建的目录里面  (环境变量及其基本变量的配置) var path = require('path') ...

  3. vue原理:diff、模板编译、渲染过程等

    一.虚拟DOM: 因为DOM操作非常消耗性能,在操作DOM时,会出现DOM的回流(Reflow:元素大小或者位置发生改变)与重绘(元素样式的改变)使DOM重新渲染. 现在的框架Vue和React很少直 ...

  4. iosselect:一个js picker项目,在H5中实现IOS的select下拉框效果

    具体文档和demo可以访问github:https://github.com/zhoushengmufc/iosselect 移动端浏览器对于select的展示样式是不一致的,ios下是类似原生的pi ...

  5. Deep learning:五十一(CNN的反向求导及练习)

    前言: CNN作为DL中最成功的模型之一,有必要对其更进一步研究它.虽然在前面的博文Stacked CNN简单介绍中有大概介绍过CNN的使用,不过那是有个前提的:CNN中的参数必须已提前学习好.而本文 ...

  6. iosselect:一个js picker项目,在H5中实现IOS的下拉效果

    iosselect是在webapp下的一个picker组件,可以轻松实现各类选择器效果.比如地区选择 时间选择 日期选择等. 下面是一个地址选择器demo截图,可以访问:http://zhoushen ...

  7. Atom 编辑器安装 linter-eslint 插件,并配置使其支持 vue 文件中的 js 格式校验

    安装方式有如下几种. 1.最常用的安装方式. # 进入atom插件文件夹 cd ~/.atom/packages/ # git clone 插件源文件 git clone https://github ...

  8. 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十二║Vue实战:个人博客第一版(axios+router)

    前言 今天正式开始写代码了,之前铺垫了很多了,包括 6 篇基础文章,一篇正式环境搭建,就是为了今天做准备,想温习的小伙伴可以再看看<Vue 基础入门+详细的环境搭建>,内容很多,这里就暂时 ...

  9. 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十三║Vue实战:Vuex 其实很简单

    前言 哈喽大家周五好,马上又是一个周末了,下周就是中秋了,下下周就是国庆啦,这里先祝福大家一个比一个假日嗨皮啦~~转眼我们的专题已经写了第 23 篇了,好几次都坚持不下去想要中断,不过每当看到群里的交 ...

随机推荐

  1. OpenTelemetry-可观察性的新时代

    有幸在2019KubeCon上海站听到Steve Flanders关于OpenTelemetry的演讲,之前Ops领域两个网红项目OpenTracing和OpenCensus终于走到了一起,可观察性统 ...

  2. Python sorted

    sorted函数: iterable:是可迭代类型;cmp:用于比较的函数,比较什么由key决定,有默认值,迭代集合中的一项;key:用列表元素的某个属性和函数进行作为关键字,有默认值,迭代集合中的一 ...

  3. PHP进阶与redis锁限制并发访问功能示例

    <?php /** * Redis锁操作类 * Date: 2017-06-30 * Author: fdipzone * Ver: 1.0 * * Func: * public lock 获取 ...

  4. JAVA代码规范 标签: java文档工作 2016-06-12 21:50 277人阅读 评论(5) 收藏

    开始做java的ITOO了,近期的工作内容就是按照代码规范来改自己负责的代码,之前做机房收费系统的时候,也是经常验收的,甚至于我们上次验收的时候,老师也去了.对于我们的代码规范,老师其实是很重视的,他 ...

  5. Java练习 SDUT-2561_九九乘法表

    九九乘法表 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 九九乘法表是数学学习的基础,今天我们就来看看乘法表的相关问题 ...

  6. 2017 ACM-ICPC 亚洲区(南宁赛区)网络赛 M. Frequent Subsets Problem【状态压缩】

    2017 ACM-ICPC 亚洲区(南宁赛区)网络赛  M. Frequent Subsets Problem 题意:给定N和α还有M个U={1,2,3,...N}的子集,求子集X个数,X满足:X是U ...

  7. SharpDX初学者教程第1部分:在Visual Studio 2013中设置SharpDX项目

    原文 http://www.johanfalk.eu/blog/sharpdx-tutorial-part-1-setting-up-a-sharpdx-project-in-visual-studi ...

  8. 第十四届中北大学ACM程序设计竞赛 J.ZBT的游戏

    问题描述 第14届中北大学程序设计竞赛来了,集训队新买了一大堆气球,气球一共有K种颜色(1<=K<=256),气球的颜色从1-K编号. ZBT童心未泯,他发明了一种摆放气球的游戏,规则如下 ...

  9. keep-alive vue组件缓存避免多次加载相应的组件

    keep-alive vue组件缓存避免多次加载相应的组件

  10. linux下修改gcc编译器版本

    可以使用如下命令行来让 gcc 选择不同的 C++ 版本: g++ -std=c++11 main.cpp 在你的系统中,由于编译器或是编译器设定上的差别,操作也许有所不同.