Maison  >  Article  >  interface Web  >  Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)

Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)

WBOY
WBOYavant
2022-01-31 06:00:313999parcourir

Cet article vous apporte quelques connaissances sur les différences entre vue3 et vue2, y compris les changements de cycle de vie, les changements d'instance, les changements de méthode, etc. J'espère qu'il sera utile à tout le monde.

Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)

1. Modifications du cycle de vie : 3.x (en haut) 2.x (en bas)

Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)
Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)

Il n'est pas difficile de voir que la fonction de cycle de vie est détruite entre Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie) et vue2. 0 Il y a des changements de temps en temps :

beforeDestroy --> beforeUnmount
destroyed --> unmounted
其他的区别主要在于书写使用的语言上的差别
在ts中使用 class 类组件书写可以 参考 vue-class-component 或者 vue-property-decorator
书写的风格和Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)的选项式区别不大。
如果使用js书写代码 则应当使用组合式。

Les problèmes causés par des changements spécifiques seront expliqués dans la méthode d'écriture combinée ci-dessous.

2. Modifications dans la méthode de définition des variables globales

// 之前(Vue 2.x)
Vue.prototype.$http = () => {}
Vue.prototype.url= 'http://123'
// 之后(Vue 3.x)
const app = createApp({})
app.config.globalProperties.$http = () => {}
app.config.globalProperties.url= 'http://123'

3. Modifications dans la création d'instances de vue

//=======vue3.x
//使用createApp函数来实例化vue,
//该函数接收一个根组件选项对象作为第一个参数
//使用第二个参数,我们可以将根 prop 传递给应用程序
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
createApp(App,{ userName: "blackLieo" })
.use(store)
.use(router)
.mount('#app')  
//由于 createApp 方法返回应用实例本身,因此可以在其后链式调用其它方法,这些方法可以在以下部分中找到。

//=======vue2.x
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
 Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

4 Modifications dans l'utilisation des emplacements

//================Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)使用插槽基本上直接使用slot进行操作//其中Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)经历了两次更迭,2.6.0版本slot升级为v-slot<p>
    <slot></slot> // 具名 作用域插槽
    <slot></slot> //默认插槽</p>//父组件调用该组件<test>
    <template>
       <p>默认插槽</p>
    </template>
    // 作用域插槽
    <template>
       <el-form>
         <el-form-item>
           <el-link>{{ current.id }}</el-link>
         </el-form-item>
         <el-form-item>
           <el-link>{{ current.name }}</el-link>
         </el-form-item>
         <el-form-item>
           <el-link>{{ current.label }}</el-link>
         </el-form-item>
         <el-form-item>
           <el-link>{{ current.group }}</el-link>
         </el-form-item>
         <el-form-item>
           <el-link>{{ current.runtime }}</el-link>
         </el-form-item>
         <el-form-item>
           <el-link>{{ current.category }}</el-link>
         </el-form-item>
       </el-form>
     </template>
 </test>

 
 //==============Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)使用插槽//在Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)中,插槽使用v-slot 简写用#<p>	
   <slot></slot>
   <slot></slot></p><helloworld>
    <template> // 可以写为v-slot:default  #后面跟的是插槽名称
       <p>默认插槽</p>
    </template>
    //作用域插槽
    <template> // 可以写为v-slot:test="newData"
      <p>{{ newData.aa }}</p>
      <p>{{ newData.bb }}</p>
    </template></helloworld>//一个组件里面具有多个插槽时,一定要带上名称,否则可能会导致作用域错乱

5. 2 Directives personnalisées :
// 注册一个全局自定义指令 `v-focus`Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }})

Dans Vue 2, les directives personnalisées sont créées via les hooks facultatifs suivants :

