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