文章目录

  • 前言
  • interface/接口定义
  • 可选属性
  • type alias
  • PropType
  • 过渡/动画
  • 点击事件


前言

这个demo是一个简单的Job List app,跟着油管上的一个博主做的,学习了解Vue3和TS语法、项目结构等。

视频地址:Vue 3 with TypeScript Tutorial

repo地址:https://github.com/HaibiPeng/frontend-needtoknow/tree/master/hyrule-jobs

interface/接口定义

我们在setup()中定义jobs对象数组,这里需要对其进行类型定义,通过泛型(generics)定义。

import { defineComponent, ref } from "vue";
import Job from "./typings/Job";

export default defineComponent({
  name: "app",
  setup() {
  	const jobs = ref<Job[]>([
	  {
	    title: "farm worker",
	    location: "lon lon ranch",
	    salary: 30000,
	    id: "1",
	  },
	  {
	    title: "quarryman",
	    location: "death mountain",
	    salary: 40000,
	    id: "2",
	  },
	  {
	    title: "flute player",
	    location: "the lost woods",
	    salary: 35000,
	    id: "3",
	  },
	  { title: "fisherman", location: "lake hylia", salary: 21000, id: "4" },
	  {
	    title: "prison guard",
	    location: "gerudo valley",
	    salary: 32000,
	    id: "5",
	  },
	]);
	return { jobs };
  }
});

Note:
setup()是一个组件选项,在创建组件之前执行,一旦 props 被解析,并作为组合式 API 的入口点。

新建Job.d.ts类型定义文件:

interface Job {
  title: string;
  location: string;
  salary: number;
  id: string;
}

export default Job;

这里用interface定义Job的内部结构/字段,描述一个对象或者函数,在后续的引用中TypeScipt会检查是否引用的是Job类型中存在的字段。

因为创建的是对象数组,因此需要在Job后加一个[],表示以Job类型创建的对象数组:

const jobs = ref<Job[]>([])

可选属性

接口里的属性不全都是必需的。 有些是只在某些条件下存在,或者根本不存在。 可选属性在应用“option bags”模式时很常用,即给函数传入的参数对象中只有部分属性赋值了。

下面是应用了“option bags”的例子:

interface SquareConfig {
  color?: string;
  width?: number;
}

type alias

在OrderTerm.d.ts中,使用type定义OderTerm的不同类型:

type OrderTerm = "location" | "title" | "salary";

export default OrderTerm;

我们一直在通过直接在类型注释中编写对象类型和联合类型来使用它们。这很方便,但是想要多次使用同一个类型并用一个名称来引用它是很常见的。

一个类型别名正是-一个名称为任何类型。类型别名的语法是:

type Point = {
  x: number;
  y: number;
};

实际上,您可以使用类型别名为任何类型命名,而不仅仅是对象类型。例如,类型别名可以命名联合类型:

type ID = number | string;

PropType

JobList.vue中定义props时,需要对props的类型进行推断和定义:

import { computed, defineComponent, PropType } from "vue";
import Job from "@/typings/Job";
import OrderTerm from "@/typings/OrderTerm";

export default defineComponent({
  props: {
    jobs: {
      // type asserti + generics
      type: Array as PropType<Job[]>,
      required: true,
    },
    order: {
      type: String as PropType<OrderTerm>,
      required: true,
    },
  },
  setup(props) {
    const orderedJobs = computed(() => {
      return [...props.jobs].sort((a: Job, b: Job) => {
        return a[props.order] > b[props.order] ? 1 : -1;
      });
    });
    return { orderedJobs };
  },
});

这里用到了vue中的PropType

过渡/动画

在对jobs进行排序时,引入了动画:

<p>Ordered by {{ order }}</p>
<transition-group name="list" tag="ul">
  <li v-for="job in orderedJobs" :key="job.id">
    <h2>{{ job.title }} in {{ job.location }}</h2>
    <div class="salary">
      <p>{{ job.salary }} rupees</p>
    </div>
    <div class="discription">
      <p>
        Lorem ipsum dolor sit amet consectetur, adipisicing elit. Rem omnis
        voluptatum eius doloremque optio iusto sequi dignissimos. Pariatur
        earum assumenda dolores possimus quidem quam, reprehenderit aliquid
        consequuntur amet non facere.
      </p>
    </div>
  </li>
</transition-group>

这里为transition-group定义了一个namelist,然后再style中添加对应的动画:

.list-move {
  transition: all 1s;
}

<transition-group> 像其它自定义组件一样,需要一个根元素。默认的根元素是一个 <span>,但可以通过 tag prop 定制。

<transition-group tag="ul">
  <li v-for="item in items" :key="item">
    {{ item }}
  </li>
</transition-group>

点击事件

<div class="order">
  <button @click="handleClick('title')">Order by title</button>
  <button @click="handleClick('salary')">Order by salary</button>
  <button @click="handleClick('location')">Order by location</button>
</div>

setup()中,定义hanleClick方法,通过传入参数决定order字段的值:

setup() {
  const order = ref<OrderTerm>("title");

  const handleClick = (term: OrderTerm) => {
    order.value = term;
  };

  return { order, handleClick };
},

JobList.vue组件中,通过在setup()中传入props参数引用jobsorder字段,通过order字段的值为key,用sort方法对jobs进行排序:

setup(props) {
  const orderedJobs = computed(() => {
    return [...props.jobs].sort((a: Job, b: Job) => {
      return a[props.order] > b[props.order] ? 1 : -1;
    });
  });
  return { orderedJobs };
},