Vue3(二)生命周期、自定义hook、组合式api、新组件、vue3改变等

本文详细介绍了Vue3中的新生命周期、自定义hook的使用、响应式数据处理工具如toRef/toRefs、浅响应式和只读选项,以及新组件如Fragment、Teleport和Suspense的用法。还涵盖了全局API的迁移和组件设计的其他改变。

目录

(一)Vue3生命周期

(二)自定义hook

(三)toRef和toRefs

1.toRef 

2.toRefs

(四)其他组合式api

1.shallowRef和shallowReactive

2.readonly和shallowReadonly

3.toRaw和markRaw

(1)toRaw

(2)markRaw:

4.customRef

5.provide和inject

6.响应式数据的判断

(五)组合式api的优势 

(六)vue3的新组件

1.Fragment组件

2.Teleport组件

3.Suspense组件

(七)其他改变

1.全局api的转移

2.其他改变


(一)Vue3生命周期

1.vue3中被改名的钩子如下

beforeDestroy ==> beforeUnmount

destroyed ==> unmounted

2.vue3提供了组合式api的钩子,和vue2的对应如下

beforeCreate ==> setup()

created ==> setup()

beforeMount ==> onBeforeMount

mounted ==> onMounted

beforeUpdate ==> onBeforeUpdate

updated ==> onUpdated

beforeUnmount ==> onBeforeUnmount

unmounted ==> onUnmounted

3.组合式api内的生命周期钩子执行时间比配置项生命周期钩子要早

(二)自定义hook

hook,本质是一个函数。自定义hook,就是将setup中使用的组合式函数进行封装,实现代码复用;与vue2的mixin类似 ;

自定义Hooks是为了处理组件逻辑的一种模式。它可以让我们在不使用组件之间复制粘贴代码的情况下重用状态逻辑。自定义hooks是简单的JavaScript函数,但是在使用是,我们要遵循两个重要(不成文)的命名约定:

  • 它们以use开头。
  • 它们可以调用其他的hook。

例如:实现获取鼠标坐标的函数,封装到文件中,组件需要就可以引入调用

在hooks/usePoints.js中:
// 自定义钩子 实现获取鼠标打点坐标
import { reactive, onMounted, onBeforeMount } from 'vue'

export default function () {
    const position = reactive({
        x: 0,
        y: 0
    })

    // 实现鼠标打点 
    function getPosition(event) {
        position.x = event.pageX
        position.y = event.pageY
        console.log(event.pageX, event.pageY);
    }

    onMounted(() => {
        window.addEventListener('click', getPosition)
    })

    onBeforeMount(() => {
        window.removeEventListener('click', getPosition)
    })
    return position
}

组件中调用:

import usePoints from './hooks/usePoints'
export default {
  setup() {
    let point = usePoints()
    return { point }
  }
}

(三)toRef和toRefs

1.toRef 

作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性

语法:const name = toRef(person ,'name')

应用:要将响应式对象中的某个属性单独提供给外部使用时

setup() {
    let person = reactive({
      name: 'csq',
      age: 18,
      job: {
        salary: 2000
      }
    })
    return {
      // toRef 创建一个新的ref对象,使得它的value值是person的某个属性的值,且能够实现响应式
      // name:ref(person.name) 虽然能够实现响应式,但动态改变的是return的这个name,而不是person.name
      name: toRef(person, 'name'),
      age: toRef(person, 'age'),
      salary: toRef(person.job, 'salary'),
    }

  }

错误思路: 

  • name:person.name 只是将person.name的静态数据传递给了name,不具备响应式;
  • name:ref(person.name) 虽然能够实现响应式,但动态改变的是return的这个name,而不是person.name;

2.toRefs

toRefs 与 toRef 功能一致,但可以批量创建多个 ref 对象,

语法:toRefs(person)

// toRefs 可以批量创建多个对象 如果是嵌套对象,只能打开第一层
return{
    ...toRefs(person),
}

(四)其他组合式api

