本篇介绍 10 点如何从实战中学习突破 Vue JS 3 的新特性,细细看完,一定会有收获~
主体译自:【Vue JS 3 — The Practical Guide】
更多学习资料:【https://github.com/Jerga99/vue-3-updates】
你的点赞投票是我最大的动力,关注走一波,【2021】有更多好看~!😀😀😀
初始化挂载
在 Vue2 中,我们在 main.js 通常这样进行初始化挂载:
new Vue({ render: h => h(App), components: { App } }).$mount('#app')
在 Vue3 中,调整为这样:
import { createApp } from 'vue' import App from './App.vue' const app = createApp(App) app.mount('#app')
为什么发生这样的变化?原因是:如果我们在 Vue2 中创建多个 Vue 实例,那么所有应用(#app)都会共享全局相同的配置。
// 全局共享、相互影响 Vue.mixin({ /* ... */ }) Vue.directive('...', { ... }) const app1 = new Vue({ el: '#app-1' }) const app2 = new Vue({ el: '#app-2' })
显然,这并不是我们想要的。在 Vue3 中,你可以创建多个实例,且每个实例都可以拥有单独的配置。
import { createApp } from 'vue' import App1 from './App1.vue' import App2 from './App2.vue' const app1 = createApp(App1) const app2 = createApp(App2) app1.mount('#app-1') app2.mount('#app-2') app1.directive('...', { ... }) app2.directive('...', { ... })
但是,这也并不影响我们设置共享全局配置,我们可以通过如下工厂函数方式实现:
import { createApp } from 'vue'; import App1 from './App1.vue'; import App2 from './App2.vue'; const createApp = (Instance) => { const App = createApp(Instance); App.directive('i-am-cool', { inserted: () => { console.log('I am cool!'); }, }); } createIdolApp(App1).mount('#app-1'); createIdolApp(App2).mount('#app-2');
Composition API
受到 React 的启发,Vue3 引入 Composition API 和 “hook” 函数。有了它,Vue 组件的使用变得更加灵活,也变得更加强大。特别是针对大型应用,以下会给出示例。
以前在 Vue2 中我们是这样写组件的:获取数据,设置数据。
<script> import { fetchResources } from '@/actions' import ResourceDetail from '@/components/ResourceDetail' import ResourceList from '@/components/ResourceList' export default { components: { ResourceDetail, ResourceList, }, data() { return { title: 'Your resources', selectedResource: null, resources: [] } }, async created() { this.resources = await fetchResources() }, computed: { hasResources() { return this.resourceCount > 0 }, activeResource() { return this.selectedResource || (this.hasResources && this.resources[0]) || null }, resourceCount(){ return this.resources.length } }, methods: { selectResource(resource) { this.selectedResource = {...resource} } } } </script>
完整代码在这里。
如果我们使用 composition API,会是这样写:
import { ref, onMounted, computed } from 'vue' import { fetchResources } from '@/actions' export default function useResources() { const resources = ref([]) const getResources = async () => resources.value = await fetchResources() onMounted(getResources); const resourceCount = computed(() => resources.value.length) const hasResources = computed(() => resourceCount.value > 0 ) return { resources, resourceCount, hasResources } }
这是一个非常简单的 Composition function 实现了获取 resources 数据的功能。
Composition 函数通常用 use 开头作为关键字,比如此处的 “useResources”,以此区别于普通函数。
下面针对以上代码关键点进行一一释义:
1.ref 会创建一个动态对象。如果你要从 ref 获取原始值,则需要取 “value” 属性,比如 —— resources.value
看以下示例:
var a = 7; var b = a; b = 10; // a = 7 // b = 10 var a = ref(7); var b = a; b.value = 100; // a = 100 // b = 100
我们将返回的 resoure 数组设置为 ref。是因为如果数组有新增项或移除项,这样做能在程序中有所表现。
一图胜万言:
2.getResources 函数用于获取数据。
3.onMounted 生命周期函数会在组件添加到 Dom 时调用。
4.computed 属性会随着它的依赖(resources or resourceCount)变化而重新计算。
5. return 最后一步我们将返回 data/function,我们再向组件暴露 useResource hook 函数以供使用。
最终 hook-in:
<script> import ResourceDetail from '@/components/ResourceDetail' import ResourceList from '@/components/ResourceList' import useResources from '@/composition/useResources'; export default { components: { ResourceDetail, ResourceList, }, data() { return { title: 'Your resources', selectedResource: null } }, setup() { return { ...useResources() // 在 setup 里 } }, computed: { activeResource() { return this.selectedResource || (this.hasResources && this.resources[0]) || null } }, methods: { selectResource(resource) { this.selectedResource = {...resource} } } } </script>
我们再 setup 中进行引用,返回值都可以再通过 this 进行调用。
我们在 computed 和 methods 也能同样进行调用 Composition 函数的返回。
注意:setup 钩子函数执行在组件实例创建(created)之前。
在组件创建前 setup 中 hook 被执行,只要 props 被解析,服务就会以 composition API 作为入口。因为此时当 setup 执行时,组件实例还未生成,没有 this 对象。
神奇吗?我们就这样将获取数据进行封装然后应用到了 hook 调用中。
再写一个对 resources 资源进行搜索过滤的功能:
- useSearchResource
import { ref, computed } from 'vue' export default function useSearchResource(resources) { const searchQuery = ref('') const setSearchQuery = searched => { searchQuery.value = searched } const searchedResources = computed(() => { if (!searchQuery.value) { return resources.value } const lcSearch = searchQuery.value.toLocaleLowerCase(); return resources.value.filter(r => { const title = r?.title.toLocaleLowerCase() return title.includes(lcSearch) }) }) return { setSearchQuery, searchedResources } }
- useResources
export default function useResources() { const resources = ref([]) const getResources = async () => resources.value = await fetchResources() onMounted(getResources); const resourceCount = computed(() => resources.value.length) const hasResources = computed(() => resourceCount.value > 0 ) const { searchedResources, setSearchQuery } = useSearchResources(resources) return { resources: searchedResources, resourceCount, hasResources, setSearchQuery } }
拆解分析:
- searchQuery 包含一个空字符串,使用了 ref,computed searchedResources 可以检测 searchQuery 的变化值。
- setSearchQuery 是一个简单的赋值给 searchQuery 的函数。
- searchedResources会在 searchQuery 或 resources 变化的时候触发。
- searchedResources 负责过滤 resources。每个 resource 包含一个 title,如果 title 包含 searchedQuery 字符串,那么 resource 就会被加入到 searchedResources 数组中。
- 最后函数返回 setSearchQuery 和 searchedResourced,再在 useResources 中进行引用及返回。
再看下在组件中使用它到底有多简单:
<template> ... <input @keyup="handleSearch" type="text" class="form-control" placeholder="Some title" /> ... </template> <script> ... methods: { ... handleSearch(e) { this.setSearchQuery(e.target.value) } } ... </script>
真有你的! 无论何时执行 setSearchQuery 更改 searchQuery 的值,都将重新执行 searchedResources 将其进行过滤操作并返回结果。
以上便是超重要的新特性 composition API 的实战介绍。
Data 选项
在 Vue2 中,data选项不是对象就函数,但是在 Vue3 中将只能是函数。这将被统一成标准。
<script> export default { data() { return { someData: '1234' } } } </script>
Filters 被移除
不会再出现这样的写法:
<h1>{{title | capitalized }} </h1>
这样的表达式不是合法有效的 Javascript,在 Vue 中实现这样的写法需要额外的成本。它可以很容易被转化为计算属性或函数。
computed: { capitalizedTitle() { return title[0].toUpperCase + title.slice(1); } }
多个根标签
在 Vue2 中我们经常这样包裹我们的根标签:
<template> <div> <h1>...</h1> <div class="container">...</div> ... </div> </template>
在 Vue3 中将不再有此限制:
<template> <h1>...</h1> <div class="container">...</div> ... </template>
Suspense
Suspense 是一种特殊的 Vue 组件,用于解析有异步数据的组件。
比如:
<Suspense> <template #default> <AsyncComponent> </template> <template #fallback> Loading Data... </template> </Suspense>
使用新的 Composition API,setup 可设置异步函数。Suspense 可以展示异步的模板直到 setup 被解析完。
实战代码:
<template> Welcome, {{user.name}} </template> <script> import { fetchUser } from '@/actions'; export default { async setup() { const user = await fetchUser(); return { user } } } </script>
<Suspense> <template #default> <UserPanel/> </template> <template #fallback> Loading user ... </template> </Suspense>