专栏首页前端知否Vue3 Composition API教程及示例

Vue3 Composition API教程及示例

Vue引入了Composition API(基于功能的API)作为当前基于Option的API的补充。该API将随Vue 3一起发布,但是现在您可以通过将Vue 3 Composition API添加到您的Vue 2应用程序中来进行尝试。

在本教程中,我将向您展示:

  • 新的Vue Composition API概述以及与基于经典Vue Options的API的比较
  • 使用新API实现Vue组件的示例:Props,data,watch,生命周期钩子
  • 充分利用新的Vue 3 Composition API(基于函数的API)的示例:将代码拆分为函数

添加Vue Composition API会发生什么变化?

一切仍将像以前一样工作。几乎没有任何变化:

  • 命令行界面
  • 模板语法
  • 对象格式
  • 数据响应
  • 计算属性,观察者和组件生命周期的概念
  • SFC格式
  • Vue框架的渐进性

基于选项的API与组合API

当前基于选项的API概念与新的合成API(基于函数的API)概念的区别在于:

  • 基于选项的API:组件包含属性/方法/选项的类型。
  • 组合API:组件将逻辑封装到函数中。

组件选项可能变得组织起来复杂且难以维护(怪异的组件)。逻辑可能涉及props和data()的属性,某些方法,某个钩子(beforeMount/mounted)以及值班的watch。因此,一个逻辑将分散在多个选项中。

使用Composition API,每个功能都是大型组件的一部分,它封装了与逻辑相关的所有代码(属性,方法,钩子,watch观察者)。现在,较小的代码(函数)可以重复使用,并且组织得很好。

在当前的Vue 2项目中使用Vue 3 Composition API

通过安装@vue/composition-api模块,我们可以在当前的Vue 2.x项目中使用新的Vue 3 Composition API。

非常简单,只需运行以下命令:

npm install @vue/composition-api

or

yarn add @vue/composition-api

然后将其导入main.js。

import Vue from 'vue';
import CompositionApi from '@vue/composition-api';

Vue.use(CompositionApi);

Vue setup()函数

setup()是新的组件选项,我们将使用新的Vue Composition API设置组件的逻辑。如果setup()函数变得复杂,我们可以轻松地将其拆分为多个具有逻辑主题的函数。

何时调用setup()?

创建组件实例时,在props解析后调用它。

现在,使用setup()函数查看Vue组件:

const MyComponent = {
  props: {
    name: String
  },

  setup(props, context) {
    console.log(props.name);
    // context.attrs
    // context.slots
    // context.emit
    // context.parent
    // context.root
  }
}

该函数有2个参数:

props

context

context具有与this.$attrs,this.$slots,this.$emit,this.$parent,this.$root对应的属性(属性,插槽,emit,parent,root)。

即使在更新后,我们也可以使用解构attrs获取最新值

const MyComponent = {
  setup(props, { attrs }) {
    
    function onClick() {
      attrs.foo // 自动更新为最新的值
    }
  }
}

*注意:this关键字在setup()函数中不可用。

所以this.$emit不能这样使用:

setup() {
  function onClick() {
    this.$emit // 无效
  }
}

Composition API中的this.$refs

为了获得对模板中元素或组件实例的引用,我们使用ref API,以便setup()可以为渲染上下文返回可响应和可变对象。

import { ref } from '@vue/composition-api'

const MyComponent = {
  setup(props) {
    const name = ref('bezkoder.com')
    const appendName = () => {
      name.value = `hello ${props.name}`
    }
    return {
      name,
      appendName
    }
  },

  template: `<div @click="appendName">{{ name }}</div>`
}

ref会自动解包为内部值,因此我们无需在模板中附加.value:。{{name}}就足够了,而不是{{name.value}}。

Vue Composition API计算值

基于Vue2选项的API语法:

export default {
  props: {
    title: String
  },

  computed: {
    vTitle() {
      return '-' + this.title + '-';
    },
    itemsQuantity() {
      return this.items.length;
    }
  },

  data() {
    return {
      items: ['This', 'is'],
    };
  },
}

Vue 3 Composition API语法:

import { ref, computed } from '@vue/composition-api';

export default {
  props: {
    title: String
  },
  setup(props) {
    const vTitle = computed(() => '-' + props.title + '-');

    const items = ref(['This', 'is']);
    const itemsQuantity = computed(() => items.value.length);

    return {
      vTitle,
      items,
      itemsQuantity,
    };
  }
};

使用新的计算API,我们可以使用get和set函数创建可写的ref对象。