1.shallowRef和shallowReactive

shallowRef: 只处理基本数据类型的响应式不进行对象的响应式处理

shallowReactive:只处理对象最外层属性的响应式(浅响应式)

什么时候使用?

  • 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 => shallowRef
  • 如果有一个对象数据,结构比较深,但变化时只是外层属性变化 => shallowReactive

2.readonly和shallowReadonly

readonly:让一个响应式数据变为只读的(深只读)

shallowReadonly:让一个响应式数据变为只读的 (浅只读)

应用场景:不希望数据被修改时

import { ref, reactive, readonly, shallowReadonly } from 'vue'
// 深只读 所有属性包括嵌套对象都不能修改 会发出警告
let p1 = readonly(person)
// 浅只读 嵌套对象的第一层属性不能被修改
let p2 = shallowReadonly(person)

3.toRaw和markRaw

(1)toRaw

作用:将一个由 reactive 生成的响应式对象转为普通对象

使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新

(2)markRaw:

作用:标记一个对象,使其永远不会再成为响应式对象

应用场景:

  • 有些值不应被设置为响应式的,例如复杂的第三方类库等
  • 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能

4.customRef

作用:创建一个自定义的 ref,显式声明对其依赖追踪和更新触发的控制方式 

实例:创建一个有防抖功能的自定义ref

setup() {
    // 一个防抖功能的自定义ref函数 delay:防抖延迟时间
    function useDebounceRef(value, delay = 200) {
      let timeout
      return customRef((track, trigger) => {
        return {
          get() {
            track() // 通知vue追踪数据的改变
            return value
          },
          set(newValue) {
            clearTimeout(timeout) //清除定时器 保证最后一次修改时延时set
            timeout = setTimeout(() => {
              value = newValue
              trigger() // 通知vue重新解析模板,更新页面数据
            }, delay)
          }
        }
      })
    }
    // 定义一个自定义ref的数据
    let str = useDebounceRef('hello', 500)

    return {
      str
    }
  }

track:通知vue追踪数据的改变

trigger:通知vue重新解析模板,更新页面数据

5.provide和inject

作用:实现爷孙组件之间的通信

由于vue3框架原因导致vue2使用的全局事件总线被取消,在vue3使用provide和inject函数可以实现父组件和它的后代组件之间可以进行通信(父子通信还是建议用props)

爷爷组件发起数据

import {provide} from 'vue'
// 发出数据
provide('thing', thing)

后代组件组件接收数据

import { inject } from 'vue';
// 接收数据
let thing = inject('thing')

6.响应式数据的判断

isRef():检查某个值是否为 ref。

isReactive():检查一个对象是否是由reactive()或shalloReactive()创建的代理。

isReadonly():检查传入的值是否为只读对象。只读对象的属性可以更改,但他们不能通过传入的对象直接赋值

isProxy():检查一个对象是否是由 reactive()、readonly()、shallowReactive() 或 shallowReadonly() 创建的代理。

(五)组合式api的优势 

1.Options API的缺点

使用传统Options API(配置式API)中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 

2.Composition API的优势 

  • 代码集中化,每一个功能的data、methods、computed等都写在一起,方便管理
  • 代码复用更方便,将实现同一功能的代码段封装在自定义hook内,可以真正实现组合式api 

(六)vue3的新组件

1.Fragment组件

在Vue3中,组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中

好处:减少标签层级,减小内存占用

2.Teleport组件

作用:<Teleport> 是一个内置组件,它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去

使用场景:一个组件模板的一部分在逻辑上从属于该组件,但从整个应用视图的角度来看,它在 DOM 中应该被渲染在整个 Vue 应用外部的其他地方。如,全屏模态框

举例:孙组件点击弹窗弹窗弹出,再点击弹窗关闭,但弹窗位置出现在整个页面正中再加个遮罩层会更好看

    <button @click="isShow = true">点我打开弹窗</button>
        <!-- 将这段弹窗样式传送到body标签上 -->
        <teleport to='body'>
            <!-- 遮罩层 -->
            <div class="mask" v-show="isShow">
                <div class="window" v-if="isShow">
                    <p>弹窗内容</p>
                    <button @click="isShow = false">关闭弹窗</button>
                </div>
            </div>
    </teleport>

