typescript vuex_Vue3+TypeScript完整项目上手教程

作者:TinssonTai

https://juejin.im/post/6875713523968802829

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

项目地址:https://github.com/vincentzyc/vue3-demo.git

TypeScript 是JS的一个超集,主要提供了类型系统和对ES6的支持,使用 TypeScript 可以增加代码的可读性和可维护性,在 reactvue 社区中也越来越多人开始使用TypeScript。从最近发布的 Vue3 正式版本来看, Vue3 的源码就是用 TypeScript 编写的,更好的 TypeScript 支持也是这一次升级的亮点。当然,在实际开发中如何正确拥抱 TypeScript 也是迁移至 Vue3 的一个小痛点,这里就针对 Vue3TypeScript 展开一些交流。

ac097ebfea0415a79e66faf731a12cfc.png

96.8%的代码都是TypeScript,支持的力度也是相当大?

  • Vue3入口: https://github.com/vuejs/vue-next

项目搭建

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

  • vite
npm init vite-app sail-vue3 # OR yarn create vite-app sail-vue3
  • vue-cli
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 项目来说,这是一个非常完美的工具。

后面的演示代码也是用vite搭的

vue2.x 走过来的掘友肯定知道 vue-cli 这个官方脚手架, vue3 的更新怎么能少得了 vue-cli 呢, vue-cli 更强调的是用 cli 的方式进行交互式的配置,选择起来更加灵活可控。丰富的官方插件适配,GUI的创建管理界面,标准化开发流程,这些都是 vue-cli 的特点。

  • vue-cli ✖ TypeScript STEP1
15916dca614627d39994c2384c07b5c4.png
  • vue-cli ✖ TypeScript STEP2
89cd0977e9ed5380d9f9920a260aeb1d.png

想要预装TypeScript,就需要选择手动配置,并check好TypeScript

忘记使用选择 TypeScript 也没事,加一行cli命令就行了

vue add typescript

最后,别忘了在 .vue 代码中,给 script 标签加上 lang="ts"

<script lang="ts">

Option API风格

Vue2.x 使用过 TypeScript 的掘友肯定知道引入 TypeScript 不是一件简单的事情:

  1. 要用 vue-class-component 强化 vue 组件,让 Script 支持 TypeScript 装饰器
  2. vue-property-decorator 来增加更多结合 Vue 特性的装饰器
  3. 引入 ts-loaderwebpack 识别 .ts .tsx 文件
  4. .....

然后出来的代码风格是这样的:

@Component({
    components:{ componentA, componentB},
})
export default class Parent extends Vue{
  @Prop(Number) readonly propA!: number | undefined
  @Prop({ default: 'default value' }) readonly propB!: string
  @Prop([String, Boolean]) 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'
  }
}

class 风格的组件,各种装饰器穿插在代码中,有点感觉自己不是在写 vue ,些许凌乱?,所以这种曲线救国的方案在 vue3 里面肯定是行不通的。

vue3 中可以直接这么写:

import { defineComponent, PropType } from 'vue'

interface Student {
  name: string
  class: stringage: number
}const Component = defineComponent({
  props: {
    success: { type: String },
    callback: {
      type: Function as PropType<() => void>
    },
    student: {
      type: Object as PropType,required: true
    }
  },
  data() {return {message: 'Vue3 code style'
    }
  },computed: {
    reversedMessage(): string {return this.message.split(' ').reverse().join('')
    }
  }
})

vueprops 进行复杂类型验证的时候,就直接用 PropType 进行强制转换, data 中返回的数据也能在不显式定义类型的时候推断出大多类型, computed 也只用返回类型的计算属性即可,代码清晰,逻辑简单,同时也保证了 vue 结构的完整性。

Composition API风格

vue3Composition API 代码风格中,比较有代表性的api就是 refreactive ,我们看看这两个是如何做类型声明的:

ref

import { defineComponent, ref } from 'vue'