bind : appelée une seule fois, lorsque la directive est première liée à un élément. Les paramètres d'initialisation uniques peuvent être effectués ici.
    inséré : Appelé lorsque l'élément lié est inséré dans le nœud parent (seul le nœud parent est garanti d'exister, mais pas nécessairement inséré dans le document).
  • update : Appelé lorsque le VNode du composant est mis à jour, mais cela peut se produire avant que son VNode enfant ne soit mis à jour. La valeur de la directive peut avoir changé ou non. Mais vous pouvez ignorer les mises à jour inutiles du modèle en comparant les valeurs avant et après la mise à jour (voir ci-dessous pour les paramètres détaillés de la fonction hook).
  • componentUpdated : Appelé une fois que tous les VNode du composant où se trouve l'instruction et ses sous-VNodes ont été mis à jour.
  • unbind : Appelé une seule fois, lorsque l'instruction n'est pas liée à l'élément.
  • Dans Vue 3, l'API des instructions personnalisées a été modifiée de manière plus sémantique, tout comme les changements du cycle de vie des composants, le tout pour une meilleure sémantique. Les changements sont les suivants :


Donc dans Vue3, vous pouvez personnaliser le. instruction comme celle-ci : Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)

const { createApp } from "vue"const app = createApp({})app.directive('focus', {
    mounted(el) {
        el.focus()
    }})
Ensuite, vous pouvez utiliser la nouvelle instruction v-focus sur n'importe quel élément du modèle, comme suit :

<input>

Mise à niveau 6.v-model

De nombreux v-model se sont produits dans Vue 3 Gros changements :
变更:在自定义组件上使用v-model时, 属性以及事件的默认名称变了
变更:v-bind的.sync修饰符在 Vue 3 中又被去掉了, 合并到了v-model里
新增:同一组件可以同时设置多个 v-model
新增:开发者可以自定义 v-model修饰符

Apprenons-en plus et comparons vue2 et vue3 en utilisant v-model sur les composants,

Par exemple :

La liaison bidirectionnelle de la zone de saisie de vue2 équivaut en fait à passer l'attribut value, et L'événement d'entrée est déclenché :


<input><input>


<input><input>

Pour le moment, v-model ne peut être lié qu'à l'attribut value du composant si nous voulons utiliser un autre attribut pour notre composant et que nous ne voulons pas mettre à jour la valeur par. déclenchant l'entrée. L'événement
但是在实际开发中,有些场景我们可能需要对一个 prop 进行 “双向绑定”, 这里以最常见的 dialog 为例子:dialog 挺合适属性双向绑定的,
外部可以控制组件的visible显示或者隐藏,组件内部关闭可以控制 visible属性隐藏,同时 visible 属性同步传输到外部。组件内部, 
当我们关闭dialog时, 在子组件中以 update:PropName 模式触发事件。
est

this.$emit('update:visible', false)
Ensuite, vous pouvez écouter cet événement dans le composant parent pour la mise à jour des données :

<el-dialog></el-dialog>
Dans le développement de vue2, nous trouverons en fait une nouvelle chose sync, donc il peut également utiliser v-bind.sync pour simplifier l'implémentation :

<el-dialog></el-dialog>
Ce qui précède mentionne l'implémentation de v-model dans Vue2 et la liaison bidirectionnelle du composant propriétés, donc dans Vue 3, cela devrait être comment y parvenir ?

Dans Vue3, utiliser v-model sur un composant personnalisé équivaut à passer un attribut modelValue et à déclencher un événement update:modelValue : sync,所以也可以使用v-bind.sync来简化实现:

<el-dialog></el-dialog>

<el-dialog></el-dialog>

上面说了 Vue2 中v-model实现以及组件属性的双向绑定,那么在 Vue 3 中应该怎样实现的呢?
在 Vue3 中, 在自定义组件上使用v-model, 相当于传递一个modelValue 属性, 同时触发一个update:modelValue事件:

<el-dialog></el-dialog><el-dialog></el-dialog>

如果要绑定属性名, 只需要给v-model传递一个参数就行, 同时可以绑定多个v-model

<template>
  <!-- 异步组件的使用 -->
  <asyncpage></asyncpage><script>import { defineAsyncComponent } from "vue";export default {
  components: {
    // 无配置项异步组件
    AsyncPage: defineAsyncComponent(() => import("./NextPage.vue")),

    // 有配置项异步组件
    AsyncPageWithOptions: defineAsyncComponent({
   loader: () => import("./NextPage.vue"),
   delay: 200,
   timeout: 3000,
   errorComponent: () => import("./ErrorComponent.vue"),
   loadingComponent: () => import("./LoadingComponent.vue"),
 })
  },}</script></template>

