做网站空间济南网站建设设计公司

当前位置: 首页 > news >正文

做网站空间,济南网站建设设计公司,wordpress 显示p标签,网店购物系统本篇文章是记录来自尚硅谷禹神2023年课程的学习笔记#xff0c;不得不说禹神讲的是真的超级棒#xff01; 文章目录 创建Vue3工程main.ts文件解析初始化项目写一个简单的效果 Vue3核心语法setup函数setup和选项式的区别setup语法糖指定组件名称 响应式数据ref函数定义基本类…    本篇文章是记录来自尚硅谷禹神2023年课程的学习笔记不得不说禹神讲的是真的超级棒 文章目录 创建Vue3工程main.ts文件解析初始化项目写一个简单的效果 Vue3核心语法setup函数setup和选项式的区别setup语法糖指定组件名称 响应式数据ref函数定义基本类型定义对象类型 reactive函数ref和reactive的区别toRef和toRefs 计算属性Computed计算属性的get和set 监听属性watch监视ref定义的基本类型监视ref定义的对象类型监听reactive定义的对象类型监听响应式对象的中的某个属性监听多个数据 watchEffect标签中的ref属性 生命周期自定义hooks 路由路由工作模式to的两种写法命名路由嵌套路由 路由传参query参数params参数 路由的propsparams参数作为props自定义props函数写法对象写法 replace属性 编程式导航route和router重定向 Pinia状态管理搭建pinia环境存储数据修改数据storeToRefs getters侦听statestore组合式写法 组件通信props自定义事件mittmitt绑定事件mitt触发事件mitt解绑事件 $attrs r e f s 、 refs、 refs、parent祖孙通信——provide、injectv-model (了解) 插槽默认插槽具名插槽作用域插槽 其他APIshallowRef与shallowReactiveshallowRefshallowReactive readonly和shallowReadonlyreadonlyshallowReadonly toRaw和markRawtoRawmarkRaw 自定义RefTeleport传送??Suspense??全局API转移到应用对象 创建Vue3工程 基于vite创建vite是一个新一代的前端构建工具。 优点 请求快速的热重载对ts、jsx支持开箱即用构建方式有改善 wepack构建流程 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安装插件 目录分析 Vite 项目中index.html 是项目的入口文件在项目最外层。加载index.html后Vite 解析 script typemodule srcxxx 指向的JavaScript。Vue3**中是通过 **createApp 函数创建一个应用实例。 main.ts文件解析 创建应用import createApp from vue 导入根组件import App from ./App.vue 挂载组件createApp(App).mount(#app) 初始化项目 编写一个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启动项目npm run dev 写一个简单的效果 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在App.vue中使用组件 templatediv classapph1你好啊/h1Person/ !–第三步使用组件–/div /templatescript langtsimport Person from ./components/Person.vue //第二步导入组件export default { //暴露name:App, //组件名components:{Person} //第一步注册组件} /scriptstyle…/style上面是使用Vue2的选项式OPotionsAPI写法来写的 Options类型的 API数据、方法、计算属性等是分散在data、methods、computed中的若想新增或者修改一个需求就需要分别修改data、methods、computed不便于维护和复用。 组合式API的优势可以用函数的方式更加优雅的组织代码让相关功能的代码更加有序的组织在一起。 Vue3核心语法 setup函数 语法模版 script langtsexport default{name:,setup(){//声明数据let 变量名 变量值//声明方法function 方法名(){//方法体}return{变量名,方法名}// 返回一个对象对象中的内容模板中可以直接使用}} /script例子 script langtsexport default {name:Person,setup(){// 数据原来写在data中注意此时的name、age、tel数据都不是响应式数据let name 张三let age 18let 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}}} /script⚠️注意setup函数中的this是undefinedVue3中已经弱化this了。 setup的返回值 若返回的一个对象这对象的属性、方法等在模版中均可以直接使用。 若返回的是一个函数则可以自定义渲染内容代码如下 setup(){return () 你好啊 //这样会直接覆盖所在组件和样式 }setup和选项式的区别 Vue2 的配置data、methos…中可以访问到 setup中的属性、方法。但在setup中不能访问到Vue2的配置data、methos…。如果与Vue2冲突则setup优先。 setup语法糖 setup函数有一个语法糖这个语法糖可以让我们把setup独立出去。 语法模版 script lang setup//写数据//写方法//不用写return了 /script错误写法❌ 例子 !– 原本的写法 – 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) //undefinedlet name 张三let age 18let tel 13888888888function changName(){name 李四}function changAge(){console.log(age)age 1}function showTel(){alert(tel)} /script指定组件名称 上述代码中我们还需要编写一个不写setup的script标签去注定组件名称。 script langtsexport default {//需要编写一个不写setup的script标签去注定组件名称name:Person,} /scriptscript setup langts… /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() ] })xxxxxxxxxx import { defineConfig } from viteimport VueSetupExtend from vite-plugin-vue-setup-extendexport default defineConfig({ plugins: [ VueSetupExtend() ]})使用插件 //在原来的语法糖中使用 script setup langts namePerson响应式数据 ref函数 定义基本类型 作用创建基本类型的响应式数据 语法let 变量名 ref(初始值) 使用方法

  1. 引入ref import {ref} from vue2. 使用ref let 变量名 ref(初始值)3. 使用响应式数据变量名.value 变量值返回值是一个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 13888888888function 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定义对象类型 ref接收的数据可以是基本类型、对象类型。 如果ref接受的是对象类型内部其实是调用了reactive函数。所以同样也是可以处理深层次。 reactive函数 作用定义一个响应式对象reactive只能处理对象类型 语法let 对象名 reactive(原对象) 使用方法
  2. 引入reactive import {reactive} from vue2. 使用reactive let 对象名 reactive(原对象)3. 替换整体对象 Object.assign(对象名,{属性名属性值,…})返回值一个Proxy的实例对象简称响应式对象。 注意reactive定义的响应式数据是“深层次”的。 ref和reactive的区别 宏观角度对比 ref用来定义基本类型数据、对象类型数据 reactive用来定义对象类型数据。
    细节对比 ref创建的变量必须使用.value可以使用volar插件自动添加.value。 reactive重新分配一个新对象会失去响应式可以使用Object.assign去整体替换。 使用原则 若需要一个基本类型的响应式数据必须使用ref。若需要一个响应式对象层级不深ref、reactive都可以。若需要一个响应式对象且层级较深推荐使用reactive。 toRef和toRefs 作用将一个响应式对象中的每一个属性转换为ref对象。toRefs与toRef功能一致但toRefs可以批量转换。 语法 toRefs(对象名)toRef(对象名,属性名) 使用方法
  3. 导入import {ref,reactive,toRefs,toRef} from vue2. 使用toRefs将解构的属性批量取出 let {属性值1属性值2} toRefs(对象名) let 属性值 toRef(对象名,属性名)例子 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对象中的gender属性取出且依然保持响应式的能力let age toRef(person,age)// 方法function changeName(){name.value ~}function changeAge(){age.value 1}function changeGender(){gender.value 女} /script计算属性Computed 作用Computed就是依赖的属性发生变化后会被重新计算根据已有数据计算出新数据和Vue2中的computed作用一致。 回顾vue2的计算属性 scriptexport default{computed:{//配置项} } /script语法模版 scriptimport {computed} from vuelet 变量名 computed(() {return 方法体}) /script计算属性的get和set 按照上面的语法模版计算属性是只可读不可修改的要想让计算属性即可读又可以写就需要用到get和set方法。 语法模版 scriptimport {computed} from vuelet 变量名 computed(() {//读取get(){ }set(){ }}) /script监听属性watch 作用监视数据的变化和Vue2中的watch作用一致。 特点Vue3中的watch只能监视以下四种数据 ref定义的数据。reactive定义的数据。函数返回一个值getter函数。一个包含上述内容的数组。 使用方法 导入watch import {watch} from vue使用watch watch(监视对象(newValue,oldValue){//回调函数 }),{配置对象}解除监视stopWatch() const stopWatch watch(监视对象(newValue,oldValue){//回调函数if(newValue 10){stopWatch()} })监视ref定义的基本类型 监视ref定义的【基本类型】数据直接写数据名即可监视的是其value值的改变。 监视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}// 监视情况一监视【ref】定义的【基本类型】数据const stopWatch watch(sum,(newValue,oldValue){console.log(sum变化了,newValue,oldValue)if(newValue 10){stopWatch()}}) /script监视ref定义的对象类型 监视ref定义的【对象类型】数据直接写数据名监视的是对象的【地址值】若想监视对象内部的数据要手动开启深度监视。 深度监视 watch(监视对象(newValue,oldValue){//回调函数 }),{deep:true}⚠️注意 若修改的是ref定义的对象中的属性newValue 和 oldValue 都是新值因为它们是同一个对象。 若修改整个ref定义的对象newValue 是新值 oldValue 是旧值因为不是同一个对象了。
    例子 templatediv classpersonh1情况二监视【ref】定义的【对象类型】数据/h1h2姓名{{ person.name }}/h2h2年龄{{ person.age }}/h2button clickchangeName修改名字/buttonbutton clickchangeAge修改年龄/buttonbutton clickchangePerson修改整个人/button/div /templatescript langts setup namePersonimport {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}}/* 监视情况一监视【ref】定义的【对象类型】数据监视的是对象的地址值若想监视对象内部属性的变化需要手动开启深度监视watch的第一个参数是被监视的数据watch的第二个参数是监视的回调watch的第三个参数是配置对象deep、immediate等等….. /watch(person,(newValue,oldValue){console.log(person变化了,newValue,oldValue)},{deep:true})/script监听reactive定义的对象类型 监视reactive定义的【对象类型】数据且默认开启了深度监视。隐式的创建了深度监听。 例子 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(){Object.assign(person,{name:李四,age:80})}function test(){obj.a.b.c 888}// 监视情况三监视【reactive】定义的【对象类型】数据且默认是开启深度监视的watch(person,(newValue,oldValue){console.log(person变化了,newValue,oldValue)})watch(obj,(newValue,oldValue){console.log(Obj变化了,newValue,oldValue)}) /script监听响应式对象的中的某个属性 监视ref或reactive定义的【对象类型】数据中的某个属性注意点如下 基本类型的监听需要写成函数形式 对象类型的监听可以直接.属性但是最好写成函数形式。如果对象坚硬的是地址值需要管制对象内部需要手动开启深度监听。 watch(()person.car,(newValue,oldValue){console.log(person.car变化了,newValue,oldValue)},{deep:true})例子 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)}) */// 监视情况四监视响应式对象中的某个属性且该属性是对象类型的可以直接写也能写函数更推荐写函数/script监听多个数据 直接使用一个数组包裹住[] 例子 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){console.log(person.car变化了,newValue,oldValue)},{deep:true})/scriptwatchEffect 立即运行一个函数同时响应式地追踪其依赖并在依赖更改时重新执行该函数。 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实现不用const stopWtach 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(清理了)stopWtach()}}) /script标签中的ref属性 作用用于注册模板引用。 特点 用在普通DOM标签上获取的是DOM节点。 templatediv classpersonh1 reftitle1尚硅谷/h1h2 reftitle2前端/h2h3 reftitle3Vue/h3input typetext refinpt brbrbutton clickshowLog点我打印内容/button/div /templatescript langts setup namePersonimport {ref} from vuelet 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,defineExpose} from vue// 数据let name ref(张三)let age ref(18)/*************************/// 使用defineExpose将组件中的数据交给外部defineExpose({name,age}) /script生命周期 概念Vue组件实例在创建时要经历一系列的初始化步骤在此过程中Vue会在合适的时机调用特定的函数从而让开发者有机会在特定阶段运行自己的代码这些特定的函数统称为生命周期钩子。 时刻生命周期整体分为四个阶段分别是创建、挂载、更新、销毁每个阶段都有两个钩子一前一后。 vue2的生命周期 创建阶段beforeCreate创建前、created创建完毕 挂载阶段beforeMount挂载前、mounted挂载完毕 更新阶段beforeUpdate更新前、updated更新完毕 销毁阶段beforeDestroy销毁前、destroyed销毁完毕 vue2 中的生命周期函数卸载methods方法中。vue3的生命周期 创建阶段setup 挂载阶段onBeforeMount、onMounted 更新阶段onBeforeUpdate、onUpdated 卸载阶段onBeforeUnmount、onUnmounted
    使用方法
  4. 导入import {生命周期函数} from vue
  5. 使用生命周期函数((){//方法体})例子 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(卸载完毕)}) /script自定义hooks 属于是一个模块化开发本质是一个函数把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路由 安装Vue-router npm i vue-router在src下新建一个router文件夹在里面书写路由代码 创建路由器并暴露出去 //第一步引入creatRouter import {createRouter,createWebHistory} from vue-router//第三步引入可能呈现的组件 import Home from /.vue路径//第二步创建路由器 const router createRouter({ history:createWebHistory(),//指定工作模式routes:[{path:/home,component:Home},] })//第四步暴露路由 export default router在main.js中配置路由器 // 引入createApp用于创建应用 import {createApp} from vue // 引入App根组件 import App from ./App.vue//第一步引入路由器 import router from ./router//创建一个应用 const app createApp(App)//第二步使用路由器 app.use(router)//挂载整个应用到app容器中 app.mount(#app)在App.vue展示 templateRouterView/RouterView /template script//第一步导入路由import {RouterLink,RouterView} from vue-router
    /script实现路由跳转 template!– 导航区 –RouterLink to/文件路径 首页/RouterLink!– 展示区 –RouterView/RouterView /template script//第一步导入路由import {RouterLink,RouterView} from vue-router
    /script使用active-class实现点击切换class RouterLink to/home active-classactive首页/RouterLink注意⚠️ 路由组件通常存放在pages 或 views文件夹一般组件通常存放在components文件夹。 通过点击导航视觉效果上“消失” 了的路由组件默认是被卸载掉的需要的时候再去挂载。
    路由工作模式 history模式 优点URL更加美观不带有#更接近传统的网站URL。 缺点后期项目上线需要服务端配合处理路径问题否则刷新会有404错误。 const router createRouter({history:createWebHistory(), //history模式/***/ })hash模式 优点兼容性更好因为不需要服务器端处理路径。 缺点URL带有#不太美观且在SEO优化方面相对较差。 const router createRouter({history:createWebHashHistory(), //hash模式/*/ })to的两种写法 字符串写法 !– 第一种to的字符串写法 – router-link active-classactive to/home主页/router-link对象写法 !– 第二种to的对象写法 – router-link active-classactive :to{path:/path路径 或者 名字跳转}Home/router-link命名路由 作用可以简化路由跳转及传参后面就讲。 给路由规则命名 routes:[{name:zhuye,path:/home,component:Home}, ]嵌套路由 写法注意 子集不用写/ 使用方法 配置路由规则使用children配置 {name:,path:/home,component:Home,children:[{path:子路由, //子集不用写/component:子路由}] }跳转路由 router-link to/父级路由/子集路由xxxx/router-link !– 或 – router-link to{path:/父级路由/子集路由}xxxx/router-link路由传参 query参数 语法 router-link to/父级路由/子集路由?后面传递query参数xxxx/router-link使用方法 为了让query参数传递的是一个动态的值我们需要用到js的模版字符串来动态获取参数、 在to的前面加一个冒号是该句话变成表达式 router-link :to/父级路由/子集路由?后面传递query参数xxxx/router-link使用反引号 将to的值引起来然后在里面使用模版字符串\({} router-link :to/父级路由/子集路由?id\){获取id的值.id}xxxx/router-link如果后面还有参数可以使用拼接 router-link to/news/detail?a1b2content欢迎你跳转 /router-link精简写法使用to的对象写法 RouterLink :to{//name:xiang, //用name也可以跳转path:/news/detail,query:{id:news.id,title:news.title,content:news.content}} {{news.title}} /RouterLink获取query参数的方法 引入useRoute import {useRoute} from vue-router创建一个useRoute对象 let route useRoute()获取query参数 route.query.属性名还可以搭配toRefs解构出Route对象 templatepquery.属性名 !– 第三步去掉route –/p /templatescriptimport {useRoute} from vue-routerimport {toRefs} from vue //第一步导入toRefslet route useRoute()let {query} toRefs(route) //第二步搭配toRefs解构出Route对象 /scriptparams参数 语法 router-link to/父级路由/子集路由/后面传递params参数1/params参数2/…xxxx/router-link使用方法 传递参数 !– 跳转并携带params参数to的字符串写法 – RouterLink :to/news/detail/001/新闻001/内容001{{news.title}}/RouterLink!– 跳转并携带params参数to的对象写法 – RouterLink :to{name:xiang, //用name跳转只能用name不能用pathparams:{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参数时需要提前在规则中占位。 params传参不能传递对象和数组。 如果有些参数可传可不传则需要在路由规则中的占位后面加一个。 路由的props params参数作为props 作用让路由组件更方便的收到参数可以将路由收到得到所有params参数作为props传给路由组件。 使用方法直接在路由规则后面添加一个props;true。 children:[{path:子路由, //子集不用写/component:子路由props:true, } ]例子 直接使用 自定义props 自定义写法就可以指定是使用params参数还是query参数。 函数写法 作用把返回的对象中每一组key-value作为props传给路由组件 语法 props(route){return Route.query }例子 对象写法 作用把对象中的每一组key-value作为props传给路由组件 props:{a:100,b:200,… }不推荐❌这种写法因为会把数据写死。 replace属性 作用控制路由跳转时操作浏览器历史记录的模式。 语法在RouterLink标签中添加replace属性即可。 RouterLink replace …….News/RouterLink浏览器的历史记录有两种写入方式分别为push和replace push是追加历史记录默认值。replace是替换当前记录。 编程式导航 知识引入在上面的页面中都是使用RouterLink来实现页面跳转的但是RouterLink是vue里面的标签并不是浏览器中所谓的html标签所以浏览器无法识别只有通过vue最终将RouterLink转化为a标签。所以如果你只会使用RouterLink来实现跳转那么就说明你的页面全是a标签但是这样就会限制一些功能导致一些需求无法实现。所以这就引出了我们的编程式导航来实现。 简单来说就是脱离RouterLink实现跳转。 使用方法 导入useRouter import {useRouter} from vue-router调用路由器 const router useRouter()实现跳转 router.push(/路径) !–可以是push跳转或者replace跳转–例子实现一个定时器跳转 router.push()括号内的参数与RouterLink中的to写法一样 字符串写法对象写法 route和router 路由组件的两个重要的属性\(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)重定向 作用将特定的路径重新定向到已有路由。 语法 {path:/,redirect:/路径 }Pinia状态管理 多个组件共享数据才使用到pinia。 搭建pinia环境 搭建方法 安装pinia环境 npm install pinia引入pinia 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)存储数据 Store是一个保存状态、业务逻辑 的实体每个组件都可以读取、写入它。 它有三个概念state、getter、action相当于组件中的 data、 computed 和 methods。 使用方法 新建一个store文件夹用来存储数据 引入defineStore用于创建store import {defineStore} from pinia// 定义并暴露一个store export const useCountStore defineStore(count,{// 动作actions:{},// 状态state(){return {sum:6}},// 计算getters:{} })组件中使用state数据 templateh2当前求和为{{ sumStore.sum }}/h2 /templatescript setup langts nameCount// 引入对应的useXxxxxStore import {useSumStore} from /store/sum// 调用useXxxxxStore得到对应的storeconst sumStore useSumStore() /script修改数据 第一种方法直接修改法 countStore.sum 666第二种方法批量修改法 countStore.$patch({sum:999,school:atguigu })第三种方式借助action修改在action当中可以编写一些业务逻辑 import { defineStore } from piniaexport const useCountStore defineStore(count, {// actions里面放置的是一个一个的方法用于响应组件中的“动作”/actions: {increment(){console.log(increment被调用了,value)/}//加increment(value:number) {if (this.sum 10) {//操作countStore中的sumthis是当前的storethis.sum value}},//减decrement(value:number){if(this.sum 1){this.sum - value}}},/*********/ })然后再组件中调用action即可 // 使用countStore const countStore useCountStore()// 调用对应action countStore.incrementOdd(n.value)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 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)侦听state 通过store的\(subscribe()方法侦听state及其变化。 语法 store.\)subsctibe((mutate,state){// 方法体 })mutate本次修改的信息state真正的数据 应用场景 可以用来保存localStorage talkStore.\(subscribe((mutate,state){localStorage.setItem(talk,JSON.stringify(talkList.value)) })需要对store中的数据进行修改 原来 修改后 解决一开始没有数据的问题 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被砍掉了。 常见使用方法 props 概述props是使用频率最高的一种通信方式常用与 父 ↔ 子。 若 父传子属性值是非函数。若 子传父属性值是函数。 父传子案例 子传父案例 自定义事件 概述自定义事件常用于子 父。 使用方法 在父组件中给子组件绑定自定义事件 Child send-toytoy \(event/子组件声明事件 const emit defineEmits([send-toy])子组件中触发事件 区分原生事件和自定义事件 原生事件 事件名是特定的click、mosueenter等事件嗲了\)event是包含事件相关的信息对象pageX、pageY、target、keyCode 自定义事件 事件名是任意名称事件对象\(event是调用emit的时候所提供的数据可以是任意类型。 案例 事件名命名规范 官方推荐使用肉串命名法来命名事件名。 mitt 概述与消息订阅与发布pubsub功能类似可以实现任意组件间通信。 配置方法 安装mitt npm i mitt新建文件src\utils\emitter.ts用来存放mitt文件 编写mitt模版 // 引入mitt import mitt from mitt; // 创建emitter const emitter mitt() // 创建并暴露mitt export default emitter精简写法 import mitt from mitt; export default mitt()在main.js中引入emitter import emitter from /路径使用方法 提供数据的组件在合适的时候触发事件。 在组件卸载的时候最后解绑一下mitt事件这样做的目的是减小内存。 onUnmounted((){emitter.off(事件名) })mitt绑定事件 语法 emitter.on(事件名,(){//方法体 })mitt触发事件 语法 emitter.emit(事件名)mitt解绑事件 语法 emitter.off(事件名) //全部解绑 emitter.all.clear()\)attrs 概述\(attrs是一个对象包含所有父组件传入的标签属性\)attrs用于实现当前组件的父组件向当前组件的子组件通信祖→孙。 使用方法 r e f s 、 refs、 refs、parent 概述 \(refs值为对象包含所有被ref属性标识的DOM元素或组件实例。【用于 父→子】\)parent值为对象当前组件的父组件实例对象。【用于子→父】 祖孙通信——provide、inject 概述实现祖孙组件直接通信。 使用方法 在祖先组件中插入provide向后代提供数据 import {provide} from vue; //导入provide provide(变量标识名,变量) //一般来说变量标识名和变量名相同在后代组件中是通过inject配置来声明接收数据 import {inject} from vue; //导入inject let 接收变量名 inject(变量标识名,吗默认值) //一般来说接收变量名和变量标识名相同v-model (了解) ??这一部分没看懂。 概述实现 父↔子 之间相互通信经常用自定义UI组件库。 前序知识 —— 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/?? \(event到底是啥啥时候能用.target 对于原生事件\)event就是事件对象–能用.target对于自定义事件$event就是触发事件所以传递的是数据–不能.target 插槽 默认插槽 前置知识 具名插槽 语法 组件名 v-slot:插槽名称标签结构/组件名推荐写法 组件名template v-slot:插槽名称标签结构/template /组件名使用 slot name插槽名称内容/slot语法糖#代表v-slot: 作用域插槽 概述数据在组件的自身单根据数据生成的结构需要组件的使用者来决定。UI组件库大量使用作用域插槽来实现。 使用方法 在子组件的插槽中传值给父组件 slot :传值变量 /slot父组件中接收插槽穿过来的数据 template v-slotparams//标签内容 /template案例 父组件中Game v-slotparams!–可以直接解构出来Game v-slot{games}–!– Game v-slot:defaultparams –!– Game #defaultparams –ulli v-forg in params.games :keyg.id{{ g.name }}/li!–解构出来使用v-forg in games–/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其他API shallowRef与shallowReactive shallowRef 作用创建一个响应式数据但只对顶层属性进行响应式处理。 语法 let 变量名 shallowRef()特点只跟踪引用值的变化不关心值内部的属性变化。 案例 shallowReactive 作用创建一个浅层响应式对象只会使对象的最顶层属性变成响应式的对象内部的嵌套属性则不会变成响应式的。 语法 const myObj shallowReactive({ … });特点对象的顶层属性是响应式的但嵌套对象的属性不是。比shallowRef性能比较好。 总结摘自官方 通过使用 shallowRef() 和 shallowReactive() 来绕开深度响应。浅层式 API 创建的状态只在其顶层是响应式的对所有深层的对象不会做任何处理避免了对每一个内部属性做响应式所带来的性能成本这使得属性的访问变得更快可提升性能。 readonly和shallowReadonly readonly 作用用于创建一个对象的深只读副本。 语法readonly(变量名) 使用方法 const original reactive({ … }); const readOnlyCopy readonly(original);特点 对象的所有的嵌套属性都将变为只读任何仓储处修改这个对象的操作都会被阻止在开发模式下还会在控制台发出警告 应用场景 创建不可变的状态快照。保护全局状态或配置不被修改 shallowReadonly 作用与readonly类似但只有作用于对象的顶层属性。 语法shallowReadonly() 使用方法 const original reactive({ … }); const shallowReadOnlyCopy shallowReadonly(original);特点 只将对象的顶层属性设置为只读对象内部的嵌套属性仍然可变。适用于只需保护对象顶层属性的场景。 toRaw和markRaw toRaw 作用用于获取一个响应式对象的原始对象 toRaw 返回的对象不再是响应式的不会触发视图更新。 案例 使用场景 在需要将响应式对象传递给非 Vue 的库或外部系统时使用 toRaw 可以确保它们收到的是普通对象。 markRaw 作用标记一个对象使其永远不会变成响应式。 语法markRaw() 案例 /* markRaw */ let citys markRaw([{id:asdda01,name:北京},{id:asdda02,name:上海},{id:asdda03,name:天津},{id:asdda04,name:重庆} ]) // 根据原始对象citys去创建响应式对象citys2 —— 创建失败因为citys被markRaw标记了 let citys2 reactive(citys)自定义Ref 引入使用Vue提供的默认ref定义响应式数据数据一变页面就更新 作用创建一个自定义的ref并对其依赖项跟踪和更新触发进行逻辑控制。 语法customRef() 模版 let msg customRef((track,trigger){ // track跟踪、trigger触发return{// get在被msg读取的时候调用get(){track() //告诉Vue数据msg很重要你要对msg进行持续关注一旦msg变化就去更新return 返回值},// set在被msg修改的时候调用set(value){ //value是修改值//方法体trigger() //通知Vue一下数据msg变化了}} })实现一个防抖效果Hooks import {customRef } from vue; // initValue初始时间 // delay:延迟时间 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} }使用Hooks Teleport传送?? 概念Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。 案例 teleport tobody div classmodal v-showisShowh2我是一个弹窗/h2p我是弹窗中的一些内容/pbutton clickisShow false关闭弹窗/button/div /teleportSuspense?? 等待异步组件时渲染一些额外内容让应用有更好的用户体验。 使用步骤 异步引入组件使用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 /template全局API转移到应用对象 app.component全局注册组件 app.config全局配置对象 app.directive注册全局指令 app.mount挂载应用 app.unmount卸载应用 app.use安装插件