营销型网站(易网拓),江苏网站建设教程,网站开发经理岗位职责,网站制作的重要性及步骤详解目录 1.Vue3简介1.1.性能提升1.2.源码升级1.3.拥抱TypeScript1.4.新特性 2.创建Vue3工程2.1.基于 vue-cli 创建2.2. 基于 vite 创建#xff08;推荐#xff09;2.3.代码运行 3.Vue3核心语法3.1.OptionsAPI(选项式API) 与 CompositionAPI(组合式API)3.2.setup3.3.ref 创建推荐2.3.代码运行 3.Vue3核心语法3.1.OptionsAPI(选项式API) 与 CompositionAPI(组合式API)3.2.setup3.3.ref 创建基本类型的响应式数据3.4 reactive 创建对象类型的响应式数据3.5 ref 创建对象类型的响应式数据3.6 ref 对比 reactive3.7.toRefs 与 toRef3.8.computed3.9.watch3.10 watchEffect3.11.标签ref属性3.12.TS接口、自定义类型、范型3.13.props3.14.生命周期3.15.自定义hook 4.路由4.1. 对路由的理解4.2.基本切换效果4.3.两个注意点4.4.路由器工作模式4.5.命名路由4.6.to的两种写法4.7.嵌套路由4.8.路由传参4.9.路由的 props配置4.10.replace属性4.11.编程式导航4.12.重定向 5.pinia5.1.准备案例代码5.2.搭建 pinia 环境5.3.存储读取数据5.4.修改数据三种方式5.5.storeToRefs5.6.getters5.7.$subscribe5.8.store组合式写法 6.组件通信6.1.props6.2.自定义事件6.3.mitt6.4.v-model6.5.$attrs6.6.$ refs、$parent6.7.provide、inject6.8.slot 7.其它API7.1. shallowRef 与 shallowReactive7.2.readonly 与 shallowReadonly7.3.toRaw 与 markRaw7.4.customRef 8.Vue3新组件8.1.teleport8.2.Suspense8.3.全局API转移到应用对象 1.Vue3简介
2020年9月18日Vue.js发布版3.0版本代号One Piece海贼王经历4800次提交、40个RFC、600次PR、300贡献者官方发版地址Release V3.0.0 One Piece vuejs/core
1.1.性能提升
打包体积减少41%初次渲染快55%更新渲染快133%内存减省54%
1.2.源码升级
使用Proxy代替defineProperty实现响应式重写虚拟DOM的实现和Tree-Shaking
1.3.拥抱TypeScript
Vue3可以更好的支持TypeScript
1.4.新特性
Composition API 组合API新内置组件其它
2.创建Vue3工程
2.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## 选择Vue 3
## Vue CLI v5.0.8
## ? Please pick a preset: (Use arrow keys)
## Default ([Vue 3] babel, eslint)
## Default ([Vue 2] babel, eslint)
## Manually select features## 启动
cd vue_test
npm run serve
2.2. 基于 vite 创建推荐
vite 是新一代前端构建工具官网地址https://vitejs.cnvite的优势如下
轻量快速的热得载HMR能实现极速的服务启动对 TypeScript、JSX、CSS 等支持开箱即用真正的按需编译不再等待整个应用编译完成webpack构建 与 vite 构建对比图如下
Bundle Based dev server #mermaid-svg-SbGsMbXjoy00Pq8d {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SbGsMbXjoy00Pq8d .error-icon{fill:#552222;}#mermaid-svg-SbGsMbXjoy00Pq8d .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SbGsMbXjoy00Pq8d .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-SbGsMbXjoy00Pq8d .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SbGsMbXjoy00Pq8d .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SbGsMbXjoy00Pq8d .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SbGsMbXjoy00Pq8d .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SbGsMbXjoy00Pq8d .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SbGsMbXjoy00Pq8d .marker.cross{stroke:#333333;}#mermaid-svg-SbGsMbXjoy00Pq8d svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SbGsMbXjoy00Pq8d .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-SbGsMbXjoy00Pq8d .cluster-label text{fill:#333;}#mermaid-svg-SbGsMbXjoy00Pq8d .cluster-label span{color:#333;}#mermaid-svg-SbGsMbXjoy00Pq8d .label text,#mermaid-svg-SbGsMbXjoy00Pq8d span{fill:#333;color:#333;}#mermaid-svg-SbGsMbXjoy00Pq8d .node rect,#mermaid-svg-SbGsMbXjoy00Pq8d .node circle,#mermaid-svg-SbGsMbXjoy00Pq8d .node ellipse,#mermaid-svg-SbGsMbXjoy00Pq8d .node polygon,#mermaid-svg-SbGsMbXjoy00Pq8d .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SbGsMbXjoy00Pq8d .node .label{text-align:center;}#mermaid-svg-SbGsMbXjoy00Pq8d .node.clickable{cursor:pointer;}#mermaid-svg-SbGsMbXjoy00Pq8d .arrowheadPath{fill:#333333;}#mermaid-svg-SbGsMbXjoy00Pq8d .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SbGsMbXjoy00Pq8d .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SbGsMbXjoy00Pq8d .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-SbGsMbXjoy00Pq8d .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-SbGsMbXjoy00Pq8d .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SbGsMbXjoy00Pq8d .cluster text{fill:#333;}#mermaid-svg-SbGsMbXjoy00Pq8d .cluster span{color:#333;}#mermaid-svg-SbGsMbXjoy00Pq8d div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SbGsMbXjoy00Pq8d :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} entry route route ... module module module module ... Bundle Server ready Native ESM based dev server #mermaid-svg-wMPiE2j4DZGQSm8d {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-wMPiE2j4DZGQSm8d .error-icon{fill:#552222;}#mermaid-svg-wMPiE2j4DZGQSm8d .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wMPiE2j4DZGQSm8d .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-wMPiE2j4DZGQSm8d .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wMPiE2j4DZGQSm8d .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wMPiE2j4DZGQSm8d .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wMPiE2j4DZGQSm8d .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wMPiE2j4DZGQSm8d .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wMPiE2j4DZGQSm8d .marker.cross{stroke:#333333;}#mermaid-svg-wMPiE2j4DZGQSm8d svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wMPiE2j4DZGQSm8d .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wMPiE2j4DZGQSm8d .cluster-label text{fill:#333;}#mermaid-svg-wMPiE2j4DZGQSm8d .cluster-label span{color:#333;}#mermaid-svg-wMPiE2j4DZGQSm8d .label text,#mermaid-svg-wMPiE2j4DZGQSm8d span{fill:#333;color:#333;}#mermaid-svg-wMPiE2j4DZGQSm8d .node rect,#mermaid-svg-wMPiE2j4DZGQSm8d .node circle,#mermaid-svg-wMPiE2j4DZGQSm8d .node ellipse,#mermaid-svg-wMPiE2j4DZGQSm8d .node polygon,#mermaid-svg-wMPiE2j4DZGQSm8d .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wMPiE2j4DZGQSm8d .node .label{text-align:center;}#mermaid-svg-wMPiE2j4DZGQSm8d .node.clickable{cursor:pointer;}#mermaid-svg-wMPiE2j4DZGQSm8d .arrowheadPath{fill:#333333;}#mermaid-svg-wMPiE2j4DZGQSm8d .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wMPiE2j4DZGQSm8d .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wMPiE2j4DZGQSm8d .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-wMPiE2j4DZGQSm8d .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-wMPiE2j4DZGQSm8d .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wMPiE2j4DZGQSm8d .cluster text{fill:#333;}#mermaid-svg-wMPiE2j4DZGQSm8d .cluster span{color:#333;}#mermaid-svg-wMPiE2j4DZGQSm8d div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wMPiE2j4DZGQSm8d :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HTTP request Dynamic import code split point Server ready entry route route ... module module module module module 具体操作如下
## 1.创建命令
npm create vuelatest
## 2.具体配置
## Need to install the following packages:
## create-vue3.14.2
Ok to proceed? (y) y
## 请输入项目名称
Project namevue3-project
## 是否使用 TypeScript 语法
Add TypeScript? Yes
## 是否启用 JSX 支持
Add JSX Support? No
## 是否引入 Vue Router 进行单页面应用开发
Add Vue Router for single Page Application development? No
## 是否引入 Pinia 用于状态管理
Add Pinia for state management? No
## 是否引入 Vitest 用于单元测试
Add Vitest for Unit Testing? No
## 是否要引入一款端到端End to End测试工具
Add an End-to-End Testing Solution? No
## 是否引入 ESLint 用于代码质量检测
Add ESLint for code quality? Yes
## 是否引入 Prettier 用于代码格式化
Add Prettier for code formatting? No文件作用 index.html 入口文件引入 /src/main.ts package.json 项目的元数据文件包括项目名称、版本、描述、作者、依赖项等。定义了项目的脚本如启动、构建、测试等命令。 public/ 存放静态资源如HTML模板index.html、图片、图标等。这些文件在构建时会被复制到输出目录通常是dist/并且可能通相对咱径在项目中引用。 src/ 项目源代码目录。main.js/main.ts项目入口文件用于创建Vue实例并挂载到DOM上。App.vue主组件文件作为所有页面组件的容器。comments/存放Vue组件的文件夹这些组件可以在整个项目中复用。assets/存放项目中会使用的静态资源如图片、字体、样式文件等。这些资源在构建时会被处理如压缩、转换等。router/如果项目使用Vue Router进行路由管理则此文件夹包含路由的配置文件如index.js 或 index.ts定义了前端路由的映射关系。store/如果项目使用Vuex进行状态管理则此文件夹包含Vuex的配置文件如 index.ts或index.ts用于管理应用的所有组件的状态。views/在Vue CLI 3 的项目中这个文件夹通常用来丰防御页面级的组件即路由对应的组件。 vite.config.ts 配置文件用于修改webpack配置、添加新的loader选项、配置代理等。 .gitignore Git版本控制忽略文件指定哪些文件或文件夹不需要纳入GIt版本控制 tsconfig.json TypeScript的配置文件定译了TypeScript编译器的选项。
2.3.代码运行
项目入口文件 index.html
!-- index.html相关代码 --!-- 创建idapp 的容器 --div idapp/div!-- 引入 /src/main.ts --script typemodule src/src/main.ts/script引入 /src/main.ts
// maint.ts 相关代码
// 引入 createApp 创建应用
import { createApp } from vue
// 引入 App 根组件
import App from ./App.vue
// createApp(App)以App作为参数生成一个应用实例对象
// mount(#app)挂载到idapp节点上。
createApp(App).mount(#app)3.Vue3核心语法
3.1.OptionsAPI(选项式API) 与 CompositionAPI(组合式API)
Vue2的API设计是Options选项风格Vue3的API设计是CompositionAPI组合风格 OptionsAPI 的弊端 Options类型的API数据、方法、计算属性等是分散在data、methods、computed中的若想新境或者改一个需求就需要分别修改data、methods、computed不便于维护和复用。 CompositionAPI 的优势 可以用函数的方式更加优雅的组织代码让相关功能的代码更加有序的组织在一起。
3.2.setup
setup 概述 setup 是 Vue3 中一个新的配置项值是一个函数组件中所用到的数据、方法、计算属性、监视等均配置在setup中。 特点如下
setup函数返回的对象中的内容可直接在模板中使用setup中访问this是undefinedsetup函数会在beforeCreate之前调用它是“领先”所有钩子执行的。
templateh2姓名{{name}}/h2br/h2年龄{{age}}/h2br/button clickchangeName修改名字/buttonbr/button clickchangeAge修改年龄/buttonbr/button clickshowTel查看电话/button
/template
script langtsexport default{name:Person,setup(){let name 张三let age 18let tel 13888888888function changeName(){name zhangsanconsole.log(name)}function changeAge(){age 1console.log(age)}function showTel(){alert(tel)}return {name,age,changeAge,changeName,showTel}// setup的返回值也可以是函数// return function(){return hello}// return (){return hello} //简写// return ()hello //简写}}
/script
style
/stylesetup 与 OptionsAPI的关系
Vue2 的选项data、methods 等中可以访问到setup中的属性、方法。但在setup中不能访问Vue2的选项data、methods 等。如果与Vue2冲突则setup优先
setup语法糖 setup 可以独立出来
templateh2姓名{{name}}/h2br/h2年龄{{age}}/h2br/button clickchangeName修改名字/buttonbr/button clickchangeAge修改年龄/buttonbr/button clickshowTel查看电话/button
/templatescript langtsexport default{name:Person}
/script
script langts setuplet name 张三let age 18let tel 13888888888function changeName(){name zhangsanconsole.log(name)}function changeAge(){age 1console.log(age)}function showTel(){alert(tel)}
/script
style
/style扩展上述代码还需要编写一个不写setup的script标签去指定组件名字比较麻烦我们可以借助vite中的插件简化
第一步npm i vite-plugin-vue-setup-extend -D第二步vite.config.ts
import VueSetupExtend form vite-plugin-vue-setup-extend//增加引入代码
export default defineConfig({plugins: [VueSetupExtend(),//增加使用代码]
})第三步使用 name“组件名”
//使用方法
script setup langts namePerson
/script3.3.ref 创建基本类型的响应式数据
作用定义响应式变量语法let xxx ref(初始值)返回值RefImpl的实例对象简称ref对象或refref对象的value属性是响应式的。注意点 JS中操作数据需要xxx.value但模板中不需要.value直接使用即可。对于let name ref(张三’) 来说name不是响应式的name.value是响应式的
templateh2姓名{{name}}/h2br/h2年龄{{age}}/h2br/button clickchangeName修改名字/buttonbr/button clickchangeAge修改年龄/buttonbr/button clickshowTel查看电话/button
/template
script langts setup namePerson1133import {ref} from vue//name和age是一个RefImpl的实例对象简称ref对象它们的value属性是响应式的。let name ref(张三)let age ref(18)let tel 13888888888function changeName(){name.value zhangsanconsole.log(name)}function changeAge(){age.value 1console.log(age)}function showTel(){alert(tel)}
/script
style
/style3.4 reactive 创建对象类型的响应式数据
作用定义 响应式对象 语法let 响应式对象 reactive(源对象) 返回值Proxy的实例对象简称响应式对象 注意点reactive 定义的响应式数据是“深层次”的reactive 会自动解包ref数据
templateh2姓名{{person.name}}/h2br/h2年龄{{person.age}}/h2br/button clickchangeName修改名字/buttonbr/button clickchangeAge修改年龄/buttonbr/button clickshowTel查看电话/button
/template
script langts setup namePerson1133import {reactive} from vuelet person reactive({name:张三,age:18,tel:13888888888})console.log(person)function changeName(){person.name zhangsanconsole.log(person.name)}function changeAge(){person.age 1console.log(person.age)}function showTel(){alert(person.tel)}
/script
style
/style3.5 ref 创建对象类型的响应式数据
ref 接收的数据可以是基本类型、对象类型若ref接收的是对象类型内部其实也是调用了reactive函数
templateh2姓名{{person.name}}/h2br/h2年龄{{person.age}}/h2br/button clickchangeName修改名字/buttonbr/button clickchangeAge修改年龄/buttonbr/button clickshowTel查看电话/button
/template
script langts setup namePerson1133import {ref} from vuelet person ref({name:张三,age:18,tel:13888888888})console.log(person)function changeName(){person.value.name zhangsanconsole.log(person.value.name)}function changeAge(){person.value.age 1console.log(person.value.age)}function showTel(){alert(person.value.tel)}
/script
style
/style3.6 ref 对比 reactive
宏观角度看
ref用来定义基本数据类型、对象类型数据reactive用来定义对象类型数据。 区别ref创建的变量必须使用.value可以使用vscode 中的volar插件自动添加.value。reactive 重新分配一个新对象会失去响应式可以使用Object.assign 去整体替换。 使用原则若需要一个基本类型的响应式数据必须使用ref。若需要一个响应式对象层级不深ref、reactive 都可以。若需要一个响应式对象且层级较深推荐使用 reactive。
templateh2姓名{{personRef.name}}/h2br/h2年龄{{personRef.age}}/h2br/button clickchangePersonRefref修改/buttonbr/h2姓名{{personReactive.name}}/h2br/h2年龄{{personReactive.age}}/h2br/button clickchangePersonReactivereactive修改/buttonbr/
/template
script langts setup namePerson1133import {ref,reactive} from vuelet personRef ref({name:张三,age:18,tel:13888888888})let personReactive reactive({name:李四,age:19,tel:13888888888})function changePersonRef(){// personRef ref({name:张三ref,age:28,tel:13888888888}) //不行不是响应式personRef.value {name:张三ref,age:28,tel:13888888888}}function changePersonReactive(){// personReactive reactive({name:张三ref,age:28,tel:13888888888}) //不行不是响应式Object.assign(personReactive,{name:李四reactive,age:29,tel:13888888888})}
/script
style
/style3.7.toRefs 与 toRef
作用将一个响应式对象中的每一个属性转换为ref对象备注toRefs 与 toRef 功能一致但 toRefs 可以批量替换
templateh2姓名{{name}}/h2br/h2年龄{{person.age}}/h2br/button clickchangeName修改名字/buttonbr/button clickchangeAge修改年龄/buttonbr/button clickshowTel查看电话/button
/template
script langts setup namePerson1133import {reactive,toRefs,toRef} from vuelet person reactive({name:张三,age:18,tel:13888888888})let {name,age} toRefs(person)let nl toRef(person,age)console.log(person)function changeName(){name.value zhangsanconsole.log(name)}function changeAge(){age.value 1console.log(age)}function showTel(){alert(person.tel)}
/script
style
/style3.8.computed
template!-- :valuev-bind:value是单向绑定数据流向页面v-modelv-model:value是双向绑定 --!-- 姓input typetext :valuefirstName --姓input typetext v-modelfirstNamebr名input typetext v-modellastNamebr全名span{{fullName}}/spanbutton clickchangeFullName修改成li-si/button
/template
script langts setup namePersonimport {ref,computed} from vuelet firstName ref(zhang)let lastName ref(san)//定义的fullName是一个计算属性且是只读的//let fullName computed((){// return firstName.value.slice(0,1).toUpperCase() firstName.value.slice(1) - lastName.value//})//定义的fullName是一个计算属性可读可写let fullName computed({get(){return firstName.value.slice(0,1).toUpperCase() firstName.value.slice(1) - lastName.value},set(val){const [str1,str2] val.split(-)firstName.value str1lastName.value str2}})function changeFullName(){fullName.value li-si}
/script
style
/style3.9.watch
作用监视数据变化特点只能监视以下四种数据 ref 定义的数据。reactive 定义的数据。函数返回一个值getter 函数。一个包含上数内容的数组。
情况一 监视 ref 定义的【基本类型】数据直接写数据名即可监视的是其value值的改这。
templatedivh1sun值{{ sum }}/h1button clickchangeSumsum/button/div
/template
script langts setup nameHelloWorld
import { ref, watch } from vue;
//数据
const sum ref(1)
//方法
function changeSum(){sum.value
}
//监视
const stopWathc watch(sum,(newSum,oldSum){console.log(newSum,oldSum)if(newSum 10){stopWathc()}
})
console.log(stopWathc)
/script
style
/style
情况二 监视ref定义的【对象类型】数据直接写数据名监视的是对象的【地址值】若想监视对象内部的数据要手动开启深度监视。
注意
- 若修改的是ref定义的对象的属性newValue 和 oldValue 都是新值因为它们是同一个对象。
- 若修改整个ref定义的对象newValue 是新值oldValue 是旧值因为不是同一个对象了。templatedivh1姓名{{ person.name }}/h1h1年龄{{ person.age }}/h1button clickchangeName修改姓名/buttonbrbutton clickchangeAge修改年龄/buttonbrbutton clickchangePerson修改人/button/div
/template
script langts setup nameHelloWorld
import { ref, watch } from vue;
const person ref({name:张三,age:18
})
function changeName(){person.value.name ~
}
function changeAge(){person.value.age
}
function changePerson(){person.value {name:李四,age:28}
}
/*
参数1被监视的数据
参数2监视的回调
参数3配置对象deep深度监视、immediate立即执行 等
*/
watch(person,(newVal,oldVal){console.log(newVal,oldVal)
},{deep:true})/script
style
/style
情况三 监视 reactive 定义的【对象类型】数据默认开启深度监视。
templatedivh1姓名{{ person.name }}/h1h1年龄{{ person.age }}/h1button clickchangeName修改姓名/buttonbrbutton clickchangeAge修改年龄/buttonbrbutton clickchangePerson修改人/button/div
/template
script langts setup nameHelloWorld
import { reactive, watch } from vue;
const person reactive({name:张三,age:18
})
function changeName(){person.name ~
}
function changeAge(){person.age
}
function changePerson(){Object.assign(person,{name:李四,age:28})
}
//新值、旧值一样
watch(person,(newVal,oldVal){console.log(newVal,oldVal)
})
/script
style
/style清况四 监视 ref 或 reactive 定义的【对象类型】数据中的某个属性注意如下
若该属性值不是【对象类型】需要写成函数形式。若该属性值是【对象类型】可以直接写或写成涵数形式建议写成函数。
结论监视对象的属性建议写成函数形式。若属性是对象则监视的是地址值 如果要监视对象内部则需要开启深度监视
templatedivh1姓名{{ person.name }}/h1h1年龄{{ person.age }}/h1h1汽车{{ person.car.c1 }}、{{ person.car.c2 }}/h1button clickchangeName修改姓名/buttonbrbutton clickchangeAge修改年龄/buttonbrbutton clickchangeC1修改车一/buttonbrbutton clickchangeC2修改车二/buttonbrbutton clickchangeCar修改车/button/div
/template
script langts setup nameHelloWorld
import { reactive, watch } from vue;
const person reactive({name:张三,age:18,car:{c1:车一,c2:车二}
})
function changeName(){person.name ~
}
function changeAge(){person.age
}
function changeC1(){person.car.c1 车车一
}
function changeC2(){person.car.c2 车车二
}
function changeCar(){person.car {c1:一,c2:二}
}
//监视一个属性基本类型
watch(()person.name,(newVal,oldVal){console.log(newVal,oldVal)
})
//监视一个属性对象类型
//结论监视对象的属性建议写成函数形式。若属性是对象则监视的是地址值 如果要监视对象内部则需要开启深度监视
watch(()person.car,(newVal,oldVal){console.log(newVal,oldVal)
},{deep:true})
/script
style
/style情况五 监视上述 多个数据
templatedivh1姓名{{ person.name }}/h1h1年龄{{ person.age }}/h1h1汽车{{ person.car.c1 }}、{{ person.car.c2 }}/h1button clickchangeName修改姓名/buttonbrbutton clickchangeAge修改年龄/buttonbrbutton clickchangeC1修改车一/buttonbrbutton clickchangeC2修改车二/buttonbrbutton clickchangeCar修改车/button/div
/template
script langts setup nameHelloWorld
import { reactive, watch } from vue;
const person reactive({name:张三,age:18,car:{c1:车一,c2:车二}
})
function changeName(){person.name ~
}
function changeAge(){person.age
}
function changeC1(){person.car.c1 车车一
}
function changeC2(){person.car.c2 车车二
}
function changeCar(){person.car {c1:一,c2:二}
}
watch([()person.name,()person.car.c1],(newVal,oldVal){console.log(newVal,oldVal)
},{deep:true})
/script
style
/style3.10 watchEffect
立即运行一个函数同时响应式地追踪其依赖并在依赖更改时得新执行该函数。watch 对经 watchEffect 都能监听响应式数据的变化但是监听数据变化的方式不同watch要明确指出监视的数据watchEffect不用明确指出监视的数据函数中用到哪些属性那就监视哪些属性
templatedivh1姓名{{ person.name }}/h1h1年龄{{ person.age }}/h1button clickchangeName修改姓名/buttonbrbutton clickchangeAge修改年龄/buttonbrbutton clickchangePerson修改人/button/div
/template
script langts setup nameHelloWorld
import { reactive, watchEffect } from vue;
const person reactive({name:张三,age:18
})
function changeName(){person.name ~
}
function changeAge(){person.age
}
function changePerson(){Object.assign(person,{name:李四,age:28})
}
//新值、旧值一样
watchEffect((){console.log(person.age)if(person.age 35){console.log(35了)}
})
/script
style scoped
/style3.11.标签ref属性
作用用于注册模板引用。
用在普通DOM标签上获取的是DOM节点用在组件标签上获取的是组件实例对象
用在普通DOM标签上
templatedivh1中国/h1h2 refh2北京/h2button clickgetH2获取h2标签实例/buttonbr/div
/template
script langts setup nameHelloWorld
import { ref,defineExpose } from vue;
const h2 ref()
function getH2(){console.log(h2.value)//h2 data-v-e17ea971北京/h2//data-v-e17ea971 是因为 style scoped 局部样式导致的
}
const a ref(0)
const b ref(1)
const c ref(2)
defineExpose({a,b})
/script
style scopedbutton{color: red;}
/style用在组件标签上
templateHelloWorld refhw/button clickgetHw获取HelloWorld实例/button
/template
script langts setup nameAppimport HelloWorld from ./components/HelloWorld.vueimport {ref} from vueconst hw ref()function getHw(){console.log(hw.value)console.log(hw.value.a)}
/script
style scoped
/style3.12.TS接口、自定义类型、范型
路径src/types/index.ts
//定义一个接口用于限制pesson对象的具体属性
export interface PersonInter {id:string,name:string,age:number,phone?:string //?表示可有可无
}
//自定义类型
//方法1
// export type PersonArr ArrayPersonInter
//方法2
export type PersonArr PersonInter[]templatediv/div
/template
script langts setup nameHelloWorld
import { type PersonInter,type PersonArr } from /types;
const person:PersonInter {id:doof01,name:张三,age:19}
const personArr:PersonArr [
{id:doof01,name:张三,age:19},
{id:doof01,name:张三,age:19,phone:13888888888}
]
console.log(person)
console.log(personArr)
/script
style scopedbutton{color: red;}
/style
3.13.props
/src/App.vue
templateHelloWorld :plpersonList/
/template
script langts setup nameAppimport { reactive } from vue;
import HelloWorld from ./components/HelloWorld.vue
import type { PersonArr } from ./types;// Ts限制// const personList:PersonArr reactive([const personList reactivePersonArr([{id:01,name:张三,age:18},{id:02,name:李四,age:19,phone:13888888888}])/script
style scoped
/style
/src/components/HellowWorld.vue
templatedivulli v-foritem in pl :keyitem.id{{ item.id }} -- {{ item.name }}/li/ul/div
/template
script langts setup nameHelloWorld
import { type PersonArr } from /types;//只接收pl(接收后页面可使用但js不能用)
// defineProps([pl])//接收pl限制类型
// defineProps{pl:PersonArr}()//接收pl限制类型限制必要性指定默认值
withDefaults(defineProps{pl?:PersonArr}(),{pl:()[{id:03,name:王五,age:18}]
})//接收后页面可使用js也要用
// const res defineProps([pl])
// console.log(res.pl)/script
style scoped
/style
3.14.生命周期 概念Vue组件实例在创建时要经历一系例的禄始化步骤在此过程中Vue会在合适的时机调味用特定的函数从而让开发者有机会在特定阶段运行自己的代码这些特定的函数统称为生命周期钩子 规律 生命周期整体分为四个阶段分别是创建、挂载、更新、销毁第个阶段都有两个钩子一前一后。 Vue2的生命周期 创建阶段beforeCreate、created挂载阶段beforeMount、mounted更新阶段beforeUpdate、updated销毁阶段beforeDestory、destroyedVue3的生命周期 创建阶段setup挂载阶段onBeforeMount、onMounted更新阶段onBeforeUpdate、onUpdated卸载阶段onBeforeUnmount!-- /src/App.vue --
templateHelloWorld refhw v-ifshowHelloWorld/button clickchangeHw卸载HelloWorld/button
/template
script langts setup nameAppimport { ref } from vueimport HelloWorld from ./components/HelloWorld.vueconst showHelloWorld ref(true)function changeHw(){showHelloWorld.value !showHelloWorld.value}
/script
style scoped
/style!-- /src/components/HelloWorld.vue --
templatedivh1{{ sum }}/h1button clickchangeSum更新/button/div
/template
script langts setup nameHelloWorld
import { ref,onBeforeMount, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted } from vue;
onBeforeMount((){console.log(挂载前)
})
onMounted((){console.log(挂载后)
})
onBeforeUpdate((){console.log(更新前)
})
onBeforeUpdate((){console.log(更新后)
})
onBeforeUnmount((){console.log(卸载前)
})
onUnmounted((){console.log(卸载后)
})
const sum ref(0)
function changeSum(){sum.value
}
/script
style scoped
/style3.15.自定义hook
本质是一个函数把setup函数使用的 Composition API 进行封装类似于 vue2.x 中的mixin优势复用代码让setup中逻辑更清楚易懂。
/src/components/HelloWorld.vue
templatedivh1{{ sum }}/h1button clickchangeSumsum/buttonbrimg v-for(item,index) in imgArr :keyindex :srcitem/imgbutton clickgetImg加载一张图片/button/div
/template
script langts setup nameHelloWorldimport useSum from /hooks/useSum;import useDog from /hooks/useDog;const {sum,changeSum} useSum()const {imgArr,getImg} useDog()
/script
style scoped
/style/src/hooks/useDog.ts
templateHelloWorld/
/template
script langts setup nameAppimport HelloWorld from ./components/HelloWorld.vue
/script
style scoped
/style/src/hooks/useSum.ts
import {ref} from vue
export default function(){const sum ref(0)function changeSum(){sum.value}return {sum,changeSum}
}
4.路由
4.1. 对路由的理解
4.2.基本切换效果
Vue3 中要使用 vue-router 的最新版本
/src/router/index.ts
// 创建一个路由器并暴露出去
// 第一步引入crateRouter
import {createRouter,createWebHashHistory} from vue-router
// 引入组件
import Home from /components/Home.vue
import News from /components/News.vue
import About from /components/About.vue
// 第二步创建路由器
const router createRouter({history:createWebHashHistory(),//路由器工作模式routes:[{path:/home,component:Home},{path:/news,component:News},{path:/about,component:About},]
})
export default router/src/main.ts
import ./assets/main.css
// 引入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)/src/App.vue
templatediv classapph1vue3路由测试/h1!-- 导航区 --div classnavigateRouterLink to/home active-classaClass首页/RouterLinkRouterLink to/news active-classaClass新闻/RouterLinkRouterLink to/about active-classaClass关于/RouterLink/div!-- 展示区 --div classmain-contentRouterView/RouterView/div/div
/template
script langts setup nameAppimport { RouterView,RouterLink } from vue-router;
/script
style scoped.aClass{color: red;}
/style/src/components/Home.vue
templatedivh2首页/h2/div
/template
script langts nameHome
/script
style scoped/style/src/components/News.vue
templatedivh2新闻/h2/div
/template
script langts nameNews
/script
style scoped/style/src/components/About.vue
templatedivh2关于/h2/div
/template
script langts nameAbout
/script
style scoped/style
4.3.两个注意点
路由组件通常放在pages 或 views 文件夹一般组件通常放在components 文件夹。通过点击导航视觉效果上“消失”了路由组件默认是被销毁掉的需要的时候再去挂载。
4.4.路由器工作模式
history 模式 优点URL更加美观不带有#更接近传的网站URL。 缺点后期项目上线需要服务端配合处理路径问题否则刷新会有404错误
const router createRouter({history:createWebHistory(),//history模式/******/
})hash 模式 优点兼容性更好因为不需要服务器端处理路径 缺点URL带有#不太美观且在SEO优化方面相对较差
const router createRouter({history:createWebHashHistory(),//hash模式/*******/
})4.5.命名路由
作用可以简化路由跳转及传参 给路由规则命名
routes:[{name:home,path:/home,component:Home},{name:news,path:/news,component:News},{name:about,path:/about,component:About},
]4.6.to的两种写法
!-- 1.to的字符串写法 --
router-link active-classactive to/home主页/router-link
!-- 2.to的对象写法 --
router-link active-classactive :to{path:/home}主页/router-link
router-link active-classactive :to{name:home }主页/router-link4.7.嵌套路由
routes:[{name:home,path:/home,component:Home},{name:news,path:/news,component:News,children:[ //嵌套路由{path:detail,component:Detail}]},{name:about,path:/about,component:About},
]4.8.路由传参
query传参
路由设置
routes:[{name:home,path:/home,component:Home},{name:news,path:/news,component:News,children:[ //嵌套路由{path:detail,component:Detail}]},{name:about,path:/about,component:About},
]传递参数
RouterLink
to/news/detail?id${item.id}title${item.title}detail${item.detial}{{ item.title }}/RouterLink//name:detail,//用name也可以跳转
path:/news/detail,
query:{id:item.id,title:item.title,detail:item.detail}
}{{ item.title }}/RouterLink
RouterLink
:to{
//name:detail,//用name也可以跳转
path:/news/detail,
query:{id:item.id,title:item.title,detail:item.detail}
}{{ item.title }}/RouterLink接收参数
import { toRefs } from vue;
import { useRoute } from vue-router;
const route useRoute()
const { query } toRefs(route)params传参
路由设置
routes:[{name:home,path:/home,component:Home},{name:news,path:/news,component:News,children:[{name:newsDetail,//params传参必须使用namepath:detail/:id/:title/:detail?,//占用?表示该参数可选component:Detail}]},{name:about,path:/about,component:About},
]传递参数
RouterLink :to/news/detail/${item.id}/${item.title}/${item.detail}{{ item.title }}/RouterLink
RouterLink :to{name:newsDetail,params:{id:item.id,title:item.title,detail:item.detail}}{{ item.title }}/RouterLink接收参数
import { toRefs } from vue;
import { useRoute } from vue-router;
const route useRoute()
const { params } toRefs(route)4.9.路由的 props配置
作用让路由组件更方便的收到参数可以将路由参数作为props传给组件
路由设置
{name:news,path:/news,component:News,children:[{name:newsDetail,path:detail/:id/:title/:detail,component:Detail,//props的布尔值写法把收到的params参数都作为props传给组件props:true//props的函数写法把返回的对象中的每一组key-value作为props传给组件// props(route){// return route.query// }//props的对象写法把对象中的每一组key-value作为props传给组件// props:{id:01,title:props标题,detail:props内容}}]},参数接收
defineProps([id,title,detail])4.10.replace属性
作用按制路由跳转时操作浏览器历史记录的模式浏览器的历史记录有两种写入方式分别为push 和 replace: push 是追加历史记录默认值replace 是替换当前记录。 开启 replace 模式
RouterLink replace :to{path:/news/detail}News/RouterLink4.11.编程式导航
路由组件的两个重要的属性$route 和 $router 变成了两个 hooks
import {useRoute,useRouter} from vue-router
const route useRoute()
const router useRouter()
console.log(route.query)
console.log(route.params)
console.log(router.push)
console.log(router.replace)
import { useRouter } from vue-router;interface detailInter {id:string,title:string,detail:string
}
const router useRouter();
function toPage(detail:detailInter){router.push({name:newsDetail,params:{id:detail.id,title:detail.title,detail:detail.detail}})
})4.12.重定向
routes:[{name:home,path:/home,component:Home},{name:news,path:/news,component:News,children:[{name:newsDetail,path:detail/:id/:title/:detail,component:Detail,props:true}]},{name:about,path:/about,component:About},{path:/,//重定向redirect:/home}
]5.pinia
5.1.准备案例代码
src/App.vue
templatediv classapph1APP/h1Count/News//div
/template
script langts setup nameApp
import Count from ./components/Count.vue;
import News from ./components/News.vue;
/script
style scoped.aClass{color: red;}
/stylesrc/components/Count.vue
templatedivh2求和{{ sum }}/h2select v-model.numbernoption value11/optionoption value22/optionoption value33/option/selectbutton clickadd加/buttonbutton clickminus减/button/div
/template
script langts nameCount setup
import { ref } from vue;
//数据
const sum ref(1)//当前求和
const n ref(1)//选择的数字
//方法
function add(){sum.value sum.value n.value
}
function minus(){sum.value sum.value - n.value
}
/script
style scoped
/stylesrc/components/News.vue
templatedivbutton clickgetNews获取一条新闻/buttonulli v-fornews in newsList :keynews.id{{ news.title }}/li/ul/div
/template
script langts nameNews setup
import axios from axios;
import { reactive } from vue;
import {nanoid} from nanoid
// 数据
const newsList reactive([{id:01,title:新闻1},{id:02,title:新闻2},{id:03,title:新闻3},
])async function getNews() {//连续解构然后给解构出来的content命名为title// const {data:{content:title}} await axios.get(https://api.uomg.com/api/rand.qinghua?formatjson)// const obj {id:nanoid(),title}const obj {id:nanoid(),title:nanoid()}newsList.unshift(obj)
}
/script
style scoped
/style
5.2.搭建 pinia 环境
安装npm install pinia src/main.ts
// 引入createApp用于创建应用
import { createApp } from vue
// 引入App根组件
import App from ./App.vue
// 创建一个应用
const app createApp(App)
// 1.引入pinia
import { createPinia } from pinia
// 2.创建pinia
const pinia createPinia()
// 3.安装pinia
app.use(pinia)
// 挂载整个应用到app容器中
app.mount(#app)5.3.存储读取数据
Store 是一个保存状态、业务逻辑 的实体每个组件都可以读取、写入它。它有三个概念state、getter、action相当于组件中的data、computed、methods具体代码 src/store/count.ts
//引入defineStore用于创建store
import {defineStore} from pinia
//定义并暴露一个store
export const useCountStore defineStore(count,{//状态真正存储数据的地方state(){return {sum:6}},//动作actions:{},//计算getters:{}
})src/store/news.ts
import {defineStore} from pinia
export const useNewsStore defineStore(news,{// 真正存储数据的地方state(){return {newsList:[{id:01,title:新闻1},{id:02,title:新闻2},{id:03,title:新闻3}, ]}}
})src/components/Count.vue
//读取
import { useCountStore } from /store/count
const countStore useCountStore()
console.log(countStore.sum)5.4.修改数据三种方式
直接修改
countStore.sum 9 批量修改
countStore.$patch({sum:999,school:学校
})借助 action 修改action 中可以编写一些业务逻缉 src/store/count.ts
import {defineStore} from pinia
export const useCountStore defineStore(count,{actions:{//记法increment(value:number){//操作countStore中的值 this.sum value}}
})countStore.increment(3 )5.5.storeToRefs
借助 storeToRefs 将 store 中的数据转为 ref 对象方便在模板中使用。注意pinia 提供的 storeToRefs 只会将数据转换而 toRefs 会将所有东西转换
// storeTorefs 只会对 store 中数据进行ref包裹
const countStore useCountStore()
const {sum} storeToRefs(countStore)5.6.getters
概念当state中的数据需要经过处理后再使用可以使用getters配置。
//引入defineStore用于创建store
import {defineStore} from pinia
//定义并暴露一个store
export const useCountStore defineStore(count,{//状态真正存储数据的地方state(){return {sum:6,}},//动作actions:{},//计算getters:{//方式1minSum(state){return state.sum /10},//方式2bigSum:statestate.sum * 10,//方式3//:number 返回的是number类型 addSum():number{return this.sum10}}
})
const {sum,minSum,bigSum,addSum} storeToRefs(countStore)5.7.$subscribe
通过 store 的 $subscribe() 方法侦听 state 及其变化
talkStore.$subscribe((mutate,state){console.log(LoveTalk,mutate.state)localStorage.setItem(talk,JSON.stringify(talkList.value))
})5.8.store组合式写法
export const useCountStore defineStore(count,(){const sum ref(6)function add(){sum.value}return {sum,add}
})6.组件通信
Vue3 中移出了事件总线可以使用pubsub代替。
vuex换成了pinia把 .sync 优化到了 v-model 里面了把 $listeners 所有的东西合并到 $attrs 中
常见搭配形式
组件关系传递方式父传子1.props2.v-model3.$refs4.默认插槽、具名插槽子传父1.props2.自定义事件3.v-model4.$parent5.作用域插槽祖传孙1.$attrs2.provide、injetc兄弟间、任意组件间1.mitt2.pinia
6.1.props
概述props是使用频率最高的一种通方信方式常用与父—子 父传子属性值是非函数 子传父属性值是函数 父组件src/pages/props/Father.vue
templatediv classfatherh3父组件/h3h4汽车{{ car }}/h4h4子给的玩具{{ toy }}/h4Child :carcar :sendToygetToy//div
/template
script setup langts nameFather
import { ref } from vue;
import Child from ../Child.vue;
//数据
const car ref(吉利)
const toy ref()
//方法
function getToy(value:string){toy.value value
}
/script
style scoped
/style子组件src/pages/props/Child.vue
!-- eslint-disable vue/multi-word-component-names --
templatediv classchildh3子组件/h3h4玩具{{ toy }}/h4h4父给的车{{ car }}/h4button clicksendToy(toy)把玩具给父亲/button/div
/template
script setup langts nameChild
import {ref} from vue
//数据
const toy ref(小汽车)
//声明接收props
defineProps([car,sendToy])
/script
style scoped
/style6.2.自定义事件
父组件src/customEvent/Father.vue
templatediv classfatherh3父组件/h3h4汽车{{ car }}/h4button clickchangeCar1点我1/buttonbrbutton clickchangeCar2(1,$event,2)点我2/buttonbrbutton clickcar BYD点我3/buttonbr!--也可以直接使用$event--button clickcar $event.toString()点我3/buttonbrh4子给的玩具{{ toy }}/h4Child send-toysaveToy//div
/template
script setup langts nameFather
import { ref } from vue;
import Child from ./Child.vue;
const car ref(吉利)
const toy ref()
//调用方法不传参默认可以接收事件event
function changeCar1(x:Event){console.log(x)
}
//调用方法可以用 $event 占用
function changeCar2(a:number,event:Event,b:number){console.log(a,event,b)
}function saveToy(value:string){toy.value value
}
/script
style scoped
/style子组件src/customEvent/Child.vue
templatediv classchildh2子组件/h2button clickemit(send-toy,toy)传给父亲/button/div
/template
script setup langts nameChildimport {ref} from vueconst toy ref(小汽车)const emit defineEmits([send-toy])
/script
style scoped
/style6.3.mitt
mitt简单方法
//引入mitt
import mitt from mitt;
//调用mitt得到emitter,emitter能绑定事件、触发事件
const emitter mitt()
//绑定事件
emitter.on(test1,(){console.log(3333)
})
setTimeout((){//触发事件emitter.emit(test1)
},2000)
//解绑事件
emitter.off(test1)
//全部解绑
emitter.all.clear()
//暴露emitter
export default emitter案例 父组件src/pages/mitt/Father.vue
templatediv classfatherChild1/Child2//div
/template
script setup langts nameFather
import Child1 from ./Child1.vue;
import Child2 from ./Child2.vue;
/script
style scoped
/style子组件1src/pages/mitt/Child1.vue
templatediv classchild1h3子组件1/h3h4玩具{{ toy }}/h4button clickemitter.emit(send-toy,toy)给子组件2传数据/button/div
/template
script setup langts nameChild1
import emitter from /utils/emitter;
import {ref} from vue
const toy ref(小汽车1)
/script
style scoped
/style子组件2src/pages/mitt/Child2.vue
templatediv classchild2h3子组件2/h3h4child1给的玩具{{ toy }}/h4/div
/template
script setup langts nameChild2
import {ref,onUnmounted} from vue
import emitter from /utils/emitter;
const toy ref()
//给emitter绑定send-toy事件
emitter.on(send-toy,(value:string){toy.value value
})
//在组件卸载时解绑send-toy事件减少内存占用
onUnmounted((){emitter.off(send-toy)
})
/script
style scoped
/style
6.4.v-model
父组件src/pages/v-model/Father.vue
templatediv classfatherh3父组件/h3!-- v-model用在html标签上:双向绑定 --input typetext v-modelusernamebr!-- 底层原理(HTMLInputElement$event.target) 断言这是html元素不会是null,防止TS红--input typetext :valueusername inputusername (HTMLInputElement$event.target).valuebr!-- v-model用在组件标签上 --!-- Diyinput v-modelusername/ --!-- 底层原理 --Diyinput:modelValueusernameupdate:modelValueusername $event/br!-- v-model重新命名 --DiyinputTwov-model:modelAccountaccountv-model:modelPasswordpassword/br/div
/template
script setup langts nameFatherimport {ref} from vueimport Diyinput from ./Diyinput.vue;import DiyinputTwo from ./DiyinputTwo.vue;const username ref(张三)const account ref(root)const password ref(123456)
/script
style scoped
/style
子组件src/pages/v-model/Diyinput.vue
templateinput typetext :valuemodelValue inputemit(update:modelValue,(HTMLInputElement$event.target).value)
/template
script setup langts nameDiyinput
defineProps([modelValue])
const emit defineEmits([update:modelValue])
/script
style scopedinput{background-color: red;color: white;}
/style
子组件src/pages/v-model/DiyinputTwo.vue
templateinput typetext :valuemodelAccount inputemit(update:modelAccount,(HTMLInputElement$event.target).value)input typetext :valuemodelPassword inputemit(update:modelPassword,(HTMLInputElement$event.target).value)
/template
script setup langts nameDiyinput
defineProps([modelAccount,modelPassword])
const emit defineEmits([update:modelAccount,update:modelPassword])
/script
style scopedinput{background-color: red;color: white;}
/style6.5.$attrs
概述 $attrs 用于实现当前组件的父组件向当前组件的子组件通信祖—孙说明$atttrs 是一个对象包含所有父组件传入的标签属性
注意$attrs 会自动排除props中声明的属性可以认为声明的props被子组件自己“消费”了父组件src/pages/Father.vue
templatediv classfatherh3父组件/h3h4b:{{ b }}/h4!-- v-bind{x:4,y:5} 相当于 :x4 :y5 --Child :aa :bb v-bind{x:4,y:5} :updateBupdateB//div
/template
script setup langts nameFather
import Child from ./Child.vue
import {ref} from vue
const a ref(1)
const b ref(2)
function updateB(value:number){b.value value
}
/script
style scoped
div{border: 2px solid red;padding:10px;
}
/style子组件src/pages/Child.vue
templatediv classchildh3子组件/h3!-- h4a:{{ a }}/h4 --h4其它{{ $attrs }}/h4GrandChild v-bind$attrs//div
/template
script setup langts nameChild
import GrandChild from ./GrandChild.vue;
defineProps([a])
/script
style scoped
div{border: 2px solid red;padding:10px;
}
/style孙组件src/pages/GrandChild.vue
templatediv classgrand-childh3孙组件/h3h4a:{{ a }},a被子组件props消费掉了/h4h4b:{{ b }}/h4h4x:{{ x }}/h4h4y:{{ y }}/h4button clickupdateB(2)增加b/button/div
/template
script setup langts nameGrandChild
defineProps([a,b,x,y,updateB])
/script
style scoped
div{border: 2px solid red;padding:10px;
}
/style6.6.$ refs、$parent
概述 $refs 用于父—子$parent 用于子—父 原理
属性说明$refs值为对象包含所有被ref属性标识的DOM元素或组件实例$parent值为对象当前组件的父组件实例对象父组件src/pages/refs-parent/Father.vue
templatediv classfatherh3父组件/h3h4资产{{ num }} 万元/h4button clickeditToy()修改Child1_toy/buttonbrbutton clickeditComputer()修改Child2_computer/buttonbrbutton clickaddBook($refs)增加所有子组件的book/buttonbrChild1 refc1/Child2 refc2//div
/template
script setup langts nameFather
import Child1 from ./Child1.vue
import Child2 from ./Child2.vue
import { ref } from vue
const c1 ref()
const c2 ref()
const num ref(4)
function editToy(){c1.value.toy 挖掘机
}
function editComputer(){c2.value.computer 联想
}
function addBook(refs:{[key:string]:any}){for(const key in refs){refs[key].book 3}
}
defineExpose({num})
/script
style scoped
div{border: 2px solid red;padding:10px;margin: 10px;
}
/style子组件1src/pages/refs-parent/Child1.vue
templatediv classchild1h3子组件1/h3h4玩具{{ toy }}/h4h4书本{{ book }} 本/h4button clickminus($parent)减少父亲num/button/div
/template
script setup langts nameChild1
import { ref } from vue
const toy ref(小汽车)
const book ref(4)
function minus(parent: { num: number}){parent.num --
}
//把数据交给外部
defineExpose({toy,book})
/script
style scoped
div{border: 2px solid red;padding:10px;margin: 10px;
}
/style子组件2src/pages/refs-parents/Child2.vue
templatediv classchild2h3子组件2/h3h4电脑{{ computer }}/h4h4书本{{ book }} 本/h4/div
/template
script setup langts nameChild2
import { ref } from vue
const computer ref(小米)
const book ref(5)
//把数据交给外部
defineExpose({computer,book})
/script
style scoped
div{border: 2px solid red;padding:10px;margin: 10px;
}
/style6.7.provide、inject
概述实现组孙组件直接通信具体使用 在祖先组件中通过 provide 配置向后代组件提供数据在后代组件中通过 inject 配置来声明接收数据
父组件src/pages/provice-inject/Father.vue
!-- eslint-disable vue/multi-word-component-names --
templatediv classfatherh3父组件/h3h4钱{{ money }}/h4h4车{{ car }}/h4Child//div
/template
script setup langts nameFather
import Child from ./Child.vue
import { provide, reactive, ref } from vue
const money ref(100)
const car reactive({brand:BYD,price:20
})
function updateMoney(value:number){money.value - value
}
//向后代提供数据
provide(moneyObject,{money,updateMoney})
provide(car,car)
/script
style scoped
div{border: 2px solid red;padding:10px;margin: 10px;
}
/style子组件src/pages/provide-inject/Child.vue
templatediv classchildh3子组件/h3GrandChild//div
/template
script setup langts nameChild
import GrandChild from ./GrandChild.vue;
/script
style scoped
div{border: 2px solid red;padding:10px;margin: 10px;
}
/style孙组件src/pages/provide-inject/Child.vue
templatediv classchildh3孙组件/h3h4父的钱{{ money }}/h4h4父的车{{ car }}/h4button clickupdateMoney(6)花钱/button/div
/template
script setup langts nameGrandChild
import { inject } from vue;const {money,updateMoney} inject(moneyObject,{money:0,updateMoney:(x: number){}})
const car inject(car)
/script
style scoped
div{border: 2px solid red;padding:10px;margin: 10px;
}
/style6.8.slot
默认插槽具名插槽作用域插槽
父组件src/pages/slot/Father.vue
templatediv classfatherh2父组件/h2div!-- 默认插槽 --Categoryh3默认插槽/h3/Category!-- 具名插槽 --Category1template v-slot:slotOneh3具名插槽/h3/template/Category1Category2template v-slotparamsh3作用域插槽/h3h4{{ params.title }}/h4h4{{ params.games }}/h4/template/Category2/div/div
/template
script setup langts nameFather
import Category from ./Category.vue
import Category2 from ./Category2.vue
import Category1 from ./Category1.vue/script
style scoped
div{border: 2px solid red;padding:10px;margin: 10px;
}
/style子组件src/pages/slot/Category.vue
templatediv classcategory!-- 默认插槽 --slot/slot!-- 默认插槽 实际上是 slot namedefault/slot --/div
/template
script setup langts nameCategory
/script
style scoped
div{border: 2px solid red;padding:10px;margin: 10px;
}
/style子组件src/pages/slot/Categroy1.vue
templatediv classcategory1!-- 具名插槽 --slot nameslotOne/slot/div
/template
script setup langts nameCategory1
/script
style scoped
div{border: 2px solid red;padding:10px;margin: 10px;
}
/style子组件src/pages/slot/Category2.vue
!-- eslint-disable vue/multi-word-component-names --
templatediv classcategory2!-- 作用域插槽 --slot :gamesgames :titletitle/slot/div
/template
script setup langts nameCategory2
import {ref,reactive} from vue
const games reactive([游戏1,游戏2
])
const title ref(游戏标题)
/script
style scoped
div{border: 2px solid red;padding:10px;margin: 10px;
}
/style7.其它API
7.1. shallowRef 与 shallowReactive
shallow浅的
shallowRef
作用创建一个响应式数据但只能顶层属性进行响应式处理。用法
let myVar shallowRef(initialValue);特点只跟踪引用值的变化不关心内部的属性变化。
shallowReactive
作用创建一个浅层响应式对象只会使对象的最顶层属性变成响应式的对象内部的嵌套属则不会变成响应式折用法
const myObj shallowReactive({....})特点对象的顶层属性是响应式的但嵌套对象属性不是。
总结
通过使用 shallowRef() 和 shallowReactive() 来绕开深度响应。浅层式API创建的状态只在其顶层是响应式的对所有深层的对象不会做出任何处理避免对每一个内部属性做响应式所带的性能成功这使得属性的访问变提更快可提升性能。7.2.readonly 与 shallowReadonly
readonly
作用用于创建一个对象的深只读副本。用法
const original reactive({......});
const readOnlyCopy readonly(original);特点 对象的所有嵌套属性都将变成只读。任何尝试修改这个对象的操作都会被阻止在开发模式下还会在控制台发出警告 应用场景 创建不可变的状态快照。保护全局状态或配置不被修改。
shallowReadonly
作用与 readonly 类似但只作用于对象的顶属属性。用法
const original reactive({...});
const shallowReadOnlyCopy shallowReadonly(original)特点 只将对象的顶层属性设置为只读对象内部的嵌套属性仍然是可变的。适用于只需保护对象顶层属性的场景。
7.3.toRaw 与 markRaw
raw未经加工的
toRaw
作用用于获取一个响应式对象的原始对象toRaw 返回的对象不再是响应式的不会触发视图更新。
官网描述这是一个可以用于临时读取而不引起代理访问/跟踪开销或是写入而不触 发更改的特方法。不建议保存对原始对象的持久引用请谨慎使用。
使用时机在需要将响应式对象传递给非Vuer的库或外部孫统时使用toRaw 可以确保它们收到的是普通对角编码
import { reactive,toRaw,markRaw,isReactive } from vue;/* toRaw */
//响应式对象
let person reactive({name:tony,age:18})
//原始对象
let rawPerson toRaw(person)markRaw
作用标记一个对象使其永远不会变成响应式的。
例如使用mockjs时为了防止把mockjs变成响应式对象可以使用markRaw 去标记 mockjs编码
/* markRaw */
let citys markRaw({{id:01,name:上海},{id:02,name:北京}
})
//根据原始对象citys去创建响应式对象citys2 --创建失败因为citys被markRaw 标记了
let city2 reactive(citys)7.4.customRef
作用创建一个自定义的ref并对其依赖项跟踪和更新触发进行逻辑控制。 实现防抖效果useMsgRef.ts
import {customRef} from vue;
export default function(initValue:string,delay:number){//track跟踪trigger触发let msg customRef((track,trigger){let timer:numberreturn {get(){track()//告诉vue要对msg持续关注一旦变化就更新return initValue},set(value){clearTimeout(timer)timer setTimeout((){initValue valuetrigger()//通知Vue数据msg变化了},delay)}}})return {msg}
}import {useMsgRef} from ./useMsgRef
let { msg } useMsgRef(hellow,2000)8.Vue3新组件
8.1.teleport
teleport传送 Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。
!-- to表示该内容显示在body标签下也可以使to#app 等 --
teleport tobodydiv classmodel v-showisShowh2这是一个弹窗/h2p我是弹窗中的一些内容/pbutton clickisShow false关闭弹窗/button/div
/teleport8.2.Suspense
suspense悬念 fallback退路;应变计划
等待异步组件时渲染一些额外内容让应用有更好的用户体验使用步骤 异步引入组件使用 Suspense 包裹组件并配置好 defalut 与 fallback
//defineAsyncComponent 是Vue 3中用于定义异步组件的API主要用于实现懒加载Lazy Loading即在实际需要时才加载组件优化页面的性能和提升用户体验尤其适用于大型应用和单页面应用SPA
import { defineAsyncComponent,Suspense } from vue
const Child defineAsyncComponent(()import(./Child.vue))templatediv classapph3App组件/h3Suspensetemplate v-slot:defaultChild//template/Suspensetemplate v-slot:fallbackh3加载中。。。/h3/template/div
/template
8.3.全局API转移到应用对象
app.component 定义全局组件
import {crateApp} from vue
import App from ./App.vue
import Hello form ./hello.vue
const app createApp(App)
app.component(Hello,Hello)
app.mount(#app)app.config 定义全局配置
import {crateApp} from vue
import App from ./App.vue
const app createApp(App)
//任何地方都可以使用x
app.config.globalProperties.x 9
//避免全局使用x时编辑器警告
declare module vue {interface ComponentCustomProperties {x:number}
}
app.mount(#app)app.directive 定义全局指令
import {crateApp} from vue
import App from ./App.vue
const app createApp(App)
app.directive(beauty,(element,{value}){element.innerText valueelement.style.color greenelement.style.backgroundColor yellow
})
app.mount(#app)h4 v-beauty1hellow/h4app.mount 挂载应用
import {crateApp} from vue
import App from ./App.vue
const app createApp(App)
//挂载应用
app.mount(#app)app.unmount 卸载应用
import {crateApp} from vue
import App from ./App.vue
const app createApp(App)
//挂载应用
app.mount(#app)
setTimeout((){卸载应用app.unmount()
},2000)app.use 安装插件
import {crateApp} from vue
import App from ./App.vue
import router from ./router
const app createApp(App)
//使用路器
app.use(router)
app.mount(#app)