vue3 - 简解

234 阅读4分钟

是什么:

前端框架

为什么:

  • 性能提升
  • vue2的按区域写代码转为vue3的按照逻辑分类写代码
  • vue2对ts支持有限

vue2可按逻辑分类写代码的mixin写法缺点:

  • 命名冲突
  • 不清楚暴露出来的变量作用
  • 重用到其它component经常会遇到问题

怎么做:

composition API

  • ref - 为基础数据类型添加响应式状态
  • reactive -为对象添加响应式状态
  • toRef - 可以用来为源响应式对象上的某个 property 新创建一个ref。然后,ref 可以被传递,它会保持对其源 property 的响应式连接。
  • toRefs - 将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的ref(类似使用拓展用算符...的方法返回数据data)
// toRef 传入对象和对象内某属性
const fooRef = toRef(state, 'foo')

// toRefs 传入对象
const state = reactive({
  foo: 1,
  bar: 2
})

const stateAsRefs = toRefs(state)

当从组合式函数返回响应式对象时,`toRefs` 非常有用,这样消费组件就可以在不丢失响应性的情况下对返回的对象进行解构/展开:

computed watch - 监控响应式数据的变化(可同时监控多个,用数组形式表示,用数组形式表示时返回的newValue,oldValue也是会按数组形式对应位置返回)

新生命周期

下表包含如何在 setup () 内部调用生命周期钩子:

选项式 APIHook inside setup
beforeCreateNot needed*
createdNot needed*
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted
errorCapturedonErrorCaptured
renderTrackedonRenderTracked
renderTriggeredonRenderTriggered
activatedonActivated
deactivatedonDeactivated

注意点:

  • onErrorCaptured - 当捕获一个来自子孙组件的异常时激活钩子函数
  • onRenderTracked - 状态跟踪(会跟踪页面上所有响应式变量和方法的状态,也就是return出来的)
  • onRenderTriggered - 状态触发(它不会跟踪每一个值,而是给你变化值的信息,并且新值和旧值都会给你明确的展示出来。)

vue3模块化-可使用import引入外部的ts文件export出的方法,实现响应式封装。

使用ts泛型封装axios,使用defineComponent替代/包裹component对象,提供输入提示和setup里的props和context参数。context有包裹attrs,slots,emit方法。

自定义函数-Hooks函数

Teleport(传送门)-瞬移组件位置,即渲染可以在app同级或者某父级某位置(可用id指定),但引入可在父级引入

app.vue/某父级.vue
<div id="#id"></div>

父级.vue
<组件>
引入组件

组件.vue
<teleport to="#id">
    <组件>
</teleport>

Suspense-异步加载组件

使用方法一:与插槽配合

1. 写一个异步组件

defineComponent 包裹要导出的实例对象能获取到更多提示。

1.1 setup 中返回一个 Promise 对象

/src/components/Async.vue

**

<template>
  <h1>{{ result }}</h1>
</template>

<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  setup() {
    return new Promise(resolve => {
      setTimeout(() => {
        return resolve({ result: "HI~Async" });
      }, 3000);
    });
  }
});
</script>

1.2 setup 中使用 async/await 代替返回 Promise(更推荐)

/src/components/Async.vue

**

<template>
  <h1>{{ result }}</h1>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";

export default defineComponent({
  async setup() {
    const result = ref("");
    await setTimeout(() => {
      result.value = "HI~Async";
    }, 3000);

    return {
      result
    };
  }
});
</script>

2. 在 App.vue 中使用异步组件

注意:

  • 使用 <Suspense></Suspense> 包裹所有异步组件相关代码
  • <Suspense></Suspense><template #default></template> 插槽包裹异步组件
  • <Suspense></Suspense><template #fallback></template> 插槽包裹渲染异步组件之前的内容

/src/App.vue

**

<template>
  <div id="app">
    <Suspense>
      <template #default>
        <Async></Async>
      </template>
      <template #fallback>
        <h1>Loading...</h1>
      </template>
    </Suspense>
  </div>
</template>

<script lang="ts">
// import { ref } from "vue";
import Async from "./components/Async.vue";

export default {
  name: "App",
  components: {
    Async
  }
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

完成

vue3 全局api修改:

观察 main.ts

可以看到,vue3 的入口文件变得简洁了很多,只有短短三行代码了。

**

import { createApp } from "vue";
import App from "./App.vue";

createApp(App).mount("#app");

vue2 中的全局对象是 Vue (),可以对它进行一系列的配置,比如说修改 Vue.prototypeVue.use()等等。这些操作是直接修改 Vue 对象的,这导致了一些问题:

  • 在单元测试中,全局配置非常容易污染全局环境,插件在测试中变得非常困难。
  • 在不同的 apps 中,共享一份有不同配置的 Vue 对象,非常困难。

所以 vue3 用 createApp 来解决上述问题。vue3 将全局对象修改为 app 实例了。所有原来对全局对象的操作,也移动到 app 实例上。

Vue3 打包后体积变小,也有全局 API 的功劳

全局 API 在 vue3 中都改为 Name exports 具名的导出。就等于支持了 WebpackTreeshaking

如何在 Vue3 的 setup 中使用 $refs 

方法一:借助 ref() 函数

通过 ref 函数,依然可以实现类似 this.$refs 的功能。

首先在 setup 中定义一个 Ref 变量

import { defineComponent, ref, onMount } from 'vue'

defineComponent({
  setup() {
    const divRef = ref(null)

    onMount(() => {
      console.log(divRef.value)
    })
    
    return {
      divRef
    }
  }
})

然后将这个 divRef 变量挂载到 DOM 上

<template>
  <div ref="divRef" />
</template>

这样当 onMount 钩子被触发的时候,div 的 DOM 会在控制台打印出来。

另外 ref 也能实现动态关联,具体实现可以参考文章 《$refs and the Vue 3 Composition API》

方法二:找到 this

通过 getCurrentInstance() 可以获得 vue 实例对象。

我们稍微改造下上文的代码

import { defineComponent, getCurrentInstance, onMount } from 'vue'

defineComponent({
  setup() {
    onMount(() => {
      console.log(getCurrentInstance().ctx.$refs.divRef)
    })
  }
})
<template>
  <div ref="divRef" />
</template>

注意,使用 getCurrentInstance 是有一些限制的,可以参考官方说明