Vue 3 正式发布,再度踩坑

2021-12-28 11:05:23 浏览数 (1)

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

未待完续

0 人点赞