目录
2)vue3组件内部组合式API setup语法糖中注册组件
14. isRef toRef toRefs readonly
15. 组合式API compostionAPI 的属性和上下文对象
一、常用的Composition API(组合API)
1.setup 2.ref函数 3.reactive函数 4.Vue3.0中的响应式原理 (面试题)
5.Vue3.0中组件的使用 6.计算属性 7.监听属性 8.Vue3.0生命周期
9.Teleport 10.自定义事件 11.状态驱动的动态css 12.Suspense
7.监听属性
- 与vue2.x中的watch配置功能一致
- 注意
- 监视reactive定义的响应式数据时,oldvalue无法正确获取,强制开始了深度监视(deep的配置失效)
- 监视reactive定义的响应式数据的某一个值时:deep配置有效
注意:
1、如果将原来的引用数据,改为和原来一摸一样的引用数据,去watch,也算是改变了,所以watch也会触发
2、使用的是reactive,如果数据是一个嵌套的多层的引用数据,改变里面层的数据,是可以监听到的。但是得不到旧值。
<template>
<div class="home">
<h1>{
{num}}</h1>
<h1>{
{msg}}</h1>
<button @click="num++">点击num++</button>
<button @click="addmsg()">点击msg+!</button>
</div>
</template>
<script>
// 注意:vue2.x和vue3.x不要混用
// 1.引入
import { watch, ref,reactive } from "vue"
export default {
data() {
return {
}
},
computed: {
},
setup() {
let num = ref(0);
let msg = ref("hello");
let person = reactive({
name:"李国栋",
age:18,
more:{
job:"前端开发工程师",
salary:"12k"
}
})
// 1.监听ref定义的响应式数据
watch(num, (newvalue, oldvalue) => {
console.log("num变量", newvalue, oldvalue);
})
// 2.监听多个ref定义的响应式数据
watch([num, msg], (newvalue, oldvalue) => {
console.log("msg数据变化了", newvalue, oldvalue);
})
let addmsg = () => {
msg.value += "!"
}
//3.监听reactive所定义的一个响应式数据的全部属性
//注意:此处无法正确的获取oldvalue
//注意:并且强制开启了深度监视(deep配置无效)
watch(person, (newvalue, oldvalue) => { //person不能写person.name,因为它是监听一个变量。那么就只有返回函数了,就是下面的4
console.log("person数据变化了", newvalue, oldvalue);
},{deep:false}) //即使设置了false,但是也会强制开启,所以为true
//4.监听reactive所定义的一个响应式数据的某一个属性
//这种方法一般不用
watch(()=>person.name, (newvalue, oldvalue) => {
console.log("person数据变化了", newvalue, oldvalue);
},{deep:false})
return { num, msg, addmsg }
}
}
</script>
8.Vue3生命周期
created可以访问this了
created做网络请求的时候,页面还没出来
mounted做网络请求的时候,页面出来了
1) 什么是生命周期
Vue中每个组件都是独立的,每个组件都有一个属于它的生命周期,从一个组件创建、数据初始化、挂载、更新、销毁、这就是一个组件所谓的生命中周期
2) Vue2.x中的生命周期
beforeCreate created
beforeMount mounted
beforeUpdate updated
beforeDestroy destroyed
activated deactivated
errorCaptured
3) Vue3.x的生命周期
在Vue3.x中,新增了一个setup生命周期函数,setup执行的时机是在beforeCreate生命函数之前执行,因为在这个函数中不能通过this来获取实例的;
同时为了命名的统一,
将beforeDestory改名为beforeUnmount,
destoryed改名为unmounted
beforeCreate(建议使用setup代替)created(建议使用setup代替)
选项式API
setup
beforeMount mounted
beforeUpdate updated
beforeUnmount unmounted
4) vue3 新增的生命周期钩子
我们可以通过在**生命周期函数**前加**on**来访问组件的生命周期
**Composition API 形式的生命周期钩子**
组合式API
onBeforeMount onMounted (unmounted==>app组件挂载了以后执行)
onBeforeUpdate onUpdated
onBeforeUnmount onUnmounted
onErrorCaptured
onRenderTracked
onRenderTriggeredeffect
<script>
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
ref
} from 'vue'
export default {
setup () {
// 其他的生命周期
onBeforeMount (() => {
console.log("App ===> 相当于 vue2.x 中 beforeMount")
})
onMounted (() => {
console.log("App ===> 相当于 vue2.x 中 mounted")
})
// 注意,onBeforeUpdate 和 onUpdated 里面不要修改值
onBeforeUpdate (() => {
console.log("App ===> 相当于 vue2.x 中 beforeUpdate")
})
onUpdated (() => {
console.log("App ===> 相当于 vue2.x 中 updated")
})
onBeforeUnmount (() => {
console.log("App ===> 相当于 vue2.x 中 beforeDestroy")
})
onUnmounted (() => {
console.log("App ===> 相当于 vue2.x 中 destroyed")
})
return {
}
}
}
</script>
9.Teleport
Vue 鼓励我们通过将 UI 和相关行为封装到组件中来构建 UI。我们可以将它们嵌套在另一个内部,以构建一个组成应用程序 UI 的树。
然而,有时组件模板的一部分逻辑上属于该组件,而从技术角度来看,最好将模板的这一部分移动到 DOM 中 Vue app 之外的其他位置
就是让它不要渲染到vue里面
模态弹框
to属性:放到指定位置
<template>
<div class="reson">
<button @click="showModel">点击弹出模态框</button>
<teleport to="body">
<div class="mask" v-show="isShow">
<div class="box">
<h1>我是模态框</h1>
<button @click="closeModel">点击关闭模态框</button>
</div>
</div>
</teleport>
</div>
</template>
<script>
import { ref } from "vue"
export default {
name: 'Reson',
setup() {
let isShow = ref(false)
//显示
let showModel = function () {
isShow.value = true
}
//隐藏
let closeModel = function () {
isShow.value = false
}
return { isShow, showModel, closeModel }
}
};
</script>
<style lang="scss" scoped>
.reson {
background-color: pink;
}
.mask {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.5);
}
.box {
width: 300px;
height: 300px;
background-color: aqua;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
10.属性
1. 组件内部 还是可以用2.0
<script>
export default {
pops:["msg"], // pops:{msg:String},
methods:{fn(){console.log(this.msg)}},
setup(props,ctx){console.log(props.title)}
}
< /script>
<template>
<div>
{
{msg}}
</div>
</template>
父组件:
<Box v-model:msg="n"></Box>
2.vue3组件内部组合式API setup中取属性值
<script>
export default {
pops:["msg","count"],// pops:{msg:String},
setup(props){
let fn=()=>{console.log(props.msg,props.count)}//必须在组件中注册属性不然setup函数收不到数据
return {fn}
}
}
< /script>
<template>
<div>
<p> {
{msg}}</p>
<button @click="fn">look</button>
</div>
</template>
父组件:
<Box :msg="n" :count="200"></Box>
3.setup语法糖中获取属性值
defineProps定义属性
<script setup>
import {defineProps} from "./Box1.vue" //注意3.2之后不用引入可以直接使用
let obj=defineProps(["msg","count"]) //它是props对象
let fn=()=>{console.log(obj.msg,obj.count)}
}
< /script>
<template>
<div>
<p> {
{obj.msg}}</p>
<button @click="fn">look</button>
</div>
</template>
父组件:
<Box :msg="n" :count="200"></Box>
11.自定义事件
1)事件名
defineEmit 3.2之前
useContext 废弃了,useContext函数的返回值又{emit,attr}
defineEmits 3.2之后
与组件和 prop 一样,事件名提供了自动的大小写转换。如果在子组件中触发一个以 camelCase (驼峰式命名) 命名的事件,你将可以在父组件中添加一个 kebab-case (短横线分隔命名) 的监听器。
//父组件HomeView
<template>
<div>
<MyBox1 @myclick="fn" @my-event="fm"></MyBox1>
</div>
</template>
<script setup>
import MyBox1 from "../components/MyBox1.vue"
let fn = (arg) => {
console.log(6666, arg)
}
let fm = (arg) => {
console.log(77777, arg);
}
</script>
//子组件MyBox1
<template>
<div @mousemove="change1">
<button @click="change">点击触发myevent事件</button>
</div>
</template>
<script >
export default {
methods: {
change() {
this.$emit('myclick', "helo666");
},
change1() {
this.$emit('myEvent', "121212");
}
},
}
</script>
2)定义自定义事件
可以通过 `emits` 选项在组件上定义发出的事件
//父组件
<HomeView @my-event="fn" @my-submit="fn1"></HomeView>
//子组件
<template>
<div @mouseenter="fm">
<button @click="fn">点我触发mybtn组件内部的myclick事件</button>
</div>
</template>
<script>
export default {
emits:["myEvent","myclick"],
methods:{
fn(){
this.$emit("myClick",200)
},
fm(){
this.$emit("click",300)
}
}
}
</script>
3)`v-model` 参数
//父组件
<HomeView v-model:title="bookTitle"></HomeView>
//子组件
//1.接收参数
props:["title"]
//2.定义事件
emits: ['update:title']
//3.触发事件
this.$emit("update:title","子组件传递给父组件的值")
4)多个 `v-model` 绑定
//2.0这样写会有矛盾
<MyVmodel v-model="msg" v-model="msg2" v-model="msg3"></MyVmodel>
//2.0应该这样写:
<MyVmodel
v-model="msg"
:msg2="msg2" @changemsg2="changemsg2"
:msg3="msg3" @changemsg3="changemsg3">
</MyVmodel>
js:
changemsg2(arg){this.msg2=arg}
changemsg3(arg){this.msg3=arg}
//3.0
<MyVmodel v-model="msg" v-model:msg2="msg2" v-model:msg3="msg3"></MyVmodel>
//父组件
<user-name
v-model:msg1="msg1"
v-model:msg2="msg2"
></user-name>
//子组件
//1.接收参数
props:["msg1","msg2"]
//2.定义事件
emits: ['update:msg1','update:msg2']
//3.触发事件
this.$emit('update:msg1',"子组件传递给父组件的值1")
this.$emit('update:msg2',"子组件传递给父组件的值2")
APP.vue
<script setup>
import {ref} from "vue"
import Box from "./Box.vue"
let msg1=ref("111")
let msg2=ref("222")
let msg3=ref("333")
</script>
<template>
<div>
<p>{
{msg1}}--{
{msg2}}--{
{msg3}}</p>
<Box v-model:value="msg1" v-model:value2="msg2" v-model:value3="msg3"></Box>
</div>
</template>
Box.vue
<script setup>
let obj = defineProps(["value", "value2", "value3"]);
let emit = defineEmits();
let fn = () => {
emit("update:value", 100);
emit("update:value2", 200);
emit("update:value3", 300);
};
</script>
<template>
<div>
<p>{
{ obj.value }}-{
{ obj.value2 }}--{
{ obj.value3 }}</p>
<button @click="fn">box2-change1</button>
</div>
</template>
12.状态驱动的动态 CSS
单文件组件的 <style>
标签可以通过 v-bind
这一 CSS 函数将 CSS 的值关联到动态的组件状态上
<template>
<div class="box1">hello</div>
<button @click="changcolor">修改box1的样式</button>
<div class="box2">hello66666</div>
<button @click="changother">修改box2的样式</button>
</template>
<script setup>
import { ref, reactive } from "vue"
let color = ref("red")
let changcolor = () => {
color.value = "blue"
}
let other = reactive({
width: "200px",
height: "100px"
})
let changother = () => {
other.width = "400px"
}
</script>
<style lang="scss" scoped>
.box1 {
color: v-bind('color');
}
.box2 {
background-color: yellow;
width: v-bind('other.width');
height: v-bind('other.height');
}
</style>
学习——面试题/笔试题:css语法中的变量
13.注册组件
7种方式,最常用的是第二种
1) 组件内部
<script>
import Box1 from "./Box1.vue"
export defult{
components:{
Box1
},
setup(){}
}
</script>
<template>
<Box1></Box1>
< /template>
2)vue3组件内部组合式API setup语法糖中注册组件
<script setup>
import Box1 from "./Box1.vue"
//只需要导入 不用写注册代码 会在打包的时候自动帮我们注册
</script>
<template>
<Box1></Box1>
< /template>
3)注册全局组件
//main.js文件:
import { createApp} from 'vue'
import 3.注册全局组件App from './App.vue'
const app=createApp(App)
import Box1 from "./Box5.vue"
app.component(Box1.name,Box1)
app.mount('#app')//注意 一定要在app.mount之前注册全局组件 否则使用不了
//App.vue文件:
<template>
<Box1></Box1>
< /template>
4)定义同步组件:
//Box1.vue文件:
<script>
import {defineComponent} from "vue"
export default defineComponent({
data(){
return {}
},
methods:{},
setup(){
}
});
</script>
5.1)定义局部异步组件:
组件内部
<script>
import {defineAsyncComponent} from "vue"
let Box1 = defineAsyncComponent(() => import("./Box1.vue")) //注意3.2之后不用引入defineAsyncComponent
export default {
components: {
Box1
},
setup() {}
}
</script>
setup语法糖:
<script setup>
import Box1 from "./Box1.vue"
import Box2 from "./Box2.vue"
import {defineAsyncComponent} from "vue"
let Box3=defineAsyncComponent(()=>import("./Box3.vue"))//注意3.2之后不用引入defineAsyncComponent,而且这个变量名直接就是注册的组件名(打包时自动注册的)
</script>
5.2)定义全局异步组件:
//main.js文件:
import { createApp,defineAsyncComponent} from 'vue'
import App from './App.vue'
const app=createApp(App)
let Box1=defineAsyncComponent(()=>import("./Box4.vue"))
app.component("Box1",Box1)
app.mount('#app')//注意 一定要在app.mount之前注册全局组件 否则使用不了
13.Suspense
- 等待异步组件时渲染一些额外的内容,让应用有更好的用户体验
`<suspense>` 组件有两个插槽。它们都只接收一个直接子节点。`default` 插槽里的节点会尽可能展示出来。如果不能,则展示 `fallback` 插槽里的节点。
<template>
<div class="fa">
<h1>父组件HomeView</h1>
<!-- <Helloworld></Helloworld> -->
<suspense>
<template #default>
<!--#是v-slot:default的语法糖-->
<MyChild></MyChild>
</template>
<template #fallback>
<div>
<h1> 正在加载中Loading...</h1>
</div>
</template>
</suspense>
</div>
</template>
<script setup>
import { defineAsyncComponent } from "vue"
// import Helloworld from "../components/HelloWorld.vue" //静态引用
let MyChild = defineAsyncComponent(() => import("../components/HelloWorld.vue")) //异步引入
</script>
<style lang="scss" scoped>
.fa {
height: 300px;
background-color: #ccc;
}
</style>
14. isRef toRef toRefs readonly
1)isRef:
isRef(msg)==> 判断msg是否为响应式的,返回值是布尔值
<script setup>
//案例一
import { ref, isRef } from "vue";
let msg = ref(0);
let msg2 = 1;
function fn() {
console.log(isRef(msg)); //true
msg.value++;
}
function fm() {
console.log(isRef(msg2)); //false
msg2++;
}
</script>
<template>
<div>
<p>{
{ msg }}</p>
<button @click="fn">msg</button>
<p>{
{ msg2 }}</p>
<button @click="fm">msg2</button>
</div>
</template>
2) toRef
let x=toRef(obj,"x") //隐式: let x=ref(obj.x),并且响应到obj对象去
<script setup>
//案例一
import { ref, isRef ,toRef,reactive} from "vue";
//toRef
let obj=reactive({x:20,y:30})
let x=toRef(obj,"x")
let change=function() {
x.value=200
console.log(11,x,111,obj.x)
}
</script>
<template>
<div>
<!-- toRef -->
<p>{
{obj.x}}----{
{x}}</p>
<button @click="change">点击改变x</button>
</div>
</template>
3) toRefs
普通对象结构,响应式数据解构
let {x,y}=toRefs(obj)
<script setup>
//案例一
import { ref, isRef ,toRef,reactive,toRefs} from "vue";
//toRefs
let obj2=reactive({a:1,b:2})
let {a,b}=toRefs(obj2)
function change2(){
a.value++
b.value++
console.log(a.value,1111,b.value)
}
</script>
<template>
<div>
<!-- toRefs -->
<p>{
{ a }}---{
{ obj2.a }}--{
{ b }}---{
{ obj2.b }}</p>
<button @click="change2">点击改变a,b</button>
</div>
</template>
4)readonly
响应式只读技术,只能读,不能改
obj3是响应式的,obj4是响应式的,而且关联 obj3是可读可写,obj4只可读
<script setup>
//案例一
import { ref, isRef ,toRef,reactive,toRefs, readonly} from "vue";
//readonly
let obj3=reactive({x:1,y:2})
let obj4=readonly(obj3)
console.log(obj3.x,obj4.x)
function change4(){ //这里就会报警告说,obj4是只读,不能修改
obj3.x=2
obj4.x=4
}
</script>
<template>
<div>
<!-- readonly -->
<p>{
{obj3.x}}---{
{obj4.x}}</p>
<button @click="change4">点击修改</button>
</div>
</template>
15. 组合式API compostionAPI 的属性和上下文对象
//Vue2.0组件内部接收外部数据时:
<script>
export default {
props: ["msg"], //props:{msg:String},
inject:["count"] //父组件提供provide:{count:200}
};
//Vue3.0
//第一种
export default {
props: ["msg"], //props:{msg:String},
inject:["count"] //父组件提供provide:{count:200}
};
//第二种 在compositionAPI
export default {
setup(props,ctx) {
//props:父祖家给子组件的属性传值
//ctx:代表Vue提供的功能:emit、inject等等
//this用不了
function btnckicked(){
ctx.emit("myevent",10) //触发父组件的事件
}
}
};
</script>
//第三种:在compositionAPI 语法糖
<script setup>
//版本3.0到3.2
import {defineprops} from "vue"
let obj=defineprops(["title"])
console.log(obj.title)
//3.2
//3.2以后
let obj=defineprops(["title"])
console.log(obj.title)
</script>