阅读 390

Vue项目实战(二)——Vue3+Vite+TypeScript实现ToDoList|刷题打卡

掘金团队号上线,助你 Offer 临门! 点击 查看详情

@Vue3+Vite+TypeScript 实现 ToDoList

不刷题,只打卡;上掘金,拿offer!

前言

I hear and I fogorget.

I see and I remember.

I do and I understand.

 小白课系列完成,下面开始我们的实战课程吧!

  • 注:本项目基于 Vue3 + Vite + TypeScript 框架实现搭建

正文

 如果看完了《Vue 小白课》,那么对 Vue3 项目的整体情况有了一定了解,并且搭建好了 Vue3 的环境。本篇我们就来练习一下 Vue3,实现 ToDoList 项目。

 倘若对 VueCLI 项目不是特别了解,请先参考:

以下是我们的搭建好的项目目录结构:

项目结构Vue3.jpg

主要文件介绍

 在这个 ToDoList 当中,涉及到知识点包括以下内容:

入口文件 -- main.js

 我们首先看下 main.js文件, 在 2.X 版本中创建一个 vue 实例是通过 new Vue()来实现的,到了 3.X 中则是通过使用 createApp 这个 API。

// 先导入createApp模块
import { createApp } from 'vue'
import App from './App.vue'

// 使用createApp方法将我们的入口文件放进去,最后挂载
createApp(App).mount('#app')
复制代码

根组件 -- App.vue

App.vue是我们项目的根组件满所有的组件都是挂载到他里面运行的。

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <HelloWorld msg="Hello Vue 3 + TypeScript + Vite" />
</template>

<script lang="ts">
// 引入 defineComponent() 以正确推断 setup() 组件的参数类型
import { defineComponent } from 'vue'
import HelloWorld from './components/HelloWorld.vue'

export default defineComponent({
  name: 'App',
  components: {
    HelloWorld,
  },
})
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
复制代码

 我们看到其中有defineComponent 函数,它只是对 setup 函数进行封装,返回 options 的对象;

export function defineComponent(options: unknown) {
  return isFunction(options) ? { setup: options } : options
}
复制代码

 defineComponent 最重要的是:在 TypeScript 下,给予组件正确的参数类型推断 。

业务组件 -- HelloWorld.vue

HelloWorld.vue就是我们要写业务的组件了。删除一些初始化显示的东西,我们的组件就是这样子:

<template>
  <h1>{{ msg }}</h1>
</template>

<script lang="ts">
import { ref, defineComponent } from 'vue'
export default defineComponent({
  name: 'HelloWorld',
  props: {
    msg: {
      type: String,
      required: true,
    },
  },
  // setup 函数是 Composition API(组合 API)的入口
  // 几乎所有东西都在setup()方法中,包括挂载的生命周期挂钩。
  setup: () => {
    const count = ref(0)
    return { count }
  },
})
</script>

<style scoped></style>
复制代码

 如果你对 Vue 2 得项目比较熟悉,看到这个组件的时候最显眼的应该是setup函数了。这个就是 Vue 3 的特性之一 ——组合式 API

 我们先对比一下 Options API(Vue 2)Composition API(Vue 3 )

Options API 约定:

  • 我们需要在 props 里面设置接收参数

  • 我们需要在 data 里面设置变量

  • 我们需要在 computed 里面设置计算属性

  • 我们需要在 watch 里面设置监听属性

  • 我们需要在 methods 里面设置事件方法

 你会发现 Options APi 都约定了我们该在哪个位置做什么事,这反倒在一定程度上也强制我们进行了代码分割。

 这种碎片化使得理解和维护复杂组件变得困难。选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块。

 现在用 Composition API,不再这么约定了,于是乎,代码组织非常灵活,我们的控制代码写在 setup 里面即可。

setup 函数的特点:

  1. setup 函数是处于 生命周期函数 beforeCreate 和 Created 两个钩子函数之间的函数 也就说在 setup 函数中是无法 使用 data 和 methods 中的数据和方法的

  2. setup 函数是 Composition API(组合 API)的入口

  3. 在 setup 函数中定义的变量和方法最后都是需要 return 出去的 不然无法再模板中使用

setup 函数的注意点:

  1. 由于在执行 setup 函数的时候,还没有执行 Created 生命周期方法,所以在 setup 函数中,无法使用 data 和 methods 的变量和方法

  2. 由于我们不能在 setup 函数中使用 data 和 methods,所以 Vue 为了避免我们错误的使用,直接将 setup 函数中的 this 修改成了 undefined

  3. setup 函数只能是同步的不能是异步的

开始实现 ToDoList

 介绍完 vue 3 项目的主要文件,我们就可以开始实现 ToDoList 项目了。

1、创建 ToDoList 组件

 我们可以将/src/components中的HelloWorld.vue文件改名为ToDoList.vue,并且 将name也改为ToDoList,将其<template>标签中不必要的内容删除,如下:

<template>
  <h1>{{ msg }}</h1>
</template>
复制代码

2、引入注册 ToDoList 组件

 在/src中的App.vue文件中,将原先注册HelloWorld的全改为ToDoList,并且 将传值msg内容改为ToDoList,如下:

<template>
  <ToDoList msg="ToDoList" />
</template>

<script lang="ts">
// 引入 defineComponent() 以正确推断 setup() 组件的参数类型
import { defineComponent } from 'vue'
import ToDoList from './components/ToDoList.vue'

export default defineComponent({
  name: 'App',
  components: {
    ToDoList,
  },
})
</script>
复制代码

 OK,以上两步做完,我们的项目运行起来是这样子的:

Vue3Title1.jpg

 来认识一下props

props 是父组件给子组件传递数据使用的

 组件实例的作用域是孤立的。这意味着不能(也不应该)在子组件的模板中直接引用父组件的数据。要让子组件使用父组件的数据,需要通过子组件的 props 选项。它分为两种方式:动态和静态

  • 静态 props
<blog-post title="My journey with Vue"></blog-post>
复制代码
  • 动态 props

 在模板中,要动态地绑定父组件的数据到子组件模板的 props,和绑定 Html 标签特性一样,使用 v-bind 绑定;

<!-- 动态赋予一个变量的值 -->
<blog-post v-bind:title="post.title"></blog-post>
复制代码

3、创建并渲染列表

 接下来,我们在setup函数中创建一个数组 items,记得要把数组 return 出来,然后在<template>里写我们的 html 结构,使用 v-for 指令来实现列表的渲染,如下:

<template>
  <h1>{{ msg }}</h1>
  <ul class="ToDoList">
    <li v-for="(item, index) in items" :key="item.id">
      <!-- 待办事项 -->
      <span v-text="item.title"></span>
      <!-- 完成按钮 -->
      <button>完成</button>
    </li>
  </ul>
</template>

<script lang="ts">
import { ref, defineComponent } from 'vue'
export default defineComponent({
  name: 'ToDoList',
  props: {
    msg: {
      type: String,
      required: true,
    },
  },
  // setup 函数是 Composition API(组合 API)的入口
  // 几乎所有东西都在setup()方法中,包括挂载的生命周期挂钩。
  setup: () => {
    const items = ref([
      {
        id: 1,
        title: '吃饭',
      },
      {
        id: 2,
        title: '睡觉',
      },
      {
        id: 3,
        title: '打豆豆',
      },
    ])
    // 注意:这里一定要return,不然在模板中是获取不到它的值的。
    return { items }
  },
})
</script>

<style scoped></style>
复制代码

 在 setup 中,我们又返回了 items 数组,其中每个元素包括 id,title,我们将 items 使用 v-for 绑定到列表中,渲染后的结果如下:

列表渲染.png

注:

  1. vue 中列表循环需加:key="唯一标识", 唯一标识可以是 item 里面 id、index 等,因为 vue 组件高度复用,增加 Key 可以标识组件的唯一性。为了更好地区别各个组件, key 的作用主要是为了高效的更新虚拟 DOM。详情请参考 -- VUE 中演示 v-for 为什么要加 key

  2. setup函数中,可以使用ref函数来创建一个响应式数据,并将其 ruturn 出去;当数据发生改变时,Vue 会自动更新 UI,

4、绑定删除事件

 接下来我们使用v-on来为按钮绑定事件,目的是点击删除按钮的时候,将列表中相应的选项删除。