const count = ref(1)
const double = computed({
  get: () => count.value * 2,
  set: val => { count.value = val - 1 }
})

double.value = 3          // set: count.value = 3 - 1 = 2
console.log(count.value)  // 2
console.log(double.value) // get: count.value * 2 = 4

Vue Composition API Watch

这就是我们使用基于Vue2选项的API语法的方式:

export default {
  data() {
    return {
      items: ['This', 'is'],
      append: ''
    };
  },

  watch: {
    items: {
      handler: function(value, oldValue) {
        this.append = '';
        value.forEach(item => {
          this.append += item + ' ';
        });
      },
      immediate: true
    }
  },
}

就像watch选项一样,每次状态改变时,我们都可以使用新的Vue watch API来执行副作用逻辑代码。

语法为:watch(源,回调,选项)

  • source:可以是getter函数,值包装器或包含上述两种类型的数组(如果要查看多个源)
  • callback:是类似于Vue2 watcher处理程序的函数,带有2个参数:newVal,oldVal。每个参数都可以是一个数组(用于观察多个源): [newVal1,newVal2,... newValN],[oldVal1,oldVal2,... oldValN]
  • options(可选):用于配置观察者类型,其中包括:lazy,deep,flush。
import { ref, watch } from '@vue/composition-api';

export default {
  setup(props) {
    const items = ref(['This', 'is']);
    const append = ref('');

    watch(
      // getter
      () => items.value,

      // callback
      (items, oldItems) => {
        append.value = '';
        items.forEach(item => {
          append.value += item + ' ';
        });
      },

      // watch Options
      {
        lazy: false // immediate: true
      }
    )

    return {
      items,
      append
    };
  }
};

如果我们想观察多个源:

watch([aRef, bRef], ([a, b], [prevA, prevB]) => {
  /* ... */
})

我们还可以将多个源观察程序拆分为较小的观察程序。这有助于我们组织代码并创建具有不同选项的观察程序:

watch(
  // getter
  () => items.value,

  // callback
  (items, oldItems) => {
    append.value = '';
    items.forEach(item => {
      append.value += item + ' ';
    });
  },

  // watch Options
  {
    lazy: false // immediate: true
  }
)

watch(
  // getter
  () => todo.value.length,

  // callback
  (length, oldLength) => {
    todoLength.value = length;
  },

  // watch Options
  {
    lazy: true // immediate: false
  }
)

Vue Composition API生命周期钩子

使用Vue2,我们通过以下方式实现Lifecycle Hooks函数:

export default {
  beforeMount() {
    console.log('V2 beforeMount!')
  },

  mounted() {
    console.log('V2 mounted!')
  }
};

新的Vue 3 Composition API具有等效的功能,我们可以在setup()函数内使用带前缀的功能:

import { onBeforeMount, onMounted } from '@vue/composition-api';

export default {
  setup() {
    onBeforeMount(() => {
      console.log('V3 beforeMount!');
    })

    onMounted(() => {
      console.log('V3 mounted!');
    })
  }
};

您可以在下表中看到Vue2 生命周期Options与Composition API之间的映射:

将逻辑封装到函数中

现在,我们将以上所有代码组合到一个Vue组件中,您可以看到一个包含多个逻辑的复杂组件。我们需要为标题,待办事项和项目实现对应的逻辑:

import { ref, reactive, computed, watch, onBeforeMount, onMounted } from '@vue/composition-api';

export default {
  props: {
    title: String,
    initInput: String
  },

  setup(props) {
    const vTitle = computed(() => '-' + props.title + '-');

    const todo = ref(props.initInput);
    const todoLength = ref(0);

    const items = ref(['This', 'is']);
    const itemsQuantity = computed(() => items.value.length);
    const append = ref('');

    watch(
      // getter
      () => items.value,

      // callback
      (items, oldItems) => {
        append.value = '';
        items.forEach(item => {
          append.value += item + ' ';
        });
      },

      // watch Options
      {
        lazy: false // immediate: true
      }
    )

    watch(
      // getter
      () => todo.value.length,

      // callback
      (length, oldLength) => {
        todoLength.value = length;
      },

      // watch Options
      {
        lazy: false // immediate: true
      }
    )

    const add = () => {
      if (todo.value) {
        items.value.push(todo.value);
        todo.value = '';
      }
    };

    const remove = index => {
      items.value.splice(index, 1);
    };

    onBeforeMount(() => {
      console.log('V3 beforeMount!');
    })

    onMounted(() => {
      console.log('V3 mounted!');
    })

    return {
      vTitle,
      todo,
      todoLength,
      items,
      itemsQuantity,
      append,
      add,
      remove
    };
  }
};

