zoukankan      html  css  js  c++  java
  • 工作记录:TypeScript从入门到项目实战(项目篇)

    Vue项目中使用

    前面两篇介绍过TypeScript基础和较深入的东西,本章介绍如何在Vue项目中使用。

    项目创建

    创建项目直接使用Vue-cli创建

    下面是步骤:

    1.运行vuecli,

    2.选择合适的目录创建项目

    3.输入项目名并,选择包管理器,输入git仓库初始化内容

    4.设置预设,如果你之前有合适的预设,可以设置该预设,这里选择手动

    5.选择功能,其中TypeScript和babel必选,其他功能视项目而定:

    6.设置配置,开启类样式组件语法(第一项),选择eslint配置为ESLint+Standard(第五项),开启保存时检查和修复代码

    7.创建项目

    8.安装一些插件

    编辑器文件支持设置

    这里以我们以后统一使用的Webstrom为例:

    找到Editor>File and COde Templates

     

    新建一个代码模板,输入名称,扩展名为vue,选择根据样式重新设置格式、选择启用该模板

     

    内容区域输入:

    <template lang="pug">
    #[[$END$]]#
    </template>
    
    <script lang="ts">
    import { Component, Vue } from 'vue-property-decorator'
    
    @Component
    export default class ${COMPONENT_NAME} extends Vue {
      
    }
    </script>
    
    <style  lang="stylus" rel="stylesheet/stylus" module>
    
    </style>

    点击应用之后,新建文件时选择刚建立的模板:

    因为选择的是类样式语法,所以需要填入类名:

     

    修改eslint、编辑器配置

    修改eslint配置文件以支持检查TypeScript文件 

    .eslintrc.js文件的parserOptions选项中新增如下代码:

    parser: '@typescript-eslint/parser'
    

    修改编辑器配置以支持保存时自动修复代码

    webstrom强大之处是对各种技术的支持,比如eslint检查代码。正常情况下,我们需要通过命令行检查代码,这非常麻烦,不过webstrom能够配置eslint,编辑器内检查不合eslint配置的代码,并且支持保存时修复。配置如下:

     

    修改声明文件

    对于我们自定义的插件、全局方法、全局变量等,TypeScript并不知道他们,为了让TypeScript认识他们,我们可以通过声明文件告诉TypeScript,如果使用model样式时的$style,我们修改shims-tsx.d.ts文件,在行末添加下面代码:

    declare module 'vue/types/vue' {
      interface Vue {
        $style: {
          [key: string]: string;
        };
      }
    
      interface VueConstructor {
        $style: {
          [key: string]: string;
        };
      }
    }
    

    对于其他内容和上面方法类似

    组件中使用

    基本用法

    因为使用类样式写组件中的js,所以写法上会略有不同,但是本质上还是不变的。

    组件引用

    组件引用通过传递参数components给装饰符@Component

    @Component({
      components: {
        MapLegend: () => import('@/components/legend/index.vue')
      }
    })
    

    过滤器

    过滤器和组件类似,通过传递filters参数,filters对象内定义局部过滤器:

    @Component({
      filters: {
        dateFormat (date:Date) {
          return dateFormat(date)
        }
      }
    })
    

    指令

    局部指令和过滤器类似,通过@Component装饰器传递参数directives

    @Component({
      directives:{
        focus: {
          inserted: function (el) {
            el.focus()
          }
        }
      }
    })
    

    props

    props不再通过对象属性形式定义,而是通过@Prop装饰器定义,其配置内容通过参数形式传入装饰器:

    @Component
    export default class Test extends Vue {
      @Prop({
        type:String,
        default:'no name',
        required:false
      }) name!:string
    
      mounted(){
        console.log(name)
      }
    }
    

    props同步版

    通过@PropSync可以创建一个props属性的同步版本(即:变量改变,所对应的props属性也会随之改变):

    @Component
    export default class Test extends Vue {
      @PropSync('name',{
        type:String,
        default:'no name',
        required:false
      }) name!:string
    
      mounted(){
        this.name='nichols'
      }
    }
    

    侦听器(watch)

    类似的,侦听器通过@Watch装饰器定义,装饰器接收两个参数,第一个监视哪个变量,第二个其他配置:

    @Component
    export default class Test extends Vue {
      isShow=false
      @Watch('isShow',{
        deep:true,
        immediate:true
      })
      onIsShowChange(val:boolean,newVal:boolean){
    
      }
    }
    

    侦听器同样可以被当做方法调用,只是执行其内部逻辑:

      mounted(){
        this.onIsShowChange(true,false)
      }
    

    emit

    在组件中触发事件让父组件侦听到是一个非常常用的操作,通过@Emit装饰符定义,所定义的函数可以被当做普通函数调用:

    @Component
    export default class Test extends Vue {
      name = ''
    
      @Emit('change')
      onChange (name: string) {
        this.name = name
      }
    
      mounted () {
        this.onChange('nichols')
      }
    }
    

    其中如果有返回值,则返回值会作为触发的参数放在前面,而传入参数会放到返回值后面

    ref

    定义ref使用@Ref装饰器定义:

    @Component
    export default class Test extends Vue {
      name = ''
    
      @Ref('form') form!:HTMLElement
    
      mounted(){
        console.log(this.form.offsetHeight)
      }
    }
    

    data

    对于组件内的数据,我们可以直接使用类属性定义组件的数据:

    @Component
    export default class Test extends Vue {
      isShow = false
      form: {
        username: string;
        password: string;
      } = {
        username: '',
        password: ''
      }
    
      mounted () {
        this.isShow = true
      }
    }
    

    函数(methods)

    函数与data定义类似,为类添加一个方法即可:

    @Component
    export default class Test extends Vue {
      log(){
        // ....
      }
    }
    

    计算属性

    而计算属性,则是类的存取器的写法(gettersetter,对应Vue的gettersetter):

    @Component
    export default class Test extends Vue {
      lastName = '尼古拉斯'
      firstName = '赵四'
    
      get name (): string {
        return `${this.firstName}·${this.lastName}`
      }
    
      set name (name: string) {
        const names = name.split('·')
        this.firstName = names[0]
        this.lastName = names[0]
      }
    }
    

    生命周期

    可以直接定义所对应的钩子名称,或者借助vue-class-component/hooks.d.ts完成:

    @Component
    export default class Test extends Vue {
      mounted () {
      }
    
      created () {
      }
    
      updated () {
      }
    
      beforeDestroy () {
      }
    
      destroyed () {
      }
    }
    

    更加详细的内容

    更详细参考vue-property-decorator文档

    类型声明

    src目录下types目录下,创建index.d.ts(或者更详细的文件名),然后定义类型,这里以扩展Event为例

    interface Event{
      DataTransfer:{
        setData():void
      }
    }
    

    为了避免全局变量混乱,可以使用export导出我们想要被外部访问的声明:

    export interface User{
      id:string;
      name:string;
      realName:string;
    }
    

    需要使用时,再在需要使用的地方导入即可:

    import { User } from '@/types/user'
    
    @Component
    export default class Test extends Vue {
      user:User={
        id: '',
        name: '',
        realName: ''
      }
    }
    

    旧项目的迁移

    安装插件

    1.启动vue ui(一把梭,就是干!),在插件项中点击添加插件,

    2.搜索TypeScript,选择@vue/cli-pluging-typescript,点击安装即可

    修改组件

    1.script标签添加属性lang="ts"

    2.组件引入添加.vue后缀名

    3.修改默认导出为类样式:

    export default {
      name:'Component1'
    }
    

    修改为:

    @Component
    export default class Component1 extends Vue{
    }
    

    4.按照基本用法,将对应的数据更改为类样式

    5.按照编辑器报错提示添加或者修改类型注释

    修改js文件

    1.js文件后缀改为.ts

    2.添加类型约束

    vuex的使用

    vuex和vue组件使用方式类似,使用类样式+装饰器的形式定义,使用的依赖是vuex-module-decoratorsvuex-class

    安装

    yarn add vuex-module-decorators vuex-class
    npm i vuex-module-decorators vuex-class
    

    创建Store

    Store的创建和常规创建方式一致,只是Vuex.Store参数中无需传入任何参数:

    import Vue from 'vue'
    import Vuex from 'vuex'
    import User from '@/store/modules/user'
    import getters from '@/store/getters'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
    })
    

    定义模块

    @Module

    使用@Module定义一个vuex模块,接收如下参数:

    属性

    数据类型

    描述

    name

    string

    模块的名称(如果有名称空间)

    namespaced

    boolean

    模块是否具有名称空间

    stateFactory

    boolean

    是否开启状态工厂(方便模块复用)

    dynamic

    true

    如果这是一个动态模块(在创建存储后添加到存储)

     

    store

    Store<any>

    将注入此模块的存储区(用于动态模块)

    preserveState

    boolean

    如果启用此选项,则加载模块时将保留状态

    创建模块语法如下:

    import { VuexModule } from 'vuex-module-decorators'
    @Module({
      dynamic: true,//启用动态模块
      name: 'User',
      store,//注入store
      namespaced: true,
      stateFactory: true//开启工厂模式
    })
    export default class User extends VuexModule {
    }
    

    state

    state的定义和组件中的data定义类似:

    @Module({
      dynamic: true,//启用动态模块
      name: 'User',
      store,//注入store
      namespaced: true,
      stateFactory: true//开启工厂模式
    })
    export default class User extends VuexModule {
      token = getToken()
    }
    

    上面代码和下面代码效果一样:

    export default {
    	state:{
      	token: getToken()
      },
      namespaced:true
    }
    

    @Mutation

    mutation的定义使用@Mutation装饰器定义:

    @Module({
      dynamic: true,//启用动态模块
      name: 'User',
      store,//注入store
      namespaced: true,
      stateFactory: true//开启工厂模式
    })
    export default class User extends VuexModule {
      token = getToken()
    
      @Mutation
      setToken (token: string) {
        this.token = token
        token ? setToken(token) : deleteToken()
      }
    }
    

    @Action

    使用@Action装饰器定义action,该装饰器接收三个参数:

    参数名

    类型

    描述

    commit

    string

    所提交的荷载

    rawError

    boolean

    是否打印原始错误类型(默认会对报错信息进行包装)

    root

    boolean

    是否允许提交根荷载

    如果不传入参数,需要手动提交荷载:

    @Module({
      dynamic: true,//启用动态模块
      name: 'User',
      store,//注入store
      namespaced: true,
      stateFactory: true//开启工厂模式
    })
    export default class User extends VuexModule {
      token = getToken()
    
      @Mutation
      setToken (token: string) {
        this.token = token
        token ? setToken(token) : deleteToken()
      }
    
      @Action
      async login () {
        this.context.commit('setToken', 'token')
        router.replace('/')
      }
    }
    

    如果指定提交的荷载名,可通过函数的返回值设定荷载值:

    @Module({
      dynamic: true,//启用动态模块
      name: 'User',
      store,//注入store
      namespaced: true,
      stateFactory: true//开启工厂模式
    })
    export default class User extends VuexModule {
      token = getToken()
    
      @Mutation
      setToken (token: string) {
        this.token = token
        token ? setToken(token) : deleteToken()
      }
    
      @Action({commit:'setToken'})
      async login ():string {
        router.replace('/')
        return 'token'
      }
    }
    

    @MutationAction  

    有时候简单地数据操作,mutation会显得有点多余,这时候,我们可以使用@MutationAction装饰器将mutatioin和action合二为一,用此装饰器定义的action会同时定义并提交荷载:

    @Module({
      dynamic: true,//启用动态模块
      name: 'User',
      store,//注入store
      namespaced: true,
      stateFactory: true//开启工厂模式
    })
    export default class User extends VuexModule {
      testStr = ''
    
      @MutationAction({ mutate: ['testStr'] })
      async setStr () {
        return new Promise<{ testStr: string }>(resolve => {
          resolve({
            testStr: 'test'
          })
        })
      }
    }
    

    需要注意的是:返回对象的数据结构必须和指定的参数名一致

    getter

    getter的定义和Vue组件中的计算属性定义类似,使用get前置于方法名:

    @Module
    class MyModule extends VuexModule {
      wheels = 2
    
      get axles() {
        return this.wheels / 2
      }
    }
    

    完整示例

    import {
      deleteRefreshToken,
      deleteToken,
      deleteUserInfo,
      getRefreshToken,
      getToken,
      getUserInfo,
      setRefreshToken,
      setToken,
      setUserInfo
    } from '@/utils/auth'
    import { UserInfo } from '@/types/user'
    import router from '@/router'
    import { Action, Module, Mutation, MutationAction, VuexModule } from 'vuex-module-decorators'
    
    @Module({
      dynamic: true,//启用动态模块,模块将在调用getModule时注册
      name: 'User',
      store,//注入store
      namespaced: true,
      stateFactory: true//开启工厂模式
    })
    export default class User extends VuexModule {
      token = getToken()
      refreshToken = getRefreshToken()
      userInfo: UserInfo | null = getUserInfo()
      testStr = ''
    
      @Mutation
      setToken (token: string) {
        this.token = token
        token ? setToken(token) : deleteToken()
      }
    
      @Mutation
      setRefreshToken (token: string) {
        this.refreshToken = token
        token ? setRefreshToken(token) : deleteRefreshToken()
      }
    
      @Mutation
      setUserInfo (user: UserInfo | null) {
        this.userInfo = user
        user ? setUserInfo(user) : deleteUserInfo()
      }
    
      @MutationAction({ mutate: ['testStr'] })
      async setStr () {
        return new Promise<{ testStr: string }>(resolve => {
          resolve({
            testStr: 'test'
          })
        })
      }
    
      @Action
      async login () {
        this.context.commit('setToken', 'token')
        this.context.commit('setRefreshToken', 'refreshToken')
        this.context.commit('setUserInfo', {})
        router.replace('/')
      }
    
      @Action
      async loginOut () {
        this.context.commit('setToken', '')
        this.context.commit('setRefreshToken', '')
        this.context.commit('setUserInfo', null)
        router.replace('/login')
      }
    }
    

    组件中使用

    组件中通过getModule()方法进行获取到模块,可以通过定义计算属性以使用state

    import { Component, Vue } from 'vue-property-decorator'
    import User from '@/store/modules/user'
    import { getModule } from 'vuex-module-decorators'
    import { namespace } from 'vuex-class'
    
    let user:User = getModule(User)
    
    @Component
    export default class test extends Vue {
      //动态数据,使用user.userInfo获取的并不是响应式数据
      @namespace('User').State('userInfo') userInfo: UserInfo
      
      login ():void {
        console.log(user.testStr)
        user.login()
      }
      
      get token ():string {
        return user.token
      }
    
      mounted ():void {
        
      }
    }
    

    小程序中使用

    小程序中使用TypeScript比较简单,在创建项目时选择语言为TypeScript,其他的和Vue类似项目类似

    不同的是,Vue项目会自动编译TypeScript,而小程序需要手动编译ts文件,这显得有点麻烦,所以我们可以使用webstrom开发小程序:

    安装小程序插件,让webstrom支持小程序语法:File>Setting>Plugins,搜索Wechat mini programs upport,完成之后重启webstrom,这时候我们可以看到在右键菜单New那一项里面多了两个小程序选项:

    配置ts文件自动编译:File>Setting>Languages & Frameworks>TypeScript,选中改变时重新编译:

     

    扩展

    本系列文章全面结合项目编写,如果你还想深入学习TypeScript的话可以看下面的网站:

    TypeScript中文网

    TypeScript 入门教程 

    TypeScript 学习资源合集

     

    如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

     

  • 相关阅读:
    第二阶段冲刺第一天
    第一次团队绩效评估
    其他团队对本团队评价的总结
    对其他团队的评价
    团队博客的检查结果
    站立会议第十天
    站立会议第九天
    站立会议第八天
    站立会议第七天
    团队站立会议10
  • 原文地址:https://www.cnblogs.com/smileZAZ/p/15362174.html
Copyright © 2011-2022 走看看