这个写法完全没有.sync什么事儿了, Vue 3 中抛弃了.sync写法, 统一使用v-model。

7.异步组件的使用

Vue3 中 使用 defineAsyncComponent 定义异步组件,配置选项 component 替换为 loader ,Loader 函数本身不再接收 resolve 和 reject 参数,且必须返回一个 Promise,用法如下:

   使用Composition API 解决我们在完成功能时,在 data、methods、computed 以及 mounted 中反复的跳转,他将零散分布的
   逻辑组合在一起维护,并可以将单独的逻辑再分为单独的文件

8.Composition API

<script>import { defineComponent, reactive } from "vue";export default defineComponent({
  beforeCreate() {
    console.log("----beforeCreate----");
  },
  created() {
    console.log("----created----");
  },
  setup() {
    const state = reactive({ count: 0 });
    console.log("----setup----");
    return {
      state,
    };
  },});</script>

如果想要了解<script setup></script>语法糖,请移步 vue3 setup语法糖(部分总结)

我们先来了解一下Composition具有的API
Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)

  • setup

setup 是 Vue3.x 新增的一个选项, 他是组件内使用 Composition API的入口。

setup 执行时机
通过一段代码 我们可以知道:

setup(props) {
    const { name } = props;
    console.log(name);
    const state = reactive({ count: 0 });
    return {
      state,
    };
  },

会出现如下所示的输出结果:
Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)

setup 执行时机是在 beforeCreate

reactive用于处理对象的双向绑定,ref处理 js 基础类型或者处理对象的双向绑定。
注意refs
它接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象具有指向内部值的单个 property.value。
Si vous souhaitez vous lier à définissez le nom de l'attribut, il vous suffit de passer un paramètre à v-model. Vous pouvez lier plusieurs v-model en même temps :
    <template>
      <p>
        </p>
    <p>计数:{{ num }}s</p>
        <p>主人年龄:{{ person.age }}</p>
        <p>主人姓名:{{ person.name }}</p>
        <p>动物类别:{{ animal.type }}</p>
        <p>动物名称:{{ animal.name }}</p>
        <p>动物年龄:{{ animal.age }}</p>
      </template><script>import { defineComponent, reactive, ref } from "vue";export default defineComponent({
      setup() {
        //使用ref声明基本类型
        const num = ref(0);
        //使用ref声明对象
        const person = ref({ age: 20, name: "张三" });
        //使用reactive声明对象
        const animal = reactive({ type: "猫", name: "小花", age: 5 });
        setTimeout(() => {
          person.value.age = person.value.age + 1;
          person.value.name = "李四";
          animal.age++;
        }, 1000);
        setInterval(() => {
          num.value++;
        }, 1000);
        return {
          num,
          animal,
          person,
        };
      },});</script>
  1. Cette façon d'écrire. n'a rien à voir avec .sync Wow, Vue 3 a abandonné la méthode d'écriture .sync et a utilisé le v-model de manière uniforme. 7. Utilisation de composants asynchrones
Utilisez DéfinirAsyncComponent dans Vue3 pour définir des composants asynchrones. Le composant d'option de configuration est remplacé par Loader. La fonction Loader elle-même ne reçoit plus de paramètres de résolution et de rejet et doit renvoyer une promesse. suit :

<template>
  <p>
    </p>
<p>计数:{{ num }}s</p>
    <p>主人年龄:{{ person.age }}</p>
    <p>主人姓名:{{ person.name }}</p>
    <p>动物类别:{{ atype }}</p>
    <p>动物名称:{{ aname }}</p>
    <p>动物年龄:{{ aage }}</p>
  </template><script>import { defineComponent, reactive, ref, toRefs } from "vue";export default defineComponent({
  setup() {
    //使用ref声明基本类型
    const num = ref(0);
    //使用ref声明对象
    const person = ref({ age: 20, name: "张三" });
    //使用reactive声明对象
    const animal = reactive({ atype: "猫", aname: "小花", aage: 5 });
    setTimeout(() => {
      person.value.age = person.value.age + 1;
      person.value.name = "李四";
      animal.aage++;
    }, 1000);
    setInterval(() => {
      num.value++;
    }, 1000);
    return {
      num,
      person,
      ...toRefs(animal),
    };
  },});</script>
