自定义指令 directives
directives 的简写
- 我们可以通过配置项
directives
来自定义指令 - 自定义指令时直接写指令名
XXX
,使用时需要加上前缀为v-XXX
<div id="app">
<!-- 使用自定义指令 big -->
<div v-big="num"></div>
</div>
let vm = new Vue({
el: "#app",
data: { num: 1 },
// 配置 directives
directives: {
// 配置 [注册函数]
big(ele, binding, node) { // 接收 3 个参数
// 参数1 - ele:挂载的 DOM 节点
console.log("ele", ele); // ele div
// 参数2 - binding:绑定对象
console.log("binding", binding);
// binding {name: 'big', rawName: 'v-big', value: 1, expression: 'num', …}
// 参数3 - node:虚拟节点
console.log("node", node);
// node VNode {tag: 'div', data: {…}, children: undefined, text: undefined, elm: div, …}
// this 指向 window
console.log("this", this);
// 操作 DOM
ele.innerText = binding.value + " -> " + binding.value * 10;
}
}
});
指令的命名
- 指令名为多个单词时,用
-
拼接
<!-- 使用自定义指令 my-instruction -->
<div v-my-instruction="value"></div>
let vm = new Vue({
el: "#app",
data: { value: 100 },
directives: {
"my-instruction"(ele, binding) {
console.log("ele", ele); // ele div
console.log("binding", binding);
// binding {name: 'my-instruction', rawName: 'v-my-instruction', value: 100, …}
}
}
});
回调函数的触发
上面的 [注册函数] 是一种简写,触发的时间有两个:
①指令与元素绑定时触发、②模版重新渲染时触发(就是说,页面被重新渲染就会触发)
指令与元素绑定时触发注册函数,注册函数执行完后,虚拟 DOM 才插入到页面中
就是说,如果我们在注册函数中设置页面的动态效果 (eg: focus),是不会生效的:
姓名:<input type="text" v-model="name" v-focus>
<button @click="change">修改</button>
let vm = new Vue({
el: "#app",
data: { name: "superman" },
directives: {
focus(el) { el.focus(); } // DOM 元素调用 focus() 方法,实现聚焦功能
// 但该 [注册函数] 执行完后,虚拟 DOM 才插入到页面中,所以聚焦功能并没有生效
},
methods: {
// 修改 DOM 元素,使页面重新渲染 ( [注册函数] 再被调用 )
change() { this.name += "!" }
}
});
我们可以发现,在页面加载完成后,input 元素并没有聚焦。之后修改 data
中的数据后,页面重新渲染,input 才聚焦
(修改 data
中的数据,页面重新渲染,注册函数再被调用,再次执行 focus()
,input 才会聚焦)
directives 的标准写法
- 简写中,注册函数只能在 [指令与元素绑定]、[模版被重新渲染] 时触发
- 如果使用标准写法,通过钩子函数,可以设置挂载元素在不同状态下的行为
- 注意:每个钩子函数都接收
el
、binding
、vnode
3 个参数
focus: {
bind() {
console.log("指令与元素绑定时触发");
},
inserted(el) {
console.log("指令所在元素插入页面时触发");
el.focus();
},
update(el) {
console.log("模板重新渲染时触发");
el.focus();
}
}
全局注册 directive
Vue.directive("自定义指令名", 配置对象)
// 注意:全局注册的关键字是 directive,不是 directives- 注意:全局注册的话,
Vue.directive()
需要在 Vue 实例创建之前编写
Vue.directive("focus", {
bind() {
console.log("指令与元素绑定时触发");
},
inserted(el) {
console.log("指令所在元素插入页面时触发");
el.focus();
},
update(el) {
console.log("模板重新渲染时触发");
el.focus();
}
});
let vm = new Vue({
el: "#app",
data: { name: "superman" },
methods: {
change() { this.name += "!" }
}
});
- 也可以使用简写(回调函数执行完后,DOM 才会插入到页面中):
Vue.directive("focus", (el, binding) => {
console.log(el, binding); // el:挂载的元素; binding:挂载的信息
console.log(binding.value); // 获取指令的属性值
});
过滤器 filter
(Vue3已弃用)
dayjs
<script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.4.1/dayjs.min.js"></script>
- 引入 dayjs 后,暴露
dayjs
方法,接收 1 个时间戳参数,默认为当前时间戳 format
为格式化方法,接收字符串参数,为格式化的模版
dayjs().format('YYYY-MM-DD HH:mm:ss')
局部过滤器
- 配置
filters
属性对象,该对象的属性值为过滤器方法 - 局部创建的过滤器,只能在挂载对象内使用
<div id="app">
time: {{time}} <br />
<!-- 过滤器的书写格式: 被过滤的数据 | 过滤后的数据 -->
fmtTime: {{time | fmtTime}}
</div>
let vm = new Vue({
el: "#app",
data: { time: 1648275087640 },
filters: {
// [被过滤的数据] 会作为第 1 参数传入
fmtTime(time) {
/* 引入 dayjs 后,暴露 dayjs 方法,接收 1 个时间戳参数,默认为当前时间戳 */
/* format 为格式化方法,接收字符串参数,为格式化的模版 */
return dayjs(time).format('YYYY-MM-DD HH:mm:ss')
}
}
});
带参数的过滤器
<!-- 管道可以配置参数 -->
fmtTime: {{time | fmtTime("YYYY_MM_DD")}}
filters: {
// 配置的参数会作为后续参数传入,第 1 参数始终是 [被过滤的数据]
fmtTime(time, fmtStr = "YYYY-MM-DD HH:mm:ss") {
// 给 fmtStr 设置默认值
return dayjs(time).format(fmtStr)
}
}
过滤器的链式调用
time: {{time}} <br />
fmtTime: {{time | fmtTime}} <br />
<!-- 管道可以链式调用 -->
date: {{time | fmtTime | date}}
filters: {
fmtTime(time, fmtStr = "YYYY-MM-DD HH:mm:ss") { return dayjs(time).format(fmtStr) ,
date(time) { return time.split(' ')[0] }
}
配合
v-bind
使用,可以设置标签元素的属性值
<div :name="msg | prefix">{{msg | prefix}}</div>
let vm = new Vue({
el: "#app",
data: { msg: "superman" },
filters: {
prefix(val) { return val.slice(0, 5) },
}
});
filter
可用computed
/methods
替代
time: {{time}} <br />
fmtTime: {{fmtTime}}
let vm = new Vue({
el: "#app",
data: { time: 1648275087640 },
computed: {
fmtTime() { return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss') }
},
});
全局过滤器
Vue.filter(name, callback)
name
:自定义名称callback
:回调函数,接收 1 个参数val
-被过滤的数据
- 注意:全局注册的话,
Vue.filter()
需在 Vue 实例创建之前编写
<input type="text" v-model="msg">
<!-- 使用过滤器格式化字符串,将首字母大写 -->
<h2>{{msg | upFirst}}</h2>
// 创建全局过滤器 - 默认 [被过滤的数据] 为第 1 参数
Vue.filter("upFirst", val => val.charAt(0).toUpperCase() + val.slice(1));
let app = new Vue({
el: "#app",
data: { msg: "superman" }
});
带参数的全局过滤器
<!-- 使用带参数的全局过滤器 -->
<h2>{{time | format('yyyy-MM-dd')}}</h2>
// 创建带参数的全局过滤器
Vue.filter("format", (val, arg) => { // 第 2 个形参开始,就是用于接收传入的参数的啦
let res = '';
if (arg == 'yyyy-MM-dd')
res = val.getFullYear() + '-' + (val.getMonth() + 1) + '-' + val.getDate();
return res;
});
let app = new Vue({
el: "#app",
data: { time: new Date() }
});
页面的渲染
data
中的数据更新后,页面会更新模板中的数据,重新渲染页面- 更新模板中的数据,参与数据显示的方法就会被调用
<div id="app">
{{name}} ----- {{myName}} ----- {{showName()}} ----- {{name | changeName}}
</div>
Vue.filter("changeName", val => {
console.log("filter");
return val.charAt(0).toUpperCase() + val.slice(1);
});
let vm = new Vue({
el: "#app",
data: { name: "superman" },
computed: {
myName() {
console.log("computed");
return this.name
}
},
methods: {
showName() {
console.log("methods-showName");
return 123;
},
others() { // 该方法没有在页面上显示数据,所以渲染页面时不会调用该方法
console.log("methods-others");
return 345;
}
}
});
- 初始化页面时,需要获取
data
中的数据name
、computed
中myName
返回的数据、methods
中showName
返回的数据、filter
的回调函数返回的数据 - 如果修改了
data
中的数据name
,页面需要重新渲染,myName
、showName
等所有参与了数据显示的方法,都会再执行一次