# TSX 笔记

# 特殊类型

# JSX-Element

const testElemeng: JSX.Element = <span class={className[test]}>{1}</span>;

# 待补充。。。

# 常用标签

# v-if

<template>
  <div class="warpper">
    <div class="content" v-if="hello">hello</div>
  </div>
</template>
render() {
  return (
  <div class = 'wrapper'>
    {
        this.hello && (
          <div class = 'content'>hello</div>
        )
      }
    </div>
  )
}

# v-if ,v-else

<template>
  <div class="wrapper">
    <div class="content1" v-if="hello">content1</div>
    <div class="content2" v-else>content2</div>
  </div>
</template>
render() {
  return (
  <div class= 'wrapper'>
    {
        this.hello ?
          (<div class ='content1'></div>) :
          (<div class= 'content2'></div>)
      }
    </div>
  )
}

# v-for

<template>
  <div class="wrapper">
    <ul>
      <li v-for="item in items" :key="item.id">{{ item.name }}</li>
    </ul>
  </div>
</template>
render() {
  return (
  <div class= 'wrapper'>
    <ul>
    {
        this.items.map(item => (
          <li>{ item.name }</li>
        ))
      }
    </ul>
    </div>
  )
}

# v-model

<Component v-model="test"></Component>
<component
  value={this.test}
  onInput={val => {
    this.test = val;
  }}
></component>

# v-html

 render (createElement) {
     return (
         <button domPropsInnerHTML={htmlContent}></button>
     )
 }

# Ref

this.$refs.

Vue3写法

setup() {
  const testRef = ref<null | HTMLElement>(null)
  return () => (
    <div ref={testRef}></div>
  )
}

# watch

const isOutSide = ref(false) //必须为响应式对象
watch(
  isOutSide,
  (neval,oldval) => {
  },
  {
    ...options
  }
)

# Emit

写法一致,或者使用装饰器写法,传参就是 return

# 生命周期

无需写在 methods,也没有了整个概念

private mounted() {}
private created() {}
private beforeCreate() {}
private beforeMount() {}
private beforeUpdate() {}
private updated() {}
private beforeDestory() {}
private destoryed() {}

# Watch

使用装饰器,装饰器内传入变量名称

import { Watch } from 'vue-property-decorator'
...
private demo: any
...
@Watch('demo')
private watchDemoVal(newVal: any, oldVal: any) {
  console.log(newVal, oldVal)
}

# 事件

大部分事件由之前的 @ (v-bind 写法) 转变成了 onChange 写法

<span onClick={(e: MouseEvent) => this.handleClick(e)}></span>

发布订阅事件

子组件

import { Emit } from 'vue-property-decorator'
...
// ? 装饰器写法如何传递参数
@Emit('test')
private test() {
  console.log('1')
}

父组件

<parent onTest={this.handleTest}></parent>

这里仍然可以使用 this.$emit('eventName',params)

# 传参

合理使用展开运算符

 render (createElement) {
     const inputAttributes = {
         class: 'input-field has-outline', // class definition
         onClick: this.handleClick // event handler
         backdrop: false // custom prop
     }
     const inputMarkup = this.multiline
         ? <textarea {...inputAttributes}></textarea>
         : <input {...inputAttributes}/>


    return inputMarkup
 }

Prop

import { Vue, Component, Prop } from 'vue-property-decorator'

@Component
export default class YourComponent extends Vue {
  @Prop(Number) readonly propA: number | undefined
  @Prop({ default: 'default value' }) readonly propB!: string
  @Prop([String, Boolean]) readonly propC: string | boolean | undefined
}

// 可以视为以下 vue2代码
 props: {
    propA: {
      type: Number,
    },
    propB: {
      default: 'default value',
    },
    propC: {
      type: [String, Boolean],
    },
  },

# Vuex

Vuex3.x+ 搭配 TS 写法规范和使用技巧

# 规范写法

文件规范( (opens new window) (opens new window))

-- store
   -- modules
      -- moduleTest
         -- index.ts
         -- types.ts
   -- action-types.ts
   -- actions.ts
   -- getters.ts
   -- mutation-types.ts
   -- mutations.ts
   -- namespaces.ts
   -- state-types.ts
   -- state.ts
   -- store.ts