8. API de Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)🎜
import { reactive, readonly } from 'vue'

const original = reactive({ count: 0 })

const copy = readonly(original)

// 通过 original 修改 count,将会触发依赖 copy 的侦听器

original.count++

// 通过 copy 修改 count,将导致失败并出现警告
copy.count++ // 警告: "Set operation on key 'count' failed: target is readonly."
🎜Si vous souhaitez en savoir plus sur le sucre de syntaxe <script setup></script>, veuillez déplacez-vous ici syntaxe de configuration de vue3 Sugar (résumé partiel)🎜🎜Commençons par comprendre l'API de Composition🎜Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)🎜🎜🎜🎜setup🎜🎜🎜🎜setup est une nouvelle option dans Vue3.x. C'est l'entrée de l'API Composition au sein du composant. 🎜🎜timing d'exécution de la configuration🎜 Grâce à un morceau de code, nous pouvons savoir : 🎜
import {
  defineComponent,
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
  onErrorCaptured,
  onRenderTracked,
  onRenderTriggered,} from "vue";export default defineComponent({
  //beforeCreate和created是vue2的
  beforeCreate() {
    console.log("------beforeCreate-----");
  },
  created() {
    console.log("------created-----");
  },
  setup() {
    console.log("------setup-----");
    // vue3.xOrganiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)写在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-----");
    });
    onErrorCaptured(() => {
      console.log("------onErrorCaptured-----");
    });
    onRenderTracked(() => {
      console.log("------onRenderTracked-----");
    });
    // 调试哪些数据发生了变化
    onRenderTriggered((event) => {
      console.log("------onRenderTriggered-----", event);
    });
  },});
🎜La sortie suivante apparaîtra : 🎜Insérer la description de l'image ici🎜🎜setup Le temps d'exécution est avant beforeCreate. Pour plus de détails, vous pouvez voir le cycle de vie expliquer plus tard. 🎜🎜🎜🎜paramètres de configuration🎜🎜🎜🎜Lors de l'utilisation de la configuration, il accepte deux paramètres : 🎜
  • props: 组件传入的属性
  • context

setup 中接受的props是响应式的, 当传入新的 props 时,会及时被更新。
由于是响应式的, 所以不可以使用 ES6 解构,解构会消除它的响应式。
错误代码示例, 这段代码会让 props 不再支持响应式:

setup(props) {
    const { name } = props;
    console.log(name);
    const state = reactive({ count: 0 });
    return {
      state,
    };
  },

对于以上的的问题,我们可以在后面的toRefs进行解释。
现在我们就来讲一下:

  1. reactive、ref、toRefs、readonly

在 vue2.x 中, 定义数据都是在data中。
但是 Vue3.x 可以使用reactiveref来进行数据定义。
那么ref和reactive他们有什么区别呢?

reactive用于处理对象的双向绑定,ref处理 js 基础类型或者处理对象的双向绑定。
注意refs
它接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象具有指向内部值的单个 property.value。
<template>
  <p>
    </p>
<p>计数:{{ num }}s</p>
    <p>主人年龄:{{ person.age }}</p>
    <p>主人姓名:{{ person.name }}</p>
    <p>动物类别:{{ animal.type }}</p>
    <p>动物名称:{{ animal.name }}</p>
    <p>动物年龄:{{ animal.age }}</p>
  </template><script>import { defineComponent, reactive, ref } from "vue";export default defineComponent({
  setup() {
    //使用ref声明基本类型
    const num = ref(0);
    //使用ref声明对象
    const person = ref({ age: 20, name: "张三" });
    //使用reactive声明对象
    const animal = reactive({ type: "猫", name: "小花", age: 5 });
    setTimeout(() => {
      person.value.age = person.value.age + 1;
      person.value.name = "李四";
      animal.age++;
    }, 1000);
    setInterval(() => {
      num.value++;
    }, 1000);
    return {
      num,
      animal,
      person,
    };
  },});</script>

我们绑定到页面是通过user.name,user.age;这样写感觉很繁琐,我们能不能直接将user中的属性解构出来使用呢?
答案是不能直接对user进行结构, 这样会消除它的响应式, 这里就和上面我们说props不能使用 ES6 直接解构就呼应上了。
那我们就想使用解构后的数据怎么办,解决办法就是使用toRefs
toRefs 用于将一个 reactive 对象转化为属性全部为 ref 对象的普通对象。具体使用方式如下:

