网站如何自己做优化,西安建设局网站小孩把,手机论坛app,进入网站后台代码Vue3新手教程 一. Vue3简介1. 性能的提升2.源码的升级3. 拥抱TypeScript4. 新的特性 二. 创建Vue3工程1. 基于 vue-cli 创建2. 基于 vite 创建(推荐)3. 一个简单的效果 三. Vue3核心语法1. OptionsAPI 与 CompositionAPI2. 拉开序幕的 setup2.1 setup 概述2.2 setup 的返回值2.… Vue3新手教程 一. Vue3简介1. 性能的提升2.源码的升级3. 拥抱TypeScript4. 新的特性 二. 创建Vue3工程1. 基于 vue-cli 创建2. 基于 vite 创建(推荐)3. 一个简单的效果 三. Vue3核心语法1. OptionsAPI 与 CompositionAPI2. 拉开序幕的 setup2.1 setup 概述2.2 setup 的返回值2.3 setup 与 Options API 的关系2.4 setup 语法糖 4. 创建响应式数据4.1 ref4.2 reactive4.3 ref 对比 reactive 5. toRefs 与 toRef6. computed7. watch7.1 监视ref定义的【基本类型】数据7.2 监视ref定义的【引用类型】数据7.3 监视reactive定义的【引用类型】数据7.4 监视ref或reactive定义的【引用类型】数据中的某个属性注意点如下7.5 监视上述的多个数据 8. watchEffect9 标签的 ref 属性10. props11. 生命周期12. 自定义hook 四. 路由1. 对路由的理解2. 基本切换效果3. 两个注意点4. 路由器工作模式5. to的两种写法6. 命名路由7. 嵌套路由8. 路由传参8.1 query参数8.2 params参数 9. 路由的props配置10. replace属性11. 编程式导航12. 重定向 五. pinia1. 准备一个效果2. 搭建 pinia 环境3. 存储读取数据4. 修改数据的三种方式5. storeToRefs6. getters7. $subscribe8. store组合式写法 六. 组件通信1. props2. 自定义事件3. mitt4. v-model5. $attrs6. $refs 、$parent7. provide、inject8. pinia9. slot9.1 默认插槽9.2 具名插槽9.3 作用域插槽 七. 其它 API1. shallowRef 与 shallowReactive2. readonly 与 shallowReadonly3.toRaw 与 markRaw4. customRef 八. Vue3新组件1. Teleport2. Suspense3. 全局API转移到应用对象4. 其他 一. Vue3简介 2020年9月18日Vue.js发布版3.0版本代号One Piecen 经历了4800次提交、40个RFC、600次PR、300贡献者 官方发版地址Release v3.0.0 One Piece · vuejs/core 截止2023年10月最新的公开版本为3.3.4
1. 性能的提升 打包大小减少41%。 初次渲染快55%, 更新渲染快133%。 内存减少54%。
2.源码的升级 使用Proxy代替defineProperty实现响应式。 重写虚拟DOM的实现和Tree-Shaking。
3. 拥抱TypeScript
Vue3可以更好的支持TypeScript。
4. 新的特性 Composition API组合API setup ref与reactive computed与watch … 新的内置组件 Fragment Teleport Suspense … 其他改变 新的生命周期钩子 data 选项应始终被声明为一个函数 移除keyCode支持作为 v-on 的修饰符 …
二. 创建Vue3工程
1. 基于 vue-cli 创建
点击查看官方文档 备注目前vue-cli已处于维护模式官方推荐基于 Vite 创建项目。 ## 查看vue/cli版本确保vue/cli版本在4.5.0以上
vue --version## 安装或者升级你的vue/cli
npm install -g vue/cli## 执行创建命令
vue create vue_test## 随后选择3.x
## Choose a version of Vue.js that you want to start the project with (Use arrow keys)
## 3.x
## 2.x## 启动
cd vue_test
npm run serve2. 基于 vite 创建(推荐)
vite 是新一代前端构建工具官网地址https://vitejs.cnvite的优势如下
轻量快速的热重载HMR能实现极速的服务启动。对 TypeScript、JSX、CSS 等支持开箱即用。真正的按需编译不再等待整个应用编译完成。webpack构建 与 vite构建对比图如下 具体操作如下点击查看官方文档 ## 1.创建命令
npm create vuelatest## 2.具体配置
## 配置项目名称
√ Project name: vue3_test
## 是否添加TypeScript支持
√ Add TypeScript? Yes
## 是否添加JSX支持
√ Add JSX Support? No
## 是否添加路由环境
√ Add Vue Router for Single Page Application development? No
## 是否添加pinia环境
√ Add Pinia for state management? No
## 是否添加单元测试
√ Add Vitest for Unit Testing? No
## 是否添加端到端测试方案
√ Add an End-to-End Testing Solution? » No
## 是否添加ESLint语法检查
√ Add ESLint for code quality? Yes
## 是否添加Prettiert代码格式化
√ Add Prettier for code formatting? No自己动手编写一个App组件:
templatediv classapph1你好啊/h1/div
/templatescript langtsexport default {name:App //组件名}
/scriptstyle.app {background-color: #ddd;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;}
/style安装官方推荐的vscode插件
总结
Vite 项目中index.html 是项目的入口文件在项目最外层。加载index.html后Vite 解析 script typemodule srcxxx 指向的JavaScript。Vue3 中是通过 createApp 函数创建一个应用实例。
3. 一个简单的效果
Vue3向下兼容Vue2语法且Vue3中的模板中可以没有根标签
templatediv classpersonh2姓名{{name}}/h2h2年龄{{age}}/h2button clickchangeName修改名字/buttonbutton clickchangeAge年龄1/buttonbutton clickshowTel点我查看联系方式/button/div
/templatescript langtsexport default {name:App,data() {return {name:张三,age:18,tel:13888888888}},methods:{changeName(){this.name zhang-san;},changeAge(){this.age 1;},showTel(){alert(this.tel);}},}
/script三. Vue3核心语法
1. OptionsAPI 与 CompositionAPI
Vue2的API设计是Options配置风格的。Vue3的API设计是Composition组合风格的。
Options API 的弊端
Options类型的 API数据、方法、计算属性等是分散在data、methods、computed中的若想新增或者修改一个需求就需要分别修改data、methods、computed不便于维护和复用。
Composition API 的优势
可以用函数的方式更加优雅的组织代码让相关功能的代码更加有序的组织在一起。 说明以上四张动图原创作者大帅老猿 2. 拉开序幕的 setup
2.1 setup 概述
setup是Vue3中一个新的配置项值是一个函数它是 Composition API “表演的舞台”组件中所用到的数据、方法、计算属性、监视…等等均配置在setup中。
特点如下
setup函数返回的对象中的内容可直接在模板中使用。setup中访问this是undefined。setup函数会在beforeCreate之前调用它是“领先”所有钩子执行的。
templatediv classpersonh2姓名{{name}}/h2h2年龄{{age}}/h2button clickchangeName修改名字/buttonbutton clickchangeAge年龄1/buttonbutton clickshowTel点我查看联系方式/button/div
/templatescript langtsexport default {name:Person,setup(){// 数据原来写在data中注意此时的name、age、tel数据都不是响应式数据let name 张三;let age 18;let tel 13888888888;// 方法原来写在methods中function changeName(){name zhang-san; //注意此时这么修改name页面是不变化的console.log(name);}function changeAge(){age 1; //注意此时这么修改age页面是不变化的console.log(age);}function showTel(){alert(tel);}// 返回一个对象对象中的内容模板中可以直接使用return {name,age,tel,changeName,changeAge,showTel}}}
/script2.2 setup 的返回值 若返回一个对象则对象中的属性、方法等在模板中均可以直接使用 重点关注 若返回一个函数则可以自定义渲染内容代码如下(了解) templatediv classpersonh2姓名{{ name }}/h2h2年龄{{ age }}/h2button clickchangeName修改名字/buttonbutton clickchangeAge年龄1/buttonbutton clickshowTel点我查看联系方式/button/div
/templatescript langts
export default {name: App,setup() {let name zhangsan;let age 16;let tel 19156165;function changeName() {name wgyf;}function changeAge() {age 1;}function showTel() {alert(tel);}return ()黑胡椒}
}
/script2.3 setup 与 Options API 的关系
Vue2 的配置data、methos…中可以访问到 setup中的属性、方法。但在setup中不能访问到Vue2的配置data、methos…。如果与Vue2冲突则setup优先。
2.4 setup 语法糖
setup函数有一个语法糖这个语法糖可以让我们把setup独立出去代码如下
templatediv classpersonh2姓名{{name}}/h2h2年龄{{age}}/h2button clickchangName修改名字/buttonbutton clickchangAge年龄1/buttonbutton clickshowTel点我查看联系方式/button/div
/templatescript langtsexport default {name:Person}
/script!-- 下面的写法是setup语法糖 --
script setup langtsconsole.log(this); //undefined// 数据注意此时的name、age、tel都不是响应式数据let name 张三;let age 18;let tel 13888888888;// 方法function changName(){name 李四; //注意此时这么修改name页面是不变化的}function changAge(){console.log(age);age 1; //注意此时这么修改age页面是不变化的}function showTel(){alert(tel);}
/script扩展上述代码还需要编写一个不写setup的script标签去指定组件名字比较麻烦我们可以借助vite中的插件简化 第一步npm i vite-plugin-vue-setup-extend -D 第二步vite.config.ts import { defineConfig } from vite
import VueSetupExtend from vite-plugin-vue-setup-extendexport default defineConfig({plugins: [ VueSetupExtend() ]
})第三步script setup langts namePerson
4. 创建响应式数据
4.1 ref
作用 定义基本类型以及引用类型的响应式变量。 语法let xxx ref(初始值)。 返回值 一个RefImpl的实例对象简称ref对象或refref对象的value属性是响应式的。 注意点
JS中操作数据需要xxx.value但模板中不需要.value直接使用即可。对于let name ref(张三)来说name不是响应式的name.value是响应式的。
基本类型(推荐):
templatediv classpersonh2姓名{{name}}/h2h2年龄{{age}}/h2button clickchangeName修改名字/buttonbutton clickchangeAge年龄1/buttonbutton clickshowTel点我查看联系方式/button/div
/templatescript setup langts namePersonimport {ref} from vue// name和age是一个RefImpl的实例对象简称ref对象它们的value属性是响应式的。let name ref(张三)let age ref(18)// tel就是一个普通的字符串不是响应式的let tel 13888888888;function changeName(){// JS中操作ref对象时候需要.valuename.value 李四;console.log(name.value);// 注意name不是响应式的name.value是响应式的所以如下代码并不会引起页面的更新。// name ref(zhang-san);}function changeAge(){// JS中操作ref对象时候需要.valueage.value 1 ;console.log(age.value);}function showTel(){alert(tel);}
/script引用类型:
templatediv classpersonh2汽车信息一台{{ car.brand }}汽车价值{{ car.price }}万/h2h2游戏列表/h2ulli v-forg in games :keyg.id{{ g.name }}/li/ulh2测试{{obj.a.b.c.d}}/h2button clickchangeCarPrice修改汽车价格/buttonbutton clickchangeFirstGame修改第一游戏/buttonbutton clicktest测试/button/div
/templatescript langts setup namePerson
import { ref } from vue// 数据
let car ref({ brand: 奔驰, price: 100 });
let games ref([{ id: ahsgdyfa01, name: 英雄联盟 },{ id: ahsgdyfa02, name: 王者荣耀 },{ id: ahsgdyfa03, name: 原神 }
]);
let obj ref({a:{b:{c:{d:666}}}
});console.log(car);function changeCarPrice() {car.value.price 10;
}
function changeFirstGame() {games.value[0].name 流星蝴蝶剑;
}
function test(){obj.value.a.b.c.d 999;
}
/scriptref主要是用来创建基本类型的响应式数据的,但是实际上也可以用来创建引用类型的响应式数据. 它的内部是这样处理的: 如果是基本类型的数据,那么通过Object.defineProperty(也就是Vue2实现响应式的方式)去创建响应式数据,但是如果是引用类型数据,那么会调用reactive去创建响应式数据. 4.2 reactive
作用定义一个响应式对象基本类型不要用它要用ref否则报错 语法 let 响应式对象 reactive(源对象)。 返回值 一个Proxy的实例对象简称响应式对象。 注意点reactive定义的响应式数据是“深层次”的。
templatediv classpersonh2汽车信息一台{{ car.brand }}汽车价值{{ car.price }}万/h2h2游戏列表/h2ulli v-forg in games :keyg.id{{ g.name }}/li/ulh2测试{{obj.a.b.c.d}}/h2button clickchangeCarPrice修改汽车价格/buttonbutton clickchangeFirstGame修改第一游戏/buttonbutton clicktest测试/button/div
/templatescript langts setup namePerson
import { reactive } from vue// 数据
let car reactive({ brand: 奔驰, price: 100 });
let games reactive([{ id: ahsgdyfa01, name: 英雄联盟 },{ id: ahsgdyfa02, name: 王者荣耀 },{ id: ahsgdyfa03, name: 原神 }
]);
let obj reactive({a:{b:{c:{d:666}}}
});function changeCarPrice() {car.price 10;
}
function changeFirstGame() {games[0].name 流星蝴蝶剑;
}
function test(){obj.a.b.c.d 999;
}
/script4.3 ref 对比 reactive
宏观角度看
ref用来定义基本类型数据、引用类型数据reactive用来定义引用类型数据。
区别
ref创建的变量必须使用.value可以使用volar插件自动添加.value。 reactive重新分配一个新对象会失去响应式可以使用Object.assign去整体替换。
使用原则
若需要一个基本类型的响应式数据必须使用ref。若需要一个响应式对象层级不深ref、reactive都可以。若需要一个响应式对象且层级较深推荐使用reactive。
5. toRefs 与 toRef
作用 将一个响应式对象中的每一个属性转换为ref对象。 备注 toRefs与toRef功能一致但toRefs可以批量转换。 语法如下
templatediv classpersonh2姓名{{person.name}}/h2h2年龄{{person.age}}/h2h2性别{{person.gender}}/h2button clickchangeName修改名字/buttonbutton clickchangeAge修改年龄/buttonbutton clickchangeGender修改性别/button/div
/templatescript langts setup namePersonimport {ref,reactive,toRefs,toRef} from vue// 数据let person reactive({name:张三, age:18, gender:男});// 通过toRefs将person对象中的n个属性批量取出且依然保持响应式的能力let {name,gender} toRefs(person);// 通过toRef将person对象中的age 属性取出且依然保持响应式的能力let age toRef(person,age);// 方法function changeName(){name.value ~;}function changeAge(){age.value 1;}function changeGender(){gender.value 女;}
/script6. computed
作用根据已有数据计算出新数据和Vue2中的computed作用一致。 templatediv classperson姓input typetext v-modelfirstName br名input typetext v-modellastName br全名span{{fullName}}/span brbutton clickchangeFullName全名改为li-si/button/div
/templatescript setup langts nameAppimport {ref,computed} from vuelet firstName ref(zhang)let lastName ref(san)// 计算属性——只读取不修改/* let fullName computed((){return firstName.value - lastName.value}) */// 计算属性——既读取又修改let fullName computed({// 读取get(){return firstName.value - lastName.value},// 修改set(val){console.log(有人修改了fullName,val)firstName.value val.split(-)[0]lastName.value val.split(-)[1]}})function changeFullName(){fullName.value li-si}
/script7. watch
作用 监视数据的变化和Vue2中的watch作用一致 特点 Vue3中的watch只能监视以下四种数据
ref定义的数据。reactive定义的数据。函数返回一个值getter函数。一个包含上述内容的数组。
我们在Vue3中使用watch的时候通常会遇到以下5种情况
7.1 监视ref定义的【基本类型】数据
直接写数据名即可监视的是其value值的改变。
templatediv classpersonh1情况一监视【ref】定义的【基本类型】数据/h1h2当前求和为{{sum}}/h2button clickchangeSum点我sum1/button/div
/templatescript langts setup namePersonimport {ref,watch} from vue// 数据let sum ref(0)// 方法function changeSum(){sum.value 1}//watch调用的时候会有一个返回值,这个返回值是一个停止监视的函数,如下所示,stopWatch就是返回的停止监听的函数,当sum的值大于等于10时,我们就不再继续监听const stopWatch watch(sum,(newValue,oldValue){console.log(sum变化了,newValue,oldValue)if(newValue 10){stopWatch()}})
/script7.2 监视ref定义的【引用类型】数据
直接写数据名监视的是数据【引用地址】的变化若想监视数据内部要手动开启深度监视。
templatediv classpersonh1情况二监视【ref】定义的【对象类型】数据/h1h2姓名{{ person.name }}/h2h2年龄{{ person.age }}/h2button clickchangeName修改名字/buttonbutton clickchangeAge修改年龄/buttonbutton clickchangePerson修改整个人/button/div
/templatescript langts setup namePerson
import { ref, watch } from vue
// 数据
let person ref({name: 张三,age: 18
})
// 方法
function changeName() {person.value.name ~
}
function changeAge() {person.value.age 1
}
function changePerson() {person.value { name: 李四, age: 90 }
}
/* watch的第一个参数是被监视的数据watch的第二个参数是监视的回调watch的第三个参数是配置对象deep、immediate等等.....
*/
// 第一种:浅层监听,监听是数据引用地址的变化啊,也就是整个数据被替换才会被监听到
// watch(person, (newValue, oldValue) {
// console.log(person变化了, newValue, oldValue)
// })// 第二种:深度监听,只要数据里面有值变化了就会被监听到
watch(person, (newValue, oldValue) {console.log(person变化了, newValue, oldValue)
}, { deep: true })/script注意 如果是进行的深度监听,并且引用地址没有改变的话(也就是不是整个对象替换),newValue和oldValue的值是一样的,因为他们是同一个对象。 7.3 监视reactive定义的【引用类型】数据 注意: 监视reactive定义的数据时,默认是开启深度监听,并且这是无法修改的.它们的newValue和oldValue是一样的,因为操作的都是原对象,引用地址没变,是同一个对象. templatediv classpersonh1情况三监视【reactive】定义的【对象类型】数据/h1h2姓名{{ person.name }}/h2h2年龄{{ person.age }}/h2button clickchangeName修改名字/buttonbutton clickchangeAge修改年龄/buttonbutton clickchangePerson修改整个人/buttonhrh2测试{{obj.a.b.c}}/h2button clicktest修改obj.a.b.c/button/div
/templatescript langts setup namePersonimport {reactive,watch} from vue// 数据let person reactive({name:张三,age:18});let obj reactive({a:{b:{c:666}}});// 方法function changeName(){person.name ~;}function changeAge(){person.age 1;}function changePerson(){// 这里不直接赋值,因为赋值后就不是原来那个对象了,watch也就监听不到了Object.assign(person,{name:李四,age:80});}function test(){obj.a.b.c 888;}watch(person,(newValue,oldValue){console.log(person变化了,newValue,oldValue);})watch(obj,(newValue,oldValue){console.log(Obj变化了,newValue,oldValue);})
/script7.4 监视ref或reactive定义的【引用类型】数据中的某个属性注意点如下 若该属性值不是 【引用类型】 需要写成函数形式。如下: watch(() person.name,(newValue,oldValue){console.log(person.name变化了,newValue,oldValue);
})若该属性值是依然是 【引用类型】可直接写也可写成函数建议写成函数。 templatediv classpersonh1情况四监视【ref】或【reactive】定义的【对象类型】数据中的某个属性/h1h2姓名{{ person.name }}/h2h2年龄{{ person.age }}/h2h2汽车{{ person.car.c1 }}、{{ person.car.c2 }}/h2button clickchangeName修改名字/buttonbutton clickchangeAge修改年龄/buttonbutton clickchangeC1修改第一台车/buttonbutton clickchangeC2修改第二台车/buttonbutton clickchangeCar修改整个车/button/div
/templatescript langts setup namePersonimport {reactive,watch} from vue// 数据let person reactive({name:张三,age:18,car:{c1:奔驰,c2:宝马}});// 方法function changeName(){person.name ~;}function changeAge(){person.age 1;}function changeC1(){person.car.c1 奥迪;}function changeC2(){person.car.c2 大众;}function changeCar(){person.car {c1:雅迪,c2:爱玛};}/* watch(() person.name,(newValue,oldValue){console.log(person.name变化了,newValue,oldValue);}) */watch(()person.car,(newValue,oldValue){console.log(person.car变化了,newValue,oldValue);},{deep:true})
/script如果监视是对象的属性,如果这个属性依然是引用类型数据,那么还是和上述一样,如果引用地址没有改变,那么newValue和oldValue是一样的,如上例的person.car,如果只改了c1或者c2的值,那么newValue和oldValue是一样的.如果改了整个person.car的值,那么newValue和oldValue才会不一样. 7.5 监视上述的多个数据
参考上面几中情况,就是上面的灵活运用
templatediv classpersonh1情况五监视上述的多个数据/h1h2姓名{{ person.name }}/h2h2年龄{{ person.age }}/h2h2汽车{{ person.car.c1 }}、{{ person.car.c2 }}/h2button clickchangeName修改名字/buttonbutton clickchangeAge修改年龄/buttonbutton clickchangeC1修改第一台车/buttonbutton clickchangeC2修改第二台车/buttonbutton clickchangeCar修改整个车/button/div
/templatescript langts setup namePersonimport {reactive,watch} from vue// 数据let person reactive({name:张三,age:18,car:{c1:奔驰,c2:宝马}});// 方法function changeName(){person.name ~;}function changeAge(){person.age 1;}function changeC1(){person.car.c1 奥迪;}function changeC2(){person.car.c2 大众;}function changeCar(){person.car {c1:雅迪,c2:爱玛};}// watch([()person.name,person.car],(newValue,oldValue){// 这里的newValue和oldValue指的是监听的数据组成的数组console.log(person.car变化了,newValue,oldValue);},{deep:true})/script8. watchEffect
官网立即运行一个函数同时响应式地追踪其依赖并在依赖更改时重新执行该函数。
watch对比watchEffect 都能监听响应式数据的变化不同的是监听数据变化的方式不同 watch要明确指出监视的数据 watchEffect不用明确指出监视的数据函数中用到哪些属性那就监视哪些属性。 示例代码
templatediv classpersonh1需求水温达到50℃或水位达到20cm则联系服务器/h1h2 iddemo水温{{temp}}/h2h2水位{{height}}/h2button clickchangePrice水温1/buttonbutton clickchangeSum水位10/button/div
/templatescript langts setup namePersonimport {ref,watch,watchEffect} from vue// 数据let temp ref(0)let height ref(0)// 方法function changePrice(){temp.value 10}function changeSum(){height.value 1}// 用watch实现需要明确的指出要监视temp、heightwatch([temp,height],(value){// 从value中获取最新的temp值、height值const [newTemp,newHeight] value// 室温达到50℃或水位达到20cm立刻联系服务器if(newTemp 50 || newHeight 20){console.log(联系服务器)}})// 用watchEffect实现不用明确指出,它会自动监视//stopWatch,与watch一样,返回的停止监听的函数const stopWatch watchEffect((){// 室温达到50℃或水位达到20cm立刻联系服务器if(temp.value 50 || height.value 20){console.log(document.getElementById(demo)?.innerText)console.log(联系服务器)}// 水温达到100或水位达到50取消监视if(temp.value 100 || height.value 50){console.log(清理了)stopWatch()}})
/script9 标签的 ref 属性
作用用于注册模板引用。 用在普通DOM标签上获取的是DOM节点。 用在组件标签上获取的是组件实例对象。 用在普通DOM标签上 templatediv classpersonh1 reftitle1尚硅谷/h1h2 reftitle2前端/h2h3 reftitle3Vue/h3input typetext refinpt brbrbutton clickshowLog点我打印内容/button/div
/templatescript langts setup namePersonimport {ref} from vue// 变量名与ref名称一样,会自动匹配let title1 ref();let title2 ref();let title3 ref();function showLog(){// 通过id获取元素const t1 document.getElementById(title1)// 打印内容console.log((t1 as HTMLElement).innerText)console.log((HTMLElementt1).innerText)console.log(t1?.innerText)/************************************/// 通过ref获取元素console.log(title1.value)console.log(title2.value)console.log(title3.value)}
/script用在组件标签上 父组件App.vue templatePerson refren/button clicktest测试/button
/templatescript langts setup nameAppimport Person from ./components/Person.vueimport {ref} from vuelet ren ref()function test(){console.log(ren.value.name)console.log(ren.value.age)}
/script子组件Person.vue中要使用defineExpose暴露内容 script langts setup namePersonimport {ref} from vue// 数据let name ref(张三)let age ref(18)/****************************//****************************/// 使用defineExpose将组件中的数据交给外部defineExpose({name,age})
/script10. props
types.ts
// 定义一个接口限制每个Person对象的格式
export interface PersonInter {id:string,name:string,age:number
}// 定义一个自定义类型Persons
export type Persons PersonInter[];App.vue中代码
templatePerson :listpersons/
/templatescript langts setup nameAppimport Person from ./components/Person.vueimport {reactive} from vueimport {Persons} from ./typeslet persons reactivePersons([{id:e98219e12,name:张三,age:18},{id:e98219e13,name:李四,age:19},{id:e98219e14,name:王五,age:20}])
/scriptPerson.vue中代码
templatediv classpersonulli v-foritem in list :keyitem.id{{item.name}}--{{item.age}}/li/ul/div
/templatescript langts setup namePersonimport {PersonInter} from ./types// 第一种写法仅接收// defineProps([list])// 第二种写法接收限制类型// defineProps{list:Persons}()// 第三种写法接收限制类型指定默认值限制必要性const props withDefaults(defineProps{list?:Persons}(),{list:()[{id:asdasg01,name:小猪佩奇,age:18}]})console.log(props)
/script11. 生命周期
概念Vue组件实例在创建时要经历一系列的初始化步骤在此过程中Vue会在合适的时机调用特定的函数从而让开发者有机会在特定阶段运行自己的代码这些特定的函数统称为生命周期钩子
规律
生命周期整体分为四个阶段分别是创建、挂载、更新、销毁每个阶段都有两个钩子一前一后。 Vue2的生命周期: 创建阶段beforeCreate、created 挂载阶段beforeMount、mounted 更新阶段beforeUpdate、updated 销毁阶段beforeDestroy、destroyed Vue3的生命周期: 创建阶段setup 挂载阶段onBeforeMount、onMounted 更新阶段onBeforeUpdate、onUpdated 卸载阶段onBeforeUnmount、onUnmounted 常用的钩子onMounted(挂载完毕)、onUpdated(更新完毕)、onBeforeUnmount(卸载之前) 示例代码 templatediv classpersonh2当前求和为{{ sum }}/h2button clickchangeSum点我sum1/button/div
/template!-- vue3写法 --
script langts setup namePersonimport { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from vue// 数据let sum ref(0)// 方法function changeSum() {sum.value 1}console.log(setup)// 生命周期钩子onBeforeMount((){console.log(挂载之前)})onMounted((){console.log(挂载完毕)})onBeforeUpdate((){console.log(更新之前)})onUpdated((){console.log(更新完毕)})onBeforeUnmount((){console.log(卸载之前)})onUnmounted((){console.log(卸载完毕)})
/script12. 自定义hook
什么是hook—— 本质是一个函数把setup函数中使用的Composition API进行了封装类似于vue2.x中的mixin。
自定义hook的优势复用代码, 让setup中的逻辑更清楚易懂。
示例代码
useSum.ts中内容如下
import {ref,onMounted} from vueexport default function(){let sum ref(0)const increment (){sum.value 1}const decrement (){sum.value - 1}onMounted((){increment()})//向外部暴露数据return {sum,increment,decrement}
} useDog.ts中内容如下
import {reactive,onMounted} from vue
import axios,{AxiosError} from axiosexport default function(){let dogList reactivestring[]([])// 方法async function getDog(){try {// 发请求let {data} await axios.get(https://dog.ceo/api/breed/pembroke/images/random)// 维护数据dogList.push(data.message)} catch (error) {// 处理错误const err AxiosErrorerrorconsole.log(err.message)}}// 挂载钩子onMounted((){getDog()})//向外部暴露数据return {dogList,getDog}
}组件中具体使用
templateh2当前求和为{{sum}}/h2button clickincrement点我1/buttonbutton clickdecrement点我-1/buttonhrimg v-for(u,index) in dogList.urlList :keyindex :src(u as string) span v-showdogList.isLoading加载中....../spanbrbutton clickgetDog再来一只狗/button
/templatescript langtsimport {defineComponent} from vueexport default defineComponent({name:App,})
/scriptscript setup langtsimport useSum from ./hooks/useSumimport useDog from ./hooks/useDoglet {sum,increment,decrement} useSum()let {dogList,getDog} useDog()
/script四. 路由
1. 对路由的理解 2. 基本切换效果
Vue3中要使用vue-router的最新版本目前是4版本。
路由配置文件代码如下
import {createRouter,createWebHistory} from vue-router
import Home from /pages/Home.vue
import News from /pages/News.vue
import About from /pages/About.vueconst router createRouter({history:createWebHistory(),routes:[{path:/home,component:Home},{path:/about,component:About}]
})
export default routermain.ts代码如下
import router from ./router/index
app.use(router)app.mount(#app)App.vue代码如下
templatediv classapph2 classtitleVue路由测试/h2!-- 导航区 --div classnavigateRouterLink to/home active-classactive首页/RouterLinkRouterLink to/news active-classactive新闻/RouterLinkRouterLink to/about active-classactive关于/RouterLink/div!-- 展示区 --div classmain-contentRouterView/RouterView/div/div
/templatescript langts setup nameAppimport {RouterLink,RouterView} from vue-router
/script3. 两个注意点
路由组件通常存放在pages 或 views文件夹一般组件通常存放在components文件夹。通过点击导航视觉效果上消失了的路由组件默认是被卸载掉的需要的时候再去挂载。
4. 路由器工作模式 history模式 优点URL更加美观不带有#更接近传统的网站URL。 缺点后期项目上线需要服务端配合处理路径问题否则刷新会有404错误。 const router createRouter({history:createWebHistory(), //history模式
})hash模式 优点兼容性更好因为不需要服务器端处理路径。 缺点URL带有#不太美观且在SEO优化方面相对较差。 const router createRouter({history:createWebHashHistory(), //hash模式
})5. to的两种写法
!-- 第一种to的字符串写法 --
router-link active-classactive to/home主页/router-link!-- 第二种to的对象写法 --
router-link active-classactive :to{path:/home}Home/router-link6. 命名路由
作用可以简化路由跳转及传参后面就讲。
给路由规则命名
routes:[{name:zhuye,path:/home,component:Home},{name:xinwen,path:/news,component:News,},{name:guanyu,path:/about,component:About}
]跳转路由
!--简化前需要写完整的路径to的字符串写法 --
router-link to/news/detail跳转/router-link!--简化后直接通过名字跳转to的对象写法配合name属性 --
router-link :to{name:guanyu}跳转/router-link7. 嵌套路由 编写News的子路由Detail.vue 配置路由规则使用children配置项 const router createRouter({history:createWebHistory(),routes:[{name:zhuye,path:/home,component:Home},{name:xinwen,path:/news,component:News,children:[{name:xiang,path:detail,component:Detail}]},{name:guanyu,path:/about,component:About}]
})
export default router跳转路由记得要加完整路径 router-link to/news/detailxxxx/router-link
!-- 或 --
router-link :to{path:/news/detail}xxxx/router-link记得去Home组件中预留一个router-view templatediv classnewsnav classnews-listRouterLink v-fornews in newsList :keynews.id :to{path:/news/detail}{{news.name}}/RouterLink/navdiv classnews-detailRouterView//div/div
/template8. 路由传参
8.1 query参数 传递参数 !-- 跳转并携带query参数to的字符串写法 --
router-link to/news/detail?a1b2content欢迎你跳转
/router-link!-- 跳转并携带query参数to的对象写法 --
RouterLink :to{//name:xiang, //用name也可以跳转path:/news/detail,query:{id:news.id,title:news.title,content:news.content}}
{{news.title}}
/RouterLink接收参数 import {useRoute} from vue-router
const route useRoute()
// 打印query参数
console.log(route.query)8.2 params参数 传递参数 !-- 跳转并携带params参数to的字符串写法 --
RouterLink :to/news/detail/001/新闻001/内容001{{news.title}}/RouterLink!-- 跳转并携带params参数to的对象写法 --
RouterLink :to{name:xiang, //用name跳转params:{id:news.id,title:news.title,content:news.title}}
{{news.title}}
/RouterLink接收参数 import {useRoute} from vue-router
const route useRoute()
// 打印params参数
console.log(route.params)备注 传递params参数时若使用to的对象写法必须使用name配置项不能用path。备注传递params参数时需要提前在规则中占位。
9. 路由的props配置
作用让路由组件更方便的收到参数可以将路由参数作为props传给组件
{name:xiang,path:detail/:id/:title/:content,component:Detail,// props的对象写法作用把对象中的每一组key-value作为props传给Detail组件// props:{a:1,b:2,c:3}, // props的布尔值写法作用把收到了每一组params参数作为props传给Detail组件// props:true// props的函数写法作用把返回的对象中每一组key-value作为props传给Detail组件props(route){return route.query}
}10. replace属性 作用控制路由跳转时操作浏览器历史记录的模式。 浏览器的历史记录有两种写入方式分别为push和replace push是追加历史记录默认值。replace是替换当前记录。 开启replace模式 RouterLink replace .......News/RouterLink11. 编程式导航
路由组件的两个重要的属性$route和$router变成了两个hooks
import {useRoute,useRouter} from vue-routerconst route useRoute()
const router useRouter()console.log(route.query)
console.log(route.parmas)
console.log(router.push)
console.log(router.replace)12. 重定向 作用将特定的路径重新定向到已有路由。 具体编码 {path:/,redirect:/about
}五. pinia
1. 准备一个效果 2. 搭建 pinia 环境
第一步npm install pinia
第二步操作src/main.ts
import { createApp } from vue
import App from ./App.vue/* 引入createPinia用于创建pinia */
import { createPinia } from pinia/* 创建pinia */
const pinia createPinia()
const app createApp(App)/* 使用插件 */{}
app.use(pinia)
app.mount(#app)此时开发者工具中已经有了pinia选项
3. 存储读取数据 Store是一个保存状态、业务逻辑 的实体每个组件都可以读取、写入它。 它有三个概念state、getter、action相当于组件中的 data、 computed 和 methods。 具体编码src/store/count.ts // 引入defineStore用于创建store
import {defineStore} from pinia// 定义并暴露一个store
export const useCountStore defineStore(count,{// 动作actions:{},// 状态state(){return {sum:6}},// 计算getters:{}
})具体编码src/store/talk.ts // 引入defineStore用于创建store
import {defineStore} from pinia// 定义并暴露一个store
export const useTalkStore defineStore(talk,{// 动作actions:{},// 状态state(){return {talkList:[{id:yuysada01,content:你今天有点怪哪里怪怪好看的},{id:yuysada02,content:草莓、蓝莓、蔓越莓你想我了没},{id:yuysada03,content:心里给你留了一块地我的死心塌地}]}},// 计算getters:{}
})组件中使用state中的数据 templateh2当前求和为{{ sumStore.sum }}/h2
/templatescript setup langts nameCount// 引入对应的useXxxxxStore import {useSumStore} from /store/sum// 调用useXxxxxStore得到对应的storeconst sumStore useSumStore()
/scripttemplateulli v-fortalk in talkStore.talkList :keytalk.id{{ talk.content }}/li/ul
/templatescript setup langts nameCountimport axios from axiosimport {useTalkStore} from /store/talkconst talkStore useTalkStore()
/script4. 修改数据的三种方式 第一种修改方式直接修改 countStore.sum 666第二种修改方式批量修改 countStore.$patch({sum:999,school:atguigu
})第三种修改方式借助action修改 action中可以编写一些业务逻辑 import { defineStore } from piniaexport const useCountStore defineStore(count, {actions: {//加increment(value:number) {if (this.sum 10) {//操作countStore中的sumthis.sum value}},//减decrement(value:number){if(this.sum 1){this.sum - value}}}
})组件中调用action即可 // 使用countStore
const countStore useCountStore()// 调用对应action
countStore.incrementOdd(n.value)5. storeToRefs
借助storeToRefs将store中的数据转为ref对象方便在模板中使用。注意pinia提供的storeToRefs只会将数据做转换而Vue的toRefs会转换store中数据。
templatediv classcounth2当前求和为{{sum}}/h2/div
/templatescript setup langts nameCountimport { useCountStore } from /store/count/* 引入storeToRefs */import { storeToRefs } from pinia/* 得到countStore */const countStore useCountStore()/* 使用storeToRefs转换countStore随后解构 */const {sum} storeToRefs(countStore)
/script
6. getters 概念当state中的数据需要经过处理后再使用时可以使用getters配置。 追加getters配置。 // 引入defineStore用于创建store
import {defineStore} from pinia// 定义并暴露一个store
export const useCountStore defineStore(count,{// 动作actions:{/************/},// 状态state(){return {sum:1,school:atguigu}},// 计算getters:{bigSum:(state):number state.sum *10,upperSchool():string{return this. school.toUpperCase()}}
})组件中读取数据 const {increment,decrement} countStore
let {sum,school,bigSum,upperSchool} storeToRefs(countStore)7. $subscribe
组件中通过 store 的 $subscribe() 方法侦听 state 及其变化
talkStore.$subscribe((mutate,state){console.log(LoveTalk,mutate,state)localStorage.setItem(talk,JSON.stringify(talkList.value))
})8. store组合式写法
import {defineStore} from pinia
import axios from axios
import {nanoid} from nanoid
import {reactive} from vueexport const useTalkStore defineStore(talk,(){// talkList就是stateconst talkList reactive(JSON.parse(localStorage.getItem(talkList) as string) || [])// getATalk函数相当于actionasync function getATalk(){// 发请求下面这行的写法是连续解构赋值重命名let {data:{content:title}} await axios.get(https://api.uomg.com/api/rand.qinghua?formatjson)// 把请求回来的字符串包装成一个对象let obj {id:nanoid(),title}// 放到数组中talkList.unshift(obj)}return {talkList,getATalk}
})六. 组件通信
Vue3组件通信和Vue2的区别
移出事件总线使用mitt代替。
vuex换成了pinia。把.sync优化到了v-model里面了。把$listeners所有的东西合并到$attrs中了。$children被砍掉了。
常见搭配形式
1. props
概述props是使用频率最高的一种通信方式常用与 父 ↔ 子。
若 父传子属性值是非函数。若 子传父属性值是函数。
父组件
templatediv classfatherh3父组件/h3h4我的车{{ car }}/h4h4儿子给的玩具{{ toy }}/h4Child :carcar :getToygetToy//div
/templatescript setup langts nameFatherimport Child from ./Child.vueimport { ref } from vue;// 数据const car ref(奔驰)const toy ref()// 方法function getToy(value:string){toy.value value}
/script子组件
templatediv classchildh3子组件/h3h4我的玩具{{ toy }}/h4h4父给我的车{{ car }}/h4button clickgetToy(toy)玩具给父亲/button/div
/templatescript setup langts nameChildimport { ref } from vue;const toy ref(奥特曼)defineProps([car,getToy])
/script2. 自定义事件
概述自定义事件常用于子 父。注意区分好原生事件、自定义事件。
原生事件 事件名是特定的click、mosueenter等等事件对象$event: 是包含事件相关信息的对象pageX、pageY、target、keyCode 自定义事件 事件名是任意名称事件对象$event: 是调用emit时所提供的数据可以是任意类型 示例 !--在父组件中给子组件绑定自定义事件--
Child send-toytoy $event/!--注意区分原生事件与自定义事件中的$event--
button clicktoy $event测试/button//子组件中触发事件
this.$emit(send-toy, 具体数据)3. mitt
概述与消息订阅与发布pubsub功能类似可以实现任意组件间通信。
安装mitt
npm i mitt新建文件src\utils\emitter.ts
// 引入mitt
import mitt from mitt;// 创建emitter
const emitter mitt()/*// 绑定事件emitter.on(abc,(value){console.log(abc事件被触发,value)})emitter.on(xyz,(value){console.log(xyz事件被触发,value)})setInterval(() {// 触发事件emitter.emit(abc,666)emitter.emit(xyz,777)}, 1000);setTimeout(() {// 清理事件emitter.all.clear()}, 3000);
*/// 创建并暴露mitt
export default emitter接收数据的组件中绑定事件、同时在销毁前解绑事件
import emitter from /utils/emitter;
import { onUnmounted } from vue;// 绑定事件
emitter.on(send-toy,(value){console.log(send-toy事件被触发,value)
})onUnmounted((){// 解绑事件emitter.off(send-toy)
})【第三步】提供数据的组件在合适的时候触发事件
import emitter from /utils/emitter;function sendToy(){// 触发事件emitter.emit(send-toy,toy.value)
}注意这个重要的内置关系总线依赖着这个内置关系
4. v-model 概述实现 父↔子 之间相互通信。 前序知识 —— v-model的本质 !-- 使用v-model指令 --
input typetext v-modeluserName!-- v-model的本质是下面这行代码 --
input typetext :valueuserName inputuserName (HTMLInputElement$event.target).value组件标签上的v-model的本质:moldeValue update:modelValue事件。 !-- 组件标签上使用v-model指令 --
AtguiguInput v-modeluserName/!-- 组件标签上v-model的本质 --
AtguiguInput :modelValueuserName update:model-valueuserName $event/AtguiguInput组件中 templatediv classbox!--将接收的value值赋给input元素的value属性目的是为了呈现数据 --!--给input元素绑定原生input事件触发input事件时进而触发update:model-value事件--input typetext :valuemodelValue inputemit(update:model-value,$event.target.value)/div
/templatescript setup langts nameAtguiguInput// 接收propsdefineProps([modelValue])// 声明事件const emit defineEmits([update:model-value])
/script也可以更换value例如改成abc !-- 也可以更换value例如改成abc--
AtguiguInput v-model:abcuserName/!-- 上面代码的本质如下 --
AtguiguInput :abcuserName update:abcuserName $event/AtguiguInput组件中 templatediv classboxinput typetext :valueabc inputemit(update:abc,$event.target.value)/div
/templatescript setup langts nameAtguiguInput// 接收propsdefineProps([abc])// 声明事件const emit defineEmits([update:abc])
/script如果value可以更换那么就可以在组件标签上多次使用v-model AtguiguInput v-model:abcuserName v-model:xyzpassword/5. $attrs
概述$attrs用于实现当前组件的父组件向当前组件的子组件通信祖→孙。
具体说明$attrs是一个对象包含所有父组件传入的标签属性。 注意$attrs会自动排除props中声明的属性(可以认为声明过的 props 被子组件自己“消费”了) 父组件
templatediv classfatherh3父组件/h3Child :aa :bb :cc :dd v-bind{x:100,y:200} :updateAupdateA//div
/templatescript setup langts nameFatherimport Child from ./Child.vueimport { ref } from vue;let a ref(1)let b ref(2)let c ref(3)let d ref(4)function updateA(value){a.value value}
/script子组件
templatediv classchildh3子组件/h3GrandChild v-bind$attrs//div
/templatescript setup langts nameChildimport GrandChild from ./GrandChild.vue
/script孙组件
templatediv classgrand-childh3孙组件/h3h4a{{ a }}/h4h4b{{ b }}/h4h4c{{ c }}/h4h4d{{ d }}/h4h4x{{ x }}/h4h4y{{ y }}/h4button clickupdateA(666)点我更新A/button/div
/templatescript setup langts nameGrandChilddefineProps([a,b,c,d,x,y,updateA])
/script6. $refs 、$parent
概述
$refs用于 父→子。$parent用于子→父。
原理如下
属性说明$refs值为对象包含所有被ref属性标识的DOM元素或组件实例。$parent值为对象当前组件的父组件实例对象。
7. provide、inject
概述实现祖孙组件直接通信
具体使用
在祖先组件中通过provide配置向后代组件提供数据在后代组件中通过inject配置来声明接收数据
具体编码
【第一步】父组件中使用provide提供数据
templatediv classfatherh3父组件/h3h4资产{{ money }}/h4h4汽车{{ car }}/h4button clickmoney 1资产1/buttonbutton clickcar.price 1汽车价格1/buttonChild//div
/templatescript setup langts nameFatherimport Child from ./Child.vueimport { ref,reactive,provide } from vue;// 数据let money ref(100)let car reactive({brand:奔驰,price:100})// 用于更新money的方法function updateMoney(value:number){money.value value}// 提供数据provide(moneyContext,{money,updateMoney})provide(car,car)
/script注意子组件中不用编写任何东西是不受到任何打扰的 【第二步】孙组件中使用inject配置项接受数据。
templatediv classgrand-childh3我是孙组件/h3h4资产{{ money }}/h4h4汽车{{ car }}/h4button clickupdateMoney(6)点我/button/div
/templatescript setup langts nameGrandChildimport { inject } from vue;// 注入数据let {money,updateMoney} inject(moneyContext,{money:0,updateMoney:(x:number){}})let car inject(car)
/script8. pinia
参考之前pinia部分的讲解
9. slot
9.1 默认插槽 父组件中Category title今日热门游戏ulli v-forg in games :keyg.id{{ g.name }}/li/ul/Category
子组件中templatediv classitemh3{{ title }}/h3!-- 默认插槽 --slot/slot/div/template9.2 具名插槽
父组件中Category title今日热门游戏template v-slot:s1ulli v-forg in games :keyg.id{{ g.name }}/li/ul/templatetemplate #s2a href更多/a/template/Category
子组件中templatediv classitemh3{{ title }}/h3slot names1/slotslot names2/slot/div/template9.3 作用域插槽
理解数据在组件的自身但是数据怎么使用由组件的使用者决定. 如下案例:
父组件中Game v-slotparams!-- Game v-slot:defaultparams --!-- Game #defaultparams --ulli v-forg in params.games :keyg.id{{ g.name }}/li/ul/Game子组件中templatediv classcategoryh2今日游戏榜单/h2slot :gamesgames a哈哈/slot/div/templatescript setup langts nameCategoryimport {reactive} from vuelet games reactive([{id:asgdytsa01,name:英雄联盟},{id:asgdytsa02,name:王者荣耀},{id:asgdytsa03,name:红色警戒},{id:asgdytsa04,name:斗罗大陆}])/script子组件只负责把games数据传到父组件,但是父组件拿到这个数据,怎么去渲染页面,由父组件决定
七. 其它 API
1. shallowRef 与 shallowReactive shallowRef 作用创建一个响应式数据但只对顶层属性进行响应式处理。 用法 let myVar shallowRef(initialValue);特点只跟踪引用值的变化不关心值内部的属性变化。 shallowReactive 作用创建一个浅层响应式对象只会使对象的最顶层属性变成响应式的对象内部的嵌套属性则不会变成响应式的 用法 const myObj shallowReactive({ ... });特点对象的顶层属性是响应式的但嵌套对象的属性不是。 通过使用 shallowRef() 和 shallowReactive() 来绕开深度响应。浅层式 API 创建的状态只在其顶层是响应式的对所有深层的对象不会做任何处理避免了对每一个内部属性做响应式所带来的性能成本这使得属性的访问变得更快可提升性能。 2. readonly 与 shallowReadonly readonly 作用用于创建一个对象的深只读副本。 用法 const original reactive({ ... });
const readOnlyCopy readonly(original);特点 对象的所有嵌套属性都将变为只读。任何尝试修改这个对象的操作都会被阻止在开发模式下还会在控制台中发出警告。 应用场景 创建不可变的状态快照。保护全局状态或配置不被修改。 shallowReadonly 作用与 readonly 类似但只作用于对象的顶层属性。 用法 const original reactive({ ... });
const shallowReadOnlyCopy shallowReadonly(original);特点 只将对象的顶层属性设置为只读对象内部的嵌套属性仍然是可变的。 适用于只需保护对象顶层属性的场景。
3.toRaw 与 markRaw toRaw 作用用于获取一个响应式对象的原始对象 toRaw 返回的对象不再是响应式的不会触发视图更新。 官网描述这是一个可以用于临时读取而不引起代理访问/跟踪开销或是写入而不触发更改的特殊方法。不建议保存对原始对象的持久引用请谨慎使用。 何时使用 —— 在需要将响应式对象传递给非 Vue 的库或外部系统时使用 toRaw 可以确保它们收到的是普通对象 具体编码 import { reactive,toRaw,markRaw,isReactive } from vue;/* toRaw */
// 响应式对象
let person reactive({name:tony,age:18})
// 原始对象
let rawPerson toRaw(person)/* markRaw */
let citysd markRaw([{id:asdda01,name:北京},{id:asdda02,name:上海},{id:asdda03,name:天津},{id:asdda04,name:重庆}
])
// 根据原始对象citys去创建响应式对象citys2 —— 创建失败因为citys被markRaw标记了
let citys2 reactive(citys)
console.log(isReactive(person))
console.log(isReactive(rawPerson))
console.log(isReactive(citys))
console.log(isReactive(citys2))markRaw 作用标记一个对象使其永远不会变成响应式的。 例如使用mockjs时为了防止误把mockjs变为响应式对象可以使用 markRaw 去标记mockjs 编码 /* markRaw */
let citys markRaw([{id:asdda01,name:北京},{id:asdda02,name:上海},{id:asdda03,name:天津},{id:asdda04,name:重庆}
])
// 根据原始对象citys去创建响应式对象citys2 —— 创建失败因为citys被markRaw标记了
let citys2 reactive(citys)4. customRef
作用创建一个自定义的ref并对其依赖项跟踪和更新触发进行逻辑控制。
实现防抖效果useSumRef.ts
import {customRef } from vue;export default function(initValue:string,delay:number){let msg customRef((track,trigger){let timer:numberreturn {get(){track() // 告诉Vue数据msg很重要要对msg持续关注一旦变化就更新return initValue},set(value){clearTimeout(timer)timer setTimeout(() {initValue valuetrigger() //通知Vue数据msg变化了}, delay);}}}) return {msg}
}组件中使用
八. Vue3新组件
1. Teleport
什么是Teleport—— Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。
// 移动到body标签里
teleport tobody div classmodal v-showisShowh2我是一个弹窗/h2p我是弹窗中的一些内容/pbutton clickisShow false关闭弹窗/button/div
/teleport2. Suspense
等待异步组件时渲染一些额外内容让应用有更好的用户体验使用步骤 异步引入组件使用Suspense包裹组件并配置好default 与 fallback
import { defineAsyncComponent,Suspense } from vue;
const Child defineAsyncComponent(()import(./Child.vue))templatediv classapph3我是App组件/h3Suspensetemplate v-slot:defaultChild//templatetemplate v-slot:fallbackh3加载中......./h3/template/Suspense/div
/template3. 全局API转移到应用对象
app.componentapp.configapp.directiveapp.mountapp.unmountapp.use
4. 其他 过渡类名 v-enter 修改为 v-enter-from、过渡类名 v-leave 修改为 v-leave-from。 keyCode 作为 v-on 修饰符的支持。 v-model 指令在组件上的使用已经被重新设计替换掉了 v-bind.sync。 v-if 和 v-for 在同一个元素身上使用时的优先级发生了变化。 移除了$on、$off 和 $once 实例方法。 移除了过滤器 filter。 移除了$children 实例 propert。 …