typescript自动开发-Vue3+TypeScript完整项目入门教程

一个完整的Vue3+Ts项目,支持.vue和.tsx编写

项目地址:

TypeScript 是 JS 的超集。 它主要提供类型系统以及对ES6的支持。 使用 TypeScript 会降低代码的可读性和可维护性。 React 和 Vue 社区中越来越多的人开始使用 TypeScript。 从最近发布的Vue3正式版来看,Vue3的源码是用TypeScript编写的,更好的TypeScript支持也是本次升级的亮点。 当然,实际开发中如何正确拥抱TypeScript也是迁移到Vue3的一个小痛点。 下面是关于Vue3和TypeScript的一些交流。

96.8%的代码是TypeScript,支持相当强

项目建设

官方仓库的Quickstart中推荐了两种方法来构建我们的SPA项目:

npm init vite-app sail-vue# OR yarn create vite-app sail-vue3

npm install -g @vue/cli # OR yarn global add @vue/cli
vue create sail-vue3
# select vue 3 preset

vite 是一个由原生 ESM 驱动的完整 Web 开发工具。 打开vite依赖的package.json,可以发现devDependencies开发依赖中已经引入了TypeScript,甚至本地开发到工具时也经常需要用到vuex、vue-router、less、sass等。 vite轻量级、开箱即用的特性满足了大多数开发场景的需求,是快速启动本地Vue项目的完美工具。

下面的demo代码也是用vite构建的

从 vue2.x 挖过来的朋友一定都知道官方脚手架 vue-cli。 vue3的更新怎么能没有vue-cli呢? 灵活可控。 丰富的官方插件适配、GUI创建管理界面、标准化的开发流程,这些都是vue-cli的特点。

如果要预装TypeScript,需要选择自动配置并勾选TypeScript

忘记使用TypeScript也没关系,添加一行cli命令即可

vue add typescript

最后,不要忘记在 .vue 代码中的 script 标签中添加 lang="ts"

<script lang="ts">

选项 API 风格

在 Vue2.x 中使用过 TypeScript 的人一定知道,引入 TypeScript 并不是一件简单的事情:

用 vue-class-component 来强化 vue 组件,让 Script 支持 TypeScript 装饰器

使用 vue-property-decorator 减少更多结合 Vue 功能的装饰器

引入ts-loader让webpack识别.ts .tsx文件

……

那么代码风格下来是这样的:

@Component({
    components:{ componentA, componentB},
})
export default class Parent extends Vue{
  @Prop(Number) readonly propA!: number | undefined
  @Prop({ default'default value' }) readonly propB!: string
  @Prop([StringBoolean]) readonly propC!: string | boolean | undefined

  // data信息
  message = 'Vue2 code style'

  // 计算属性
  private get reversedMessage (): string[] {
      return this.message.split(' ').reverse().join('')
  }

  // method
  public changeMessage (): void {
    this.message = 'Good bye'
  }
}

代码中散布着类风格的组件和各种装饰器。 感觉自己不是在写vue,有点乱。 所以,这些节省曲线的方案在vue3中肯定行不通。

在vue3中,可以直接这样写:

import { defineComponent, PropType } from 'vue'

interface Student {
  name: string
  classstring
  agenumber
}

const Component 
= defineComponent({
  props: {
    success: { typeString },
    callback: {
      typeFunction as PropType<() => void>
    },
    student: {
      typeObject as PropType,
      requiredtrue
    }
  },
  data() {
     return {
        message'Vue3 code style'
    }
  },
  computed: {
    reversedMessage(): string {
      return this.message.split(' ').reverse().join('')
    }
  }
})

vue 对 props 进行复杂类型验证时,直接使用 PropType 进行强制转换。 当类型没有显式定义时,data中返回的数据也可以推断出大多数类型。 Computed也只使用了返回类型的估计属性,即是的,代码清晰,逻辑简单,也保证了vue结构的完整性。

组合API风格

在vue3的Composition API代码风格中,比较有代表性的API是ref和reactive。 让我们看看这两种类型是如何声明的:

参考

import { defineComponent, ref } from 'vue'