<template>
  <p>
    </p>
<p>计数:{{ num }}s</p>
    <p>主人年龄:{{ person.age }}</p>
    <p>主人姓名:{{ person.name }}</p>
    <p>动物类别:{{ atype }}</p>
    <p>动物名称:{{ aname }}</p>
    <p>动物年龄:{{ aage }}</p>
  </template><script>import { defineComponent, reactive, ref, toRefs } from "vue";export default defineComponent({
  setup() {
    //使用ref声明基本类型
    const num = ref(0);
    //使用ref声明对象
    const person = ref({ age: 20, name: "张三" });
    //使用reactive声明对象
    const animal = reactive({ atype: "猫", aname: "小花", aage: 5 });
    setTimeout(() => {
      person.value.age = person.value.age + 1;
      person.value.name = "李四";
      animal.aage++;
    }, 1000);
    setInterval(() => {
      num.value++;
    }, 1000);
    return {
      num,
      person,
      ...toRefs(animal),
    };
  },});</script>

有时我们想跟踪响应式对象 (ref 或 reactive) 的变化,但我们也希望防止在应用程序的某个位置更改它,这时候,我们就需要readonly
例如,当我们有一个被传递的响应式对象时,我们不想让它在传递的的时候被改变。为此,我们可以基于原始对象创建一个只读的 proxy 对象:

import { reactive, readonly } from 'vue'

const original = reactive({ count: 0 })

const copy = readonly(original)

// 通过 original 修改 count,将会触发依赖 copy 的侦听器

original.count++

// 通过 copy 修改 count,将导致失败并出现警告
copy.count++ // 警告: "Set operation on key 'count' failed: target is readonly."
  • Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)钩子

在最开始的时候,我们就看到了两张图,其中的变化我们可以总结一下

Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)

我们可以看到beforeCreatecreatedsetup替换了(但是 Vue3 中你仍然可以使用, 因为 Vue3 是向下兼容的, 也就是你实际使用的是 vue2 的)。
其次,钩子命名都增加了on; Vue3.x 还新增用于调试的钩子函数onRenderTriggeredonRenderTricked
下面我们简单使用几个钩子, 方便大家学习如何使用,Vue3.x 中的钩子是需要从 vue 中导入的:

import {
  defineComponent,
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
  onErrorCaptured,
  onRenderTracked,
  onRenderTriggered,} from "vue";export default defineComponent({
  //beforeCreate和created是vue2的
  beforeCreate() {
    console.log("------beforeCreate-----");
  },
  created() {
    console.log("------created-----");
  },
  setup() {
    console.log("------setup-----");
    // vue3.xOrganiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)写在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-----");
    });
    onErrorCaptured(() => {
      console.log("------onErrorCaptured-----");
    });
    onRenderTracked(() => {
      console.log("------onRenderTracked-----");
    });
    // 调试哪些数据发生了变化
    onRenderTriggered((event) => {
      console.log("------onRenderTriggered-----", event);
    });
  },});

具体怎么使用,是干什么的请参考官方文档,这里就不一一赘述了。

  • watch 与 watchEffect 的用法

watch 函数用来侦听特定的数据源,并在回调函数中执行副作用。默认情况是惰性的,也就是说仅在侦听的源数据变更时才执行回调。

watch(source, callback, [options])

参数说明:

  1. source: 可以支持 string,Object,Function,Array;
  2. 用于指定要侦听的响应式变量 callback:
  3. 执行的回调函数 options:支持 deep、immediate 和 flush 选项。

其实整体和原来的Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)有一定的相似性,基本参数没有发生大的改变。

接下来我会分别介绍这个三个参数都是如何使用的, 如果你对 watch 的使用不明白的请往下看:

