Vue3.0初体验(Composition API)

前端开发
2020年09月10日
0

项目环境

  • @vue-cli:4.5.4
  • node: 14.8.0

安装

当前版本的vue-cli已经支持vue3.0的preview版本,所以我们可以直接通过以下代码进行安装,在选择版本时选中vue3.0(preview)即可。

shell
vue create [project-name]

setup

To start working with the Compsition API we first need a place where we can actually use it. In a Vue component, we call this place the setup.

js
export default { // (e.g. setup (props, { attrs, slots, emit })) setup (props, context) { console.log(props); console.log(context); // 在setup()中,无法访问到this. } }

composition API

reactive()

reactive()用于创建一个响应式的引用值.

vue
<template> <!-- vue3.0 内置Fragment --> <h1>{{ title }}</h1> <div> {{ message }} </div> </template> <script> export default { setup () { const state = reactive({ title: "reactive() 用于声明一个响应式的对象", message: "Hello Reactive.", }); return state; } } </script>

ref()

ref()用于声明一个响应式的原始值,它返回一个对象,该对象只有一个value属性。(e.g. const one = ref(1); // => { value: 1 })

我们可以通过isRef()来判断某个值是否为一个ref

vue
<template> <div class="page-container"> <h2>{{ title }}</h2> <div class="message">{{ message }}</div> </div> </template> <script lang="ts"> import { defineComponent, isRef, onMounted, ref, } from 'vue'; export default defineComponent({ name: 'Ref', setup () { const title = ref('ref() 用于声明单一的原始值'), message = ref('Hello ref.'); let nonRef= 'non-ref'; onMounted(() => { setTimeout(() => { message.value = title.value; // 组件挂载完3s后 将message的值变成title的值 console.log('title is ref? ', isRef(title)) console.log('nonRef is ref? ', isRef(nonRef)) }, 3000) }) return { title, message } } }); </script>

computed()

vue3.0提供的computed(),它可以接受一个回调函数作为参数,也可以接受一个包含了getter、setter的对象作为参数;

vue
<template> <div class="page-container"> <h2>Computed</h2> <div class="message"> FirstName: {{ firstName }} <br /> LastName: {{ lastName }} <br /> FullName: {{ fullName }} </div> <button @click="changeName(fullName === 'Wu Wang' ? 'San Zhang' : 'Wu Wang')">ChangeName</button> </div> </template> <script lang="ts"> import { computed, defineComponent, isRef, ref } from 'vue'; export default defineComponent({ name: 'Computed', setup () { let firstName = ref('San'), lastName = ref('Zhang'); // 当参数为一个函数时,返回一个只读的ref值 const zero = computed( () => 0 ) console.log(isRef(zero)); // 当参数为一个含有 getter 和 setter 函数的对象时, // computed 返回一个可读写的ref值 const fullName = computed({ get () { return firstName.value + ' ' + lastName.value; }, set (name: string) { let [first, last] = name.split(' '); firstName.value = first; lastName.value = last; } }); const changeName = function (name: string) { fullName.value = name; } return { firstName, lastName, fullName, changeName } } }); </script>

watch()

通过watch(),我们可以很轻易地监听某个值的变化。

watch.vue

vue
<template> <div class="page-container"> <h2>Watch</h2> <p> <input type="text" v-model="keyword" /> </p> <div class="message"> {{ message }} </div> </div> <watch-on-cleanup /> </template> <script lang="ts"> import { defineComponent, ref, watch } from 'vue'; import WatchOnCleanup from '@/components/Main/WatchOnCleanup.vue'; export default defineComponent({ name: 'Watch', components: { WatchOnCleanup }, setup () { const keyword = ref(''), message = ref('输入1秒之后将结果显示.'), loading = ref(false); const stop = watch( keyword, (keyword, prevKeyword) => { setTimeout(() => { if (keyword === '') { message.value = '输入1秒之后将结果显示.' } else { message.value = keyword } }, 1000) } ); return { keyword, message } } }); </script>

components/Main/WatchOnCleanup.vue

vue
<template> <div class="page-container"> <h2>Watch onCleanup</h2> <p> <input type="text" v-model="keyword" /> </p> <div class="message"> {{ message }} </div> </div> </template> <script lang="ts"> import { defineComponent, ref, watch } from 'vue'; export default defineComponent({ name: 'WatchOnCleanup', setup () { const keyword = ref(''), message = ref('onCleanup 实现 防抖功能'); const asyncFunc = (val: string) => { return setTimeout(() => { message.value = val; }, 1000); } const stop = watch( keyword, (keyword, prev, onCleanup) => { const t = asyncFunc(keyword); // onCleanup 如果watch监听被重复执行了,则会先清除上一次未完成的异步任务 onCleanup(() => clearTimeout(t)); } ); // stop(); // 可以通过执行watch的返回值来结束监听动作 return { keyword, message } } }) </script>

LifeCycle

在vue3.0中,提供了与以往版本稍有不同的生命周期钩子

  • beforeCreate() -> setup()
  • created() -> setup()
  • beforeMount() -> onBeforeMount()
  • mounted() -> onMounted()
  • beforeUpdate() -> onBeforeUpdate()
  • updated() -> onUpdated()
  • beforeDestroy() -> onBeforeUnmount()
  • destroyed() -> onUnmouted()
  • errorCaptured() -> onErrorCaptured()

当然,vue3.0是兼容2.x版本的,所以之前的那些生命周期钩子也是可以使用的。

vue
<template> <div class="page-container"> <h2>{{ title }}</h2> <ul class="list"> <li class="item" v-for="(item, index) of list" :key="index" > <span class="old">{{ item[0] }}</span> <span class="divider">-></span> <span class="new">{{ item[1] }}</span> </li> </ul> </div> </template> <script lang="ts"> import { app, el } from '@/main'; import { defineComponent, reactive, ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onErrorCaptured, } from 'vue'; export default defineComponent({ name: 'LifeCycle', setup (props, context) { const list = reactive([ ['2.x', '3.0'], ['beforeCreate()', 'setup()'], ['created()', 'setup()'], ['beforeMount()', 'onBeforeMount()'], ['mounted()', 'onMounted()'], ['beforeUpdate()', 'onBeforeUpdate()'], ['updated()', 'onUpdated()'], ['beforeDestroy()', 'onBeforeUnmount()'], ['destroyed()', 'onUnmouted()'], ['errorCaptured()', 'onErrorCaptured()'] ]); let title = ref('LifeCycle'); console.log('setup called.'); onBeforeMount(() => { console.log('onBeforeMount'); }); onMounted(() => { console.log('onMounted'); setTimeout(() => { title.value = title.value + ' has changed.'; }, 3000); }); onBeforeUpdate(() => { console.log('onBeforeUpdate'); }); onUpdated(() => { console.log('onUpdated'); setTimeout(() => { app.unmount(el); // 1s后卸载应用 }, 1000) }); onBeforeUnmount(() => { console.log('onBeforeUnmount'); }) onUnmounted(() => { console.log('onUnmount'); }); return { list, title } }, beforeCreate () { console.log('beforeCreate') }, created () { console.log('created') }, beforeMount () { console.log('beforeMount') }, mounted () { console.log('mounted') }, beforeUpdate () { console.log('beforeUpdate') }, updated () { console.log('updated'); }, beforeDestroy () { console.log('beforeDestroy') }, destroyed () { console.log('destroyed') }, errorCaptured () { console.log('errorCaptured') } }); </script>

output:

plain-text
setup called. beforeCreate created onBeforeMount beforeMount onMounted mounted onBeforeUpdate beforeUpdate onUpdated updated onBeforeUnmount onUnmount

Provide() And Inject()

强大的provide()inject(),意味着我们可以用来替代某些需要使用vuex来完成工作的场景。

父组件provide()提供的值,在其任意的子孙组件中都可以通过inject()来获取。

父组件:

vue
<template> <div class="page-container"> <comp-header></comp-header> <comp-content></comp-content> </div> </template> <script lang="ts"> import { defineComponent, provide } from 'vue'; import CompHeader from '@/components/Main/ProvideAndInject/Header/index.vue'; import CompContent from '@/components/Main/ProvideAndInject/Content/index.vue'; export default defineComponent({ components: { CompHeader, CompContent }, setup () { provide('globalTitle', 'Provide & Inject'); provide('globalLogo', 'Logo.'); provide('globalContent', 'Vue3.0中,父组件通过provide(key, value)提供的值,在其任意的子孙组件里面都可以通过inject(key)来获取。'); } }); </script>

components/Main/ProvideAndInject/Header/index.vue

vue
<template> <h2>{{ title }}</h2> <logo /> </template> <script lang="ts"> import { inject, ref, defineComponent } from 'vue'; import Logo from './Logo.vue'; export default defineComponent({ name: 'CompHeader', components: { Logo }, setup () { const title = ref(inject('globalTitle')); return { title } } }); </script>

components/Main/ProvideAndInject/Header/Logo.vue

vue
<template> <div class="logo">CompHeader Sub Component&lt;{{ logo }}&gt;</div> </template> <script lang="ts"> import { defineComponent, inject, ref } from 'vue'; export default defineComponent({ name: 'Logo', setup () { const logo = ref(inject('globalLogo')); return { logo } } }) </script>

components/Main/ProvideAndInject/Content/index.vue

vue
<template> <div class="content"> <p>From Views/ProvideAndInject:{{ content }}</p> </div> </template> <script lang="ts"> import { defineComponent, inject, ref } from 'vue'; export default defineComponent({ name: 'CompContent', setup () { const content = ref(inject('globalContent')); return { content } } }) </script>

renderFunction

vue3.0提供h()函数来替代以往的render()函数。

vue
<script lang="ts"> import { h, ref, defineComponent, onMounted } from 'vue'; export default defineComponent({ setup () { const count = ref(0), title = ref('RenderFunction'); onMounted(() => { setInterval(() => { count.value ++ }, 500) }) return () => { return h( 'div', [ h('h2', title.value), h('p', 'count: ' + count.value) ] ) } } }) </script>

【demo地址】