setup() {
        let isShow = ref(false)
        return {
            isShow
        }
    },

3.Suspense组件

<Suspense> 是一个内置组件,用来在组件树中协调对异步依赖的处理。它让我们可以在组件树上层等待下层的多个嵌套异步依赖项解析完成,并可以在等待时渲染一个加载状态

使用步骤:

(1)异步引入组件

import { defineAsyncComponent } from 'vue'
// 引入异步组件
const child = defineAsyncComponent(() => import('./components/child.vue'))

(2)用Suspense组件将异步组件包裹,并配置default和fallback项

<Suspense> 组件有两个插槽:#default 和 #fallback。两个插槽都只允许一个直接子节点。在可能的时候都将显示默认槽中的节点。否则将显示后备槽中的节点。

在app.vue中:
<!-- suspense组件 等待异步组件时渲染一些额外内容 -->
    <Suspense>
      <template #default>
        <child></child>
      </template>
      <template #fallback>
        child组件加载中,请稍后
      </template>
    </Suspense>

在child组件中:
setup() {
        // 异步两秒返回promise对象
        return new Promise((resolve, rej) => {
            setTimeout(() => {
                resolve('wait')
            }, 2000)
        })
    },

(七)其他改变

1.全局api的转移

vue2中常使用的全局api如config,direactive,component等,由原来的Vue.xxx的调用方法改为了vue3中的app.xxx(app是创建应用实例对象)

2.其他改变

(1)data配置项应始终被声明为一个函数

(2)过渡类名的修改

.v-enter ==> .v-enter-from

.v-leave==> .v-leave-from

(3)移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes

(4)移除v-on.native修饰符

(5)移除过滤器filter