//监听reactive对象:watch(
      () => animal.aage,
      (curAge, preAge) => {
        console.log("新值:", curAge, "老值:", preAge);
      }
    );//监听ref变量
 watch(num, (newVal, oldVal) => {
      console.log("新值:", newVal, "老值:", oldVal);
    });
    //多个值的监听
 watch([() => animal.aage, num], ([curAge, newVal], [preAge, oldVal]) => {
      console.log("新值:", curAge, "老值:", preAge);
      console.log("新值:", newVal, "老值:", oldVal);
    });
    //监听对象复杂时,请使用深度监听 让函数的第三个参数为deep:truewatch(
      () => state.animal,
      (newType, oldType) => {
        console.log("新值:", newType, "老值:", oldType);
      },
      { deep: true }
    );

默认情况下,watch 是惰性的, 那什么情况下不是惰性的, 可以立即执行回调函数呢?其实使用也很简单, 给第三个参数中设置
immediate: true即可。

//停止监听函数
   const stopWatchRoom = watch(
      () => state.animal,
      (newType, oldType) => {
        console.log("新值:", newType, "老值:", oldType);
      },
      { deep: true }
    );
    setTimeout(() => {
      // 停止监听
      stopWatchRoom();
    }, 3000);

还有一个监听函数watchEffect,介绍一下watchEffect,看看它的使用和watch究竟有何不同,在上面代码的基础上,我们来编写。

watchEffect(() => {
      console.log(num);
    });

执行结果首先打印一次num值;然后每隔一秒,打印num值。
从上面的代码可以看出, 并没有像watch一样需要先传入依赖,watchEffect会自动收集依赖, 只要指定一个回调函数。在组件初始化时, 会先执行一次来收集依赖, 然后当收集到的依赖中数据发生变化时, 就会再次执行回调函数。所以总结对比如下:

  • watchEffect 不需要手动传入依赖
  • watchEffect 会先执行一次用来自动收集依赖
  • watchEffect无法获取到变化前的值, 只能获取变化后的值

9.Hooks

在之前vue用的mixin,所谓的混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
现在的Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)提供了一种新的东西 vue-hooks。具体的vue-hooks,请参考网上教程

为什么产生了hooks
首先从class-component/vue-options说起:

  • 跨组件代码难以复用
  • 大组件,维护困难,颗粒度不好控制,细粒度划分时,组件嵌套存层次太深影响性能
  • 类组件,this不可控,逻辑分散,不容易理解
  • mixins具有副作用,逻辑互相嵌套,数据来源不明,且不能互相消费

当一个模版依赖了很多mixin的时候,很容易出现数据来源不清或者命名冲突的问题,而且开发mixins的时候,逻辑及逻辑依赖的属性互相分散且mixin之间不可互相消费。这些都是开发中令人非常痛苦的点。
hooks存在以下优势:

  • 允许hooks间相互传递值
  • 组件之间重用状态逻辑
  • 明确指出逻辑来自哪里

我们先来封装一个hooks,假如这个hooks是一个实现年龄加减,获取双倍年龄的函数。用单独的文件存储 bus/useAge.ts

import { ref, Ref, computed } from "vue";type CountResultProps = {
  age: Ref<number>;
  doubleAge: Ref<number>;
  increase: (curAge?: number) => void;
  decrease: (curAge?: number) => void;};export default function useCount(initValue = 20): CountResultProps {
  const age = ref(initValue);
  const increase = (curAge?: number): void => {
    if (typeof curAge !== "undefined") {
      age.value += curAge;
    } else {
      age.value += 1;
    }
  };
  const doubleAge = computed(() => age.value * 2);
  const decrease = (curAge?: number): void => {
    if (typeof curAge !== "undefined") {
      age.value -= curAge;
    } else {
      age.value -= 1;
    }
  };
  return {
    age,
    doubleAge,
    increase,
    decrease,
  };}</number></number>

在组件里面调用该hooks

<template>
  <p>
    </p>
