认识自定义指令
页面加载完成后,input框默认获取焦点
自定义指令不能再setup中写,全局自定义指令用的更多
默认实现方式一 App.vue
这里要想通过ref拿到input框的步骤:
1.先给input框绑定 ref = "input"
2.在setup中初始化,必须也叫input:const input = ref(null)
这样后续就会自动绑定了
<template>
//ref的属性比较特殊,不用加 v-bind就可以绑定
<input type = "text" ref="input">
</template>
<script>
import {ref,onMounted} from 'vue'
export default{
setup(){
const input = ref(null);
onMounted(()=>{
input.value.focus();
})
return{
input
}
}
}
</script>
一旦我的页面多了,需要该需求的页面多了,再用默认的方式实现此操作,会非常的麻烦,所以我们希望用个v-focus,并对他实现(自定义指令)
方式二:(局部自定义指令)
<template>
<input type = "text" v-focus>
</template>
<script>
export default{
//局部自定义指令
directives:{
//自定义一个focus指令,使用的时候加 v- 就行
focus:{
//指令的生命周期,会传来4个参数
//mounted(el,bindings,vnode,preVnode)
mounted(el){
el.focus();
}
}
}
}
</script>
补充
el:
指令绑定到的元素。这可用于直接操作 DOM。
vnode
上面作为 el 参数收到的真实 DOM 元素的蓝图。
prevNode
上一个虚拟节点,仅在 beforeUpdate 和 updated 钩子中可用。
注意:除了 el 之外,你应该将这些参数视为只读,并且永远不要修改它们。
如果你需要跨钩子共享信息,建议通过元素的自定义数据属性集 (opens new window)进行共享。
方式三:(全局自定义指令) 在main.js中
const app = createApp(App)
app.directive("focus",{
mounted(el){
el.focus();
}
})
app.mount('#app')
然后直接在要使用的页面中使用,即可
<template>
<input type = "text" v-focus>
</template>
自定义指令的生命周期
1.created:在绑定元素的attribute或事件监听器被应用之前调用。
<input>的框里面会有其他属性或监听器,在他们还没有被调用的时候,就开始生命周期了
2.beforeMount:当指令第一次绑定到元素并且在挂载父组件之前被调用
元素属性等已经被创建出来,但是还没有被挂载的时候
3.mounted:在绑定元素的父组件被挂在后调用
4.beforeUpdate:在更新包含组件的VNode之前调用
5.updated:在包含组件的VNode及子组件的VNode更新后调用
6.beforeUnmount:在卸载绑定元素的父组件之前调用
7.unmounted:当指令与元素解除绑定且父组件已卸载时,只调用一次
自定义指令也是可以有修饰符的,比如.lazy、.stop
<template>
<input type = "text" v-why>
<button @click="increment">
当前计数:{{counter}}
</button>
//当我给自定义指令传入修饰符,并且赋值
v-why.aaa.bbb="'coderwhy'"
</template>
<script>
export default{
import {ref} from 'vue'
//局部自定义指令
directives:{
why:{
created(bindings){
//修饰符传值,是传到了bindings中
console.log(bingdings)
//获取修饰符传过来的具体值
bindings.value
//获取修饰符(拿到的是一个对象)
是一个键值对的形式aaa="true"
当我使用到aaa的时候才为true
bingings.modifiers
},
beforecreated(){
},
mounted(){
},
beforeUpdate(){
},
updated(){
},
beforeUnmount(){
},
unmounted(){
},
}
},
setup(){
const counter = ref(0)
const increment()=()=> counter.value++
return{
counter,
increment,
}
}
}
</script>
自定义指令练习
将时间戳 转变为 正常时间
App.vue
<template>
自己定义时间的格式,只需要传递一下参数就行
<h2 v-format-time="'YYYY/MM/DD'">{{timestamp}}</h2>
</template>
<script>
import {ref} from 'vue'
export default{
setup(){
const timestamp = 1624452193
return{
timestamp
}
}
}
</script>
全局自定义指令main.js 但是会造成main.js的代码过多,所以我们最好单独建一个文件夹,专门用来存放全局自定义指令
directives/index.js
import registerFormatTime from './format-time'
export default function registerDirectives(app){
registerFormatTime(app)
}
directives/format-time.js
1.通过安装第三点方库:npm install dayjs
import dayjs from 'dayjs'
export default function(app){
let formatString = "YYYY-MM-DD HH:mm:ss";
//全局注册.directive
app.directive("format-time",{
//2.修改timestamp,bindings拿到时间格式化
mounted(el,bindings){
//判断是否有传入参数,没有就用默认的格式,有就获取具体参数值
if(formatString)
formatString = bindings.value
3.拿到el中的值(字符串),el可以拿真实DOM,也就是timestamp值
const textContent = el.textContent;
4.将字符串转换为int型
let timestamp = parseInt(textContent)
if(textContent.length === 10){
5.长度为10表示秒钟,单位转换为毫秒
timestamp = timestamp * 1000
}
//6.重新对textContent赋值 HH:24小时制
el.textContent =
dayjs(timestamp).
format(formatString);
}
})
}
directives/format-time.js中存在的问题
let formatString = "YYYY-MM-DD HH:mm:ss";
其实是一个闭包,当我在App.vue中,第一次<h2 v-format-time="'YYYY/MM/DD'">{{timestamp}}</h2>确实对他传入了参数,并且我希望他是返回一个我指定参数样式的时间戳
但是如果我后面又写了很多<h2 v-format-time>{{timestamp}}</h2>无参的,按道理他返回的是默认时间戳的样式,但是由于他是一个闭包,它实际上后续返回的任然是前面指定样式后的时间戳的格式
解决方法
如果只是将let formatString = "YYYY-MM-DD HH:mm:ss";放在created里面,那么mounted里面就会取不到formatString的值,所以不能这样写。
我们需要将他放在bindings对象里面,因为mounted和created生命周期里的bindings参数对象是同一个东西。
main.js
import registerDirectives from './directives/index'
registerDirectives(app);
认识Teleport
应用程序最终会形参一颗DOM树结构,但是我不想成为树
<template>
<div class="app">
<h2>当前计数</h2>
<button>+1</button>
</div>
</template>
<script>
export default{
components:{
}
}
</script>
默认挂载的样子:
<template>
<div class="app">
<teleport to="#why">
<h2>当前计数</h2>
<button>+1</button>
</teleport>
</div>
</template>
移动组件: HelloWorld
<template>
<div class="HelloWorld">
<h2>HelloWorld</h2>
</div>
</template>
App
<template>
<div class="app">
组件也同样移动到id标签为why的地方了
<teleport to="#why">
<h2>当前计数</h2>
<button>+1</button>
<HelloWorld/>
</teleport>
</div>
</template>
<script>
import HelloWorld from './HelloWorld'
export default{
components:{
HelloWorld
}
}
</scrpit>
认识Vue插件
plugins/plugins_object.js
export default{
必须要有一个install函数,默认传app函数
install(app){
全局属性
app.config.globalProperties.name = "coderwhy"
}
后续可以再任何页面,通过window.name拿到coderwhy
}
main.js
import pluginObject from './plugins/pluginObject'
app.use(pluginObject)
在组件中获取定义的.name值
setup中获取该属性,因为他用不了this
setup(){
const instance = getCurrentInstance();
console.log(instance.appContext.
config.globalProperties.name)
}
也可以将插件写成一个函数