VUE3.0学习笔记
vue设计思想:
- 更加注重模块上的拆分。在2.0中无法单独使用部分模块,需要引入完整的vuejs,vue3中的模块之间耦合度低,模块可以独立使用。
- vue2中很多方法挂载到了实例中导致没有使用也会被打包(还有很多组件也是一样)。vue3重写API,通过构建工具Tree-Shaking 机制实现按需引入,减少用户打包后的体积。
- vue3允许自定义渲染器,扩展能力强,不会发生以前的事,改写vue源码改造渲染方式,扩展更方便。
- vue3使用Monorepo管理项目,将模块拆分到package目录中。
Monorepo是管理项目代码的一种方式,指在一个项目仓库中管理多个模块/包。(一个仓库可以维护多个模块,方便版本管理和依赖管理,模块之间的引用,调用都很方便)
新特性:
1、双向数据绑定(proxy)
- 可以直接监听数组的变化
- 对象深度监听性能更好,无需一层层递归,为每个属性添加代理,性能更好,可监听任何方式的数据改变
- 可监听新增和删除属性
2、CompositionAPI
vue2使用的是OptionsApi,OptionsApi的弊端:
- 编写某部分功能时,代码会分布在data,computed,methods等多个Options Api
- 对于没有参与复杂功能开发的同事, 在代码较分散的时候, 难以理解其逻辑
Composition Api的优势
- 将同一个业务逻辑的代码写在一起, 可读性更强
- 在setup中书写没有this, 避免this指向出现的问题, 开发者不需要关注this指向问题
3、类型检测
更好的支持TypeScript
4、碎片化节点 Fragment
- template元素可以当做不可见的包裹元素,并且在v-if上使用,但是最终template不会被渲染出来
- 不再限制template只有一个根节点, render函数也可以返回数组
- 类似于小程序的block, react的`React.Fragments`
5、移出了一些api
- Vue3从实例中移除了 $on、$off 和 $once 方法, 建议使用第三方库mitt
- Vue3移除了$children; 建议使用 [$refs](https://v3.cn.vuejs.org/guide/component-template-refs.html#模板引用)
- Vue3移除了过滤器filter, 建议用方法调用 (局部方法, 全局方法) 或计算属性来替换
- 全局变量: `Vue.prototype` 替换为 `app.config.globalProperties`
1、v-model
vue3.0支持多个v-model的绑定
App.vue
1 <template> 2 {{title}} 3 {{name}} 4 5 <Input v-model:title="title" v-model:name="name"/> 6 </template> 7 8 <script> 9 import Input from "./components/Input" 10 export default { 11 name: 'App', 12 components: { 13 Input, 14 }, 15 data() { 16 return { 17 title: "聂丽芳", 18 name: "www" 19 } 20 }, 21 } 22 </script>
Input.vue
1 <template> 2 <div class="Input"> 3 <input type="text" @input="first" :value="title"> 4 <input type="text" @input="last" :value="name"> 5 </div> 6 </template> 7 <script> 8 export default { 9 name: 'Input', 10 props: { 11 title: { 12 default: () => "聂丽芳" 13 }, 14 name: { 15 default: () => "前端娱乐圈" 16 } 17 }, 18 methods: { 19 first(e) { 20 this.$emit("update:title", e.target.value) 21 }, 22 last(e) { 23 this.$emit("update:name", e.target.value) 24 } 25 } 26 } 27 </script>
2、Setup
创建组件实例,然后初始化props
,紧接着就调用setup
函数。 从生命周期的角度来看,它会在beforeCreate
之前执行。也就是创建组件先执行setup
、beforeCreate
、create
。
this指向
由于不能在setup
函数中使用data
、methods
,为了避免使用Vue
出错,所以把setup
函数中this
修改为了undefined
。
函数参数
- props
- context
props
接收组件传递过来的所有数据,并且都是响应式的。
1 <template> 2 <div>聂丽芳</div> 3 </template> 4 <script> 5 export default { 6 name: 'App', 7 props: { 8 title: { 9 type: String 10 } 11 }, 12 setup(props) { 13 console.log(props.title) 14 } 15 } 16 </script>
Ps:props数据不能使用解构,否则响应式数据失效
1 <template> 2 <div>聂丽芳</div> 3 </template> 4 <script> 5 export default { 6 name: 'App', 7 props: { 8 title: { 9 type: String 10 } 11 }, 12 setup({ title }) { 13 console.log(title) // 这里响应式数据将失效 14 } 15 } 16 </script>
context
该参数提供一个上下文对象,从原来的2.x中选择性的暴露了一些属性。
- attrs
- slots
- emit
1 <template> 2 <div>聂丽芳</div> 3 </template> 4 <script> 5 export default { 6 name: 'App', 7 props: { 8 title: { 9 type: String 10 } 11 }, 12 setup(props, { attrs, slots, emit } ) { 13 console.log(attrs) 14 } 15 } 16 </script>
上面,
attrs
和slots
都是内部组件实例上对应项的代理,可以确保在更新后仍然还是最新的值。所以这里可以使用解构语法。
返回值
可以将setup
函数返回值渲染到页面上。但前提是,setup
返回值必须是一个对象,否则返回其它值则渲染无效。
3、Reactive
该方法接收一个参数{}
创建一个响应式对象。跟Vue2.x的Vue.observable
一样。如果该参数不是对象的话,也可以渲染到模板上,但不是响应式的数据。
1 <template> 2 <div class="test"> 3 姓名: {{ name.value }} 4 {{ test() }} 5 </div> 6 </template> 7 8 <script> 9 import { reactive } from "vue" 10 export default { 11 name: 'test', 12 data() { 13 return { 14 15 } 16 }, 17 setup() { 18 let name = reactive({value: "聂丽芳"}) 19 function test() { 20 name.value = "abc"; // 该方法测试响应式数据,可以看到执行完该方法视图也会发生改变 21 } 22 return { 23 name, 24 test 25 } 26 } 27 } 28 </script>
4、Ref
该方法接收一个参数,可以是单个值,也可以是一个对象,并且都是响应式的数据。当传入一个对象时{}
,内部将调用reactive
方法进行转换为响应式数据。返回值里面带有.value
属性取值,当使用模板渲染的时候可省去.value
。
1 <template> 2 <div class="test"> 3 姓名: {{ name }} 4 {{ test() }} 5 </div> 6 </template> 7 8 <script> 9 import { ref } from "vue" 10 export default { 11 name: 'test', 12 data() { 13 return { 14 15 } 16 }, 17 setup() { 18 19 let name = ref("聂丽芳") 20 function test() { 21 name.value = "abc"; // 只是渲染模板可以省略.value,但是在逻辑中还得写哦 22 } 23 return { 24 name, 25 test 26 } 27 } 28 } 29 </script>
5、Computed
该方法可以传入一个函数,默认该函数就是getter
,不管getter
返回值为一个ref
响应式数据还是一个普通变量
,数据都是只读
不能改变。
1 <script> 2 import { ref, computed } from "vue" 3 export default { 4 name: 'test', 5 setup() { 6 let name = ref("蛙人") 7 let test = computed(() => name.value); 8 test.value = "123" // 修改无效,只能只读 9 } 10 } 11 </script>
传入一个对象set
和get
函数方法,这样就可以修改啦
1 <script> 2 import { ref, computed } from "vue" 3 export default { 4 name: 'test', 5 setup() { 6 let name = ref("聂丽芳") 7 let test = computed({ 8 get() { 9 return name.value; 10 }, 11 set(val) { 12 return name.value = val; 13 } 14 }); 15 test.value = "123" 16 } 17 } 18 </script>
6、Readonly
该方法接收传入一个对象,默认是只读功能,是深层对象只读,不管嵌套多少层的属性都是只读状态。
1 <script> 2 import { readonly } from "vue" 3 export default { 4 name: 'test', 5 setup() { 6 let obj = { 7 name: "聂丽芳", 8 sex: "male", 9 prodution: { 10 proName: "音响" 11 } 12 } 13 let only = readonly(obj) 14 only.name = "前端娱乐圈" // 修改无效 15 only.prodution.proName = "欢迎关注" // 修改无效 16 console.log(only) 17 } 18 } 19 </script>
7、WatchEffect
该方法接收一个函数并且立即执行,并当该函数里的变量变更时,重新执行该函数。该方法无法获取到原值,只能是改变之后的值。
如果要监听哪个值,需要在该函数里写出来,否则监听无效。
1 import { ref, watchEffect } from "vue" 2 export default { 3 name: 'test', 4 setup() { 5 let name = ref("聂丽芳"); 6 let age = ref(23); 7 watchEffect(() => { 8 name.value; // 监听name 9 age.value; // 监听age 10 11 console.log(name.value) 12 console.log(age.value) 13 }) 14 15 setTimeout(() => { 16 name.value = "前端娱乐圈" 17 }, 5000) 18 19 setTimeout(() => { 20 age.value = 18 21 }, 1000) 22 } 23 } 24 </script>
取消监听
有时候我们想在触发一定的条件后取消监听。这时可以执行watchEffect
的返回值。
1 import { ref, watchEffect } from "vue" 2 export default { 3 name: 'test', 4 setup() { 5 let name = ref("聂丽芳"); 6 let age = ref(23); 7 let stop = watchEffect(() => { 8 name.value; // 监听name 9 age.value; // 监听age 10 11 console.log(name.value) 12 console.log(age.value) 13 }) 14 15 setTimeout(() => { 16 name.value = "前端娱乐圈" 17 }, 5000) 18 19 setTimeout(() => { 20 age.value = 18; 21 setTimeout(stop, 300) 22 }, 1000) 23 } 24 } 25 </script>
更多配置项:副作用刷新时机我们一般使用post
onTrigger可以帮助我们调试watchEffect
import { watchEffect, ref } from 'vue' let message = ref<string>('') let message2 = ref<string>('') watchEffect((oninvalidate) => { //console.log('message', message.value); oninvalidate(()=>{ }) console.log('message2', message2.value); },{ flush:"post", onTrigger () { } })
8、Watch
watch
等同于Vue2.x中的this.$watch
,watch
需要监听特定数据,默认情况是懒执行,也就是只有当数据发生变化时才执行第二个参数函数。
对比WatchEffect
,Watch
允许我们
- 懒执行函数
- 更明确哪些状态改变触发监听器
- 可以监听获取到变化前后的值
监听单个值
1 <script> 2 import { ref, watch } from "vue" 3 export default { 4 name: 'test', 5 setup() { 6 let name = ref("聂丽芳"); 7 8 watch(name, (newVal, oldVal) => { 9 console.log(newVal, oldVal) // 前端娱乐圈, 聂丽芳 10 }) 11 12 setTimeout(() => { 13 name.value = "前端娱乐圈" 14 }, 1000) 15 16 } 17 } 18 </script>
监听多个值
监听多个值,返回的是一个数组对象。
1 <script> 2 import { ref, watch } from "vue" 3 export default { 4 name: 'test', 5 setup() { 6 let name = ref("聂丽芳"); 7 let age = ref(23); 8 9 watch([name, age], (newVal, oldVal) => { 10 console.log(newVal, oldVal) // ["前端娱乐圈", 18], ["聂丽芳", 23] 11 }) 12 13 setTimeout(() => { 14 name.value = "前端娱乐圈" 15 age.value = 18 16 }, 1000) 17 18 } 19 } 20 </script>
生命周期系列
在Vue3.X也可以在setup
函数下使用生命周期,这些钩子函数写法跟之前的生命周期写法不同。
注意:这些生命周期写法只能在
setup
函数下使用,在其它地方使用则会报错。
与Vue2.x版本生命周期相对应的组合式Api
- beforeCreate --> setup
- created --> setup
- beforeMount --> onBeforeMount
- mounted --> onMounted
- beforeUpdate --> onBeforeUpdate
- updated --> onUpdated
- beforeDestroy --> onBeforeUnmount
- destroyed --> onUnmount
`setup` 是围绕 `beforeCreate` 和 `created` 生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在 `setup` 函数中编写
Provide && Inject
该方法和Vue2.x的 provide
、inject
一样的。但是Vue3新特性这俩方法只能在setup
中使用。
- Provide:接收2个参数,第一个
key
值,第二个value
值,进行传递 - Inject:接收2个参数,第一个是
provide
的key
值,默认第二个参数可选,可以设置默认值(当找不到key值,设置一个默认值)
App.vue
1 <script> 2 import test from "./components/test" 3 import { provide, ref } from "vue" 4 export default { 5 name: 'App', 6 components: { 7 test 8 }, 9 setup() { 10 let name = ref("聂丽芳") 11 provide("name", name) // 传入一个响应式数据 12 }, 13 } 14 </script>
test.vue
1 <template> 2 {{ NAME }} 3 </template> 4 <script> 5 import { inject } from "vue" 6 export default { 7 name: 'test', 8 setup() { 9 let NAME = inject("name") 10 console.log(NAME) // 蛙人 11 12 let title = inject("key", 123) 13 console.log(title) // 这时就会触发默认值,因为这里找不到这个key值 14 15 return { 16 NAME 17 } 18 } 19 } 20 </script>
移除过滤器filters
在Vue3.x中移除过滤器,不再支持。建议使用computed
去替代
不再限制Template一个根节点
Vue3.x中将不在限制模板中只有一个根节点,根组件可以任意多个元素。
<template> <div>首页</div> <div>新闻</div> </template>
废弃on,off,once实例方法
Vue3.x中 $on
,$off
和 $once
实例方法已被移除,应用实例不再实现事件触发接口。
Teleport 是什么呢?
Header
中使用到Dialog
组件,我们实际开发中经常会在类似的情形下使用到 Dialog
,此时Dialog
就被渲染到一层层子组件内部,处理嵌套组件的定位、z-index
和样式都变得困难。 Dialog
从用户感知的层面,应该是一个独立的组件,从 dom 结构应该完全剥离 Vue 顶层组件挂载的 DOM;同时还可以使用到 Vue 组件内的状态(data
或者props
)的值。简单来说就是,即希望继续在组件内部使用Dialog
, 又希望渲染的 DOM 结构不嵌套在组件的 DOM 中。 此时就需要 Teleport 上场,我们可以用<Teleport>
包裹Dialog
, 此时就建立了一个传送门,可以将Dialog
渲染的内容传送到任何指定的地方。 接下来就举个小例子,看看 Teleport 的使用方式index.html
文件中定义一个供挂载的元素:<body> <div id="app"></div> <div id="dialog"></div> </body>
定义一个Dialog
组件Dialog.vue
, 留意 to
属性, 与上面的id
选择器一致:
<template>
<teleport to="#dialog">
<div class="dialog">
<div class="dialog_wrapper">
<div class="dialog_header" v-if="title">
<slot name="header">
<span>{{ title }}</span>
</slot>
</div>
</div>
<div class="dialog_content">
<slot></slot>
</div>
<div class="dialog_footer">
<slot name="footer"></slot>
</div>
</div>
</teleport>
</template>
最后在一个子组件Header.vue
中使用Dialog
组件, 这里主要演示 Teleport 的使用,不相关的代码就省略了。header
组件
<div class="header"> ... <navbar /> <Dialog v-if="dialogVisible"></Dialog> </div>
suspense
Suspense
只是一个带插槽的组件,只是它的插槽指定了default
和 fallback
两种状态。
Suspense
是 Vue3.x 中新增的特性, 那它有什么用呢?别急,我们通过 Vue2.x 中的一些场景来认识它的作用。 Vue2.x 中应该经常遇到这样的场景:
<template> <div> <div v-if="!loading"> ... </div> <div v-if="loading"> 加载中... </div> </div> </template>
v-if
来控制数据显示。 如果你使用过vue-async-manager
这个插件来完成上面的需求, 你对Suspense
可能不会陌生,Vue3.x 感觉就是参考了vue-async-manager
. Vue3.x 新出的内置组件Suspense
, 它提供两个template
slot, 刚开始会渲染一个 fallback 状态下的内容, 直到到达某个条件后才会渲染 default 状态的正式内容, 通过使用Suspense
组件进行展示异步渲染就更加的简单。:::warning 如果使用 Suspense
, 要返回一个 promise :::Suspense
组件的使用:<Suspense> <template #default> <async-component></async-component> </template> <template #fallback> <div> Loading... </div> </template> </Suspense>
asyncComponent.vue
:
<template> <div> <h4>这个是一个异步加载数据</h4> <p>用户名:{{user.nickname}}</p> <p>年龄:{{user.age}}</p> </div> </template> <script> import { defineComponent } from "vue" import axios from "axios" export default defineComponent({ setup(){ const rawData = await axios.get("http://xxx.xinp.cn/user") return { user: rawData.data } } }) </script>
Composition Api
1、setup函数
参数1: `props`
在setup中获取props不能使用this; 只能通过该参数进行访问
因为 `props` 是响应式的,不能使用 ES6 解构,它会消除 prop 的响应性。
如果要使用解构, 可以使用toRefs完成
const { title } = toRefs(props)
参数2: context
包含属性: attrs, slots, emit, expose
attrs: 所以非props的attribute
slots: 父组件传入的插槽
emit: 子组件emit触发事件, 因为setup中不能使用this.$emit, 所以只能使用该属性
expose: 暴露公共 property (函数)
2、返回值
setup的返回值可以在模板template中被使用;
通过setup的返回值来替代data选项;
通过**返回一个执行函数**来代替在methods中定义的方法
3、关于this
在 `setup()` 内部,`this` 不是该活跃实例的引用, 因为 `setup()` 是在解析其它组件选项之前被调用的,所以 `setup()` 内部的 `this` 的行为与其它选项中的 `this` 完全不同
4、使用refs
import { ref } from 'vue' export default { setup(){ const titleRef = ref(null) return { titleRef } } }
5、单文件组件 `<script setup>`
setup顶层编写方式,**vue3.2.2**已经变成正式特性
1、优势:
- 更少的样板内容,更简洁的代码。
- 能够使用纯 Typescript 声明 props 和抛出事件。
- 更好的运行时性能 (其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理)。
- 更好的 IDE 类型推断性能 (减少语言服务器从代码中抽离类型的工作)。
2、顶层的绑定会被暴露给模板
任何在 `<script setup>` 声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容等) 都能在模板中直接使用
3、在 `<script setup>` 中必须使用 `defineProps` 和 `defineEmits` API 来声明 `props` 和 `emits`
import { defineProps, defineEmits } from 'vue'; const props = defineProps({ message: { type: String, default: "Hello" } }) const emit = defineEmits(["increment", "decrement"]); const emitEvent = () => { emit('increment', "100000") }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)