一文学会Vue3新特性

原文:一文学会Vue3新特性
公众号:前端极客技术

Vue3.0 周边生态现在已经完善得差不多了,新项目可以开始使用Vue3来开发了,今天我们先来学习Vue3的一些新特性。

Composition API

为什么选择Composition API

Composition API翻译成中文就是组合式API。有的人可能会疑惑为什么要用Composition API?原来Vue2中的options API不是也能实现吗?

我们先来Vue3官方文档中的例子:

假设我们的应用中有一个显示某个用户的仓库列表的视图。此外,我们还希望有搜索和筛选功能。实现此视图组件的代码可能如下所示:

// src/components/UserRepositories.vue

export default {
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: { 
      type: String,
      required: true
    }
  },
  data () {
    return {
      repositories: [], // 1
      filters: { ... }, // 3
      searchQuery: '' // 2
    }
  },
  computed: {
    filteredRepositories () { ... }, // 3
    repositoriesMatchingSearchQuery () { ... }, // 2
  },
  watch: {
    user: 'getUserRepositories' // 1
  },
  methods: {
    getUserRepositories () {
      // 使用 `this.user` 获取用户仓库
    }, // 1
    updateFilters () { ... }, // 3
  },
  mounted () {
    this.getUserRepositories() // 1
  }
}

该组件有以下几个职责:

  • 从假定的外部 API 获取该用户的仓库,并在用户有任何更改时进行刷新
  • 使用 searchQuery 字符串搜索仓库
  • 使用 filters 对象筛选仓库

使用 (data、computed、methods、watch) 组件选项来组织逻辑通常都很有效。然而,当我们的组件开始变得更大时,逻辑关注点的列表也会增长。尤其对于那些一开始没有编写这些组件的人来说,这会导致组件难以阅读和理解。

这是一个大型组件的示例,其中逻辑关注点按颜色进行分组。

这种碎片化使得理解和维护复杂组件变得困难。选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块。

如果能够将同一个逻辑关注点相关代码收集在一起会更好。而这正是组合式 API 使我们能够做到的。

Vue3兼容大部分Vue2语法,所以在Vue3中写Vue2语法是没问题的(废除的除外)。

setup

setup是Vue3新增的一个选项,是使用Composition API的入口。

setup的执行时机

setup只在初始化时执行一次,所有的 Composition API 函数都在这里使用。

setup是在生命周期beforeCreate之前执行。我们通过下面的代码来验证一下:

beforeCreate() {
  console.log('---------beforeCreate--------');
},
setup() {
  console.log('---------setup--------');
  return {};
},
// ---------setup--------
// ---------beforeCreate--------

由上面的执行结果我们可以推断出,setup执行时,组件对象还没有创建,此时this是undefined,因此在setup函数中不能通过this对象访问data/computed/methods/props

setup参数

setup有两个可选参数:

  • props:组件传入的属性(响应式对象,且可以监听)
  • context:上下文对象。setup中不能访问Vue2中最常用的this对象,所以context中就提供了this中最常用的三个属性:attrsslotemit
import { toRef } from 'vue'
setup(props) {
  const title = toRef(props, 'title')
  console.log(title.value)
}

由于props是响应式的,所以不能使用 ES6 解构,解构会消除prop的响应性。如果需要解构prop,可以在setup函数中使用toRefs函数来完成此操作。

export default {
  setup(props, context) {
    // Attribute (非响应式对象)
    console.log(context.attrs)
    // 插槽 (非响应式对象)
    console.log(context.slots)
    // 触发事件 (方法)
    console.log(context.emit)
  }
}

reactive

reactive函数接受一个普通对象,返回一个响应式数据对象。

const data = reactive({
  count: 0,
  result: computed(() => data.count + 1)
})

ref 和 isRef

  • ref:接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象具有指向内部值的单个 property .value。

  • isRef:检查值是否为一个 ref 对象。

export default {
  setup() {
    const count = ref(0)
    console.log('count is ref:', isRef(count))
    console.log('count: ', count)
    console.log('count.value:', count.value)
    return {
      count
    }
  }
}

toRefs

将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref

我们通过reactive创建的对象,如果在模板中使用,就必须以xxxx.xxx的形式,但如果用到的地方比较多就比较麻烦。如果用ES6解构,就会失去响应式。

如果我们利用toRefs可以将一个响应式 reactive 对象的所有原始属性转换为响应式的ref属性。

前面提到的setup函数的props要解构可以使用toRefs

示例代码如下:

<template>
  <div class="hello">
    <p>number: {{number}}</p>
  </div>
</template>

<script>
import { toRefs, reactive } from 'vue'
export default {
  setup() {
    const state = reactive({
      number: 0,
      date: '20210628'
    })
    const state2 = toRefs(state)
    setInterval(() => {
      state.number++ 
    }, 1000)
    return {
      ...state2
    }
  }
}
</script>

computed函数

与Vue2中的 computed 配置功能一致,返回的是一个 ref 类型的对象。

setup() {
  const state = reactive({
    count: 0,
    plusOne: computed(() => state.count + 1)
  })
  return {
    ...toRefs(state)
  }
}

watch函数

监视指定的一个或多个响应式数据,一旦数据变化,就自动执行监视回调。

import { watch, ref, reactive } from 'vue'
export default {
  setup() {
    const name = ref('前端极客技术')
    const otherName = reactive({
      firstName: '前端',
      lastName: '技术'
    })
    watch(name, (newValue, oldValue) => {
      console.log(newValue, oldValue)
    })
    watch(
      () => {
        return otherName.firstName + otherName.lastName
      },
      value => {
        console.log(value)
      }
    )

    setTimeout(() => {
      name.value = '前端极客'
      otherName.firstName = '前端极客'
    }, 3000)
  }
}

