Vue 3 正式发布,再度踩坑
Vue 3 终于在 2020.9.18 发布了第一个正式版「One Piece」,到现在已经一周了。终于有时间来体验一把正式版的 Vue 3 是什么样子了。
准备
初始化项目
这次,我不再使用 vite 来建立项目,而使用 vue-cli。
vue create vue3-blog
cd vue3-blog
vue add typescript
yarn add vue-router@next
yarn add vuex@next
yarn serve
注意在选择 vue 版本的时候选择 vue3-preview
? Please pick a preset: Default (Vue 3 Preview) ([Vue 3] babel, eslint)
首先打开 App.vue,清理一下默认的模板,如下
<template>
<router-view> </router-view>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'App',
})
</script>
<style></style>
注:除了这个文件使用 .vue
后缀之外,其他一律文件采用 tsx
编写。
引入路由
在 src
目录新建一个 router.ts
,写入如下代码
/*
* @Author: Innei
* @Date: 2020-09-25 15:16:26
* @LastEditTime: 2020-09-25 15:31:18
* @LastEditors: Innei
* @FilePath: /vue3-blog/src/router.ts
* @Mark: Coding with Love
*/
import { defineAsyncComponent } from 'vue'
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
const routes: RouteRecordRaw[] = [
{
name: 'root',
path: '/',
component: () => import('./App.vue'),
children: [
{
path: '/',
component: defineAsyncComponent(() =>
import('./views/home').then(mo => mo.HomeView),
),
name: 'home',
},
],
},
]
export const router = createRouter({
history: createWebHashHistory(),
routes,
})
router.beforeEach((before, to, next) => {
// todo
next()
})
export default router
写法略微和 vue2-router 有点不同。
接下来来写一个视图(view)。新建一个目录views
,新建home/index.tsx
。
写如下代码。
import { defineComponent, ref } from 'vue'
export const HomeView = defineComponent({
setup() {
const names = ref([{ name: 'foo' }, { name: 'bar' }])
return () => (
<div class="">
<p>HomeView</p>
<ul>
{names.value.map(item => {
return <li>{item.name}</li>
})}
</ul>
</div>
)
},
})
执行yarn serve
之后,应该会显示如下。
数据
如果使用 vue 3 composition api 的写法,所有的数据操作都发生在 setup 函数。写法类似于 react hooks。
接下来我以调用 api 获取文章标题,渲染一个列表为例,填一填遇到的坑。
代码如下
import { useApi } from '@/hooks/useApi'
import { PostResModel } from '@/models/post'
import { defineComponent, ref } from 'vue'
export const HomeView = defineComponent({
setup() {
const api = useApi()
const posts = ref<PostResModel[]>([])
api('Post')
.gets(1, 10)
.then(res => {
const data = res.data
// posts.push(...data)
posts.value = data
})
return () => (
<div class="">
<p>HomeView</p>
<ul>
{posts.value.map(post => {
return <li>{post.title}</li>
})}
</ul>
</div>
)
},
})
api 的部分暂时忽略,返回为的 response 为一个 data
的数组。包括了 title
的字段。像上面的写法是可以达到预期效果的。
但是有几个达不到预期的写法,在这里也提一下。
首先是数据的更改的时候。
如果用了 reactive
包裹了 data,如:
// const posts = ref<PostResModel[]>([])
let posts = reactive<PostResModel[]>([])
那么,想要在获取数据之后改变 posts
中的值,貌似只能用 posts.push()
的方式,以下方式会失去响应式。
posts = res.data // 不能达到预期
posts = reactive<PostResModel[]>(res.data) // 不能达到预期
posts.push(...res.data) // 可以
但是如果用 ref
。那就可以这样写了。
posts.value = res.data
// or
posts.value.push(...res.data)
注意,ref 需要通过 .value
获取被 proxy 的值。
个人认为,一般的对象可以用 reactive
wrap,而 array 以及原始类型可以用 ref
wrap。reactive
的好处是不用多写一个 .value
。
未待完续