<p>计数:{{ num }}s</p>
    <p>主人年龄:{{ person.age }}</p>
    <p>主人姓名:{{ person.name }}</p>
    <p>动物类别:{{ atype }}</p>
    <p>动物名称:{{ aname }}</p>
    <p>动物年龄:{{ aage }}</p>
    <p>count: {{ age }}</p>
    <p>双倍年龄: {{ doubleAge }}</p>
    <p>
      <button>加1</button>
      <button>减一</button>
    </p>
  </template><script>import {
  defineComponent,
  reactive,
  ref,
  toRefs,
  watch,
  watchEffect,} from "vue";import useAge from "../bus/useAge.ts";export default defineComponent({
  setup() {
    //使用ref声明基本类型
    const num = ref(0);
    //使用ref声明对象
    const person = ref({ age: 20, name: "张三" });
    //使用reactive声明对象
    const animal = reactive({ atype: "猫", aname: "小花", aage: 5 });
    setTimeout(() => {
      person.value.age = person.value.age + 1;
      person.value.name = "李四";
      animal.aage++;
      animal.aname = "小橘";
    }, 1000);
    setInterval(() => {
      num.value++;
    }, 1000);
    const { age, doubleAge, increase, decrease } = useAge(22);
    return {
      num,
      person,
      ...toRefs(animal),
      age,
      doubleAge,
      increase,
      decrease,
    };
  },});</script>

10.vue2.x 与vue3.x的响应式数据对比

在Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)中,应该很多人都使用了$set这个东西吧。
数据更新了,页面为什么不变化呢?什么时候我们用$forceUpdate强制更新呢?
在vue2.x中,实现数据监听使用的是Object.defineProperty。而vue3.x使用的是Proxy

  1. Object.defineProperty只能劫持对象的属性, 而 Proxy 是直接代理对象

由于Object.defineProperty只能劫持对象属性,需要遍历对象的每一个属性,如果属性值也是对象,就需要递归进行深度遍历。
但是Proxy 直接代理对象, 不需要遍历操作

  1. Object.defineProperty对新增属性需要手动进行Observe

因为Object.defineProperty劫持的是对象的属性,所以新增属性时,需要重新遍历对象,
对其新增属性再次使用Object.defineProperty进行劫持。也就是 Vue2.x
中给数组和对象新增属性时,需要使用$set才能保证新增的属性也是响应式的,
$set内部也是通过调用Object.defineProperty去处理的。

  1. Proxy有多种拦截方法,如apply,deleteProperty等等,是Object.defineProperty()不具备的。
  2. Proxy是返回值是一个对象,可以直接进行操作,而defineProperty()要先遍历所有对象属性值才能进行操作。
    但是相对来说,Object.defineProperty()兼容性高一些。

11.Teleport

Teleport 是 Vue3.x 新推出的功能

Teleport 是什么呢?

Teleport 就像是哆啦 A 梦中的「任意门」,任意门的作用就是可以将人瞬间传送到另一个地方。有了这个认识,我们再来看一下为什么需要用到 Teleport 的特性呢,看一个小例子:

例如我们在使用Dialog组件时,我们实际开发中经常会使用到 Dialog,此时Dialog就被渲染到一层层子组件内部,处理嵌套组件的定位、z-index和样式都变得困难。但是组件希望位于页面的最上方,这时候我们将Dialog组件挂载在body上面是最好控制的,我们能够很好的通过zIndex来控制Dialog的位置,当他嵌套在templat里面的时候就不那么容易了。简单来说就是,即希望继续在组件内部使用Dialog,又希望渲染的 DOM 结构不嵌套在组件内部的 DOM 中。

此时就需要 Teleport 上场,我们可以用<teleport></teleport>包裹Dialog, 此时就建立了一个传送门,可以将Dialog渲染的内容传送到任何指定的地方。

<template>
  <p>
    </p>
<p>计数:{{ num }}s</p>
    <p>主人年龄:{{ person.age }}</p>
    <p>主人姓名:{{ person.name }}</p>
    <p>动物类别:{{ atype }}</p>
    <p>动物名称:{{ aname }}</p>
    <p>动物年龄:{{ aage }}</p>
    <p>count: {{ age }}</p>
    <p>倍数: {{ doubleAge }}</p>
    <p>
      <button>加1</button>
      <button>减一</button>
    </p>
    <el-button>点击打开 Dialog</el-button>    >
    <teleport>
      <el-dialog>
        <span>这是一段信息</span>
        <template>
          <span>
            <el-button>取 消</el-button>
            <el-button>确 定</el-button>            >
          </span>
        </template>
      </el-dialog>
    </teleport>
  </template><script>import {
  defineComponent,
  reactive,
  ref,
  toRefs,} from "vue";export default defineComponent({
  setup() {
    //使用ref声明基本类型
    const num = ref(0);
    const dialogVisible = ref(false);
    //使用ref声明对象
    const person = ref({ age: 20, name: "张三" });
    //使用reactive声明对象
    const animal = reactive({ atype: "猫", aname: "小花", aage: 5 });
    setTimeout(() => {
      person.value.age = person.value.age + 1;
      person.value.name = "李四";
      animal.aage++;
      animal.aname = "小橘";
    }, 1000);
    setInterval(() => {
      num.value++;
    }, 1000);

    return {
      dialogVisible,
      num,
      person,
      ...toRefs(animal),
    };
  },});</script>