const Component = defineComponent({
setup() {
  const year = ref(2020)
  const month = ref('9')

  month.value = 9 // OK
  const result = year.value.split(''// => Property 'split' does not exist on type 'number'
 }
})

分析里面的代码我们可以发现,如果我们不给出ref定义的类型,vue3也可以根据初始值进行类型推断,然后在需要指定复杂类型时只需传递一个子类即可。

Tips:如果只有setup方法,可以直接在defineComponent中传入setup函数

const Component = defineComponent(() => {
    const year = ref(2020)
    const month = ref('9')

    month.value = 9 // OK
    const result = year.value.split(''// => Property 'split' does not exist on type 'number'
})

反应性的

import { defineComponent, reactive } from 'vue'

interface Student {
  name: string
  class?: string
  agenumber
}

export default defineComponent(
{
  name: 'HelloWorld',
  setup() {
    const student = reactive({ name'阿勇'age16 })
    // or
    const student: Student = reactive({ name'阿勇'age16 })
    // or
    const student = reactive({ name'阿勇'age16class'cs' }) as Student
  }
})

声明反应式时typescript自动开发,建议使用套接字,然后有很多关于如何使用类型确定的选项。 这就是TypeScript的语法糖,本质上是一样的。

定制挂钩

Vue3参考react hooks开发了Composition API,这意味着Composition API也可以自定义和封装hook。 接下来,我们以 TypeScript 风格封装一个计数器逻辑钩子( useCount ):

首先,我们来看看如何使用这个钩子:

import { ref } from '/@modules/vue'
import  useCount from './useCount'

export default {
  name'CountDemo',
  props: {
    msgString
  },
  setup() {
    const { current: count, inc, dec, set, reset } = useCount(2, {
      min: 1,
      max15
    })
    const msg = ref('Demo useCount')

    return {
      count,
      inc,
      dec,
      set,
      reset,
      msg
    }
  }
}

其疗效为:

贴出useCount的源码:

import { ref, Ref, watch } from 'vue'

interface Range {
  min?: number,
  max?: number
}

interface Result {
  current: Ref,
  inc(delta?: number) => void,
  dec(delta?: number) => void,
  set(value: number) => void,
  reset() => void
}

export default function useCount(initialVal: number, range?: Range): Result {
  const current = ref(initialVal)
  const inc = (delta?: number): void => {
    if (typeof delta === 'number') {
      current.value += delta
    } else {
      current.value += 1
    }
  }
  const dec = (delta?: number): void => {
    if (typeof delta === 'number') {
      current.value -= delta
    } else {
      current.value -= 1
    }
  }
  const set = (value: number): void => {
    current.value = value
  }
  const reset = () => {
    current.value = initialVal
  }

  watch(current, (newVal: number, oldVal: number) => {
    if (newVal === oldVal) return
    if (range && range.min && newVal < range.min) {
      current.value = range.min
    } else if (range && range.max && newVal > range.max) {
      current.value = range.max
    }
  })

  return {
    current,
    inc,
    dec,
    set,
    reset
  }
}

分析源码

这里首先定义了hooks函数的输入参数类型和返回类型。 输入参数Range和返回Result分别由一个socket指定。 这样做后,最大的好处是,在使用 useCount 函数时, ide 会手动提示需要哪些参数以及每个参数的类型是什么,防止业务逻辑错误。

接下来减少reduce inc和reduce dec这两个函数中的typeo类型保护检测,因为在某些特定场景下传入的delta类型值不是很确定,比如调用模板中的方法,类型检测可能会失败,传入类型是本机事件。

关于ref类型值,这里不太声明类型,因为vue3会进行手动类型推断,但如果是复杂类型,可以使用类型判断的方法:ref(initObj) as Ref

小建议

任意脚本

早期使用 TypeScript 时,很多挖掘者喜欢使用任何类型,硬生生地将 TypeScript 写成 AnyScript。 虽然使用起来很方便,但是却失去了 TypeScript 的类型检测意义。 当然,写类型的习惯是需要慢慢养成的typescript自动开发,没必要一时着急。

维图尔

vetur代码检测工具在编写vue代码时会非常有用。 就像构建vue项目离不开vue-cli一样,vetur为vscode提供插件支持,抢着升级vue3这波工作,顺便还带上vetur Bar。

一个完整的Vue3+ts项目

├─public
│      favicon.ico
│      index.html
└─src
    │  App.vue
    │  main.ts
    │  shims-vue.d.ts
    ├─assets
    │  │  logo.png
    │  └─css
    │          base.css
    │          main.styl
    ├─components
    │  │  HelloWorld.vue
    │  └─base
    │          Button.vue
    │          index.ts
    │          Select.vue
    ├─directive
    │      focus.ts
    │      index.ts
    │      pin.ts
    ├─router
    │      index.ts
    ├─store
    │      index.ts
    ├─utils
    │  │  cookie.ts
    │  │  deep-clone.ts
    │  │  index.ts
    │  │  storage.ts
    │  └─validate
    │          date.ts
    │          email.ts
    │          mobile.ts
    │          number.ts
    │          system.ts
    └─views
        │  About.vue
        │  Home.vue
        │  LuckDraw.vue
        │  TodoList.vue
        └─address
                AddressEdit.tsx
                AddressList.tsx


  ...


<script lang="ts">
import dayjs from "dayjs";
import { ref, reactive, onMounted } from "vue";
import { Button, Step, Steps, NoticeBar } from "vant";

export default {
  components: {
    Button,
    Step,
    Steps,
    NoticeBar,
  },
  setup() {
    const nameinput = ref();
    const selectionStart = ref(0);
    const twoNow = dayjs().subtract(2, "day").format("YYYY-MM-DD HH:mm:ss");
    const now = dayjs().format("YYYY-MM-DD HH:mm:ss");
    const now2 = dayjs().add(2, "day").format("YYYY-MM-DD HH:mm:ss");
    const formData = reactive({
      name: "",
      phone: "",
      code: "",
    });

    onMounted(() => {
      (nameinput.value as HTMLInputElement).focus();
    });

    const insertName = () => {
      const index = (nameinput.value as HTMLInputElement).selectionStart;
      if (typeof index !== "number"return;
      formData.name =
        formData.name.slice(0, index) + "哈哈" + formData.name.slice(index);
    };

    return {
      nameinput,
      formData,
      insertName,
      selectionStart,
      twoNow,
      now,
      now2,
    };
  },
};


   ...


<script lang="ts">
import dayjs from "dayjs";
import { defineComponent } from "vue";
import HelloWorld from "@/components/HelloWorld.vue"; // @ is an alias to /src
import { Button, Dialog, Toast } from "vant";

export default defineComponent({
  name: "Home",
  components: {
    HelloWorld,
    Button,
  },
  data() {
    return {
      direction: "top",
      pinPadding: 0,
      time: "",
      timer: 0,
      color: "red",
    };
  },
  methods: {
    showToast() {
      Toast("字体颜色已改蓝色");
      this.color = "blue";
    },
    handleClick() {
      Dialog({
        title: "标题",
        message: "这是一个全局按钮组件",
      });
    },
    initTime() {
      this.time = dayjs().format("YYYY-MM-DD HH:mm:ss");
      this.timer = setInterval(() => {
        this.time = dayjs().format("YYYY-MM-DD HH:mm:ss");
      }, 1000);
    },
  },
  created() {
    this.initTime();
  },
  beforeUnmount() {
    clearInterval(this.timer);
  },
});


<style vars="{ color }">
.text-color {
  color: var(--color);
}

import { ref, reactive } from "vue";
import { AddressList, NavBar, Toast, Popup } from "vant";
import AddressEdit from './AddressEdit'
import router from '@/router'

export default {
  setup() {
    const chosenAddressId = ref('1')
    const showEdit = ref(false)

    const list = reactive([
      {
        id'1',
        name'张三',
        tel'13000000000',
        address'浙江省杭州市西湖区文三路 138 号东方通信大厦 7 楼 501 室',
        isDefaulttrue,
      },
      {
        id'2',
        name'李四',
        tel'1310000000',
        address'浙江省杭州市拱墅区莫干山路 50 号',
      },
    ])
    const disabledList = reactive([
      {
        id'3',
        name'王五',
        tel'1320000000',
        address'浙江省杭州市滨江区江南大道 15 号',
      },
    ])

    const onAdd = () => {
      showEdit.value = true
    }
    const onEdit = (item: any, index: string) => {
      Toast('编辑地址:' + index);
    }

    const onClickLeft = () => {
      router.back()
    }

    const onClickRight = () => {
      router.push('/todoList')
    }

    return () => {
      return (
        

          <NavBar
            title="地址管理"
            left-text="返回"
            right-text="Todo"
            left-arrow
            onClick-left={onClickLeft}
            onClick-right={onClickRight}
          />
          <AddressList
            vModel={chosenAddressId.value}
            list={list}
            disabledList={disabledList}
            disabledText="以下地址超出配送范围"
            defaultTagText="默认"
            onAdd={onAdd}
            onEdit={onEdit}
          />
          
            
          
        

      );
    };
  }
};

结束

不知不觉中,Vue 已经到了 One Piece 3 时代,Vue3 的新特性让拥抱 TypeScript 的姿态更加从容端庄,Vue 对于小型项目开发也更加有勇气。

请各位帅哥美女多多支持帅编,回复“1”即可加入前端技术交流群,回复"2"即可领取 500G 前端干货

  • 转发就是最大的支持

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 typescript typescript自动开发-Vue3+TypeScript完整项目入门教程 https://www.wkzy.net/game/178195.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务