Vue 3 正式发布,再度踩坑

3 年前(已编辑)
学习 /
1408
1
这篇文章上次修改于 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

未待完续

评论区加载中...