我们可以很清楚的看到teleport上有一个to属性,这个属性是讲当前节点传送到制定位置去的。位置应该传送到哪里呢?
答案就是在index.html上面
下面是我们的首页 你会看到有一个p的ID名为dialogLL,teleport就将节点挂载在这里来了

nbsp;html>
  
    <meta>
    <link>
    <meta>
    <title>Vite App</title>
  
  
    <p></p>
    <p></p>
    <script></script>
  

如图所示:
Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)

12.Suspense

Suspense是 Vue3.x 中新增的特性, 那它有什么用呢?我们通过 Vue2.x 中的一些场景来认识它的作用。 Vue2.x 中应该经常遇到这样的场景:

<template>
  <p>
    </p>
<p>...</p>
    <p>加载中...</p>
  </template>

在前后端交互获取数据时, 是一个异步过程,一般我们都会提供一个加载中的动画,当数据返回时配合v-if来控制数据显示。
它提供两个template slot, 刚开始会渲染一个 fallback 状态下的内容, 直到到达某个条件后才会渲染 default 状态的正式内容, 通过使用
Suspense组件进行展示异步渲染就更加的简单。
具体的使用,我们可以用下面的例子来表示:
这是需要等待取值完成的的组件:

<template>
  <h1>{{ getData.result }}</h1></template><script>export default {
  name: "NewModel",
  async setup() {
    let getData = await new Promise((resolve) => {
      setTimeout(() => {
        return resolve({ result: "OK" });
      }, 3000);
    });
    return {
      getData,
    };
  },};</script>

在其他组件内调用它,当等待取值的组件取值完成后,会将loading状态变为OK状态

<template>
  <p>
    <suspense>
      <template>
        <newsuspense></newsuspense>
      </template>
      <template>
        <h1>Loadding...</h1>
      </template>
    </suspense>
  </p></template><script>import NewSuspense from "./suspens.vue";export default {
  name: "AppMain",
  components: {
    NewSuspense,
  },};</script><style></style>

效果如图:
Organiser et partager les techniques classiques de vue3 et la différence avec vue2 (partie)

13.片段(Fragment)

在 Vue2.x 中, template中只允许有一个根节点:

<template>
    <p>
        <span></span>
        <span></span>
    </p></template>

但是在 Vue3.x 中,你可以直接写多个根节点:

<template>
    <span></span>
    <span></span></template>

14.Tree-Shaking变化

Vue3.x 在考虑到 tree-shaking的基础上重构了全局和内部 API, 表现结果就是现在的全局 API 需要通过 ES Module的引用方式进行具名引用, 比如在 Vue2.x 中,我们要使用 nextTick:

// vue2.ximport Vue from "vue"Vue.nextTick(()=>{
    ...})或者 
this.nextTick(()=>{
    ...})

Vue.nextTick() 是一个从 Vue 对象直接暴露出来的全局 API,其实 $nextTick() 只是 Vue.nextTick() 的一个简易包装,只是为了方便而把后者的回调函数的 this 绑定到了当前的实例。
在 Vue3.x 中改写成这样:

import { nextTick } from "vue"nextTick(() =>{
    ...})

受影响的 API

这是一个比较大的变化, 因为以前的全局 API 现在只能通过具名导入,这一更改会对以下 API 有影响:

  1. Vue.nextTick
  2. Vue.observable(用 Vue.reactive 替换)
  3. Vue.version
  4. Vue.compile(仅限完整版本时可用)
  5. Vue.set(仅在 2.x 兼容版本中可用)
  6. Vue.delete(与上同)

相关推荐:vue.js视频教程

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer