vue的全局自定义指令
除了核心功能默认内置的指令(v-model , v-show等等),Vue 也允许注册自定义指令。
Vue.directive( “eventName”, options)
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
- bind:只调用一次,。在这里可以进行一次性的初始化设置。指令第一次绑定到元素时调用
- inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
- update:所在组件的 VNode ( 抽象DOM )更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新
- componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
- unbind:只调用一次,指令与元素解绑时调用。
<body>
<div id='app'>
<!-- <div v-dd>你好,世界</div> -->
<cpn></cpn>
</div>
<script src='./js/vue.js'></script>
<script>
Vue.directive('dd', {
bind() {
console.log('bind:初始化设置只会执行一次,第一次被绑定到元素时调用');
//console.log(document.getElementById('div').innerText); //获取不到,此时模板还没有被挂载到页面
},
inserted() {
console.log('inserted:被绑定元素插入父节点时调用');
console.log(document.getElementById('div').innerText);
},
update() {
console.log('update:所在组件更新时调用');
console.log(document.getElementById('div').innerText); //页面还没有更新
},
componentUpdated() {
console.log('页面已经更新');
console.log(document.getElementById('div').innerText); //页面已经更新
},
unbind() {
console.log('指令断开链接');
}
})
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn: {
data() {
return { msg: '哈哈哈' }
},
template: '<div v-dd id="div">{{msg}}<button @click="fn">btn</button><button @click="cle">cle</button></div>',
methods: {
fn() {
// console.log('ok');
this.msg += '嘿嘿嘿'
},
cle() {
this.$destroy()
}
}
}
}
})
</script>
</body>
vue的局部自定义指令
如果想注册局部指令,组件中也接受一个 directives的选项:
<body>
<div id='app'>
<cpn></cpn>
</div>
<script src='./js/vue.js'></script>
<script>
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn: {
template: '<div v-dd>我是cpn</div>',
directives: {
dd: {
inserted() {
console.log('hello world');
}
}
}
}
}
})
</script>
</body>
vue的自定义指令的钩子函数参数
指令钩子函数会被传入以下参数:
- el:指令所绑定的元素,可以用来直接操作 DOM 。
- binding:一个对象,包含以下属性:
- name:指令名,不包括 v- 前缀。
- value:指令的绑定值,例如:v-my-directive=“1 + 1” 中,绑定值为 2。
- oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
- expression:字符串形式的指令表达式。例如 v-my-directive=“1 + 1” 中,表达式为 “1 + 1”。
- arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 “foo”。
- modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
- vnode:Vue 编译生成的虚拟节点。
- oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
<body>
<div id='app'>
<cpn></cpn>
</div>
<template id="cpn">
<div v-dd.foo.bar="1+2">
我是cpn{{msg}}
<button @click="btn">更新</button>
<button @click="boom">销毁</button>
</div>
</template>
<script src='./js/vue.js'></script>
<script>
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn: {
data() {
return {
msg: '你好,世界'
}
},
template: '#cpn',
methods: {
btn() {
this.msg += '你也好'
},
boom() {
this.$destroy()
}
},
directives: {
dd: {
bind(el, binding, vnode, oldVnode) {
console.log('%c%s', 'color:red', 'bind_el:', el);
console.log('%c%s', 'color:red', 'bind_binding:', binding);
console.log('%c%s', 'color:red', 'bind_vnode:', vnode);
console.log('%c%s', 'color:red', 'bind_oldVnode:', oldVnode);
},
inserted(el, binding, vnode, oldVnode) {
console.log('%c%s', 'color:red', 'inserte_del:', el);
console.log('%c%s', 'color:red', 'inserted_binding:', binding);
console.log('%c%s', 'color:red', 'inserted_vnode:', vnode);
console.log('%c%s', 'color:red', 'inserted_oldVnode:', oldVnode);
},
update(el, binding, vnode, oldVnode) {
console.log('%c%s', 'color:red', '更新1el:', el);
console.log('%c%s', 'color:red', '更新1binding:', binding);
console.log('%c%s', 'color:red', 'update_vnode:', vnode);
console.log('%c%s', 'color:red', 'update_oldVnode:', oldVnode);
},
componentUpdated(el, binding, vnode, oldVnode) {
console.log('%c%s', 'color:red', '更新2el:', el);
console.log('%c%s', 'color:red', '更新2binding:', binding);
console.log('%c%s', 'color:red', 'componentUpdated_vnode:', vnode);
console.log('%c%s', 'color:red', 'componentUpdated_oldVnode:', oldVnode);
},
unbind(el, binding, vnode, oldVnode) {
console.log('%c%s', 'color:red', '销毁el:', el);
console.log('%c%s', 'color:red', '销毁binding:', binding);
console.log('%c%s', 'color:red', 'unbind_vnode:', vnode);
console.log('%c%s', 'color:red', 'unbind_oldVnode:', oldVnode);
}
}
}
}
}
})
</script>
</body>
vue的动态指令参数
<body>
<div id='app'>
<h1>自定义指令动态参数</h1>
<cpn></cpn>
</div>
<!-- <template id='cpn'>
<div>
<div v-for='(item,index) in items' :key='item' @click='change_index(index)'
:class="['nav_item',{'nav_active':index === current_index}]">{{item}}</div>
</div>
</template> -->
<template id='cpn'>
<!-- <div v-change_class> -->
<div v-change_class="{
class_name:'nav_item',
active_class:'nav_active',
current_index
}">
<!-- <div v-for='(item,index) in items' :key='item' @click='change_index(index)'
:class="['nav_item',{'nav_active':index === current_index}]">{{item}}</div> -->
<div v-for='(item,index) in items' :key='item' @click='change_index(index)' class="nav_item">{{item}}</div>
</div>
</template>
<script src='./js/vue.js'></script>
<script>
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn: {
template: '#cpn',
directives: {
change_class: {
bind(el, binding) {
console.log('bind');
console.log(binding);
const options = binding.value;
const { class_name, active_class, current_index } = options
const children = el.getElementsByClassName(class_name)
// children[current_index].className += ' active_class'
children[current_index].className += ` ${active_class}`
},
update(el, binding) {
// console.log('update');
// console.log(binding);
// 拿到旧的元素
const old_options = binding.oldValue
const options = binding.value;
const { class_name, active_class, current_index } = options
const { current_index: old_indx } = old_options
const children = el.getElementsByClassName(class_name)
children[current_index].className += ` ${active_class}`
children[old_indx].className = class_name
}
}
},
data() {
return {
items: ['项目1', '项目2', '项目3'],
current_index: 0
}
},
methods: {
change_index(index) {
this.current_index = index
}
}
}
}
})
</script>
</body>
vue的自定义指令的函数简写
在很多时候,你可能想在bind 和 update时触发相同行为,而不关心其它的钩子。比如这样写:
change_class(el, binding) {
const old_options = binding.oldValue
const options = binding.value;
if (old_options) {
const { current_index: old_indx } = old_options
const { class_name, active_class, current_index } = options
const children = el.getElementsByClassName(class_name)
children[old_indx].className = class_name
}
const { class_name, active_class, current_index } = options
const children = el.getElementsByClassName(class_name)
children[current_index].className += ` ${active_class}`
}
渲染函数
Vue 推荐在绝大多数情况下使用模板来创建你的 HTML。然而在一些场景中,你真的需要 JavaScript 的完全编程的能力。这时你可以用渲染函数,它比模板更接近编译器。
节点、树以及虚拟 DOM
在深入渲染函数之前,回顾一些浏览器的工作原理
<div id='app'>
<h1>My title</h1>
<!-- TODO: Add tagline -->
</div>
以这段 HTML 为例:
当浏览器读到这些代码时,它会建立一个“DOM 节点”树来保持追踪所有内容,如同你会画一张家谱树来追踪家庭成员的发展一样。
每个元素都是一个节点。每段文字也是一个节点。甚至注释也都是节点。
高效地更新所有这些节点会是比较困难的,不过所幸你不必手动完成这个工作。你只需要告诉Vue 你希望页面上的HTML 是什么
- 这可以是在一个模板里:
<body>
<div id='app'>
{{msg}}
</div>
<script src='./js/vue.js'></script>
<script>
const app = new Vue({
el: '#app',
data: {
msg: '你好,世界'
}
})
</script>
</body>
或者一个渲染函数里:
render(createElement) {
return createElement('h2', this.msg)
}
在这两种情况下,Vue 都会自动保持页面的更新
render渲染函数详解
render函数就是用来渲染一个DOM的, 函数里面会有一个形参叫做createElement, createElement是专门用来渲染出DOM的函数,这个函数返回的不是一个实际的DOM.它更准确的名字可能是 createNodeDescription,因为它所包含的信息会告诉 Vue 页面上需要渲染什么样的节点,包括及其子节点的描述信息。我们把这样的节点描述为“虚拟节点 (virtual node)”,也常简写它为“VNode”。“虚拟 DOM”是我们对由 Vue 组件树建立起来的整个VNode 树的称呼。
createElement函数的参数
render(createElement) {
return createElement(
'div', //第一个参数是标签的名称,可以是字符串,组件对象
// 向模板中添加属性
{
attrs: { id: 111, class: 'main' },
style: {
color: 'red',
fontSize: '30px'
},
},
// 文本节点内容
//'aaa' //Strign
[
createElement('h1', this.name),
createElement('h2', this.num),
createElement('h3', '爱老虎油'),
]
// this.msg
)
}
有一点要注意:正如v-bind:class 和 v-bind:style在模板语法中会被特别对待一样,createElement函数的object参数在 VNode数据对象中也有对应的顶层字段。该对象也允许你绑定普通的HTML 特性,也允许绑定如innerHTML 这样的 DOM 属性 (这会覆盖 v-html指令)。
{
//接收普通特性
attrs: { id: 111, class: 'main' },
//接收对象
style: {
color: 'red',
fontSize: '30px'
},
},