深入vue3学习
Posted lin_fightin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入vue3学习相关的知识,希望对你有一定的参考价值。
生命周期
onX
onMounted(()=>{
console.log('123');
})
setup没有create和beforeCreated这两个声明周期,因为这两个做的事情可以在setup中做,而其他生命周期就是onX的i形式出现。
自定义hooks
我们知道vue2的痛点就是逻辑都写一起了,只要业务一复杂,代码量大。vue3的好处就是能将功能拆分出来,形成每个文件,这样就将代码逻辑抽离出来,一个功能一块代码。
比如我们现在做一个计数器,那么我们可以
import {ref, Ref} from 'vue'
export const useConter = () => {
const state = ref<number>(123)
const add = () => {
state.value++
}
return {state, add}
}
创建一个文件,写一个hooks
然后
setup(props, context) {
const { state, add } = useConter()
return { state, add };
},
回到app.vue中在setup中使用。
这样我们的计数器这块的功能代码就已经被抽离出去了。
改变title
export const useTitle = (title: string) => {
const titleRef = ref<string>(title)
watch(titleRef, (n: string) => {
document.title = n
})
return { titleRef }
}
可以看到,我们需要依赖watch的值监听n的改变才会改变document.title,这也是与react不同的一点,react只要值改变了就会重新刷新组件,所以会自动更新其依赖的值,而vue不会,如果你将document.title = titleRef.value,当其值改变的时候,document.title并不会刷新,所以需要依赖watch来手动改变它的值。
比如
export const useTitle = (title: string) => {
const titleRef = ref<string>(title)
document.title = titleRef.value
return { titleRef }
}
这样写,当titleRef改变的时候,其不会运行。响应式只会去修改template上的内容,不会重新刷新该hooks。这也是与react相差很大的一点。
实验性语法
vue3还有一个大胆的实验性语法
<script lang="ts" setup>
import { ref } from 'vue'
import AA from './component/aa.component.vue'
const test = ref('test')
</script>
在script上面加上setup,那么就不用返回了,这个定义的变量或者引入的组件可以直接在template使用,而
props和emit则是这样使用
<script lang="ts" setup>
import { ref, defineEmits, defineProps } from 'vue'
import AA from './component/aa.component.vue'
const test = ref('test')
const props = defineProps({
test: String
})
const emit = defineEmits(['in', 'on'])
emit('in', '传过去的值')
</script>
通过defineProps还有defineEmits来使用props和emit。不过这个还是实验性语法,不知道会不会正式被更新。
高级特性 H函数
vue需要使用模板来创建html,但是在某种特殊情况上需要使用js来,那么就需要渲染函数,它比模板更接近编译器。
template会被渲染函数生成对应的vnode,如果想用js的编程能力,可以自己来编写createVnode函数,生成对应的vNode。
h函数就是用来创建vnode的,准备的命名应该是CreateVnode函数,但是为了简便就称为h函数。
h接受三个参数,(了解)
第一个: 标签名 组件 异步组件 函数组件等等 必须
第二个: props 可选
第三个:string array object 子vnode 另一个h()等等 可选
import {defineComponent, h} from 'vue'
export default defineComponent({
render(){
return h('h2', {class: 'aa'}, '123123')
}
})
渲染出来就是
<h2 class="aa" data-v-55c42084="">123123</h2>
setup代替render
export default defineComponent({
setup() {
return () => {
return h("h2", { class: "aa" }, "123123");
};
},
});
只要返回的时候返回一个函数,该函数返回一个vnode即可代替render。
插槽的使用
import CC from "./cc.component.vue";
export default defineComponent({
setup() {
return () => {
return h(
CC,
{},
{
default: (props) => h("span", null, `插槽${props.name}呀`),
}
);
};
},
});
将default这个插槽传进去cc
import { h, defineComponent } from "vue";
export default defineComponent({
setup(props, context) {
return () => {
return h("div", null, [
h("h2", null, "cc"),
context.slots.default
? context.slots.default({ name: "我是cc传出去的" })
: h("span", {}, "默认插槽"),
]);
};
},
});
cc里通过context.slots.default去拿到该函数并且调用返回vnode。
jsx的babel配置
在vue中使用jsx,只需要在Babel中配置对应的插件即可。
npm install @vue/babel-plugin-jsx -D
在babel.config.js文件里面配置
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: [
'@vue/babel-plugin-jsx'
]
}
<script lang='tsx' >
import { h, defineComponent, render, ref } from "vue";
export default defineComponent({
setup() {
const state = ref<number>(0);
return () => {
return (
<div>
<h2>{state.value}</h2>
<button
onClick={() => {
state.value++;
}}
>
+
</button>
</div>
);
};
},
});
</script>
有template就没有render,有render就没template
好家伙,我看到了react的影子。就是组件的传递这些还是用slot这些。
自定义指令
<input type="text" v-focus>
这个v-focus就是自定义指令
export default defineComponent({
name: "AA",
components: {},
props: {
data: String,
},
//自定义指令
directives: {
focus: {
mounted(el, bindings, vnode, preVnode){
console.log('ipt标签已挂载');
el.focus()
console.log(el)
console.log(bindings);
console.log(vnode);
console.log(preVnode);
}
}
},
通过directives来定义自定义指令。
focus就是自定义指令,里面有几个生命周期,mounted就是v-focus对应的dom挂载的时候调用的。
el能拿到dom,然后调用.focus就可以获取焦点。
这是局部指令,只能在该组件使用。
修饰符
bindings是一个对象,可以拿到v-focus.ccc=“123”中的123的值和ccc的修饰符
vnode是该虚拟节点对象
全局指令
app.directive("focus", {
mounted(el) {
el.focus();
},
});
在main.ts中的app上挂载这方法,就可以在全局上使用。
其他生命周期
created 在绑定元素挂在前调用
beforeMounted
mounted
beforeUpdate
updared
beforeUnmount
unmount
封装一个自定义指令
import { App, onMounted } from "vue";
const formater = (app: App<Element>, key: string) => {
app.directive(key, {
mounted(el,bindings) {
const textContent = el.textContent;
const foramtter = bindings.value || 'HH-MM-YY'
console.log('textContent', textContent);
console.log('bindings', bindings.value);
},
});
};
export { formater }
在main.ts中使用即可
const app = createApp(App);
app
.use(store, key)
.use(router)
.use(i18n)
.use(ElementPlus)
.mount("#app");
app.config.globalProperties.$tt = t;
//注册指令
formater(app, 'formater')
<div v-formater="'hh-mm-cc'">12321321321</div>
认识Teleport
我们知道,组件A在在组件B中使用的时候,那么a的template会被挂载到b的template的某个位置。
teleport的功能是将组件挂载到另一个app
类似于react的Portals
他有两个属性,
to:指定将其中的内容移动到目标元素,可以选择选择器
disabled:是否禁用teleport功能。
<teleport to="#lin">aaa</teleport>
这样aaa就会挂载在id为lin的组件上,与app同为兄弟节点,
Vue插件
我们刚才使用全局指令就是给全局添加一个功能,但其实vue也可以使用插件的模式。
有两种模式:
1 一个对象,但是必须包含install函数,会在安装插件时执行
2 函数类型, 这个函数会在安装插件时自动执行
const testPlugins = {
install(){}
}
export { testPlugins }
在main.ts中
app
.use(store, key)
.use(router)
.use(i18n)
.use(ElementPlus)
.use(testPlugins)
使用app.use()包裹起来,源码中会调用该对象的install方法并且执行。
就像 testPlugins.install(app)这样操作,将app传进去。
const testPlugins = {
install(app: App<Element>){
app.config.globalProperties.name = '123'
}
}
app.config就是其配置属性,在globalProperties上面增加属性相当于在全局增加属性,也就是说在其他组件可以通过this.name拿到123这个值。
而想通过setup里面拿就比较麻烦
因为setup是没有this的,所以只能通过
const instance = getCurrentInstance()
console.log(instance?.appContext.config.globalProperties.$name);
getCurrentInstance可以拿到当前的组件实例,而全局属性只能从appcontext全局上下文中拿到,这样就能拿到我们定义的属性。
另一种plugin写法
const testPlugins = (app: App<Element>) => {
app.config.globalProperties.$name = "123";
};
通过函数,vue内部还是会调用testPlugins(app)调用。
以上是关于深入vue3学习的主要内容,如果未能解决你的问题,请参考以下文章