vue3笔记
vue3官网
尚硅谷视频
黑马视频
## vue3的main.js
1.vue3
import { createApp } from 'vue' // 引入createApp方法
import App from './App.vue' // 引入App根组件
createApp(App).mount('#app') // 创建一个 Vue 实例,并将其挂载到 id 为 "app" 的 DOM 元素上2.vue2
import Vue from 'vue' // 引入vue
import App from './App.vue' // 引入App根组件
// 创建一个 Vue 实例,并在创建时指定根组件的渲染函数,然后将该实例挂载到 id 为 "app" 的 DOM 元素上
new Vue({render: h => h(App),
}).$mount('#app')3.render函数
render: function (createElement) {return createElement(App);
}
↓ 对象的增强写法render (createElement) {return createElement(App);
}
↓ createElement 简写成 h
render (h){return h(App);
}
↓ 箭头函数
render: h => h(App);createElement 函数是 Vue.js 的渲染函数中常用的一个函数,用于创建虚拟 DOM 元素,要有return返回值。
/ createElement 函数的第一个参数:标签名或组件选项,可以是 HTML 标签名字符串,表示创建普通的 HTML 元素,也可以是组件的选项对象,表示创建一个组件实例。/import App from './App'
new Vue({el: '#app',render: h => h(App)
})
↓ 等价与
import App from './App'
new Vue({el: '#app',template: '<App></App>',components: {App
}
})
上面两种的效果是一样的,可以看出 h(App)函数 的作用是:使用App作为这个Vue实例的template,同时一并了注册App组件;
Vue3 模板结构可以不使用根标签
<template><img alt="Vue logo" src="./assets/logo.png"><HelloWorld msg="Welcome to Your Vue.js App"/><div> hello vue3 </div>
</template>
Vue3 引入组件后,可以再模板中直接使用
<template><ReactiveDemo /><ComputedDemo />
</template><script setup>
import ReactiveDemo from '@/components/ReactiveDemo.vue'
import ComputedDemo from '@/components/ComputedDemo.vue'
</script>
Composition API (组合式api)
选项式API和组合式API的区别:
1.vue2 选项式API(Options API)
<script>
export default {// data() 返回的属性将会成为响应式的状态,并且暴露在this上data() {return {count: 0}},// methods 是一些用来更改状态与触发更新的函数
,它们可以在模板中作为事件监听器绑定methods: {increment() {this.count++}},// 生命周期钩子会在组件生命周期的各个不同阶段被调用mounted() {console.log('通常项目中初始化在这里调接口')}
}
</script>2.vue3 组合式API(Composition API)
<script>
import { ref, computed, onMounted } from 'vue'
export default {setup () {// 定义变量const count = ref(1)// 定义计算属性const doubleCount = computed(() => {return count.value * 2})// 定义方法const changeCount = () => {count.value = 100}// 生命周期函数onMounted(() => {console.log(count.value, 'onMounted')})// 统一将变量和方法等暴露出去return {count, doubleCount, changeCount}},
}
</script>
- 选项式 API可以用包含多个选项的对象来描述组件的逻辑,例如 data、methods 和 mounted。
- 选项所定义的属性都会暴露在函数内部的 this 上,它会指向当前的组件实例;
- 选项式 API 以“组件实例”的概念为中心 (即 this);
- 组合式 API可以使用导入的 API 函数来描述组件逻辑;
<script setup>
中的导入和顶层变量/函数都能够在模板中直接使用; - 组合式 API 的核心思想是直接在函数作用域内定义响应式状态变量,并将从多个函数中得到的状态组合起来处理复杂问题;
setup函数
<template><div id="app"><p>名字:{{ name }}</p><p>年龄:{{ age }}</p><p><button @click="sayHello">弹框</button></p></div>
</template><script>
export default {name: "App",// setup是一个函数,有返回值setup() {// 定义数据let name = "张三";let age = 18;// 定义方法function sayHello() {alert(`我是${name},我今年${age},你好啊!`); // 使用不需要用this,因为this是undefined,函数作用域;}/ setup函数的返回值是一个对象, 返回值可以直接在模板中使用,不需要点;/return { name, age, sayHello }}
};
</script>
setup是一个函数,有返回值,返回值是一个对象可以直接在模板中使用;
在setup函数中定义的东西,一定要return出去,才能被使用;
- 组件中所用到的:数据、方法等等,均要配置在setup函数中;
setup 中不能使用 this;
- setup 默认情况下不能使用async/await;
在beforcreatd前执行,
所以this是undefined,所以vue3中没有this;
setup函数的参数
props对象:
- 父组件传递过来的所有prop数据,要在组件中的props中接收;
- 不接收,则存在context对象的attrs中;
- 它是响应式的,它不能解构,一但解构,结构出的变量会丢失响应式;
1.解构了 props 对象,解构出的变量将会丢失响应性,推荐通过 props.xxx 的形式来使用其中的 props。
export default {props: {title: String},setup(props) { // 通过 props.xxx 的形式来使用其中的 props。 console.log(props.title)}
}
2.必须解构 props 对象,或者需要将某个 prop 传到一个外部函数中并保持响应性,那么可以使用 toRefs() 和 toRef() 这两个工具函数。
import { toRefs, toRef } from 'vue'export default {setup(props) {// 将 `props` 转为一个其中全是 ref 的对象,然后解构const { title } = toRefs(props)// `title` 是一个追踪着 `props.title` 的 refconsole.log(title.value)// 或者,将 `props` 的单个属性转为一个 refconst title = toRef(props, 'title')}
}
contex上下文对象:
包含了attrs,slots,emit,expose;
emit可以触发自定义事件的执行从而完成子传父通信;
该上下文对象是非响应式的,可以安全地解构;
attrs:一个对象,父组件传递给过来的非 props 属性对象,即未在props中接收的属性。
slots:一个函数,用于访问组件的插槽内容。
emit:一个用于触发自定义事件的函数。
expose:一个用于暴露组件的公共 API 的函数。
export default {setup(props, context) {// 透传 Attributes(非响应式的对象,等价于 $attrs)console.log(context.attrs)// 插槽(非响应式的对象,等价于 $slots)console.log(context.slots)// 触发事件(函数,等价于 $emit),子传父通信;console.log(context.emit)// 暴露公共属性(函数)console.log(context.expose)}
}
contex上下文对象:
- 包含了attrs,slots,emit,expose;
- emit可以触发自定义事件的执行从而完成子传父通信;
- 该上下文对象是非响应式的,可以安全地解构;
attrs:一个对象,父组件传递给过来的非 props 属性对象,即未在props中接收的属性。
slots:一个函数,用于访问组件的插槽内容。
emit:一个用于触发自定义事件的函数。
expose:一个用于暴露组件的公共 API 的函数。
export default {setup(props, context) {// 透传 Attributes(非响应式的对象,等价于 $attrs)console.log(context.attrs)// 插槽(非响应式的对象,等价于 $slots)console.log(context.slots)// 触发事件(函数,等价于 $emit),子传父通信;console.log(context.emit)// 暴露公共属性(函数)console.log(context.expose)}
}
expose
- 通过expose方法,可以将数据和方法从组件的内部暴露给组件的使用者。这样,外部的组件就可以直接访问该组件的实例时,访问这些数据或调用这些方法。
- 要使用expose方法,你需要在expose函数中调用它,并传入一个对象作为参数。该对象中的键值对定义了要暴露的属性或方法。
setup() {// 定义要暴露的属性和方法const myValue = ref(42);const myMethod = () => {console.log("Hello from myMethod!");};// 让组件实例处于`关闭状态`,即不向父组件暴露任何东西expose()// 让组件实例处于`开启状态`,可以有选择性的向父组件暴露,本组件的属性或方法expose({myMethod});}
setup语法糖
<script setup>
import { ref, onMounted } from 'vue'// 响应式状态
const count = ref(0)// 用来修改状态、触发更新的函数
function increment() {count.value++
}// 生命周期钩子
onMounted(() => {console.log(`计数器初始值为 ${count.value}。`)
})
</script>
- setup语法糖,不用在return变量和函数了,在模板中可以直接使用;
- script标签添加setup, 省略 export default{ };
ref函数
- import { ref } from “vue”; 从vue中引入ref函数;
作用:定义响应式的数据; (多用于基本数据类型)
- 语法:
const xxx = ref (数据)
- 创建一个包含响应式数据的引用对象,(reference对象,简称ref对象)
模板中使用不需要 .value;
js操作需要 .value;
接收的数据可以是:基本类型、也可以是对象类型。
基本类型的数据:响应式依然是靠Object.defineProperty()的get与set完成的数据劫持。
对象类型的数据:内部通过reactive函数实现的。
<template><div id="app"><p>名字:{{ name }}</p><p>年龄:{{ age }}</p><p>工作: {{job.type}}</p> //模板中使用不需要.value<p><button @click="changeInfo">改变</button></p></div>
</template><script>
import { ref } from "vue"; // 引入ref函数
export default {name: "App",setup() {// 定义基本数据类型let name = ref("张三");let age = ref(18);// 定义引用数据类型let job = ref({ type: "前端工程师" });// 定义修改ref定义的响应式数据的方法function changeInfo() {// 基本数据类型name.value = "李四";age.value = 28;// 引用数据类型job.value.type = "后端工程师";}return { name, age, job, changeInfo};},
};
</script>
reactive函数
- import { reactive } from “vue”; 从vue中引入reactive函数;
- 作用:
定义一个对象或数组类型的响应式数据;
(基本类型不要用它,要用ref函数);
-语法:const xxx = reactive(源对象);
接收一个对象或数组,返回一个代理对象(Proxy的实例对象,简称proxy对象); - reactive定义的响应式数据是“深层次的”。
内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
js和html中不需要 .value
<template><div id="app"><p>名字:{{ person.name }}</p><p>工作: {{ person.job.type }}</p><p><button @click="changeInfo">改变</button></p></div>
</template><script>
import { reactive } from "vue"; // 引入reactive函数
export default {name: "App",setup() {// 通过reactive函数定义对象或数组;let person = reactive({name: "张三",job: {type: "前端工程师",},});// 定义方法function changeInfo() {// 修改不需要.valueperson.name = "赵四";person.job.type = "后端工程师";}return { person, changeInfo }},
};
</script>
ref与reactive对比
reactive和ref的区别:
- reactive和ref都是vue3中提供的响应式API,都用于定义响应式数据的;
- reactive通常用于定义引用数据类型,其本质是基于 Proxy 实现对象代理,所以reactive不能用于定义基本类型数据;
- ref通常是用于定义基本数据类型,其本质是基于 Object.defineProperty() 重新定义属性的方式实现;
vue2、vue3响应式
- Proxy
- Reflect
- vue3中使用proxy 实现的数据响应式,避免了vue2中不能检测到,对象添加、删除属性和数组通过下标修改元素而无法被检测的问题;
computed函数
- computed(箭头函数) ,只获取,参数是一个函数,依靠返回值;
- computed( { } ),获取和设置,get(){ },set(){ },依靠返回值;
set方法的参数是要设置的新属性值;
<!-- 只读取计算属性 -->
<template><div id="app"><p>姓 : <input type="text" v-model="person.xing" /> </p><p>名 : <input type="text" v-model="person.ming" /> </p><p>姓名: {{ fullName }}</p> // 使用计算属性<p>姓名: {{ person.fullName }}</p> // 计算属性直接追加到person对象上使用</div>
</template><script>
import { reactive, computed } from "vue";
export default {name: "App",setup() {// reactive函数;let person = reactive({xing: "",ming: "",});// 计算属性 fullName, computed函数的参数可以是一个函数, 依靠返回值let fullName = computed(() => {return person.xing + person.ming;});
/直接把计算属性追加到person响应式对象上,直接返回person对象 person.fullName = computed(() => person.xing + person.ming ) /// 返回值return {person,fullName, // 返回计算属性};},
};
</script>
修改计算属性
<!-- 获取和修改计算属性 --><p>姓名: <input type="text" v-model="fullName" /></p>import { reactive, computed } from "vue"; //引入computed函数// 完整的计算属性, 获取和修改, get+setperson.fullName = computed({get(){return person.firstName + '-' + person.lastName},set(value){ // value是input输入框的新值const nameArr = value.split('-')person.firstName = nameArr[0]person.lastName = nameArr[1]}
})
watch函数
watch(参数1,参数2,参数3);
- wantch( 监视的属性, 处理函数, { immediate:true, deep:true } );
- 参数1:一个变量,监视单个数据;一个数组,监视多个数据 [ xx, xxx ];
一个箭头函数,依靠返回值,精确监视对象某个属性
; - 参数2:一个函数,对监视数据的处理函数;
- 参数3:一个对象,是否开启深度监视,是否一进入页面就监视 ( 默认immediate:false);
监视reactive定义的响应式数据,1. 不能获取oldValue,oldValue和newValue一致;2. 强制开启了deep:true,设置deep:false无效;
- ref定义的值类型, 监视不需要.value;
- ref定义的引用数据类型, 监视需要.value,等价与监视reactive定义的对象(情况三):1.无法获取oldvalue;2.
newvalue和oldvalue一致;3. 强制开启了deep:true;4. 设置deep:false也无效。 - ref定义的引用数据类型, 监视不使用 .value,可以使用 deep:true;1.无法获取oldvalue;2.
newvalue和oldvalue一致;
/ 情况一:监视ref定义的一个响应式数据 /
watch(sum, (newValue, oldValue) => {console.log('sum变化了', newValue, oldValue)
}, { immediate:true })/ 情况二:监视多个ref定义的响应式数据 /
监视的数据都放到一个数组中;newVal和oldVal返回的也是数组形式。watch([sum,msg],(newValue,oldValue)=>{console.log('sum或msg变化了',newValue,oldValue)
}) / 情况三:监视reactive定义的响应式数据 /1. 无法正确获得oldValue;2. oldValue和newValue一致;3.
强制开启了深度监视deep:true;4. 设置deep:false也无效;watch(person, (newValue, oldValue)=>{console.log('person变化了', newValue, oldValue) // newValue === oldValue; true
}, { immediate:true, deep:false }) //此处的deep配置不再奏效/ 情况四:监视reactive定义的响应式数据中的某一个属性 /1. 箭头函数,依靠返回值;2. 可以获取新值,旧值; 3. 设置deep有效;watch(() => person.job, (newValue,oldValue) => {console.log('person的job变化了', newValue, oldValue)
}, { immediate:true, deep:true }) / 情况五:监视reactive定义的响应式数据中的多个属性 /1. 放到一个数组中, 箭头函数依靠返回值;2. 可以获取新值,旧值; 3. 设置deep有效; 4.newVal和oldVal返回的也是数组形式。watch([ () => person.job, () => person.name ], (newValue,oldValue) => {console.log('person的job变化了', newValue,oldVal ue)
}, { immediate:true,deep:true })/ 特殊情况:监视的是reactive定义的对象中的某个对象,即对象嵌套的比较深 /1. 开启deep:true才能监测到变化;2. 可以设置deep:false; 3. 无法获取oldVal,newVal和oldValue一致;
const person = reactive({name: '张三',age: 18,obj: {salaray: {money: 10000}}
})watch(() => person.job, (newValue,oldValue) => {console.log('person的job变化了', newValue, oldValue) // newValue === oldValue; true}, { deep:true }) // 设置deep:tue或false有效;
watchEffect函数
watchEffect ( 监视的回调函数 )
- 不需要指定监视某个属性, 回调函数中用到那个属性就会监视那个属性;
watch:既要指明监视的属性,也要指明监视的回调。
watchEffect:不用指明监视哪个属性,监视的回调函数中用到哪个属性,那就监视哪个属性。
watchEffect有点类似于computed:但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。watchEffect(()=>{const x1 = sum.value // 用到了sum,就监视person;const x2 = person.age // 用到了person,就监视person;console.log('watchEffect配置的回调执行了')
})
Vue3生命周期
vue3.0的生命周期:
- 除了setup,其他的要加on;
- beforeDestroy 改名为 beforeUnmount;;
- destroyed 改名为 unmounted;
- beforeCreate和created 改为 setup;
- 其他的生命周期以组合式API的形式写在setup函数中;
- 写成调用函数的形式,参数是一个箭头函数;
- 从vue中引入的生命周期是一个函数,传入一个函数代表执行函数;
父子生命周期执行顺序:
- 挂载阶段
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
- 更新阶段
父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated
- 销毁阶段
父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed
Vue3生命周期
beforeCreate -> setup()
created -> setup() 开始创建组件之前,在beforeCreate和created之前执行,创建的是data和method。
beforeMount -> onBeforeMount() 组件挂载到节点上之前执行的函数。
mounted -> onMounted() 组件挂载完成后执行的函数。
beforeUpdate -> onBeforeUpdate() 组件更新之前执行的函数。
updated -> onUpdated() 组件更新完成之后执行的函数。
beforeUnmount -> onBeforeUnmount() 组件卸载之前执行的函数。
unmounted -> onUnmounted() 组件卸载完成后执行的函数keepalive专属 - 被包含在<keep-alive>中的组件,因为生命周期函数不再执行,会多出两个生命周期钩子函数。
activated -> onActivated() 被激活时执行,刚进入页面时执行。
deactivated -> onDeactivated() 失活时执行,比如从 A 组件,切换到 B 组件,A 组件消失时执行。捕获异常 - 用于捕获组件中未处理的 JavaScript 错误
errorCaptured -> onErrorCaptured(error,vm) 当捕获一个来自子孙组件的异常时激活钩子函数。
当组件中出现 JavaScript 错误时,这个错误会沿着组件树向上冒泡直到根组件,如果任何一个组件中定义了 onErrorCaptured 钩子函数,则会调用该函数来捕获错误。
这个钩子函数接收两个参数:error 和 vm。
error: 表示捕获到的 JavaScript 错误对象。
vm: 传递给该钩子函数的组件实例对象,可以通过它来访问到组件的状态和属性。在 DOM 更新之后执行回调函数 - 作用是等待当前 DOM 更新队列中的所有同步和异步更新完成后,再执行传入的回调函数。1.等待当前 DOM 更新队列中的所有同步和异步更新完成后执行回调函数。2.用于确保在 DOM 更新之后执行操作,例如获取更新后的 DOM 元素。
await nextTick()
DOM 更新后执行的操作xxx
hook函数
- 本质是一个函数,把setup函数中使用的Composition API进行了封装。
- 类似于vue2.x中的mixin。
- 自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。
toRef、toRefs函数
toRef: 将一个响应式状态或其他可响应式对象转换为一个只读的 ref 对象。
toRef( 对象 , '属性名' )
- 语法:
const refObj = toRef( sourceObj, key )
- sourceObj 是一个响应式状态对象或其他可响应式对象。
- key 是sourceObj 中一个已存在的属性名,指定要创建引用的属性。
- toRef 返回一个 ref 对象,该对象具有以下特性:
1. 当sourceObj的ke属性发生变化时,ref 对象也会相应地变化。
2. refObj引用对象是只读的,无法直接修改其值。如果需要修改,可以通过修改 sourceObj 中的对应属性来实现。
torefs:将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。
toRefs( '对象' )
语法:toRefs(person)
- 可以解构使用,不会失去响应式;
- 通过使用toRefs,可以将响应式对象的属性解构为独立的普通变量,并在模板或其他逻辑中使用它们,同时保持其响应式特性。
- 扩展:toRefs 与toRef功能一致,但可以批量创建多个 ref 对象。
<template><div><p>person:{{ person }}</p><p>姓名: {{ name }}</p><p>年龄: {{ age }}</p><p>工资: {{ salary }}</p><button @click="name += '三'">姓名++</button><button @click="age++">年龄++</button><button @click="salary++">工资++</button></div>
</template><script>
import { reactive, toRef, toRefs } from "vue"; // 引入 toRef, toRefs函数
export default {name: "App",setup() {let person = reactive({name: "张三",age: 18,job: {type: "前端程序员",salary: 15,},});return {person, // 响应式的person对象name: toRef(person, "name"), // 将响应式对象中的某个属性单独提供给外部使用时。age: toRef(person, "age"),salary: toRef(person.job, "salary"),/ ...toRefs(person) 解构toRefs函数处理的数据,name,age,job;把整个响应式的person对象解构出来的属性,都暴露给外边使用,也不会失去响应式; /};},
};
</script>
shallowReactive函数 与 shallowRef 函数
shallow:浅的,浅层次的;
- shallowReactive:只处理对象第一层属性的响应式(浅响应式)。
- shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
- 什么时候使用?
- 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
- 如果有一个对象数据,只读它的属性,或后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。
eadonly函数 与 shallowReadonly函数
- readonly:让一个响应式数据变为只读的(深只读)。
- shallowReadonly:让一个响应式数据变为只读的(浅只读, 只有第一层可读, 深层次的可以修改)。
- 应用场景: 不希望数据被修改时。
let person = reactive({name: "张三",age: 18,job: {type: "前端程序员",salary: 15,},});person = readonly(person) // 整个person对象只读person = shallowReadonly(person) // 只有person对象第一层只读name,age;深层次job对象中的数据可以修改
toRaw函数 与 markRaw函数
raw:未加工的,原始的;mark:标记,记号;
toRaw:
- 作用:将一个由reactive生成的响应式对象转为普通对象。
- 使用 toRaw,可以获取到原始的非响应式对象,使其不受代理的影响。
- 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
markRaw:
- 作用:标记一个响应式的对象,使其永远失去响应式。
- 使用 markRaw 可以将对象标记为非响应式,从而避免其被 Vue 的响应式系统追踪和观察。 应用场景:
- 有些值不应被设置为响应式的,例如复杂的第三方类库等。
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
customRef函数
custom:自定义;track:追踪;trigger:触发;delay:延迟;
- 自定义ref函数,返回的ref对象是响应式的;
- customRef( 箭头函数 );
- 箭头函数的参数:参数1:track函数;参数2:trigger函数;
- track( ):追踪依赖数据的变化;
- trigger( ):触发更新;
- 箭头函数需要返回 get函数和set函数;
- get函数,用于获取 ref 的当前值。在读取 ref.value 时会调用该函数。
- set函数,用于设置 ref 的新值。在给 ref.value 赋值时会调用该函数。
- customRef 允许我们创建一个定制化的 ref,可以自定义其读取和修改的行为。
作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。
import { customRef } from 'vue'const myCustomRef = customRef((track, trigger) => {// 定义初始值let value = 0// 定义增加初始值的方法const increment = () => {value++trigger() // 触发更新}// 定义减少初始值的方法const decrement = () => {value--trigger() // 触发更新}// 返回get和set方法,返回increment方法和decrement方法;return {get() {track() // 追踪依赖的数据,value;return value},set(newValue) {value = newValuetrigger() // 触发更新},increment,decrement}
})const count = myCustomRef.valueconsole.log(count) // 0myCustomRef.increment()
console.log(count) // 1myCustomRef.decrement()
console.log(count) // 0myCustomRef.value = 5
console.log(count) // 5
vue3父子通信
defineProps 和 defineEmits 都是只能在
<script setup>
中使用的编译器宏;
不需要导入,且会随着<script setup>
的处理过程一同被编译掉。
// 父组件
<template><div>父组件:{{ count }}</div><hr /><Son :count="count" @cutomFn="Fn" /> // 传递自定义属性,传递自定义事件;
</template><script setup>
import { ref } from 'vue'
import Son from '@/components/Son.vue'
const count = ref(100)
// 自定义事件的处理函数,val参数就是子组件传递过来的值;
const Fn = (val) => {count.value = val
}
---------------------------------------------------------------------------
// 子组件
<template><div>子组件:{{ count }}</div><button @click="emitCustomFn">改变</button>
</template><script setup>
// 接收父组件传递过来的自定义属性; { }
const props = defineProps({count: {type: Number,}
})
// 接收父组件传递过来的,要触发的自定义事件;[ '要触发的自定义事件名' ]
const emit = defineEmits(['cutomFn'])
// 触发自定义事件,并传值
const emitCustomFn = () => {emit('cutomFn', props.count + 1) // props.xxx
}
</script>
-
父传子
- 通过
defineProps()函数
来配置props选项,接收父组件传递的数据; const props = defineProps( { } )
- props接收到传递过来的值后,可以再模板中直接使用;但是再js中使用需要
props.xxx
;
- 通过
-
子传父
- 通过
defineEmits()
函数来配置props选项,接收父组件传递的数据; const emit = defineEmits( [ '自定义事件名' ] )
// 数组内,事件名加引号;
- 通过
provide和inject 依赖注入
实现祖孙组件间的通信;
provide 提供数据;
==> provide( “数据名”, 传递的数据);inject 接收数据;
⇒ inject( “数据名” );
作用:实现祖孙组件之间通信 (跨级通信)
过程:祖先组件有一个 provide
选项来提供数据,后代组件有一个 inject
选项来开始接收数据。
祖先组件中:
import { provide } from 'vuesetup(){......let car = reactive({name:'奔驰',price:'40万'})provide('car',car)......
}后代组件中:
import { inject } from 'vue
setup(props,context){......const car = inject('car')return {car}......
}
模板引用ref
- 通过ref标识获取真实dom对象或组件实例对象;
- 要mounted中使用;
- 调用ref函数生成一个ref对象;
- 通过ref标识绑定ref对象到标签;
给dom绑定ref
<template><div ref="divRef">App</div> // 3. 给dom绑定ref
</template><script setup>
import { ref } from 'vue' // 1. 引入ref函数
const divRef = ref(null) // 2. 创建divRef变量,并赋值为null
</script>
给组件绑定ref
/ 父组件 /
<template><Ref ref="sonRef" /> // 3. 给组件绑定ref
</template><script setup>
import Ref from './components/ref.vue' // 引入组件
import { ref, onMounted } from 'vue' // 1. 引入ref和onMounted函数
const sonRef = ref(null) // 2. 创建sonRef变量,赋值为null
// 5. 在父组件的mounted中使用子组件的属性和方法
onMounted(() => {console.log(sonRef.value.count)sonRef.value.sayHello()
})
</script>/ 子组件 /
<template><div>Son组件</div>
</template><script setup>
import { ref } from 'vue'
const count = ref(100)
const sayHello = () => {console.log('hello')
}
// 4. 通过defineExpose方法暴露出去该组件的属性和方法
defineExpose({count,sayHello
})
</script>
defineExpose()
defineExpose( { } );
- 默认情况下在
<script setup>
语法糖下组件内部的属性和方法是不开放给父组件访问的,是关闭的; - 可以通过defineExpose编译宏指定哪些属性和方法允许访问;
- 作用:
用于暴露组件的内部方法和属性给父组件使用。
defineOptions
用来定义 Options API 的选项;
- 用来直接在
<script setup>
中声明组件选项,而不必使用单独的<script>
块; - 仅支持vue3.3+;
defineOptions({name: 'Son' // 组件名
})
判断响应式数据的方法
- import { isRef,isReactive,isReadonly,isProxy } from ‘vue’
- 返回的都是布尔值;
- isRef:检查一个值是否为一个 ref 对象;
- isReactive:检查一个对象是否是由 reactive 创建的响应式代理;
- isReadonly:检查一个对象是否是由 readonly 创建的只读代理;
- isProxy:检查一个对象是否是由
reactive
或者readonly
方法创建的代理;
新组件
Fragment 标签
fragment:碎片,片段;
- 在Vue2中: 组件必须有一个根标签;
在Vue3中:组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中;
- 好处: 减少标签层级, 减小内存占用;
Teleport 标签
fragment:传送;
- 将组件的内容渲染到指定的位置;
- to属性: 选择器;
- 在组件的模板中定义的某个位置,将内容渲染到 DOM 中的其他位置。
- 在遵循组件嵌套规则的情况下,将内容渲染到组件树之外的地方,例如
<body>
元素之下。
<button @click="isShow = true">点击弹窗</button>
<teleport to="body"><div v-if="isShow" class="mask"><div class="dialog"><h3>我是一个弹窗</h3><button @click="isShow = false">关闭弹窗</button></div></div>
</teleport>
Suspense标签
suspense:悬念;
- 异步依赖。
<Suspense>
组件有两个插槽:#default 和 #fallback。- 用于处理异步组件的加载状态和错误处理;
- 等待异步组件时渲染一些额外内容,让应用有更好的用户体验。
方法1: 通过defineAsyncComponent(() => import('路径'));
import {defineAsyncComponent} from 'vue' // 引入定义异步组件的函数
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
import() // 异步引入方法2:使用Suspense包裹组件,通过插槽,配置好 #default 与 #fallback;
<template> <Suspense><template #default><h1>这是异步组件的内容...</h1> </template><template #fallback><h1>异步组件正在加载中...</h1> </template></Suspense></template>#default 插槽用于显示异步组件加载完成后的内容,即 <h1>这是异步组件的内容</h1>。
#fallback 插槽用于显示异步组件加载过程中的占位符,即 <h1>异步组件正在加载中...</h1>。
/ 当异步组件加载过程中,<Suspense> 组件会显示 #fallback 插槽中的内容,待异步组件加载完成后,会切换到显示 #default 插槽中的内容。/
其他
全局API的转移
- Vue 2.x 有许多全局 API 和配置
- 例如:注册全局组件、注册全局指令等;
//注册全局组件
Vue.component('MyButton', {data: () => ({count: 0}),template: '<button @click="count++">Clicked {{ count }} times.</button>'
})//注册全局指令
Vue.directive('focus', {inserted: el => el.focus()
}
- Vue3.0中对这些API做出了调整:
- 将Vue.xxx调整到应用实例(app)上;
- 将Vue.xxx调整到应用实例(app)上;
其他改变
- data选项应始终被声明为一个函数。
- 移除键盘事件的keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes;
- 移除v-on.native修饰符,可以直接给组件绑定原生事件;
- 移除过滤器 filter;
- 过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设!
- 建议用方法调用或计算属性去替换过滤器。