<think>我们之前讨论了Vue2Vue3生命周期的差异,现在用户的问题是在uni-app开发中如何组合使用Vue2Vue3生命周期。 注意:uni-app是一个使用Vue.js开发跨平台应用的框架,支持Vue2Vue3。用户可能同时使用Vue2Vue3的写法,或者需要迁移。 关键点: 1. uni-app中,页面组件都可以使用生命周期钩子。 2. uni-app自身也提供了一些页面生命周期(如onLoad, onShow等),这些与Vue生命周期并存。 3.Vue3的Composition API中,可以使用组合式函数来组织代码,包括生命周期钩子。 步骤: 1. 区分Vue2Vue3在uni-app中的使用方式。 2. 注意uni-app页面生命周期Vue生命周期的关系。 3. 如何在一个项目中同时使用(比如迁移阶段)或选择使用。 详细说明: 一、Vue2选项式API(Options API)在uni-app中的使用 - 在Vue2项目中,使用选项式API定义生命周期钩子,如created, mounted等。 - 同时,uni-app页面还可以使用onLoad, onShow等页面生命周期钩子(这些是uni-app特有的,与Vue生命周期独立)。 Vue3组合式API(Composition API)在uni-app中的使用 - 在Vue3项目中,可以使用Composition API(通过`setup`函数)新的生命周期钩子(如onMounted, onUnmounted等)。 - 同时,uni-app页面生命周期钩子(如onLoad, onShow)在Composition API中可以通过`onXXXX`函数的形式使用(注意:这些是uni-app提供的,不是Vue提供的)。 三、组合使用Vue2Vue3生命周期的场景 - 场景1:项目从Vue2升级到Vue3,部分组件使用Options API,部分使用Composition API。 - 在同一个组件中,不建议混用(即一个组件内不要同时使用Options APIComposition API定义生命周期)。 - 但在整个项目中,可以同时存在两种写法的组件。 - 注意:在Vue3中,Options API生命周期钩子仍然可用(除了`beforeDestroy``destroyed`被重命名为`beforeUnmount``unmounted`,但为了兼容,Vue3也支持旧的名称)。 - 场景2:在Vue3的Composition API中,如何同时使用Vue生命周期钩子uni-app页面生命周期钩子? - 在`setup`中,使用Vue3的钩子(如`onMounted`)uni-app的钩子(如`onLoad`)是并行的,互不影响。 - 示例: ```javascript import { onMounted } from &#39;vue&#39;; export default { setup() { // uni-app页面生命周期 onLoad(() => { console.log(&#39;页面加载&#39;); }); onShow(() => { console.log(&#39;页面显示&#39;); }); // Vue3生命周期 onMounted(() => { console.log(&#39;组件挂载&#39;); }); } } ``` 四、注意事项 - 在uni-app中,页面生命周期钩子(如onLoad, onShow)的执行时机与Vue生命周期钩子不同。例如: - onLoad: 页面加载时触发,早于Vue的beforeCreatecreated。 - onShow: 页面显示时触发,可能在Vue的mounted之后(具体取决于页面初始化情况)。 - 避免在同一个组件中混合使用Options APIComposition API定义同一个生命周期(例如,不要在setup中使用onMounted的同时又在methods同级的选项中定义mounted),因为这样会导致两个钩子都被调用,且执行顺序可能不确定(Composition API的钩子先注册,但Options API的钩子后执行?实际上,Vue3中Composition API的钩子在Options API钩子之前执行,但uni-app的页面生命周期钩子又有所不同)。 五、迁移建议 - 如果使用Vue3,推荐使用Composition API,因为它提供了更好的逻辑组织复用。 - 在迁移过程中,可以逐步将Options API组件改写为Composition API。 六、示例对比(Vue2 vs Vue3在uni-app中的生命周期使用) Vue2(Options API)示例: ```javascript export default { data() { return { message: &#39;Hello&#39; }; }, onLoad() { console.log(&#39;页面加载 - uni-app生命周期&#39;); }, created() { console.log(&#39;Vue实例创建完成&#39;); }, mounted() { console.log(&#39;Vue实例挂载完成&#39;); } }; ``` Vue3(Composition API)示例: ```javascript import { onMounted } from &#39;vue&#39;; export default { setup() { // uni-app页面生命周期 onLoad(() => { console.log(&#39;页面加载 - uni-app生命周期&#39;); }); // Vue3生命周期 onMounted(() => { console.log(&#39;Vue实例挂载完成&#39;); }); } }; ``` 七、uni-app页面生命周期Vue生命周期执行顺序(以Vue3为例) 1. onLoad (uni-app) 2. onShow (uni-app) 3. Vue的beforeCreate(在Composition API中,setup函数相当于beforeCreatecreated阶段,所以在setup函数内部代码会在这些阶段执行) 4. Vue的created(同上,setup函数内部) 5. onReady (uni-app) 页面初次渲染完成 6. Vue的onMounted (在Composition API中注册的onMounted) 注意:在Vue3的Composition API中,没有直接的beforeCreatecreated钩子,因为setup函数本身就在这两个钩子期间运行。所以,在setup函数中直接写的代码就相当于created钩子中的代码。 八、总结 - 在uni-app中,Vue2Vue3生命周期可以分别按照各自框架的规则使用。 - 同时,uni-app的页面生命周期(如onLoad, onShow)是独立于Vue生命周期的,可以在任何写法中使用。 - 在Vue3的Composition API中,使用`onXXXX`函数来注册Vue生命周期钩子,而uni-app页面生命周期钩子也是通过类似的方式(直接使用onLoad等)注册,但它们是两个不同的系统。 注意:用户提供的引用[1]提到uni-app中获取DOM的问题,但这里我们主要关注生命周期,所以不展开。 最后,生成相关问题:</think>### Uni-app 中组合使用 Vue2 Vue3 生命周期的实践指南 在 Uni-app 开发中,Vue2 Vue3生命周期可以共存,但需注意执行顺序兼容性。以下是关键实践方法: #### 1. **基础组合方式** - **Vue2 选项式 API + Vue3 组合式 API 共存** 在 `.vue` 文件中同时使用两种 API: ```vue <script> import { onMounted } from &#39;vue&#39; export default { // Vue2 选项式 API created() { console.log(&#39;Vue2 created&#39;) }, mounted() { console.log(&#39;Vue2 mounted&#39;) }, // Vue3 组合式 API setup() { onMounted(() => { console.log(&#39;Vue3 onMounted&#39;) }) } } </script> ``` **执行顺序**: `created` (Vue2) → `setup()` (Vue3) → `mounted` (Vue2) → `onMounted` (Vue3)[^1] #### 2. **Uni-app 页面生命周期Vue 生命周期的整合** Uni-app 的页面生命周期(如 `onLoad`, `onShow`)可同时与 Vue2/Vue3 生命周期配合: ```vue <script> export default { // Uni-app 页面生命周期 onLoad() { console.log(&#39;页面加载&#39;) }, onShow() { console.log(&#39;页面显示&#39;) }, // Vue2 生命周期 mounted() { console.log(&#39;DOM挂载完成&#39;) }, // Vue3 Composition API setup() { import { onMounted } from &#39;vue&#39; onMounted(() => { console.log(&#39;组合式挂载&#39;) }) } } </script> ``` **执行顺序**: `onLoad` → `onShow` → `created` (Vue2) → `setup()` (Vue3) → `mounted` (Vue2) → `onMounted` (Vue3)[^1] #### 3. **跨版本兼容性处理** - **销毁/卸载钩子的统一** Vue3 将 `beforeDestroy/destroyed` 重命名为 `beforeUnmount/unmounted`,建议在代码中统一使用新名称: ```javascript // 兼容写法 export default { beforeUnmount() {}, // Vue3 优先使用 beforeDestroy() {} // Vue2 兼容保留 } ``` - **响应式数据隔离** Vue2 的 `data` Vue3 的 `ref/reactive` 避免直接混用: ```vue <script> import { ref } from &#39;vue&#39; export default { data() { return { count: 0 } // Vue2 数据 }, setup() { const message = ref(&#39;&#39;) // Vue3 数据 return { message } } } </script> ``` #### 4. **组合式 API 的最佳实践** 推荐将逻辑封装到自定义 Hook 中复用: ```javascript // hooks/useUserData.js import { onMounted, ref } from &#39;vue&#39; export function useUserData() { const user = ref(null) onMounted(async () => { user.value = await uni.request({ url: &#39;/api/user&#39; }) }) return { user } } ``` ```vue <script> import { useUserData } from &#39;@/hooks/useUserData&#39; import { onBeforeUnmount } from &#39;vue&#39; export default { setup() { const { user } = useUserData() onBeforeUnmount(() => { console.log(&#39;组件卸载前清理&#39;) }) return { user } }, // Vue2 生命周期 mounted() { console.log(&#39;DOM操作放在这里&#39;, this.$refs.container) } } </script> ``` #### 5. **注意事项** 1. **执行顺序冲突** Vue2 的 `mounted` Vue3 的 `onMounted` 可能竞争操作 DOM,建议将 DOM 操作统一放在 Vue2 的 `mounted` 中。 2. **平台差异处理** 在 `uvue` 等特殊环境中,部分生命周期行为可能不同: ```javascript onLoad() { // 在 app-uvue 中获取当前 activity 需通过页面栈 const pages = getCurrentPages() const activity = pages[pages.length - 1].$scope.activity } ``` 3. **迁移路径** ```mermaid graph LR A[Vue2项目] --> B[逐步引入setup] B --> C[替换data为ref/reactive] C --> D[迁移生命周期到onXxx] D --> E[移除Options API] ``` > **关键建议**:新项目推荐纯 Vue3 开发,旧项目迁移时优先保持 Vue2 生命周期主体,逐步替换为 Composition API[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值