1 watch
用法 :1 可以监听某个ref对象。此时watch的第一个参数可以直接是ref
如下
<input type="text" v-model="message" />
import {ref} from "vue"
setup(props,ctx){
const message =ref("")
watch(message,(newVal,oldVal)=>{
console.log("newVal:",newVal)
console.log("oldVal:",oldVal)
})
return{
message:""
}
}
2 可以监听某个reactive对象。此时watch的第一个参数必须是一个函数
<input type="text" v-model="message" />
import {reactive} from "vue"
setup(props,ctx){
const message =reactive({message:""})
watch(()=>{
return state.message
},(newVal,oldVal)=>{
console.log("newVal:",newVal)
console.log("oldVal:",oldVal)
})
return{
message:""
}
}
3 对于props对象。我们进行监听的时候也必须采用函数的形式,哪怕监听的是props中某个对象的属性。
const props = defineProps({
data: {
type: Object,
default: () => {}
},
imgList: {
type: Array,
default: () => []
}
});
watch(
() => props.data.checked,
(n: boolean): void => {
console.log("n---", n);
n ? (isShow.value = true) : (isShow.value = false);
console.log("isShow:", isShow.value);
console.log("imgList:", props.imgList);
}
);
请注意:这里我们监听props中传递过的来的data对象里面的checked属性,此时我们watch的第一个参数也必须采用函数形式,否则会报错
2 父子组件传值:
依旧通过props和emit来实现,只不过setup()中没有this。而是通过setup(props,context)来实现
子--》父
contex.emit("my-emit",payload)
注意:vue3中新增了一个emits选项。可以通过在emits选项中指定自定义事件名称来向父级派送事件
如下:
----------父组件-------
<button @click="flag=true">打开</button>
<tel :visable="flag" @close="closeFlag"></tel>
import { reactive,ref } from "vue"
import tel from "./tel.vue"
export default {
name: 'HelloWorld',
components:{tel},
setup(){
const flag=ref(false)
const closeFlag=(val)=>{
console.log("val:",val)
flag.value=val
}
return {
flag,
closeFlag
}
}
}
子组件---tel.vue------
<teleport to='body'>
<div v-if="visable" class="wrap">
<h2>
<span>标题</span>
<span @click="$emit('close',false)">x</span>
</h2>
<div class="p12">内容区</div>
</div>
</teleport>
import {ref} from "vue"
export default {
name:'tel',
emits:['close'], //注意这里,新增了emits选项
props:['visable'],
setup(props,ctx){
const closeTel=()=>{
ctx.emit('close',false)
}
return{
closeTel
}
}
};
在vue中关于父子组件传值有一类特殊的存在。那就是v-mode。vue2中v-model实则是:vaule和input事件的语法糖;通过v-model绑定的值,在子组件中默认props的值的名称就是value;当然你也可以自定义model选项去更改默认的value这个名称和input事件;而在vue3中,这个默认的名称则变成了modelValue;而input事件则变成了update:事件
父组件:
<script setup>
import { ref } from 'vue';
import Count from './Count.vue';
defineProps({
msg: String,
});
const num = ref(0);
</script>
<template>
<h2>{{ num }}</h2>
<Count v-model="num"></Count>
</template>
<style scoped>
a {
color: #42b983;
}
</style>
子组件:
<template>
<div>
<button @click="updateCount">我是count组件</button>
</div>
</template>
<script setup>
import { ref, defineProps, defineEmits } from 'vue';
const props = defineProps({
modelValue: {
type: Number,
default: 0,
},
});
const emits = defineEmits(['update:modelValue']);
const updateCount = () => {
console.log(props.modelValue+ 1);
emits('update:modelValue', props.modelValue+ 1);
};
</script>
<style scoped></style>
这里特别注意在定义emits时,update要和model一起作为事件。写成const emits = defineEmits(['update']);是错误的;
当然,如果你不喜欢modelValue这个默认值。则可以再v-model后面添加:attr来实现;比如上面的例子。我们改成这样:
父组件:
<script setup>
import { ref } from 'vue';
import Count from './Count.vue';
defineProps({
msg: String,
});
const num = ref(0);
</script>
<template>
<h2>{{ num }}</h2>
<Count v-model:count="num"></Count>
</template>
<style scoped>
a {
color: #42b983;
}
</style>
子组件:
<template>
<div>
<button @click="updateCount">我是count组件</button>
</div>
</template>
<script setup>
import { ref, defineProps, defineEmits } from 'vue';
const props = defineProps({
count: {
type: Number,
default: 0,
},
});
const emits = defineEmits(['update:count']);
const updateCount = () => {
console.log(props.count + 1);
emits('update:count', props.count+ 1);
};
</script>
<style scoped></style>
这里只要注意一点:v-model:attr。这里的attr就是我们在子组件中props传过来的值
3 vue3中全局挂在axios
const app=createApp(App)
app.globalProperty.$axios=axios
4 vue-router 无任何页面路径匹配时
{
path: '/:pathMatch(.*)*',
name: 'notFound',
// redirect: 'notFound',
component: () => import('../views/notFound.vue')
}
5 新增api---expose选项的使用
vue3中新增了一个expose选项,用来在子组件中暴露一些属性和方法给外部。常用的场景是父组件中直接操作子组件的方法。在vue3.2中则直接被defineExpose()取代。具体用法如下:
父组件
<script setup>
import {getCurrentInstance, ref} from "vue"
import Test from './components/test.vue'
const testRef=ref(null)
const toggle=(flag)=>{
flag?testRef.value.open():testRef.value.close()
}
</script>
<template>
<el-button @click="toggle(true)"> 打开</el-button>
<el-button @click="toggle(false)"> 关闭</el-button>
<hr/>
<Test msg="Vite + Vue" ref="testRef"/>
</template>
<style scoped>
</style>
----------------------------------------------
子组件
<template>
<div class="mask" v-if="show">
<div class="cont">
<span @click="close">x</span>
<h1>this is cont</h1>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const show=ref(false);
const open=()=>{
show.value=true
}
const close=()=>{
show.value=false
}
defineExpose({close,open})
</script>
<style scoped>
.mask{
position: fixed;
top:0%;
left:0;
z-index:10;
background:rgba(0,0,0,0.5);
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.cont{
width:500px;
height: 420px;
background: #fff;
}
</style>
我们在子组件中通过defineExpose暴露了2个方法,分别是close,open。这样我们在父组件中通过ref引用拿到子组件,然后直接调用其close和open方法就可以正常打开弹窗(子组件),控制子组件显示和隐藏的show变量被隐藏在子组件内部。
6 插件注册与使用
一个简单的插件实例:
prew插件基础代码:
loading.vue
----------------------------------------------------------
<template>
<div v-if="isShow" class="loading">
<div class="loading-content">
<h1>Loading...</h1>
<h2 @click="hide">x</h2>
</div>
</div>
</template>
<script setup >
import { ref } from 'vue'
const isShow = ref(false)//
const show = () => {
isShow.value = true
}
const hide = () => {
isShow.value = false
}
defineExpose({
show,
hide
})
</script>
<style scoped lang="less">
----------------------------------------------------------
loading.js
import { createVNode, render, } from 'vue';
import Loading from './loading.vue'
export default {
install(app) {
//createVNode vue提供的底层方法 可以给我们组件创建一个虚拟DOM 也就是Vnode
const vnode = createVNode(Loading)
//render 把我们的Vnode 生成真实DOM 并且挂载到指定节点
render(vnode, document.body)
// Vue 提供的全局配置 可以自定义
app.config.globalProperties.$testLoading = {
show: () => vnode.component?.exposed?.show(),
hide: () => vnode.component?.exposed?.hide()
}
}
}
----------------------------------------------------------
main.js注册
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import Loading from "./libs/loading"
createApp(App).use(ElementPlus).use(Loading).mount('#app')
页面中使用:
test.vue页面
---------------------------------------------------------
<template>
<el-button @click="testPlugin"> 测试插件</el-button>
</template>
<script setup>
import {getCurrentInstance, ref} from "vue"
const {proxy}=getCurrentInstance()
const testPlugin=()=>{
console.log("proxy",proxy)
proxy.$testLoading.show()
}
</scritp>
特别注意:
(1)由于vue3提供了expose选项。用于向外部提供一些子组件内部的方法。这里插件的方法show和hide由于我们通过defineExpose()对外暴露。然后我们在注册到golobalProperties上的时候通过vnode.component.exposed就能拿到show和hide方法。;
(2)在页面中我们通过获取实例的方式去调用.$testLoading.show()的时候,通过const {proxy}=getCurrentInstance()就可以直接调用。无需通过instance.config.globalProperties.$testLoaidng.show()这样一长串的方式去调用