Vue 自定义指令有全局注册和局部注册两种方式
vue.use()本身就是一个方法,用于安装插件
通过源码看看Vue.use()的逻辑:
export function initUse (Vue: GlobalAPI) {
Vue.use = function (plugin: Function | Object) {
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
const args = toArray(arguments, 1)
args.unshift(this)
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
installedPlugins.push(plugin)
return this
}
}
Vue.use(plugin)调用之后,插件的install方法就会默认接受到一个参数,这个参数就是Vue,该方法需要在调用 new Vue() 之前被调用。
使用 Vue.use() 注册插件,本质上就是执行了一个 install 方法,install 里的内容由开发者自己定义;插件不管是函数还是install方法 ,第一个参数总是vue对象
Vue.use和Vue.prototype的区别:Vue.use适用于注册vue生态内的插件,Vue.prototype适用于注册生态外的插件;一般Vue.use是有install方法的
install方法是一个用于全局注册组件、指令或插件的方法
当 install 方法被同一个插件多次调用,插件将只会被安装一次
install方法第一个参数是vue的构造器,第二个参数是可选的选项对象
注册全局组件(并不是use方法本身能注册全局组件,是此install方法或函数中的代码逻辑可以实现)
export default {
install(Vue, option) {
组件
指令
混入
挂载vue原型
},
}
通过 Vue.directive( id, [definition] )
方式注册全局指令。提供install方法,然后在入口文件中进行 Vue.use()
调用
// 定义一个vue指令
Vue.directive('my-directive', {
//只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
bind(el, binding, vnode, oldVnode) {},
//被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中),需要父节点dom时使用这个钩子
inserted(el, binding, vnode, oldVnode) {},
//所在组件的 VNode 更新时调用,**但是可能发生在其子 VNode 更新之前**。指令的值可能发生了改变,也可能 没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新
update(el, binding, vnode, oldVnode) {},
//指令所在组件的 VNode **及其子 VNode** 全部更新后调用。
componentUpdated(el, binding, vnode, oldVnode) {},
// 只调用一次,指令与元素解绑时调用。
unbind(el, binding, vnode, oldVnode) {},
})
// 注册 (指令函数)
Vue.directive('my-directive', function () {
// 这里将会被 `bind` 和 `update` 调用
})
一些钩子函数:
bind:
只调用一次,指令第一次绑定到元素时调用,可以定义一个在绑定时执行一次的初始化动作;
el
是指令绑定组件对应的dom
,binding
是我们的指令本身,包含指令name
、value
、expression
、arg
等,vnode
就是当前绑定组件对应的vnode
结点,oldVnode
就是vnode
更新前的状态
inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。
update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值。
componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。
unbind: 只调用一次, 指令与元素解绑时调用
实际使用:自定义一个全局的v-model指令
export const myModel = {
bind(el, binding, vnode, oldVnode) {
// 初始化的时候赋值一次
el.value = vnode.context[binding.expression]
// 监听input事件
el.addEventListener('input', (e) => {
// vnode.context是指令所在组件的上下文环境,可以理解就是指令绑定的值所在的组件实例
// binding.expression获取绑定的变量
vnode.context[binding.expression] = e.target.value
})
//监听绑定的变量,vnode.context.$watch使用Vue实例上的watch方法
vnode.context.$watch(binding.expression, (v) => {
el.value = v
})
},
}
export default {
install(Vue) {
Vue.directive('myModel', myModel)
},
}
main.js中进行注册:
import myModel from './derectives/myModel'
Vue.use(myModel)
组件中使用及测试:
data() {
return {
iptVal: 'hello',
}
},
methods: {
test1() {
console.log(this.iptVal)
},
test2() {
this.iptVal = 'test'
},
},
组件内定义一个指令,在初始化的时候input自动聚焦
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
},
},
},
使用:
const copy = {
bind(el, { value }) {
el.$value = value
el.handler = () => {
if (!el.$value) {
// 值为空的时候,给出提示。可根据项目UI仔细设计
console.log('无复制内容')
return
}
// 动态创建 textarea 标签
const textarea = document.createElement('textarea')
// 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
textarea.readOnly = 'readonly'
textarea.style.position = 'absolute'
textarea.style.left = '-9999px'
// 将要 copy 的值赋给 textarea 标签的 value 属性
textarea.value = el.$value
// 将 textarea 插入到 body 中
document.body.appendChild(textarea)
// 选中值并复制
textarea.select()
const result = document.execCommand('Copy')
if (result) {
console.log('复制成功') // 可根据项目UI仔细设计
}
document.body.removeChild(textarea)
}
// 绑定点击事件,就是所谓的一键 copy 啦
el.addEventListener('click', el.handler)
},
// 当传进来的值更新的时候触发
componentUpdated(el, { value }) {
el.$value = value
},
// 指令与元素解绑的时候,移除事件绑定
unbind(el) {
el.removeEventListener('click', el.handler)
},
}
export default copy
const ellipsis = {
inserted: function (el, binding) {
// 设置超出隐藏,可以优化下,让其支持多行
// el.style.overflow = 'hidden'
// el.style.textOverflow = 'ellipsis'
// el.style.whiteSpace = 'nowrap'
// 还是设置个全局样式吧
el.classList.add('one-line-ellipsis')
// 鼠标移入提示title
if (el.clientWidth < el.scrollWidth) {
el.title = el.innerHTML
}
},
}
export default ellipsis
批量注册指令
import copy from './copy'
import ellipsis from './ellipsis'
const directives = {
copy,
ellipsis,
}
export default {
install(Vue) {
Object.keys(directives).forEach((key) => {
Vue.directive(key, directives[key])
})
},
}
Vue.use()注册
import Directives from './derectives/index'
Vue.use(Directives)