到目前为止,我们已经接触到了 vue 官方的指令有:
v-text
、v-html
(v-once
、v-slot
)v-bind
、v-on
v-if
、v-else-if
、v-show
v-for
、v-model
除此之外 vue 还允许开发者自定义指令,来满足自己的个别需求。
应用:权限管理
# 一、自定义指令
# 1、简介
官方描述:
有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
vue 指令分析:
以v-show
指令为例,它就是一个 vue 操作 DOM 元素的 display 属性而实现的一个语法糖。
从某种角度来讲,vue 指令就是对 一些原生 DOM 操作进行封装,方便后续使用
# 2、代码体验
使用指令的方式操作 DOM,简单实现如下:
除了 elm 之外,其它参数都应该是只读的,切勿进行修改。
<div id="app">
v-text指令:<span v-text="msg1"></span> <br /><br />
v-textBig指令:<span v-redtext="msg2"></span>
</div>
<script type="text/JavaScript">
new Vue({
el: '#app',
data: {
msg1: 'vue的指令',
msg2: '自定义的指令'
},
directives: {
redtext(elm,binding) {
// 1、参数介绍
// elem参数:当前使用的真实DOM元素
// binding参数:绑定包含数据变量的信息的一个对象
console.log(elm instanceof HTMLElement)
console.log(binding.value)
// 2、功能实现
elm.innerHTML = binding.value
elm.style.color = "red"
}
}
});
</script>
体会:
- 使用 vue 提供的两个参数就可以对原始 DOM 进行操作
- 指令命名建议不要出现大写(我使用 redText 就报错了 🤔)
# 3、钩子函数 ✍
上面是一个自定义指令的简写(bind 和 updata 构造函数的简写),并不是完整写法。
有些情况,使用前面函数式的写法是不可以实现目标效果的。
下面,就分析一些各个钩子函数的使用:
<div id="app">
<button @click="keyValue = keyValue + 'd '">更新数据</button>
<br />
<input type="text" v-focus:value="keyValue" />
<br />
内容:{{keyValue}}
</div>
<script type="text/JavaScript">
new Vue({
el: '#app',
data: {
keyValue:''
},
directives: {
focus: {
// 过程一:自定义指令和DOM元素绑定时(一上来)
bind(elm,binding){
console.log("绑定成功")
elm.value = binding.value
},
// 过程二:对应的DOM元素插入页面时(父节点存在)
inserted(elm,binding){
console.log("DOM元素成功渲染,执行……")
elm.focus()
},
// 过程三:对应的DOM元素重新解析时
update(elm,binding) {
console.log("DOM元素重新解析成功")
elm.value = binding.value
},
}
}
});
</script>
# 4、全局自定义指令
要设置全局的话,其实和过滤器、组件注册等类似
提示
换个角度思考:若使用私有的,自定义指令优点将很难发挥出来
在后续的组件化开发中,自定义指令大多数使用的是全局模式
// 简写形式
Vue.directive('color', function (el, binding){
el.style.color = binding.value
})
// 完整形式
Vue.directive('color', {
bind (el, binding) {
el.style.color = binding.value
}
update (el, binding) {
el.style.color = binding.value
}
})
# 二、生命周期函数
既然上面我们讲到了钩子函数,那就不得不讲一下 vue 的生命周期钩子了。
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。
那我们可不可以像自定义中一样,在其功能实现的各个阶段进行操作呢?
生命周期钩子的函数,就提供了让用户可以在不同阶段添加自己的代码的机会
# 1、代码引入(mounted)
提示
生命周期钩子的 this
上下文指向调用它的 Vue 实例
<script>
new Vue({
el: '#app',
mounted() {
console.log(this) // vue实例
}
})
</script>
下面我们实现一个小小的功能:DOM 页面一渲染完毕,就自动开始执行某些操作。
分析实现:
如果使用原生 js 的话,我第一时间相对的就是使用立即执行函数。但在 vue 中,我会转而思考可不可以用 bottom 触发事件回调的方式代替;最佳方案还得是 vue 的生命周期钩子
# 2、解决想法
- 立即执行函数
<div id="timer"></div>
<script>
;(function () {
let timeLeft = 60
const countdownID = setInterval(function () {
if (timeLeft <= 0) {
clearInterval(countdownID)
document.getElementById('timer').innerHTML = '时间到'
} else {
document.getElementById('timer').innerHTML = timeLeft + ' 秒'
}
timeLeft -= 1
}, 1000)
})()
</script>
- 事件回调(但是问题改变了 🤔)
<div id="app">
<button @click="startCountdown">开始倒计时</button>
<div id="timer">{{ timeLeft }}秒</div>
</div>
<script type="text/JavaScript">
new Vue({
el: '#app',
data: {
timeLeft: 60,
},
methods: {
startCountdown() {
const countdownID = setInterval(() => {
if (this.timeLeft <= 0) {
clearInterval(countdownID)
document.getElementById('timer').innerHTML = '时间到'
} else {
this.timeLeft--
}
}, 1000)
}
}
});
</script>
# 3、vue 解决
如果使用 vue 的生命周期钩子,上面的两种效果我们都可以实现
- 自动触发(mounted 钩子)
<div id="app">
<div id="timer">{{ timeLeft }}秒</div>
</div>
<script type="text/JavaScript">
new Vue({
el: '#app',
data: {
timeLeft: 60,
},
mounted() {
const countdownID = setInterval(() => {
if (this.timeLeft <= 0) {
clearInterval(countdownID)
document.getElementById('timer').innerHTML = '时间到'
} else {
this.timeLeft--
}
}, 1000)
},
})
</script>
- 手动停止
methods 中 和 钩子函数中 的定时器 id 共用怎么办? —— 挂载 👏 到 vm 示例上
<div id="app">
<button @click="stopCountdown">暂停</button>
<button @click="startCountdown">启动</button>
<div id="timer">{{ timeLeft }}秒</div>
</div>
<script type="text/JavaScript">
new Vue({
el: '#app',
data: {
timeLeft: 60,
timeTemp: 0
},
methods: {
stopCountdown() {
this.timeTemp = this.timeLeft
clearInterval(this.countdownID)
},
startCountdown() {
this.timeLeft = this.timeTemp
this.countDown()
},
countDown() {
// 将id值挂载到vm🚩实例上
this.countdownID = setInterval(() => {
this.timeLeft--
if (this.timeLeft <= 0) clearInterval(this.countdownID)
}, 1000)
}
},
mounted() {
this.countDown()
},
})
</script>