watchEffect

为了根据响应式状态自动应用和重新应用副作用,我们可以使用 watchEffect 方法。它立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。

const count = ref(0)

watchEffect(() => console.log(count.value))
// -> logs 0

setTimeout(() => {
  count.value++
  // -> logs 1
}, 100)

生命周期

通过下面的图来看下Vue2和Vue3生命周期钩子的对比:

  • Vue3新增了setup
  • Vue2中的beforeDestroy名称变更为beforeUnmount
  • Vue2中的destroyed变更为 unmounted
  • Vue3.x 还新增用于调试的钩子函数onRenderTriggered和onRenderTricked
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')
  })
}

getCurrentInstance

getCurrentInstance 支持访问内部组件实例,用于高阶用法或库的开发。

import { getCurrentInstance } from 'vue'

const MyComponent = {
  setup() {
    const internalInstance = getCurrentInstance()

    internalInstance.appContext.config.globalProperties // 访问 globalProperties
  }
}

getCurrentInstance 只能在 setup 或生命周期钩子中调用。

如需在 setup 或生命周期钩子外使用,请先在 setup 中调用 getCurrentInstance() 获取该实例然后再使用。

Teleport

Teleport是Vue3中新增的特性,翻译成中文就是传送的意思。Teleport提供了一种简洁的方式,让组件的html在父组件界面外的特定标签下插入显示。也就是我们可以把 子组件 或者 dom节点 插入到任何你想插入到的地方去。

语法:

使用to属性,指定要插入的节点。其值为选择器

<teleport to="body"></teleport>

我们使用Teleport实现一个模态窗口:

<template>
  <button @click="modalOpen = true">
    弹出一个全屏模态窗口</button>

  <teleport to="body">
    <div v-if="modalOpen" class="modal">
      <div>
        这是一个模态窗口!
        我的父元素是"body"!
        <button @click="modalOpen = false">Close</button>
      </div>
    </div>
  </teleport>
</template>

<script>
import { ref } from 'vue';
export default {
  setup() {
    const modalOpen = ref(true)
    return {
      modalOpen
    }
  }
};
</script>

<style scoped>
.modal {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  background-color: rgba(0,0,0,.5);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.modal div {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background-color: white;
  width: 300px;
  height: 300px;
  padding: 5px;
}
</style>

Suspense

Suspense 是一个试验性的新特性并且其 API 可能随时更改。它不应该被用在生产环境。

在正确渲染组件之前进行一些异步请求是很常见的事。组件通常会在本地处理这种逻辑,绝大多数情况下这是非常完美的做法。

<suspense> 组件提供了另一个方案,允许等待整个组件树处理完毕而不是单个组件。

这个 <suspense> 组件有两个插槽。它们都只接收一个子节点。default 插槽里的节点会尽可能展示出来。如果不能,则展示 fallback 插槽里的节点。

常见的一个用例:用到了异步组件

// 父组件
<template>
  <suspense>
    <template #default>
      <async-demo />
    </template>
    <template #fallback>
      <div>
        Loading...
      </div>
    </template>
  </suspense>
</template>

<script>
import {defineAsyncComponent} from 'vue'
export default {
  components: {
    AsyncDemo: defineAsyncComponent(() => import('./AsyncDemo.vue'))
  }
}
</script>


// 子组件
<template>
  <h2>AsyncDemo</h2>
  <p>{{msg}}</p>
</template>

<script lang="ts">
export default {
  name: 'AsyncDemo',
  setup () {
     return new Promise((resolve) => {
       setTimeout(() => {
         resolve({
           msg: 'async import'
         })
       }, 2000)
     })
  }
}
</script>

Fragment

在Vue2中,template中只允许有一个根节点,但是在Vue3中,可以直接写多个根节点。

<template>
  <div></div>
  <div></div>
  <div></div>
</template>

Vue3破坏性变化

  • Global API 改为应用程序实例调用
  • Global and internal APIs重构为可做摇树优化
  • model选项和v-bind的sync 修饰符被移除,统一为v-model参数形式
  • 渲染函数API修改
  • 函数式组件仅能通过简单函数方式创建
  • 废弃在SFC的template上使用functional或者添加functional选项的方式声明函数式组件
  • 异步组件要求使用defineAsyncComponent 方法创建
  • 组件data选项应该总是声明为函数
  • 自定义组件白名单执行于编译时
  • is属性仅限于用在component标签上
  • $scopedSlots 属性被移除,都用$slots代替
  • 特性强制策略变更
  • 自定义指令API和组件一致
  • 一些transition类名修改:
    • v-enter -> v-enter-from
    • v-leave -> v-leave-from
  • watch 选项 和$watch 不再支持点分隔符字符串路径, 使用计算函数作为其参数
  • Vue 2.x中应用程序根容器的 outerHTML 会被根组件的模板替换 (或被编译为template)。Vue 3.x现在使用应用根容器的innerHTML取代.

Vue3移除的方法或属性

  • 移除keyCode 作为 v-on 修饰符
  • on,on, on,off and $once 移除
  • Filters移除
  • Inline templates attributes移除

最后

本文只是介绍了Vue3中的一些常用的新特性,更多具体内容需要大家详细阅读Vue3的官方文档:

https://v3.cn.vuejs.org/

如果你觉得这篇内容对你有帮助的话:

  1. 点赞支持下吧,让更多的人也能看到这篇内容

  2. 关注公众号:前端极客技术,我们一起学习一起进步。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值