vue3 学习笔记(不断更新中...)(2024.11.13)
组合式API
setup()
11
响应式API
- ref
ref 用于创建响应式数据(通常用来定义 基本类型数据)
在JavaScript代码中,需要使用 .value 来操作数据
let count = ref(1)
console.log(count.value) // 1
count.value++
console.log(count.value) // 2
在Template模板中不需要
<script setup>
import { ref } from 'vue'
const count = ref(1)
</script>
<template>
<button>{{ count }}</button>
</template>
- reactive
reactive 用于创建一个响应式对象 (通常用来定义 引用类型数据)
let obj = reactive({ count: 1 })
obj.count++ // 2
- computed
使用 computed 创建一个计算属性,通过 .valule 暴露返回值
- 创建一个只读的计算属性
默认情况下,创建的计算属性是只读的,如果修改会报错
let count = ref(1)
let getCount = computed(() => count.value)
console.log(getCount.value) // 1
getCount.value++ //error:Write operation failed: computed value is readonly
- 创建一个可写的计算属性
computed 可以接收一个带有 get 和 set 函数的对象来创建一个 可写 的计算属性
let count = ref(1)
let getCount = computed({
get: () => count.value,
set: (val) => {
count.value = val + 1
}
})
console.log(getCount.value) // 1
getCount.value = 1
console.log(getCount.value) // 3
- watch
作用:用于监听响应式数据的变化,初始化的时候不执行
- 监听 ref
let count = ref(1)
watch(
count,
(newVal, oldVal) => {
console.log(newVal, oldVal) // 2, 1
}
)
count.value++
- 监听 reactive
当监听一个响应式对象时,会自动启用深度监听模式
let data = reactive({ count: 1 })
watch(
data,
(newValue, oldValue) => {
console.log(newValue, oldValue) // 2 1
}
)
data.count++
- 监听 reactive 中的某个属性
let data = reactive({ count: 1 })
watch(
() => data.count,
(newVal, oldVal) => {
console.log(newVal, oldVal) // 2 1
}
)
data.count++
- 停用监听器
let stop1 = watch(source, callback)
// 停用
stop1()
- watchEffect
watchEffect 不需要指定监听的数据源
它是自动跟踪函数内部使用的所有响应式数据源,当任何一个数据发生变化时,都会重新执行这个函数
let count = ref(1)
watchEffect(() => console.log(count.value))
// 输出:2
count.value++
- 属性设置
flush: 'post'
默认情况下,监听器是在组件渲染之前执行
设置flush: 'post' 将会使监听器延迟到组件渲染之后再执行
- 属性设置
flush: 'sync'
在某些情况下,可能有必要在响应式依赖发生改变时立即触发监听器。可以通过设置 flush: 'sync'来实现
需要注意:当如果有多个属性同时更新,可能会导致性能和数据一致性的问题
- watchPostEffect()
相当于 watchEffect 使用 flush: 'post' 的别名
- watchSyncEffect()
相当于 watchEffect 使用 flush: 'sync' 的别名
工具函数
- toRef
在获取一个响应式对象的子属性的时候,会丢失其响应式,如下面:
let form = reactive({ count: 1 })
let count = form.count
count++
console.log(form.count, count) // 1, 2
为了对响应式对象解构的时候,延续数据的响应式,需要使用 toRef:
let form = reactive({ count: 1 })
let count = toRef(form, 'count')
count.value++
console.log(form.count) // 2
form.count++
console.log(count.value) // 3
使用 toRef 基于响应式对象创建的属性,与其源属性保持同步。改变源属性的值,也会更新这个新创建的属性
- toRefs
toRefs 和 toRef 的区别就是多了个 s,一个是复数一个是单数的区别。
使用 toRefs 可以将整个响应式对象都解构出来,不再是针对其中单个属性,如下:
let form = reactive({ count: 1 })
let formRefs = toRefs(form)
form.count++
console.log(formRefs.count.value) // 2
formRefs.count.value++
console.log(form.count) // 3
移除的API
Vue.set、Vue.delete 、 Vue.observable、Vue.filter、Vue.mixin
组件
新增组件
- Fragment
在 Vue2 中只能有一个根节点,Vue3 中可以支持多个根节点
其实在 Vue3 中每个组件还是一个根节点,只不过是使用 Fragment 组件将多根节点组件包裹起来了, fragment 和 keep-alive 一样,是一个抽象组件,不会被渲染出来。
<template>
<div>测试1</div>
<div>测试2</div>
</template>
- Teleport
teleport 组件允许将插槽内容渲染到任意位置,没有父子组件的限制
<Teleport to="#test">
<h1>测试内容</h1>
</Teleport>
Props
interface TeleportProps {
/**
* 必填项。指定目标容器。
* 可以是选择器或实际元素。
*/
to: string | HTMLElement
/**
* 当值为 `true` 时,内容将保留在其原始位置
* 而不是移动到目标容器中。
* 可以动态更改。
*/
disabled?: boolean
}
访问子组件实例
父组件定义一个变量 childRef 来引用子组件,定义的变量名称和子组件标签定义的 ref 需要一样
<script setup>
import { ref } from 'vue'
import Child from 'Child.vue'
const childRef = ref(null)
console.log(childRef.value)
</script>
<template>
<Child ref="childRef"></Child>
</template>
父子组件通信
- 父传子
在 vue2 中我们使用 v-bind + props 的方式向子组件传值
在 vue3 中是使用 v-model + defineProps 的方式向子组件传值。并且支持写多个 v-model
<!--父组件-->
<script setup>
import Child from './Child.vue'
import { ref } from 'vue'
const name = ref('时光凉忆')
const age = ref(26)
</script>
<template>
<Child v-model:name="name" v-model:age="age"/>
</template>
<!--子组件-->
<script setup>
const props = defineProps({
name: String,
age: Number
})
</script>
<template>
<p>名字:{{name}}</p>
<p>年龄:{{age}}</p>
</template>
- 子传父
在 vue2 中使用 $emit 向父组件传值
在 vue3 中使用 defineEmits 向父组件传值
<!--父组件-->
<script setup>
import Child from './Child.vue'
import { ref } from 'vue'
const count = ref(1)
function addCount(e) {
count.value += e
}
function resetCount() {
count.value = 1
}
</script>
<template>
<Child @addCount="addCount" @resetCount="resetCount"/>
</template>
<!--子组件-->
<script setup>
const emit = defineEmits(['addCount', 'resetCount'])
function addCount() {
emit('addCount', 1)
}
function resetCount() {
emit('resetCount')
}
</script>
<template>
<button @click="addCount">点击累加</button>
<button @click="resetCount">重置</button>
</template>
- 子组件直接修改父组件传过来的数据
在 vue2 中:子组件如果想直接修改父组件传过来数据,需要指定 v-model.sync
在 vue3 中,.sync 被移除了。通过定义一个 emit 事件,指定 update:count 来进行修改数据
<!--父组件-->
<script setup>
import Child from './Child.vue'
import { ref } from 'vue'
const count = ref(1)
</script>
<template>
<Child v-model:count="count"/>
</template>
<!--子组件-->
<script setup>
const props = defineProps({
count: Number
})
const emit = defineEmits(['update:count'])
function updateCount() {
emit('update:count', 100)
}
</script>
<template>
<button @click="updateCount">更新父组件数据</button>
</template>
- 父组件获取子组件的属性或调用子组件的方法
默认情况下,父组件是不可以获取到子组件的属性。如果想这么做的话,我们可以在子组件使用 defineExpose 指定暴露出去的属性
defineExpose 可以指定那些属性、方法被其父组件访问和使用
<!--子组件-->
<script setup>
import { ref } from 'vue'
const content = ref('无形装逼,最为致命')
const testFunc = () => {
console.log(content.value)
}
defineExpose({
content,
testFunc
})
</script>
<!--父组件-->
<script setup>
import Child from './Child.vue'
// 这里声明的变量应该和子组件标签定义的ref保持一致
const child = ref(null)
console.log(child.value.content) // 无形装逼,最为致命
child.value.testFunc() // 这里会执行子组件的testFunc函数
</script>
<template>
<Child ref="child"/>
</template>
异步加载组件
- vue2
const MyComponent = () => import('./MyComponent.vue')
- vue3
import { defineAsyncComponent } from 'vue'
// 会为MyComponent.vue 及其依赖创建一个单独的一个快
// 它只会按需加载 (及改异步组件在页面中被渲染时)
const MyComponent = defineAsyncComponent(() => import('./MyComponent.vue'))
生命周期
vue3 中移除了 beforeCreate 和 created,添加了 setup 函数
所有钩子函数添加一个前缀
on引用方式,因为 vue3 都是模块化,如
import {onMounted} from 'vue'
| vue2 | vue3 |
|---|---|
| beforeCreate created |
setup() |
| beforeMount Mounted |
onBeforeMount onMounted |
| beforeUpdate updated |
onBeforeUpdate onUpdated |
| beforeDestroy destroyed |
obBeforeUnmount onUnmounted |
CSS
css 样式穿透
在 Vue2 中修改子组件或者组件库的样式,都会使用样式穿透 /deep/ .class{}
在 Vue3 中样式穿透的语法变成了 :deep(.class)
<style scoped>
:deep(.el-form) {
.el-input {
width: 200px;
}
}
</style>
css 的v-bind
在css中使用js的变量来动态写样式
<script setup>
import { ref } from 'vue'
const color = ref('red')
</script>
<style>
.text {
color: v-bind('color');
}
</style>
注意: 在 <script setup> 中使用 v-bind,里面的变量需要引号包裹起来,特别注意。在 setup() 中不需要
高级函数
逻辑复用 hooks
在 vue2 中我们想要实现逻辑复用一般用 mixins,但是有一些弊端
在 vue3 中保留了 mixins, 但更推荐使用Hooks模式。通过自定义组合式函数,这些函数包含状态、生命周期函数等,可以在多个组件中实现复用。
下面以官方鼠标跟踪器示例举例进行讲解,封装函数:
// mouse.js
import { ref, onMounted, onUnmounted } from 'vue'
// 按照惯例,组合式函数名以“use”开头
export function useMouse() {
// 被组合式函数封装和管理的状态
const x = ref(0)
const y = ref(0)
// 组合式函数可以随时更改其状态。
function update(event) {
x.value = event.pageX
y.value = event.pageY
}
// 一个组合式函数也可以挂靠在所属组件的生命周期上
// 来启动和卸载副作用
onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
// 通过返回值暴露所管理的状态
return { x, y }
}
下面是在组件中使用的方式:
<script setup>
import { useMouse } from './mouse.js'
const { x, y } = useMouse()
</script>
<template>Mouse position is at: {{ x }}, {{ y }}</template>
<!--Mouse position is at: 0, 0-->
我们通过上面代码发现,在自定义hooks函数中,我们依然可以使用响应式API、生命周期钩子等,可以实现 mixins 想要的效果
全局API转移
在 vue2 全局API 都是挂载在 Vue.prototype 上
在 vue3 改成了 app.config.globalProperties
const app = createApp(App)
app.config.globalProperties.$myGlobalMethod = function () {
// ...
}
调用的话:
this.$myGlobalMethod()
插槽
- 默认插槽
子组件写上 <slot></slot>,父组件在引入子组件标签内部写入内容,就会自动填充到子组件上面
比如我们有一个如下的子组件:
<template>
<header>头部</header>
<main>
<slot></slot>
</main>
<footer>尾部</footer>
</template>
父组件调用子组件:
<template>
<child>
<p>中间的主要内容</p>
</child>
</template>
- 具名插槽
具名插槽适用:当一个组件需要多个插槽,指定某个位置的内容
使用 <slot name="header"></slot> 定义一个插槽,使用 <template v-slot:header></template> 去指定插入的内容
比如我们有一个公共弹窗组件Dialog:
<template>
<div id="dialog">
<slot name="header"></slot>
<slot name="footer"></slot>
</div>
</template>
调用这个弹窗组件:
<template>
<Dialog>
<template v-slot:header>
<p>弹窗组件头部</p>
</template>
<template v-slot:footer>
<p>弹窗组件底部</p>
</template>
</Dialog>
</template>
具名插槽的缩写:
跟 v-on 和 v-bind 一样,v-slot 也有缩写。比如 v-slot:header 可以简写成 #header,所以我们可以将上面调用代码改成如下:
<template>
<Dialog>
<template #header>
<p>弹窗组件头部</p>
</template>
<template #footer>
<p>弹窗组件底部</p>
</template>
</Dialog>
</template>
- 作用域插槽
子组件可以通过动态绑定的方式,将子组件的值通过插槽传递给父组件
如下一个子组件:
<template>
<header>头部</header>
<main>
<slot name="main" title="演示作用域插槽"></slot>
</main>
</template>
父组件:
<template>
<Child #main="{title}">
<p>子组件传递过来的值:{{title}}</p>
</Child>
</template>
- 动态插槽
根据条件动态的去匹配,不同名称的插槽位置
举例如下,有一个如下的子组件:
<template>
<header>
头部:
<slot name="header"></slot>
</header>
<main>
主体:
<slot name="main"></slot>
</main>
<footer>
底部:
<slot name="footer"></slot>
</footer>
</template>
父组件:
<script setup>
import { ref } from 'vue'
import Child from 'Child.vue'
const slotName = ref('')
</script>
<template>
<button @click="slotName = 'header'">切换到头部</button>
<button @click="slotName = 'main'">切换到主体</button>
<button @click="slotName = 'footer'">切换到底部</button>
<Child>
<template v-slot:[slotName]>测试内容</template>
</Child>
</template>
vue3 学习笔记(不断更新中...)(2024.11.13)的更多相关文章
- 保姆级尚硅谷SpringCloud学习笔记(更新中)
目录 前言 正文内容 001_课程说明 002_零基础微服务架构理论入门 微服务优缺点[^1] SpringCloud与微服务的关系 SpringCloud技术栈 003_第二季Boot和Cloud版 ...
- AIX学习笔记(更新中)
AIX操作系统基本命令 系统的进入和退出login: 输入用户名(例如:user01)password: 输入用户口令若用户名及口令均正确,则用户将登陆成功.此时系统会出现命令提示符 $或#,即表示可 ...
- KISSY学习笔记(更新中)
序:身为一个JAVA开发工程师,前端代码我尽量是使用原生的JS来写的,或是使用一些JQ的开源组件(但是也只是使用,没有好好去研究过JQ这个框架).目前由于工作需要,必须要使用KISSY,打算借此机会, ...
- ubuntu学习笔记--不断更新中
1.rpm软件包相关: rpm软件包安装命令: rpm -ivh linuxqq-v1.0.2-beta1.i386.rpm rpm软件默认安装路径查询: rpm -ql *.rpm ubuntu如何 ...
- GOF 的23种JAVA常用设计模式 学习笔记 持续更新中。。。。
前言: 设计模式,前人总结下留给后人更好的设计程序,为我们的程序代码提供一种思想与认知,如何去更好的写出优雅的代码,23种设计模式,是时候需要掌握它了. 1.工厂模式 大白话:比如你需要一辆汽车,你无 ...
- 并发编程学习笔记(4)----jdk5中提供的原子类及Lock使用及原理
(1)jdk中原子类的使用: jdk5中提供了很多原子类,它会使变量的操作变成原子性的. 原子性:原子性指的是一个操作是不可中断的,即使是在多个线程一起操作的情况下,一个操作一旦开始,就不会被其他线程 ...
- amazeui学习笔记--css(常用组件11)--分页Pagination
amazeui学习笔记--css(常用组件11)--分页Pagination 一.总结 1.分页使用:还是ul包li的形式: 分页组件,<ul> / <ol> 添加 .am-p ...
- [学习笔记] 在Eclipse中导入项目
参考前文:[学习笔记] 在Eclips 中导出项目 选择已经导出的文件: 导入之后,项目结构如下: 至此,完成.
- CockroachDB学习笔记——[译]CockroachDB中的SQL:映射表中数据到键值存储
CockroachDB学习笔记--[译]CockroachDB中的SQL:映射表中数据到键值存储 原文标题:SQL in CockroachDB: Mapping Table Data to Key- ...
- [学习笔记] 在Eclipse中导出可以直接运行的jar,依赖的jar中的类解压后放在运行jar中
前文: [学习笔记] 在Eclipse中导出可以直接运行的jar,依赖的jar打在jar包中 使用7z打开压缩包,查看所有依赖的jar都被解压以包名及class的方式存储在了运行jar中,此时jar的 ...
随机推荐
- java基础 -网络编程笔记
666,InetAddress package com.hspedu.api; import java.net.InetAddress; import java.net.UnknownHostExce ...
- [TK] 矩阵取数游戏<简单版> hzoi-tg-906-2
本题是一个坐标DP问题 状态转移 首先我们注意到,一个状态只能由两种前置状态得到:取左边的数和取右边的数,因此我们以状态为阶段定义如下: \(f[a][b][c]\) 为状态转移数组,其中 \(a\) ...
- eBPF 概述:第 4 部分:在嵌入式系统运行
1. 前言 在本系列的第 1 部分和第 2 部分,我们介绍了 eBPF 虚拟机内部工作原理,在第 3 部分我们研究了基于底层虚拟机机制之上开发和使用 eBPF 程序的主流方式. 在这一部分中,我们将从 ...
- 墨天轮访谈 | IvorySQL王志斌—IvorySQL,一个基于PostgreSQL的兼容Oracle的开源数据库
分享嘉宾:王志斌 瀚高IvorySQL产品经理 整理:墨天轮社区 导读 大家好,我是瀚高IvorySQL产品经理王志斌,IvorySQL是基于PostgreSQL的衍生开源项目. 我今天分享的内容主要 ...
- DDD之聚合与聚合根
目的:高内聚,低耦合,有关系的实体紧密协作,关系很弱的实体被隔离:把关系紧密的实体放到一个聚合中,每个聚合中有一个实体作为聚合根,对 所有聚合对象的访问都通过聚合根来进行,外部对象只能持有对聚合根的引 ...
- 封装JWT - 生成 jwt 和解析 jwt
1. ASP.NET Core 身份验证和授权验证的功能由Authentication,Authorization中间件提供 :app.UseAuthentication(),app.UseAutho ...
- 在实例化对象的时候new关键字具体做了哪些操作?
a 创建了一个空对象 {}b 通过原型链把空对象和构造函数连接起来__proto__ = prototype c 构造函数的this指向新对象,并执行函数体 d 判断构造函数的返回值,返回对象就使用该 ...
- 04-react的基本:条件渲染
import reactDom from "react-dom" // 条件渲染 if else let loading = false // 写一个函数用于加载 const lo ...
- 你在使用 KubeSphere 吗?
如果你正在使用 KubeSphere,欢迎在社区分享你的使用和实践经验,赢取 KubeSphere 周边礼品(T恤.帆布袋.徽章等)以及 Kubernetes 技术书籍. 为什么我们要征集用户案例? ...
- 云原生爱好者周刊:VMware Tanzu 社区办发布,无任何限制!
云原生一周动态要闻: VMware Tanzu 推出社区版 Kubernetes Cluster API 1.0 版已生产就绪 Linkerd 2.11 发布 Cartografos 工作组推出云原生 ...