以下可以认为是按照 Vuex官方 建议顺序使用 Vuex

入口文件 store.ts

import Vue from "vue";
import Vuex from "vuex";
import state from "./state";
import { mutations } from "./mutations";
import { actions } from "./actions";
import { getters } from "./getters";
import moduleTest from "./modules/modules/moduleTest";

Vue.use(Vuex);

// 全局注册
const store = new Vuex.Store({
  state,
  mutations,
  actions,
  getters,
  modules: { moduleTest }
});

// 动态注册方法,Vuex(2.3.0+)可以用store.registerModule方法在进入路由时进行注册,离开路由时候销毁 actions, mutations, getters, state,在一定范围内相同名称不会被覆写
const moduleNameList = new Set<string>();
const registerModule = store.registerModule.bind(store);
// 模块名去重
store.registerModule = (name: any, m: any) => {
  const moduleName: string = Array.isArray(name) ? name.join(".") : name;
  if (moduleNameList.has(moduleName)) {
    console.log(`警告: ${name}已经被注册过`);
  }
  moduleNameList.add(moduleName);
  registerModule(name, m);
};
export default store;

state-types.ts 数据常量类型

export const testType = 'testType'
...

states.ts Vuex 数据变量定义入口

import * as stypes from './state-types'
// 变量类型用接口表示
export interface State {
   [stypes.testType] : boolean
   ...
}
// 定义 state 对象
const state: State = {
  // 设置默认值
  [stypes.testType]: true
}
export default state

getters.ts Vuex 取数据语法糖

// 这里需要使用到GetterTree这个泛型改造
import { GetterTree } from 'vuex'
import * as stypes from './state-types'
import { State } from './state'

export const getters: GetterTree<State, any> = {
  [stypes.testType](state: State) {
    return state[stypes.testType]
  }
  ...
}

使用 GetterTree 改造原因,Vuex 初始化规范的 getters 类型如下

export interface Module<S, R> {
  namespaced?: boolean;
  state?: S | (() => S);
  getters?: GetterTree<S, R>;
  actions?: ActionTree<S, R>;
  mutations?: MutationTree<S>;
  modules?: ModuleTree<R>;
}

使用 getters 语法糖取 state 数据

import * as stypes from "./state-types";
import { Getter } from "vuex-class";

@Component({})
export default class extends Vue {
  @Getter(stypes.testType)
  // 这里一定要用变量接受,非空断言符号代表此变量一定有初值,如果不打就要给定初始值
  private testType!: boolean;
}

mutation-types 同步更新数据方法类型

export const testMutation = "testMutation";

mutations 同步更新数据方法

import { MutationTree } from 'vuex'
import * as mtypes from './mutation-types'
import * as stypes from './state-types'
import { State } from './state'

export const mutations: MutationTree<any> = {
  [mtypes.testMutation](state: State,payload: any) {
    state[stypes.testType] = payload
  }
  ...
}

使用该同步方法

import { Mutation } from `vuex-class`;
import { Component, Vue } from "vue-property-decorator";
import * as mtypes from "./mutattion-types";

@Component({})
export default class Test extends Vue {
  @Mutation(mtypes.testMutation)
  private testMutation!: (...args: any) => void;
}

action-types.ts 批量同步或异步操作方法类型

export const testAction = "testAction";

actions.ts 批量同步或异步操作方法

import { Commit, ActionTree } from 'vuex'
import * as mtypes from './mutation-types'
import * as atypes from './action-types'
import { State } from './state'
import $http from './request'
export const actions: ActionTree<State, any> = {
  [atypes.testAction](context: { commit: Commit }, data: any) {
    context.commit(mtypes.testMutation,data)
  }

  // 异步操作
  async [atypes.testAction](context: { commit: Commit }) {
    const {data,code} = await $http.getList()
    if(code === 200) {
      context.commit(mtypes.testMutation, data || {})
    }
  }

}

使用方法

