一、Vue3启程
1. 初始Vue3
姓名:{{name}}
年龄:{{age}}
// Vue2中--创建实例的方式
new Vue({
//指定挂载容器
// el:'#app',
//定义属性
data() {
return {
name:'张三',
age:20
}
},
//定义方法
methods: {
updateData(){
this.name = '李四'
this.age = 25
}
},
}).$mount('#app') //指定当前vue实例挂载的容器
// Vue3中--创建实例的方式
Vue.createApp({
//注意:这个配置对象里面除了不能写el选项,之前怎么写,现在还可以怎么写
//定义属性
data() {
return {
name:'张三',
age:20
}
},
//定义方法
methods: {
updateData(){
this.name = '李四'
this.age = 25
}
},
}).mount('#app') //只能通过mount方法指定挂载的容器,不用通过el选项指定
2. Vue2和Vue3的响应式
学生:{{stu}}
食物:{{foods}}
// Vue2
new Vue({
data() {
return {
//学生对象
stu:{
name:'张三',
age:20
},
//食物数组
foods:['榴莲','葡萄','香蕉']
}
},
methods: {
updateStuName(){
this.stu.name = '李四'
},
addStuSex(){
// 直接给对象添加的属性,不具备响应式
// this.stu.sex = '男'
// 如果要给对象添加属性,并且添加的属性也要具备响应式,要使用$set方法
// 方法的第一个参数是指定的对象,第二个参数是属性名,第三个参数是属性值。
this.$set(this.stu,'sex','男')
},
delStuAge(){
// 直接删除对象身上的属性,是不具备响应式的
// delete this.stu.age
// 如果要删除对象身上的属性,并且还要具备响应式,要使用$delete方法
// 方法的第一个参数是指定的对象,第二个参数是属性名
this.$delete(this.stu,'age')
},
updateFoods2(){
// 直接根据索引修改数组元素,不具备响应式
// this.foods[1] = '西瓜'
// 操作数组中的元素,并且还要具备响应式,只能使用数组的以下方法:
// push unshift pop shift splice reverse sort
// this.foods.splice(1,1,'西瓜')
// 如果就是想通过下标去操作数组,还要具备响应式,使用$set方法
this.$set(this.foods,1,'西瓜')
}
},
}).$mount('#app')
// 总结Vue2的响应式:不能直接给对象添加属性,删除对象的属性,不能直接操作数组的下标,
// 但是,Vue2同时也提供了解决这些问题的方案。
// Vue3
Vue.createApp({
data() {
return {
//学生对象
stu:{
name:'张三',
age:20
},
//食物数组
foods:['榴莲','葡萄','香蕉']
}
},
methods: {
updateStuName(){
this.stu.name = '李四'
},
addStuSex(){
// 在Vue3中,直接给对象添加属性,新的属性依然具备响应式
this.stu.sex = '男'
},
delStuAge(){
// 在Vue3中,直接删除对象的属性,依然具备响应式
delete this.stu.age
},
updateFoods2(){
// 在Vue3中,根据下标操作数组,依然具备响应式
this.foods[1] = '西瓜'
}
},
}).mount('#app')
// 总结Vue3的响应式:解决了再Vue2中的所有问题。
3. Vue2和Vue3的响应式原理
// Vue2的响应式原理:
// 这里的obj是源对象
let obj = {
name:'张三',
age:20
}
// 在页面中显示姓名和年龄
document.getElementById('name').innerText = obj.name
document.getElementById('age').innerText = obj.age
// 这里的obj2代理对象---由obj2代理obj
let obj2 = {}
// 给obj2定义name属性
Object.defineProperty(obj2,'name',{
get(){
return obj.name
},
set(value){
obj.name = value
document.getElementById('name').innerText = obj.name
}
})
// 给obj2定义age属性
Object.defineProperty(obj2,'age',{
get(){
return obj.age
},
set(value){
obj.age = value
document.getElementById('age').innerText = obj.age
}
})
// Vue3的响应式原理:
// 这里的obj是源对象
let obj = {
name:'张三',
age:20
}
// 在页面中显示姓名和年龄
document.getElementById('name').innerText = obj.name
document.getElementById('age').innerText = obj.age
// 这里的obj2代理对象---由obj2代理obj
// new Proxy(源对象,{...})的方式,创建代理对象
let obj2 = new Proxy(obj,{
//读取属性,参数分别是:源对象,属性名
get(target, property){
// 直接根据源对象返回源对象身上的属性
// return target[property]
// 通过发射对象,发射输出源对象身上的属性
return Reflect.get(target,property)
},
//设置属性,参数分别是:源对象,属性名,属性值
set(target, property,value){
// target[property] = value
if(Reflect.has(target,property)){
Reflect.set(target, property,value)
document.getElementById(`${property}`).innerText = value
}
},
//删除属性,参数分别是:源对象,属性名
deleteProperty(target, property){
// delete target[property]
Reflect.deleteProperty(target, property)
}
})
4. 引出Vue3新推出的组合式API
学生信息
姓名:{{stuName}}
年龄:{{stuAge}}
汽车信息
车名:{{carName}}
车价:{{carPrice}}
手机信息
名称:{{phoneName}}
颜色:{{phoneColor}}
食物信息
名称:{{foodName}}
价格:{{foodPrice}}
// 什么是组合式API(Composition API),就是Vue推出的一些新的方法,这个方法在setup中使用
// 从Vue身上获取ref组合式API函数
let {ref} = Vue
Vue.createApp({
// 注意:Vue2中,Vue实例的data选项可以是一个对象,也可以是一个方法,由方法返回一个对象
// 但是,组件中data选项必须是一个方法。
// Vue3中,无论是Vue实例,还是组件,data选项都必须是一个方法。
// 我们之前习惯将所有的数据放在data选项中定义,所有的方法放在methods选项中定义,
// 所有的计算属性放在computed选项中定义,所有的侦听器放在watch选项中定义,
// 这样就会导致一个业务的代码会拆分到多个结构中去写,如果一个页面中要操作很多个业务,代码后期维护成本会很高。
// 所以,Vue3引入了组合式API,简化之前繁琐的过程,将相同业务的代码靠在一起写。
/* data: function () {
return {
//定义学生数据
stuName: '张三',
stuAge: '20',
//汽车信息
carName: '奔驰',
carPrice: '50W',
//手机信息
phoneName: 'iphone',
phoneColor: '白色',
//食物信息
foodName: '汉堡',
foodPrice: '¥20'
}
},
methods: {
//修改学生的方法
updateStu(){
this.stuName = '李四'
this.stuAge = 30
},
//修改汽车的方法
updateCar(){
this.carName = '宝马'
this.carPrice = '40W'
},
//修改手机的方法
updatePhone(){
this.phoneName = '华为'
this.phoneColor = '蓝色'
},
updateFood(){
this.foodName = '蛋糕'
this.foodPrice = '¥30'
}
}, */
// setup方法是所有组合式API的入口
setup() {
// 定义学生的信息
// 在setup中,直接定义的数据是不具备响应式的,
// 如果要使数据具备响应式,需要使用ref组合式API对数据进行包装,包装后返回的是ref对象
let stuName = ref('张三')
let stuAge = ref('20')
let updateStu = () => {
//ref对象的value属性保存的是值
stuName.value = '李四'
stuAge.value = 30
}
// 定义汽车的信息
let carName = ref('奔驰')
let carPrice = ref('50W')
let updateCar = () => {
carName.value = '宝马'
carPrice.value = '40W'
}
// 定义手机的信息
let phoneName = ref('iphone')
let phoneColor = ref('白色')
let updatePhone = () => {
phoneName.value = '华为'
phoneColor.value = '蓝色'
}
// 定义食物的信息
let foodName = ref('汉堡')
let foodPrice = ref('¥20')
let updateFood = () => {
foodName.value = '蛋糕'
foodPrice.value = '¥30'
}
//返回模板中需要使用的数据
return{
stuName,
stuAge,
updateStu,
carName,
carPrice,
updateCar,
phoneName,
phoneColor,
updatePhone,
foodName,
foodPrice,
updateFood
}
}
}).mount('#app')
5. ref和reactive
姓名:{{name}}
学生:{{stu}}
let {ref,reactive} = Vue
Vue.createApp({
setup() {
let name = ref('张三')
let updateName = ()=>{
name.value = '张杰'
}
/* let stu = ref({
name:'李四',
age:20
})
let updateStu = ()=>{
// 注意:修改ref对象的值,每次都要先点value
stu.value.name = '李明'
stu.value.age = 30
} */
// reactive组合式API方法,根据源对象返回一个代理对象(Proxy对象)
let stu = reactive({
name:'李四',
age:20
})
let updateStu = ()=>{
// Proxy对象,不需要先点value
stu.name = '李明'
stu.age = 30
}
return {
name,
updateName,
stu,
updateStu
}
}
}).mount('#app')
二、脚手架
1. Vue-Cli
Vue CLI 4.x以上,Node.js版本 8.9以上
npm install -g @vue/cli
# OR
yarn global add @vue/cli
# 查看版本
vue --version
# 创建项目
vue create hello-world
# 运行
npm run serve
main.js
// vue2
/* import Vue from 'vue'
import App from './App.vue'
new Vue({
render:h=>h(App)
}).$mount("#app") */
// vue3
// 从vue中导入createApp方法,通过这个方法,创建vue实例
import { createApp } from 'vue'
// 导入App组件
import App from './App.vue'
// 通过createApp方法创建一个vue实例,渲染App组件,并将结果挂载到#app容器中。
createApp(App).mount('#app')
2. Vite
Vite 需要 Node.js版本 12.0以上
npm init @vitejs/app
# OR
yarn create @vitejs/app
# 然后按照提示操作即可
# 安装依赖
npm install
# 运行
npm run dev
3. 计算属性
计算属性
// 在Vue3中,定义计算属性,需要引入computed组合式API
import {ref,computed} from 'vue'
export default {
// Vue2中的计算属性
// 数据
/* data() {
return {
firstName:'张',
lastName:'杰'
}
},
// 计算属性
computed:{
// 只读的计算属性
// fullName(){
// return this.firstName+'.'+this.lastName
// }
// 读写计算属性
fullName:{
//返回计算机属性的结果
get(){
return this.firstName+'.'+this.lastName
},
//修改计算属性的值
set(val){
let arr = val.split('.')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
} */
// Vue3中的计算属性
setup() {
let firstName = ref('张')
let lastName = ref('杰')
//computed()函数的参数是一个回调函数,回调函数的返回值,就是计算属性的返回值
// 定义只读的计算属性
// let fullName = computed(()=>{
// return firstName.value + '.' + lastName.value
// })
// 定义读写计算属性
let fullName = computed({
get(){
return firstName.value + '.' + lastName.value
},
set(val){
let arr = val.split('.')
firstName.value = arr[0]
lastName.value = arr[1]
}
})
return{
firstName,
lastName,
fullName
}
}
};
4. 侦听器
侦听器
// 引入组合式API watch 和 watchEffect
import {ref,reactive, watch, watchEffect} from 'vue'
export default {
// Vue2中的侦听器
/* //数据
data() {
return {
money:10000,
stu:{
name:'张三',
age:20,
car:{
name:'奔驰',
price:50
}
}
}
},
//侦听器
watch:{
//根据数据的名称定义一个方法,用于该方法去侦听该属性值是否发生变化(参数1是新值,参数2是旧值)
// 注意:默认情况下,侦听器一上来不会立刻执行,必须要侦听到值重新发生变化后,才执行。
// money(nval,oval){
// console.log(nval,oval);
// }
// 完整写法,侦听器定义成一个对象
money:{
//表示侦听器默认执行一次
immediate:true,
//定义侦听器的方法
handler(nval,oval){
console.log(nval,oval);
}
},
// 监听学生数据,注意:只有整个学生对象变化了才会监听到,如果只是修改对象身上的属性,监听不到。
// stu(nval,oval){
// console.log(nval,oval);
// }
// 解决方案:监听器改成一个对象
stu:{
//表示侦听器开启深度监视
deep:true,
handler(nval,oval){
console.log(nval,oval);
}
}
} */
// Vue3中的侦听器
setup() {
let money = ref(10000)
let stu = reactive({
name:'张三',
age:20,
car:{
name:'奔驰',
price:50
}
})
// watch函数有三个参数:1.侦听谁,2.回调函数,3.配置对象(可以省略)
// 简单用法:一上来没有立刻执行
// watch(money,(nval,oval)=>{
// console.log(nval,oval);
// })
// 完整用法:加上第三个参数,配置对象
watch(money,(nval,oval)=>{
console.log(nval,oval);
},{
//立刻执行
immediate:true,
})
// 监视reactive的数据,默认就开启深度监视,并且无法关闭
// watch(stu,(nval,oval)=>{
// console.log(nval,oval);
// })
// 对于reactive的数据,可以采用监视部分属性
watch(()=>stu.name,(nval,oval)=>{
console.log(nval,oval);
})
// 如果监视的是reactive里面的对象属性,默认是不开启深度监视的,需要手动开启
watch(()=>stu.car,(nval,oval)=>{
console.log(nval,oval);
},{
deep:true
})
//watchEffect监听器,只有一个回调函数参数,并且没有参数
// 特点:
// 1.默认会执行一次
// 2.不需要明确监视谁,回调函数里面用到了谁,谁变了,就会重新执行回调函数。
watchEffect(()=>{
console.log('我是watchEffect');
let m = money.value
let name = stu.name
})
return{
money,
stu
}
}
};
5. 过滤器
过滤器
export default {
data() {
return {
money:10000.12345
}
},
// 注意:在Vue2中可以定义过滤器,但是在Vue3中已经取消了过滤器。
/* filters:{
toFixed2(val){
return val.toFixed(2)
}
} */
// Vue3推荐我们使用方法 或 计算属性的方式,实现之前过滤器的效果。
methods: {
toFixed2(val){
return val.toFixed(2)
}
},
computed:{
toFixed2Money(){
return this.money.toFixed(2)
}
}
};
6. 响应式
响应式
//Vue3中的所有组合式API,都要采用按需引入的方式导入
import {ref,reactive} from 'vue'
export default {
//setup是所有组合式API的入口
setup() {
//使用ref定义基本类型数据
let money = ref(10000)
let updateMoney = ()=>{
money.value += 1000
}
//使用ref定义引用类型数据
// ref方法,返回的是ref对象,ref对象的value属性是一个代理对象(Proxy)
let car = ref({
name:'奔驰',
price:'50W'
})
let updateCar = ()=>{
// 注意:这里每次修改数据时,必须要先.value再.具体的属性
car.value.name = '宝马',
car.value.price = '40W'
}
// 注意:reactive只能定义引用类型(对象和数组)
// reactive方法,直接返回一个代理对象(Proxy)
let stu = reactive({
name:'张三',
age:20
})
let updateStu = ()=>{
stu.name = '李四'
stu.age = 25
}
// 总结:通常情况下:
// 1.基本类型的数据,选择用ref定义
// 2.引用类型的数据,选择用reactive定义
//setup方法里面返回出去的成员,在模板可以使用
return{
money,
updateMoney,
car,
updateCar,
stu,
updateStu
}
}
}
7. fragment组件
在vue3的模板中,不再需要根标签,它内部有一个fragment的组件作为模板的根标签
三、Vue3高阶
1. Hook函数
useCar
import {ref,computed} from 'vue'
//导出去一个函数
export default function(){
//汽车数据
let carName = ref('保时捷')
let carPrice = ref(100)
//汽车的计算属性
let carPrice2 = computed(()=>{
return (carPrice.value*0.8).toFixed(2)
})
//操作汽车的方法
let updateCar = ()=>{
carName.value = '宾利'
carPrice.value = 300
}
//返回暴露给外界的内容
return {
carName,
carPrice,
carPrice2,
updateCar
}
}
usePhone
import {ref,computed} from 'vue'
export default function(){
//手机数据
let phoneName = ref('华为')
let phonePrice = ref(5000)
//手机的计算属性
let phonePrice2 = computed(()=>{
return (phonePrice.value*0.5).toFixed(2)
})
//操作手机的方法
let updatePhone = ()=>{
phoneName.value = '苹果'
phonePrice.value = 9000
}
//返回暴露给外界的内容
return {
phoneName,
phonePrice,
phonePrice2,
updatePhone
}
}
组件
Hook函数
汽车信息
- 汽车名称:{{carName}}
- 汽车价格:{{carPrice}}万
- 优惠价格:{{carPrice2}}万
-
手机信息
- 手机名称:{{phoneName}}
- 手机价格:{{phonePrice}}
- 优惠价格:{{phonePrice2}}
-
// 导入hook函数
import useCar from '../hooks/useCar'
import usePhone from '../hooks/usePhone'
export default {
setup() {
// 返回模板中需要使用的数据
return {
//返回汽车信息
...useCar(),
//返回手机信息
...usePhone()
}
}
}
2. 生命周期
生命周期
数量:{{count}}
// 组合式API生命周期函数
import {onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
export default {
// beforeCreate() {
// console.log('创建之前');
// },
// created() {
// console.log('创建完成');
// },
// beforeMount() {
// console.log('挂载之前1');
// },
// mounted() {
// console.log('挂载完成1');
// },
// beforeUpdate() {
// console.log('更新之前1');
// },
// updated() {
// console.log('更新完成1');
// },
//注意:在vue3中,对beforeDestroy和destroyed这两个生命周期函数,进行了重命名
/* beforeDestroy() {
console.log('销毁之前');
},
destroyed() {
console.log('销毁完成');
}, */
// 在vue3中,beforeUnmount 替换了 beforeDestroy;unmounted 替换了 destroyed
// beforeUnmount() {
// console.log('卸载之前1');
// },
// unmounted() {
// console.log('卸载完成1');
// },
data() {
return {
count:1
}
},
// setup()函数,可以替代beforeCreate 和 created 这两个生命周期函数
setup() {
console.log('setup');
//组合式API生命周期函数,会先与传统的生命周期函数执行
onBeforeMount(()=>{
console.log('挂载之前2');
})
onMounted(()=>{
console.log('挂载完成2');
})
onBeforeUpdate(()=>{
console.log('修改之前2');
})
onUpdated(()=>{
console.log('修改完成2');
})
onBeforeUnmount(()=>{
console.log('卸载之前2');
})
onUnmounted(()=>{
console.log('卸载完成2');
})
}
}
3. toRef和toRefs
toRef和toRefs
学生信息
- 姓名:{{name}}
- 姓名:{{age}}
- 车名:{{car.name}}
- 车价:{{car.price}}
import { reactive,toRef,toRefs } from 'vue'
export default {
setup() {
// 定义数据
let stuData = reactive({
name:'张三',
age:20,
car:{
name:'大众',
price:'20W'
}
})
return{
// toRef()函数,可以用来为一个 reactive 对象的属性创建一个 ref
// 这样做的好处是,简化了模板中的表达式。
// toRef()函数,需要传两个参数:1.reactive 对象,2.具体的属性名
// name:toRef(stuData,'name'),
// age:toRef(stuData,'age'),
// car:toRef(stuData,'car')
// 假如 reactive 对象中,有100个属性,上面的操作要写100次,所以,一般都直接用toRefs函数
// toRefs函数,把一个响应式对象转换成普通对象,该普通对象的每个 属性 都是一个 ref
...toRefs(stuData)
}
}
}
4. 其他的组合式API
其他的组合式API
学生信息:{{stuData}}
num3的值:{{num3}}
汽车信息:{{car}}
手机信息:{{phone}}
年龄:{{age}}
import {ref,reactive,readonly,isRef,unref, shallowRef, isReactive, shallowReactive,customRef,toRaw, markRaw} from 'vue'
export default {
setup() {
// 定义数据
// readonly()函数,返回一份只读数据,这个只读是“深层的”,内部任何嵌套的属性也都是只读的
let stuData = readonly({
name:'张三',
age:20,
car:{
name:'大众',
price:20
}
})
let num1 = ref(100)
let num2 = 200
// isRef()函数,检查一个值是否为一个 ref 对象
// isProxy()函数,检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
// isReactive()函数,检查一个对象是否是由 reactive 创建的响应式代理
// isReadonly()函数,检查一个对象是否是由 readonly 创建的只读代理
// let num3 = (isRef(num1)?num1.value:num1) + (isRef(num2)?num2.value:num2)
// unref()函数,如果参数是一个 ref 则返回它的 value,否则返回参数本身
let num3 = unref(num1) + unref(num2)
// ref() 返回的对象 value 属性值是 reactive对象(代理对象)
// shallowRef() 返回的对象 value 属性值是 object对象(普通对象),不再具备任何响应式了
let car = shallowRef({
name:'大众',
type:{
typeName:'SUV'
}
})
let updateCar = ()=>{
// 由于value返回的是object对象,所以,这里不再具有响应式
car.value.name = '奔驰'
car.value.type.typeName = '跑车'
}
// shallowReactive() 返回一个浅层的响应式代理,只对对象的第一层属性创建响应式
let phone = shallowReactive({
name:'华为',
type:{
typeName:'滑盖手机'
}
})
// toRaw() 将代理对象转为一个普通对象返回
let phone2 = toRaw(phone)
console.log(phone2);
console.log('--------------------');
//定义了一份数据
// markRaw() 记一个对象为“永远不会转为响应式代理”
let food = markRaw({
name:'面包'
})
// 注意:food2就是一个普通对象
let food2 = reactive(food)
console.log(food2);
let updatePhone = ()=>{
//修改name,会触发页面更新
// phone.name += "!"
//修改type里面的属性,不会触发页面更新
phone.type.typeName += "!"
}
//自定义一个ref
function useDebouncedRef(value, delay = 200) {
let timeout
// customRef(),用于自定义一个 ref
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
},
}
})
}
let age = useDebouncedRef(20,2000)
return {
stuData,
num3,
car,
updateCar,
phone,
updatePhone,
age
}
}
}
四、组件传值
1. 父子组件传值
父
@updateName="name=$event" @updateAge="age=$event" @updateSex="sex=$event">
HelloWorld
你好世界
子
Son1
姓名:{{myName}}
年龄:{{myAge}}
性别:{{mySex}}
import { ref } from 'vue';
import GrandSon1 from './GrandSon1.vue'
export default {
name: "Son1",
components:{
GrandSon1
},
// 接收父组件传过来的数据
props:['name','age'],
// Vue2中的方式
/* data() {
return {
myName:this.name,
myAge:this.age,
mySex:this.sex
}
},
methods: {
updateData(){
this.myName = '李四'
this.myAge = 30
this.mySex = '女'
this.$emit('updateName',this.myName)
this.$emit('updateAge',this.myAge)
this.$emit('updateSex',this.mySex)
}
}, */
//setup函数中通过参数props接收父组件传递进来的参数
//注意:props参数中,只会接收props选项中接收的参数
//context参数里面有三个对象:attrs,emit,slots
//attrs用于获取没有采用props选项接收的参数
//emit用于触发自定义事件
//slots用于获取插槽信息
setup(props,{attrs,emit,slots}) {
//slots对象返回的是插槽里面所有内容的虚拟DOM
console.log(slots.one()[0].children);
//获取姓名和年龄
let myName = ref(props.name)
let myAge = ref(props.age)
//获取性别
let mySex = ref(attrs.sex)
//修改数据的方法
let updateData = ()=>{
//修改自身数据
myName.value = '李四'
myAge.value = 30,
mySex.value = '女'
//触发自定义事件,将最新数据回传给父组件
emit('updateName',myName.value)
emit('updateAge',myAge.value)
emit('updateSex',mySex.value)
}
return{
myName,
myAge,
mySex,
updateData
}
}
};
2. 祖孙组件传值
祖
import {reactive, provide} from 'vue'
let phone = reactive({
name:'华为',
price:5000
})
//provide将指定的数据添加为依赖数据,让后代组件可以直接使用
provide('phone',phone)
孙
GrandSon1
手机信息:{{phone}}
import {inject} from 'vue'
// inject注入祖级组件中设置为依赖的数据
let phone = inject('phone')
let updatePhone = ()=>{
phone.name = '苹果'
phone.price = 8000
}
return {
phone,
updatePhone,
}
3. v-model
父
@click="testClick" /> 子Son2
姓名:{{myName}}
年龄:{{myAge}}
性别:{{mySex}}
let testClick = (e)=>{
alert(e)
}
import { ref } from 'vue';
export default {
name: "Son2",
//props选项接收父组件参数
props:['name','age','sex'],
//emits选项确定父组件可以触发哪些事件
//注意:因为click跟原生事件同名,如果不在emits里面配置的话,父组件会触发两次click事件
emits:['click'],
setup(props,{emit}) {
let myName = ref(props.name)
let myAge = ref(props.age)
let mySex = ref(props.sex)
let updateData = ()=>{
myName.value = '谢娜'
myAge.value = 35,
mySex.value = '女'
//注意:自定义事件名称必须命名为update:属性名
//就可以实现对父组件中指定属性的双向绑定
emit('update:name',myName.value)
emit('update:age',myAge.value)
emit('update:sex',mySex.value)
}
let emitClick = ()=>{
//触发一个click事件
emit('click','哈哈')
}
return{
myName,
myAge,
mySex,
updateData,
emitClick
}
}
};
4. 异步组件
定义
Son3
import {ref} from 'vue'
export default {
name: "Son3",
setup() {
let name = ref('周杰伦')
let age = ref(20)
//注意:通常情况下,setup方法直接返回对象,不要返回Promise对象。
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve({
name,
age
})
}, 2000);
})
}
使用
Loading...
// defineAsyncComponent组合式API,用于定义异步组件
import {defineAsyncComponent} from 'vue'
// 异步导入组件
let Son3 = defineAsyncComponent(()=>import('./components/Son3.vue'))
5. teleport组件
Son4
五、vuex4 & vue-router4
1. 创建router对象
// createRouter方法,用于创建路由器对象
// createWebHashHistory方法,用于生成hash模式的路由,路由地址中包含一个#
// createWebHistory方法,用于生成history模式的路由
import {createRouter,createWebHashHistory} from 'vue-router'
// 创建当前项目中的路由器对象
let router = createRouter({
//定义路由模式
history:createWebHashHistory(),
//定义具体的路由信息
routes:[
//每一条路由信息,配置一个对象
{
path:'/',
name:'home',
component:()=>import('../views/Home.vue')
},
{
path:'/store',
name:'store',
component:()=>import('../views/Store.vue')
},
{
path:'/list/:id',
props:true,
name:'list',
component:()=>import('../views/List.vue')
},
{
path:'/news',
name:'news',
component:()=>import('../views/News.vue')
},
{
path:'/page1',
name:'page1',
component:()=>import('../views/Page1.vue')
},
{
// 注意:不可以写通配符*
// path:'*',
path:'/:pathMatch(.*)*',
name:'error404',
component:()=>import('../views/Error404.vue')
}
]
})
export default router
2. 使用router
//useRouter方法,返回当前项目中的路由器对象
//useRoute方法,返回当前路由信息对象
import {useRouter,useRoute} from 'vue-router'
//返回当前项目中的路由器对象
let $router = useRouter()
//获取当前路由信息
let $route = useRoute()
//通过props,也能获取都路由参数
props:['id']
//监听路由参数id
watch(()=>$route.params.id,(nval)=>{
//清空数组
showList.splice(0)
//向数组中添加最新的数据
showList.push(...list.filter(r=>r.typeId==$route.params.id))
},{
//一上来,先执行一次
immediate:true
})
3. 创建store对象
// 从vuex中导入createStore方法,该方法,用于创建全局状态管理对象
import { createStore } from 'vuex'
// 导入汽车模块
import car from './modules/car.js'
// 创建一个全局状态管理对象
let store = createStore({
//定义状态
state:{
firstName:'张',
lastName:'三'
},
//定义围绕状态的计算属性
getters:{
fullName(state){
return state.firstName+'.'+state.lastName
}
},
//定义同步方法
mutations:{
updateFirstName(state,val){
state.firstName = val
},
updateLastName(state,val){
state.lastName = val
}
},
//定义异步方法
actions:{
updateFirstName(store,val){
setTimeout(() => {
store.commit('updateFirstName',val)
}, 1000);
},
updateLastName(store,val){
setTimeout(() => {
store.commit('updateLastName',val)
}, 1000);
}
},
//模块
modules:{
car
}
})
//导出全局状态管理对象
export default store
4. 使用store
//useStore方法,返回当前项目中的全局状态管理对象
import { useStore } from "vuex";
// 获取全局状态管理对象
let $store = useStore();
let firstName = computed(() => {
return $store.state.firstName;
});
let lastName = computed(() => {
return $store.state.lastName;
});
let fullName = computed(() => {
return $store.getters.fullName;
});
let carName = computed(() => {
return $store.state.car.carName;
});
let address = computed(() => {
return $store.state.car.address;
});
let carInfo = computed(() => {
return $store.getters["car/carInfo"];
});
function updateFirstName() {
//调用mutations里面的方法,修改姓
$store.commit("updateFirstName", "李");
}
function updateLastName() {
//调用actions里面的方法,修改名
$store.dispatch("updateLastName", "四");
}
function updateCarName() {
//调用mutations里面的方法,修改车名
$store.commit("car/updateCarName", "宾利");
}
function updateCarAddress() {
//调用actions里面的方法,修改地址
$store.dispatch("car/updateCarAddress", "英国");
}
5. 注册
// 导入当前项目中创建的全局状态管理对象
import store from './store'
// 导入当前项目中创建的路由器对象
import router from './router'
// 使用createApp方法创建一个Vue实例,该方法的参数是App组件,表示渲染App组件
// use方法,用于给当前vue实例添加功能
// mount方法,用于将渲染后的内容,挂载到指定的容器中
createApp(App).use(store).use(router).mount('#app')