const Component = defineComponent({
setup() {
  const year = ref(2020)
  const month = ref('9')
  month.value = 9 // OKconst 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 // OKconst result = year.value.split('') // => Property 'split' does not exist on type 'number'
})

reactive

import { defineComponent, reactive } from 'vue'

interface Student {
  name: string
  class?: stringage: number
}export default defineComponent({
  name: 'HelloWorld',
  setup() {
    const student = reactive({ name: '阿勇', age: 16 })// orconst student: Student = reactive({ name: '阿勇', age: 16 })// orconst student = reactive({ name: '阿勇', age: 16, class: 'cs' }) as Student
  }
})

声明 reactive 的时候就很推荐使用接口了,然后怎么使用类型断言就有很多种选择了,这是 TypeScript 的语法糖,本质上都是一样的。

自定义Hooks

vue3 借鉴 react hooks 开发出了 Composition API ,那么也就意味着 Composition API 也能进行自定义封装 hooks ,接下来我们就用 TypeScript 风格封装一个计数器逻辑的 hooks ( useCount ):

首先来看看这个 hooks 怎么使用:

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

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

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

出来的效果就是:

a2732fa512a9411c25d8faa58f6324ed.gif

贴上 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) returnif (range && range.min && newVal       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 分别用一个接口来指定,这样做了以后,最大的好处就是在使用 useCount 函数的时候,ide就会自动提示哪些参数是必填项,各个参数的类型是什么,防止业务逻辑出错。

a449fda78c57b0e2747ecd724aee518c.png

接下来,在增加 inc 和减少 dec 的两个函数中增加了 typeo 类型守卫检查,因为传入的 delta 类型值在某些特定场景下不是很确定,比如在 template 中调用方法的话,类型检查可能会失效,传入的类型就是一个原生的 Event

关于 ref 类型值,这里并没有特别声明类型,因为 vue3 会进行自动类型推导,但如果是复杂类型的话可以采用类型断言的方式:ref(initObj) as Ref

小建议 ?

AnyScript

在初期使用 TypeScript 的时候,很多掘友都很喜欢使用 any 类型,硬生生把TypeScript 写成了 AnyScript ,虽然使用起来很方便,但是这就失去了 TypeScript 的类型检查意义了,当然写类型的习惯是需要慢慢去养成的,不用急于一时。

Vetur

vetur 代码检查工具在写vue代码的时候会非常有用,就像构建 vue 项目少不了 vue-cli 一样,vetur 提供了 vscode 的插件支持,赶着升级 vue3 这一波工作,顺带也把 vetur 也带上吧。

bc270f6877d639bbbbfdc0e3b12e84b7.png

一个完整的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
  • .vue写法

  ...



   ...




  • tsx写法
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 室',
        isDefault: true,
      },
      {
        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 (
        
            
            left-text="返回"
            right-text="Todo"
            left-arrow
            onClick-left={onClickLeft}
            onClick-right={onClickRight}
          />            vModel={chosenAddressId.value}
            list={list}
            disabledList={disabledList}
            disabledText="以下地址超出配送范围"
            defaultTagText="默认"
            onAdd={onAdd}
            onEdit={onEdit}
          />

      );
    };
  }
};

结束

不知不觉, Vue 都到3的One Piece时代了, Vue3 的新特性让拥抱 TypeScript 的姿势更加从容优雅, Vue 面向大型项目开发也更加有底气了,点击查看更多。

点个『在看』支持下 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/397043.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JS中window.showModalDialog()详解

window.showModalDialog()方法用来创建一个显示HTML内容的模态对话框。 window.showModelessDialog()方法用来创建一个显示HTML内容的非模态对话框。 使用方法&#xff1a; vReturnValue window.showModalDialog(sURL [, vArguments] [,sFeatures]) vReturnValue window.show…

一个简单的javascript节流器实现

节流器 javascript的节流器主要用于延缓某些动作的执行&#xff0c;比如ajax请求&#xff0c;如果input框注册了input事件&#xff0c;那么当用户输入时就会持续的触发这个事件&#xff0c;如果回调函数中持续的通过ajax调用后台的接口&#xff0c;就会对服务器产生一定压力。这…

一些会用到的知识

为什么80%的码农都做不了架构师&#xff1f;>>> HtmlAgilityPack 用来解析HTML代码 microsoft.mshtml CsQuery 解析HTML代码 转载于:https://my.oschina.net/uwith/blog/813725

eclipse怎么升级到java ee,如何为Java EE开发人员升级Eclipse?

Is there any non-painful way to upgrade an Eclipse installation? I have tried browsing the eclipse site but I cannot find an useful description.解决方案Add the update URL to your available sites:Window > Preferences > Install/Update > Available S…

LeetCode 7 Reverse Integer(反转数字)

题目来源&#xff1a;https://leetcode.com/problems/reverse-integer/ Reverse digits of an integer. Example1: x 123, return 321Example2: x -123, return -321 解题思路&#xff1a; 其实这道题看起来非常简单&#xff0c;要实现也是几行代码的事。但是有个小问题容易被…

各种蕴含算法思想的DP - 3

内容中包含 base64string 图片造成字符过多&#xff0c;拒绝显示转载于:https://www.cnblogs.com/cmyg/p/9566723.html

python图像对比_用python实现对比两张图片的不同