import { Component, Vue } from "vue-property-decorator";
import { Action } from "vuex-class";
import * as atypes from "./action-types";

@Component({})
export default class Test extends Vue {
  @Action(atypes.testAction)
  private testAction!: (...args: any) => void;
}

# 分命名空间写法

  1. store 入口添加模块

    import testModule from "./modules/testModule";
    const state = new Vuex.store({
      modules: { testMoudle }
    });
    
  2. 编写全局 namespace ,使用别名后可在全局引用对应方法,可视为模块装饰器

    import { namespace } from "vuex-class";
    
    // namespace参数为注册模块名
    export const testNamespace = namespace("testMoudle");
    
  3. 编写对应模块代码

    --- moudles
        --- testModule
            --- index.ts
            --- types.ts
        ...
    
    types.ts;
    
    import { ActionContext } from "vuex";
    import { State } from "./state";
    
    export const TEST_TYPE = "TEST_TYPE";
    
    // 导出state类型
    export interface TestMoudleState {
      [TEST_TYPE]: boolean;
    }
    // 后续定义actions需要用到 context的类型
    export type TestMoudlActionType = ActionContext<TestMoudleState, State>;
    
    index.ts
    
    import { TestMoudleState, TEST_TYPE } from './types'
    
    // state
    const state: TestMoudleState = {
      [TEST_TYPE]: true
      ...
    }
    
    // getters
    const getters = {
      [TEST_TYPE]: (gstate: TestMoudleState) => gstate
      ...
    }
    
    // actions
    const actions = {
      // 解构出commit
      [TEST_TYPE]: ({ commit }: TestMoudlActionType, val: boolean) => commit(TEST_TYPE, val)
      ...
    }
    
    // mutations
    const mutations = {
      [TEST_TYPE]: (mstate: TestMoudleState, val: boolean) => (mstate[TEST_TYPE] = val)
    }
    
    export default {
      state,
      getters,
      actions,
      mutations,
      namespaced: true
    }
    

    使用技巧

    import { testNamespace } from "./namespaces";
    import { TEST_TYPE } from "./types";
    
    @Component({})
    export default class Test extends Vue {
      @testNamespace.Action(TEST_TYPE)
      public testType!: (...args: any) => void;
      @testNamespace.Getter(TEST_TYPE)
      private testType!: boolean;
    }
    

# 从新增接口到 vuex 进行通信的全过程写法

  • 不分模块:state-types ==> state ==> mutation-types ==> mutations ==> action-types ==> actions
  • 分模块:namespace ==> modules ==> 正常写 Vuex 步骤

# Vue-Router

写法基本一致,4.x待学习

# 编写组件

编写一个组件模板

@Component({})
export default ComponentName extends Vue {
  // 接受上层组件参数
  @Prop({ddefault: ''})
  private test!: string // 非空断言符

  // 定义变量已知默认值
  private test1: boolean = false
  // 定义变量未知默认值
  private test2!: boolean

  // 接受Vuex states,actions type型
  @Getter(stypes.testType)
  private testType!: any
  @Action(atypes.testAction)
  private testAction!: (...args: any) => void
  // 接受Vuex states, actions namespace型
  @TestNamespace.Getter(TEST_TYPE)
  private test-type!: booean
  @TestNamespace.Action(TEST_TYPE)
  private testType!: (val:boolean) => void

  // 定义方法
  private methodName():any {

  }
  // 生命周期钩子
  ...

  // render TSX
  private render() {
    return (
      <tag></tag>
    )
  }
}

# 坑点

# 阻止事件冒泡

<div
  onClick={(e) => {
    e.stopropagation()
    this.handleClick()
  }}
  ></div>

# 获取dom

setup() {
  const eRef = ref<null | HTMLElement>(null)
}

# 动态class写法

<div style={{display: 'block'}}></div>

<div class={['class1',this.condition && class2,this.condition ? 'class1' : 'class2']}></div>

# slots

//parent.tsx
setup(props,{slots}) {
  return () => slots.default()
}

// child.tsx 
...

// use
<parent>
<child></child>
<child></child>
</parent>
Last update: 8/22/2021, 10:08:10 PM