<button v-on:click="deleteItem(index)">完成</button>
复制代码

 这里我们增加了click方法,方法名为deleteItem,参数为数组下标index(数字类型)。

 紧接着上面定义的 deleteItem 方法,我们接着在setup中定义deleteItem事件并返回

// 点击完成按钮,删除对应事项
let deleteItem = (index: number) => {
  items.value.splice(index, 1)
}
复制代码

注:

 在setup函数中没有 this,所有的 ref 对象,要在 js 中获取它的值,都要加上一个value属性

5、使用 input 输入框添加新事项

 截至目前我们使用的都是现成的列表,接下来我们使用输入框来动态为列表添加条目:

<div>
  待办:<input type="text" v-model="newItem" />
  <button @click="addNewItem">添加</button>
</div>
复制代码

 使用指令v-model在表单控件或者组件上创建双向绑定,即绑定 newItem,使用@来绑定事件监听器,点击添加按钮会响应事件addNewItem

addNewItem同样在setup中编写如下:

// 点击添加按钮,添加新的待办事项
let addNewItem = () => {
  // 输入框非空判断
  if (!newItem.value) {
    return
  }
  // 使用push为数组添加新元素
  let obj = {
    id: id, // id 唯一且自增
    title: newItem.value, // todo 标题
  }
  items.value.push(obj)
  // id 自增
  id++
  // 清空输入框
  newItem.value = ''
}
复制代码

 在 setup中,我们声明一个变量id默认为 0,items默认为空数组,newItem为空字符串。输入框输入内容后,点击添加按钮 items 中 push 一条内容,其中包括 id 与 title,然后 id 自增,并且最后将输入框清空。

总结

 通过以上几个关键的知识点,我们最终就实现了 ToDoList 的基本功能,效果如下:

todolist.gif

 如果喜欢,可以再加一些样式。

 路漫漫其修远兮,与诸君共勉。

参考文献:

附 ToDoList.vue 代码

<template>
  <h1>{{ msg }}</h1>
  <div>
    待办:<input type="text" v-model="newItem" />
    <button @click="addNewItem">添加</button>
  </div>
  <ul class="ToDoList">
    <li v-for="(item, index) in items" :key="item.id">
      <!-- 待办事项 -->
      <span v-text="item.title"></span>
      <!-- 完成按钮 -->
      <button v-on:click="deleteItem(index)">完成</button>
    </li>
  </ul>
</template>

<script lang="ts">
import { ref, defineComponent } from 'vue'
export default defineComponent({
  name: 'ToDoList',
  props: {
    msg: {
      type: String,
      required: true,
    },
  },
  // setup 函数是 Composition API(组合 API)的入口
  // 几乎所有东西都在setup()方法中,包括挂载的生命周期挂钩。
  setup: () => {
    const items = ref([
      {
        id: 1,
        title: '吃饭',
      },
      {
        id: 2,
        title: '睡觉',
      },
      {
        id: 3,
        title: '打豆豆',
      },
    ])
    // 绑定输入框内容
    let newItem = ref('')
    // id 唯一且自增
    let id = 0
    // 点击完成按钮,删除对应事项
    let deleteItem = (index: number) => {
      items.value.splice(index, 1)
    }
    // 点击添加按钮,添加新的待办事项
    let addNewItem = () => {
      // 输入框非空判断
      if (!newItem.value) {
        return
      }
      // 使用push为数组添加新元素
      let obj = {
        id: id, //
        title: newItem.value, // todo 标题
      }
      items.value.push(obj)
      // id 自增
      id++
      // 清空输入框
      newItem.value = ''
    }
    // 注意:这里一定要return,不然在模板中是获取不到它的值的。
    return { items, id, newItem, deleteItem, addNewItem }
  },
})
</script>

<style scoped></style>
复制代码

后记:Hello 小伙伴们,如果觉得本文还不错,记得点个赞或者给个 star,你们的赞和 star 是我编写更多更丰富文章的动力!GitHub 地址

知识共享许可协议
db 的文档库db 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于github.com/danygitgit上的作品创作。
本许可协议授权之外的使用权限可以从 creativecommons.org/licenses/by… 处获得。

文章分类
前端
文章标签