from PIL import Image from PIL import ImageChops def compare_images(path_one, path_two, diff_save_location): """ 比较图片&#xff0c;如果有不同则生成展示不同的图片 参数一: path_one: 第一张图片的路径 参数二: path_two: 第二张图片的路径 参数三:…

Kafka 分布式环境搭建

这篇文章将介绍如何搭建kafka环境&#xff0c;我们会从单机版开始&#xff0c;然后逐渐往分布式扩展。单机版的搭建官网上就有&#xff0c;比较容易实现&#xff0c;这里我就简单介绍下即可&#xff0c;而分布式的搭建官网却没有描述&#xff0c;我们最终的目的还是用分布式来解…

Docker Machine搭建并加入节点

对于集群服务器来讲&#xff0c;要在每台机器上手动安装Docker是一件及其痛苦的事情&#xff0c;还好有Docker Machine这一工具&#xff0c;Docker三剑客中的一角. 一、Docker Machine介绍 这个工具已经出了比较久了&#xff0c;Docker Machine官方介绍&#xff1a;https://doc…

ASPNET5的依赖注入

ASP.NET5设计的时候就是以DI为基础的&#xff0c;它可以利用内建的框架在Startup类的方法中&#xff0c;把依赖注入进去。应用服务也可以被配置的注入。默认的服务容器提供一些基本的功能&#xff0c;它并不打算代替现代主流的DI框架。 1. 什么是Dependency Injection? DI的概…

java 权限控制 demo_Java-访问控制权限

Java面向对象-访问控制权限Java中&#xff0c;可以通过一些Java关键字&#xff0c;来设置访问控制权限&#xff1b;主要有 private(私有)&#xff0c; package(包访问权限)&#xff0c;protected(子类访问权限)&#xff0c;public(公共访问权限)privatepackageprotectedpublic同…

《未来世界的幸存者》笔记

https://ruanyf.github.io/survivor/ 这两天一直在上下班途中&#xff0c;读阮一峰的这本书 《未来世界的幸存者》。还是有不少感慨的。做一下记录。 未来人类社会的形态&#xff0c;将发生颠覆性的变化。旧的社会结构已经在崩溃了&#xff0c;但社会底层的机会变得更少了&…

python类中没有属性_如何在python语言中在类中删除属性和添加属性

在python语言中的类&#xff0c;可以使用class定义类&#xff0c;调用__init__方法进行初始化&#xff1b;默认传入self&#xff0c;可以在后面在添加几个属性。可以使用setattr()添加属性&#xff0c;也可以使用delattr()删除属性。下面利用几个实例说明这两个方法的用法&…

mysql 数据复制停止工作_linux – Mysql GTID复制停止工作

我在主服务器和从服务器之间设置了mysql gtid复制.有趣的是,我发现复制在几分钟后停止工作,我必须使用stop slave并启动slave来重启mysql复制.谁能告诉我是什么原因导致这个问题&#xff1f;改变奴隶主&#xff1a;mysql> change master to-> master_host master.com,-&…

python学生管理系统gui版好例子网_python图书管理系统gui 相关实例(示例源码)下载 - 好例子网...

开发语言&#xff1a;Python | 大小&#xff1a;19.05M | 发布时间&#xff1a;2019-05-27 | 发布人&#xff1a;李易峰 相关标签&#xff1a; 立即下载 开发语言&#xff1a;Python | 大小&#xff1a;0.21M | 发布时间&#xff1a;2020-08-23 | 发布人&#xff1a;聂嘉辉 相关…

COM组件的运行机制

COM组件的运行机制  构造一个创建COM组件的最小框架结构    IUnknown *pUnkNULL;    IObject *pObjectNULL;    CoInitialize(NULL);    CoCreateInstance(CLSID_Object, CLSCTX_INPROC_SERVER, NULL, IID_IUnknown, (void**)&pUnk);    pUnk->Qu…

申请去国外读博士

必须要做的事情是&#xff1a; 考过GRE和TOEFL 准备推荐信 个人CV 联系导师一般就是发邮件 转载于:https://www.cnblogs.com/zhulinmails/p/5078695.html

动态编译

下面的demo就涵盖了动态编译和运行类的过程 package 动态编译; import java.lang.reflect.Method;import java.net.URL;import java.net.URLClassLoader; import javax.tools.JavaCompiler;import javax.tools.ToolProvider; public class Demo { public static void main(Stri…

python怎么打开spyder_Python开发环境Spyder安装方法

Spyder(Scientific PYthon Development EnviRonment)是一个强大的交互式 Python 语言开发环境&#xff0c;提供高级的代码编辑、交互测试、调试等特性&#xff0c;支持包括 Windows、Linux 和 OS X 系统。 本文在只安装过python2.7的Win7环境下进行安装Spyder&#xff0c;其他环…

矩阵、向量求导法则

复杂矩阵问题求导方法&#xff1a;可以从小到大&#xff0c;从scalar到vector再到matrix。 x is a column vector, A is a matrix practice: 转载于:https://www.cnblogs.com/vincentQin/p/5406010.html