vue3的知识整理

embedded/2024/10/10 21:51:41/

1. vue3的生命周期

vue3的生命周期一般有2种形式写法,一种是基于vue2options API的写法,一种是vue3特有的Composition API

options API的生命周期
基本同vue2的生命周期基础,只是为了与生命周期beforeCreatecreated对应,将beforeDestroydestroyed更名为beforeUnmountunmounted,使用方法同vue2

<template><p>生命周期</p><p>{{msg}}</p><button @click="changeMsg">修改值</button><button @click="toHome">跳转页面</button>
</template>
<script>
import { useRouter } from 'vue-router'export default {name: 'lifeCycles',data() {return  {msg: 'hello vue3'}},setup() {console.log('setup')// 在`setup`函数中创建`router`对象,相当于vue2中的`this.router`const router = useRouter()const toHome = () => {router.push({path: '/'})}return {toHome}},beforeCreate() {console.log('beforeCreate');},created() {console.log('created');},beforeMount() {console.log('beforeMount');},mounted() {console.log('mounted');},beforeUpdate() {console.log('beforeUpdate');},updated() {console.log('updated');},// 由vue2 beforeDestroy改名beforeUnmount() {console.log('beforeUnmount');},// 由vue2 destroyed改名unmounted() {console.log('unmounted');},methods: {changeMsg() {this.msg = 'after changed'}}
}
</script>
初次渲染时

点击修改值的生命周期

点击跳转,组件销毁的生命周期

composition API的生命周期
composition API的生命周期钩子函数是写在setup函数中的,它所有生命周期是在vue2生命周期名字前加on,且必须先导入才可使用

在这种写法中,是没有onBeforeCreateonCreated周期的,setup等同于(或者说是介于)这两个生命周期

<template><p>composition API生命周期</p><p>{{msg}}</p><button @click="changeMsg">修改值</button><button @click="toHome">跳转页面</button>
</template>
<script>
import { useRouter } from 'vue-router'
// 必须先导入生命周期
import { onBeforeMount, onMounted, onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted
} from 'vue'
export default {name: 'lifeCycles',data() {return  {msg: 'hello vue3'}},setup() {console.log('setup')onBeforeMount(() => {console.log('onBeforeMount');})onMounted(() => {console.log('onMounted');})onBeforeUpdate(() => {console.log('onBeforeUpdate');})onUpdated(() => {console.log('onUpdated');})onBeforeUnmount(() => {console.log('onBeforeUnmount');})onUnmounted(() => {console.log('onUnmounted');})// 在`setup`函数中创建`router`对象,相当于vue2中的`this.router`const router = useRouter()const toHome = () => {router.push({path: '/'})}return {toHome}},methods: {changeMsg() {this.msg = 'after changed'}}
}</script>
初次渲染

点击修改data中的值

页面跳转,组件销毁

2. 如何理解 Composition API 和 options API ?

Composition API带来了什么:

  • 更好的代码组织
  • 更好的逻辑复用,避免mixins混入时带来的命名冲突和维护困难问题
  • 更好的类型推导

options API
使用options API,当代码很多时,即当data, watch, methods等有很多内容时,业务逻辑比较复杂,我们需要修改一部分时,可能要分别到data/methods/模板中对应修改,可能需要我们在页面反复横跳来修改,逻辑块会比较散乱。

Composition API
Composition API则会将业务相关的部分整合到一起,作为一个模块,当要修改,统一到一处修改,代码看起来会更有条理

image.png

它包含的内容包括:

  • reactive
  • ref相关(ref, toRef, toRefs,后面会具体介绍)
  • readonly
  • watch和watchEffect
  • setup
  • 生命周期钩子函数

两者的选择:

  • 不建议共用,否则容易引起混乱(思路、组织方式、写法都不太一样)
  • 小型项目,业务逻辑简单的,建议用options API,对新手也比较友好
  • 中大型项目、逻辑复杂,建议使用Composition API

Composition API它属于高阶技巧,不是基础必会的,有一定的学习成本,是为了解决复杂业务逻辑而设计,就像hooksReact中的地位一样

3. 如何理解ref,toRef,toRefs

ref

  • 通过ref方式创建响应式的值类型,并赋予初始值,并通过.value的方式获取值或修改值
  • 通过reactive方式创建响应式的引用类型,并赋予初始值,修改和获取方式同普通对象一样
  • 除了以上两种用法,还可以使用ref来声明dom元素,也就是类似vue2中的用法
<template><p>ref demo</p><p>{{nameRef}}今年{{ageRef}}岁了</p><p>他喜欢{{hobbies.type}}</p>
</template><script>
// 导入ref, reactive, onMounted
<template><p>ref demo</p><p>{{nameRef}}今年{{ageRef}}岁了</p><p>他喜欢{{hobbies.type}}</p><p ref="eleRef">我是refTemplate使用方式的内容</p>
</template><script>
import { ref, reactive, onMounted } from 'vue'
export default {name: 'ref',setup() {// refconst ageRef = ref(3); // ref创建响应式的值类型,并赋予初始值const nameRef = ref('小花')console.log(ageRef.value) // 通过.value方式获取值ageRef.value = 18 // 通过.value方式修改值// reactiveconst hobbies = reactive({type: 'basketball'})console.log(hobbies.type) // basketball,通过obj[key]方式获取值hobbies.type = 'aaaaa' // 通过obj[key]=xxx方式修改值// refTemplateconst eleRef = ref(null)onMounted(() => {// 跟vue2的区别在于,vue2使用this.$refs['eleRef']方式获取dom,这里通过eleRef.value方式获取console.log(eleRef.value) // domconsole.log(eleRef.value.innerHTML) // 我是refTemplate使用方式的内容})// 注意,这些对象都要return出去,否则就不是响应式return {ageRef,nameRef,hobbies,eleRef}}
}
</script>

image.png

PS: 小建议, ref定义的数据可以加个 Ref后缀,这样就能区分 refreactive定义的变量了

toRef

看定义有点绕

  • 针对一个响应式对象(reactive封装)的属性prop
  • 通过toRef创建一个ref对象,这个ref对象和reactive对象的某属性两者保持引用关系

注意,如果toRef是通过普通对象来生成ref对象,那么普通对象和ref对象都将不是响应式的

<template><p>toRef demo</p><p>小花今年 - {{ageRef}}岁 - {{state.age}}岁</p>
</template><script>
import { toRef, reactive } from 'vue'
export default {name: 'toRef',setup() {// 定义一个响应式的reactive引用对象const state = reactive({name: '小花', age: 3})// 如果是普通对象,使用toRef,那么它们将都不是响应式的// 也就是说ageRef和state都不是响应式// const state = {//   name: '小花', //   age: 3// }// 通过toRef创建一个响应值类型ageRef, 这个ageRef和state.age属性保持双向引用const ageRef = toRef(state, 'age')// 修改state.age值时,ageRef也会跟着改setTimeout(() => {state.age = 25}, 1000)// 修改ageRef值时,state.age也会跟着改setTimeout(() => {ageRef.value = 30}, 2000)return {state, ageRef}}
}
</script>
初始时

1s后,state.age改了,ageRef也跟着改了

2s后,ageRef改了,state.age也跟着改了

toRefs

  • 将响应式对象(reactive)的所有属性prop,转换为对应prop名字的ref对象
  • 两者保持引用关系
<template><p>toRef demo</p><!-- 这样,模板中就不用写state.name, state.age了,直接写name和age即可 --><p>{{name}}今年 - {{age}}岁</p>
</template><script>
import { toRefs, reactive } from 'vue'
export default {name: 'toRef',setup() {// 定义一个响应式的reactive引用对象const state = reactive({name: '小花', age: 3})// 相当于// const age = toRef(state, 'age')// const name = toRef(state, 'name')// const stateAsRefs = { age, name }const stateAsRefs = toRefs(state)// 修改state.age值时,就会映射到ref类型的age上setTimeout(() => {state.age = 25}, 1000)// return stateAsRefs 等同于:// const { age: age, name: name } = stateAsRefs// return { age, name }return stateAsRefs}
}
</script>

应用:
当使用composition API时,抽象出一个模块,使用toRefs返回响应式对象,这样,在接收的时候,我们就可以使用解构的方式获取到对象里面的内容,这也是比较符合我们常用的方式

// 封装一个模块,使用toRefs导出对象
export function useFeature() {const state = reactive({x: 1,y: 2})// ...return toRefs(state)
}
// 导入时,可以使用解构方式
import { useFeature } from './features'export default {setup() {// 可以在不丢失响应式的情况下解构const  { x, y } = useFeature()return { x, y }}
}

ref, toRef, toRefs 使用小结:

  • reactive做对象的响应式,用ref做值类型的响应式
  • setup中返回toRefs(state),或toRef(state, prop)
  • ref变量命名建议用xxxRef
  • 合成函数返回响应式对象时,使用toRefs
为什么需要 ref ?
  • 如果没有ref,普通的值类型定义,没法做响应式
  • computed,setup,合成函数,都有可能返回值类型,要保证其返回是响应式的
  • 如果vue不定义ref,用户可能会自己造ref,反而更加混乱
为什么需要.value ?
  • ref是一个对象(保证响应式),value用来存储值
  • 通过.value属性的getset实现响应式
  • 用于模板、reactive时,不需要.value,这是因为vue编译会自动识别,其他情况则需要使用
为什么需要 toRef 和 toRefs ?
  • 目的:为了不丢失响应式的情况下,把对象数据分散、扩散(或者说是解构)
  • 前提:针对的是响应式对象(reactive封装的对象)
  • 本质:不创建响应式(创建是ref和reactive的事),而是延续响应式

4. watch和watchEffect的区别

  1. watchwatchEffect都可以监听data的变化
  2. watch需要指定监听的属性,默认初始时不会触发,如果初始要触发,需要配置immediate: true
  3. watchEffect是不需要指定监听的属性,而是自动监听其用到的属性,它初始化时,一定会执行一次,这是为了收集要监听的属性
<template><p>watch 的使用</p><p>numberRef: {{numberRef}}</p><p>{{name}}-{{age}}</p>
</template><script>
import { ref, reactive, toRefs, watch, watchEffect } from 'vue'
export default {name:'Watch',setup() {const numberRef = ref(1000)const state = reactive({name: '小花', age: 3})// 监听ref变量watch(numberRef, (newVal, oldVal) => {console.log('watch:', newVal, oldVal);  // watch: 1000 undefined// watch: 200 1000}, {immediate: true // 第三个参数可选,是一些配置项,immediate表示初始化时就执行监听})setTimeout(() => {numberRef.value = 200}, 1000)// 监听对象watch(// 第一参数是监听对象,如果是对象需要使用函数返回形式() => state.age,// 第二个参数是监听的变化值(newVal, oldVal) => {console.log('watch:', newVal, oldVal);  // watch: 3 undefined// watch: 18 3},// 第三个参数是配置项{immediate: true, // 初始变化就监听deep: true // 深度监听})setTimeout(() => {state.age = 18}, 1000)return {numberRef,...toRefs(state)}}
}
</script>
watch
  // watchEffect监听watchEffect(() => {console.log('watchEffect');console.log(numberRef.value);console.log(state.age);})
watchEffect

5. 在setup中怎么获取组件实例

  • setup和其它compostion API中没有this
  • 如果一定要获取,要使用getCurrentInstance获取,并且在挂载后才可获取数据
  • 如果是options API,则可以像vue2一样正常使用this
<template><p>get instance</p>
</template><script>
import { getCurrentInstance, onMounted } from 'vue'
export default {name: 'GetInstance',data() {return  {x: 1,y: 2}},// composition API// 没有this,需要getCurrentInstance来获取组件实例// 且setup本身是beforeCreate和Created生命周期间的钩子,拿不到data,所以要在onMounted中获取setup() {console.log('this', this);  // this undefinedconst instance = getCurrentInstance()console.log('instance', instance.data.x); //  instance undefinedonMounted(() => {console.log('instance', instance.data.x); // instance 1}) },// options APImounted() {console.log(this.x);  // 1}
}
</script>

6. vue3升级了哪些重要的功能

参考官网升迁指南

createApp
// vue2
const app = new Vue({/*options*/})
Vue.use(/*...*/)
Vue.mixin(/*...*/)
Vue.component(/*...*/)
Vue.directive(/*...*/)// vue3
const app = Vue.createApp({/*options*/})
app.use(/*...*/)
app.mixin(/*...*/)
app.component(/*...*/)
app.directive(/*...*/)
emits属性
  1. setup中可以使用emit向父组件发出事件
  2. 在子组件中,需要emits声明向父组件发出的事件集合
<template>parent<Child msg="hello" @log="log" />
</template>
<script>
import Child from './child.vue'
export default {name: 'emits',components:{Child},methods: {log() {console.log('child emit me!')}}
}
</script>
<!-- Child.vue -->
<script>
export default {name: 'child',props: {msg: {type: String}},emits: ['log'],  // 需要声明接收的父组件传递的方法// 在setup方法中,可以使用emit方法与父组件通信setup(props, {emit}) {emit('log')},methods: {one(e) {console.log('one');},two(e) {console.log('two');}}
}
</script>
多事件处理
<template><!-- 可以同时触发多个事件 --><button @click="one($event), two($event)">触发多事件</button>
</template>
fragment

vue2只允许template中只有一个元素,如果多个元素,必须用一个元素包裹
vue3则允许template中可以直接有多个元素,这样就可以减少dom层级

移除.sync

vue2中的.sync

vue2中使用.sync是对以下语句的语法糖,父组件通过v-bind:xxx.sync='xxx'来向子组件说明这个属性是双向绑定的,子组件中通过$emit('update:xxx', newVal)来更新这个值

<text-documentv-bind:title="doc.title"v-on:update:title="doc.title = $event"
></text-document><!-- sync语法糖 -->
<text-document v-bind:title.sync="doc.title"></text-document>

vue3中,废除了.sync的写法,换成一种更具有语义的写法v-model:xxx,在父组件中使用v-model:xxx方式说明这个属性是双向绑定的,子组件中通过$emit('update:xxx', newVal)来更新这个值

<template><p>{{name}}-{{age}}</p><UserInfo v-model:name="name"v-model:age="age"/>
</template><script>
import { reactive, toRefs } from 'vue'
import UserInfo from './userInfo.vue'
export default {name: 'vmodel',components: {UserInfo},setup() {const userInfo = reactive({name: '小花',age: 3})return toRefs(userInfo)}
}
</script><!-- userInfo.vue -->
<template><input type="text" :value="name" @input="$emit('update:name', $event.target.value)"><input type="text" :value="age" @input="$emit('update:age', $event.target.value)">
</template> 
<script>
export default {props: {name: {type: String},age: {type: String}}
}
</script>
异步组件的导入方式不一样

vue2child: () => import('child.vue')
vue3:需要defineAsyncComponent导入,child: defineAsyncComponent(() => import('child.vue'))

teleport

teleport将我们的模板移动到DOMVue app之外的其他位置,比如可以使用teleport标签将组件在body

<template><p>这是放在当前组件下的内容</p><teleport to="body"><p>假设这是个弹窗,直接放到body下</p> </teleport>
</template>
image.png
最后编辑于:2024-09-27 20:39:29


喜欢的朋友记得点赞、收藏、关注哦!!!


http://www.ppmy.cn/embedded/125555.html

相关文章

VScode连接远程服务器踩坑实战(新版离线vscode-server安装)

想要用VScode连接远程服务器&#xff0c;但远程服务器并没有连接外网&#xff0c;因此需要离线手动安装vscode-server但网上的方法都是旧版本的安装&#xff0c;没有新版本的配置。因此记录一下我都踩坑实战。 1、VScode扩展安装与配置 &#xff08;1&#xff09;vscode扩展安…

微信小程序——音乐播放器

一、界面设计 播放页面&#xff1a; 显示当前播放歌曲的封面图片、歌曲名称、歌手名称。有播放 / 暂停按钮、上一首、下一首按钮。进度条显示播放进度&#xff0c;可以拖动进度条调整播放位置。音量调节滑块。 歌曲列表页面&#xff1a; 展示歌曲列表&#xff0c;包括歌曲名称、…

Vue.js 中<teleport> 组件,<Suspense> 组件

一、&#xff1c;teleport&#xff1e; 组件 在 Vue.js 中&#xff0c;<teleport> 是一个非常强大的内置组件&#xff0c;用于将子组件或元素“传送”到 DOM 中的不同位置&#xff0c;而不仅限于它们在父组件中的结构。这可以帮助解决许多布局和样式方面的问题&…

zotero主页面显示的标签名与信息处的标签名不一致

问题描述&#xff1a;我在网页导入了论文之后&#xff0c;自动匹配了一些该论文的信息&#xff0c;但是很多都是空的&#xff0c;最大的问题就是找不到出版物的信息&#xff1b; 解决&#xff1a;最后发现在信息中是叫刊名&#xff0c;其中年对应的是在日期部分&#xff1b; 极…

java项目之美食推荐商城的设计与实现源码(springboot+vue+mysql)

项目简介 美食推荐商城的设计与实现实现了以下功能&#xff1a; 美食推荐商城的设计与实现的主要使用者分为&#xff1a; 管理员在后台主要管理购物车管理、字典管理、公告信息管理、留言板管理、美食管理、美食收藏管理、美食评价管理、美食订单管理、商家管理、用户管理、管…

需求9——通过一个小需求来体会service层的作用

昨天在完成了睿哥的需求验收之后&#xff0c;暂时没有其他任务&#xff0c;因此今天可能会比较有空闲时间。趁着这个机会&#xff0c;我打算把之前完成的一些需求进行总结&#xff0c;方便以后复习和参考。 在8月份的时候&#xff0c;我负责了一个需求&#xff0c;该需求的具体…

Java API接口开发规范

文章目录 一、命名规范1.1 接口命名1.2 变量命名 二、接收参数规范2.1 请求体&#xff08;Body&#xff09;2.2 查询参数&#xff08;Query Parameters&#xff09; 三、参数检验四、接收方式规范五、异常类处理六、统一返回格式的定义七、API接口的幂等性&#xff08;Idempote…

网 络 安 全

网络安全是指保护网络系统及其所存储或传输的数据免遭未经授权访问、使用、揭露、破坏、修改或破坏的实践和技术措施。网络安全涉及多个方面&#xff0c;包括但不限于以下几个方面&#xff1a; 1. 数据保护&#xff1a;确保数据在传输和存储过程中的完整性和保密性&#xff0c;…