是时候利用Vue Composition API了:将复杂的组件拆分为多个对应于不同逻辑的函数:

import { ref, computed, watch, onBeforeMount, onMounted } from "@vue/composition-api";

function useTitle(props) {
  const vTitle = computed(() => "-" + props.title + "-");

  return {
    vTitle
  };
}

function useTodoLength(todo) {
  const todoLength = ref(0);

  watch(
    // getter
    () => todo.value.length,

    // callback
    (length, oldLength) => {
      todoLength.value = length;
    },

    // watch Options
    {
      lazy: false // immediate: true
    }
  );

  return {
    todoLength
  };
}

function useItems(todo) {
  const items = ref(["This", "is"]);
  const itemsQuantity = computed(() => items.value.length);
  const append = ref("");

  watch(
    // getter
    () => items.value,

    // callback
    (items, oldItems) => {
      append.value = "";
      items.forEach(item => {
        append.value += item + " ";
      });
    },

    // watch Options
    {
      lazy: false // immediate: true
    }
  );

  const add = () => {
    if (todo.value) {
      items.value.push(todo.value);
      todo.value = "";
    }
  };

  const remove = index => {
    items.value.splice(index, 1);
  };

  return {
    items,
    itemsQuantity,
    append,
    add,
    remove
  };
}

export default {
  props: {
    title: String,
    initInput: String
  },

  setup(props) {
    const todo = ref(props.initInput);

    onBeforeMount(() => {
      console.log("V3 beforeMount!");
    });

    onMounted(() => {
      console.log("V3 mounted!");
    });

    return {
      todo,
      ...useTitle(props),
      ...useTodoLength(todo),
      ...useItems(todo)
    };
  }
};

最后

使用旧的基于Vue选项的API时,您可能会感到熟悉舒适;或者,您不希望将所有内容都作为函数,而是继续保持OOP思维方式的属性/方法。这些想法都没问题。同时开发者们也在积极开发Vue,并使其逐年变得更好,并为我们提供更多选择。何不尝试一下,感觉也很不错哦。

本文分享自微信公众号 - 前端知否(qianduanzhifou),作者:QETHAN

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-03-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Vue.js render函数那些事儿

    大多时候,我会使用template, vue单文件去渲染组件。虽然知道Vue中有个render函数,但却很少在项目中去主动使用它。使用最多的地方是在使用一些UI...

    前端知否
  • ES6+好用的小技巧,让你的代码更干净,短巧,易读

    前端知否
  • 如何在Nuxt应用程序中加载外部脚本

    我需要加载一个样式表和几个脚本。我将分享我如何使用Nuxt完成此操作以及实现此操作的不同方法。

    前端知否
  • 一分钟了解Python生成器

    生成器函数 :生成器函数类似正常的函数,但是当它需要返回值的时候使用 yield 来代替 return。如果一个函数包含 yield,那么就可以说这个函数是生成...

    小小科
  • 近期面试Java后端的一些感悟

    高并发编程、分布式框架、Spring等常用框架可以说是现在Java后端求职的必备技能。

    Java团长
  • DebConf19 敲定在巴西库里奇巴召开 时间未公布

    DebConf19 峰会的举办地今天正式敲定为巴西的库里奇巴,但是具体的召开时间仍未公布。DebConf是年度Debian开发者和贡献者峰会,首届于2008年在...

    Debian社区
  • 解密Myspace密码的姿势

    *本文原创作者:泰格实验室,本文属FreeBuf原创奖励计划,未经许可禁止转载 一、背景 MySpace成立于2003年9月,作为比FTI(Facebook、...

    FB客服
  • 数据结构 | 每日一练(18)

    ——老子

    闫小林
  • 总结了10余年工作经验,浪迹在知乎的“老”程序员给出了这50条建议

    4、注释贵精不贵多。杜绝大姨妈般的“例注”。漫山遍野的碎碎念注释,实际就是背景噪音。

    养码场
  • 如何预测蛋白质三维结构(SWISS-MODEL)

    根据对天然蛋白质结构与功能分析建立起来的数据库里的数据,可以预测一定氨基酸序列肽链空间结构和生物功能;也可以通过分子动力学、分子热力学等,根据能量最低、同一位置...

    用户1359560

扫码关注云+社区

领取腾讯云代金券