Vue и Vuex в Typescript — практическое руководство

Vue.js

Обзор: Недавно я изучал vue и typeScript и обнаружил, что есть некоторые методы и методы написания, которые необходимо разобрать и обобщить. В основном ссылаются на справочные статьиvue-property-decorator,vue-class-component, поэтому выберите несколько ключевых моментов для осаждения и размышлений.

опубликовать: 2019-09-15

Доступен сReact и Redux в TypeScript — руководство по статической типизациисравнивать

содержание

  • Введение, конфигурация среды (vue-cli)
  • Vue — шаблон компонента (использование vue-property-decorator)
  • Vuex — управление состоянием (использование vuex-module-decorators, vuex-class)

Введение, конфигурация среды

vue-cli содержит параметры машинописного текста, просто выберите

vue create repo
# 手动配置的时候需要选择 TypeScript
Check the features needed for your project:
 ◉ Babel
 ◉ TypeScript
 ◯ Progressive Web App (PWA) Support
 ◯ Router
 ◉ Vuex
❯◉ CSS Pre-processors
 ◉ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing

Ознакомление с декораторами vuex-module-decorators и vuex-class см.

официальная документация vuex-module-decorators

vue-class ReadMe.

Vue — шаблон компонента

Использование Typescript для разработки Vue, в основном с использованием vue-property-decorator, чтобы предоставить классу компонентов различные возможности, в частности

  • @ComponentВ основном используется для реализации компонентов Vue в виде стиля класса.

    import { Component, Vue } from 'vue-property-decorator';
    
    @Component
    export default class HelloWorld extends Vue {
    
        public render() {
            return (<h5>Hello world</h5>);
        }
    }
    

    с предыдущим.vueОтличие в том, что с помощьюrenderдля визуализации компонента. В настоящее времяclass Hello worldСостояние и логика компонентов, реализованных в

    • dataСостояние компонента, критическое в vuedataкак толькоclassсвойства для достижения

      import { Component, Vue } from 'vue-property-decorator';
      
      @Component
      export default class HelloWorld extends Vue {
      
          private message: string = 'world';
      
          public render() {
              const { message }: HelloWorld = this;
      
              return (<h5>Hello {message}</h5>);
          }
      }
      

      использоватьprivat messageкак состояние компонента;

    • computedЧасто используются вычисляемые свойства компонентов в компонентах vue в виде class-style.get()оператор для реализации

      @Component
      export default class HelloWorld extends Vue {
      
          private message: string = 'world';
      
          get subMessage(): string {
              return `boy ${this.message}`;
          }
      
          public render() {
              const { message, subMessage }: HelloWorld = this;
      
              return (
                  <div>
                      <h5>Hello {message}</h5>
                      <span>{subMessage}</span>
                  </div>
              );
          }
      }
      
    • methodsа такжеdataТочно так же он также реализован как атрибут класса, который часто используется для реализации событий операций на странице, таких как клики, выбор и т. д.

      @Component
      export default class HelloWorld extends Vue {
      
          private message: string = 'world';
      
          get subMessage(): string {
              return `boy ${this.message}`;
          }
      
          public setMessage(msg: string): void {
              this.message = msg;
          }
      
          public render() {
              const { message, subMessage, setMessage }: HelloWorld = this;
      
              return (
                  <div>
                      <h5>Hello {message}</h5>
                      <span>{subMessage}</span>
                      <button onClick={() => setMessage('person')}>
                          Click
                      </button>
                  </div>
              );
          }
      }
      
  • @PropЯвляясь ключевой частью реализации однонаправленного потока данных, он также является ключом к передаче данных между компонентами.

    import { Component, Prop, Vue } from 'vue-property-decorator';
    
    @Component
    export default class HelloWorld extends Vue {
    
        @Prop({ default: 'Click' }) public readonly porpA!: string;
    
        public render() {
            const { porpA }: HelloWorld = this;
    
            return (
                <div>
                    <h5>{porpA}</h5>
                </div>
            );
        }
    }
    

    допустимый@PropКонфигурация, связанная с параметрамиoptions, и vue-property-decorator также предоставляет@PropSync, Так и будетpropsа такжеcomputedСоставное использование, конкретное использование

    // 父组件 parent
    <Child 
        name="hello world" 
        @update:name="handleUpdate" 
    />
    // 子组件 child
    @Component
    export default class Child extends Vue {
      @PropSync('name', { type: String }) 
      syncedName!: string
      
      public setSyncedName() {
          this.syncedName = 'delete';
      }
    }
    

    Эквивалентно предыдущему однофайловому компонентному режиму

    export default {
      props: {
        name: {
          type: String
        }
      },
      computed: {
        syncedName: {
          get() {
            return this.name
          },
          set(value) {
            this.$emit('update:name', value)
          }
        }
      }
    }
    
  • @EmitЭквивалентноthis.$emit, часто используется для запуска пользовательских событий

    // tsx
    @Emit()
    addToCount(n: number) {
        this.count += n
    }
    // vue
    methods: {
        addToCount(n) {
            this.count += n
            this.$emit('add-to-count', n)
        }
    }
    
  • @WatchОтносится к свойству прослушивания, то есть при прослушиванииdataилиpropsКогда есть изменение, соответствующее изменение будет запущено

    import { Component, Watch, Vue } from 'vue-property-decorator';
    
    @Component
    export default class HelloWorld extends Vue {
    
        private message: string = 'world';
        @Watch('message', { immediate: true, deep: true })
        public onMsgChange(val: string, oldVal: string) {
            console.log('onMsgChange', val, oldVal);
        }
    
    
        public render() {
            const { message }: HelloWorld = this;
    
            return (
                <div>
                    <h5>Hello {message}</h5>
                    <input type='text' v-model={this.message}/>
                </div>
            );
        }
    }
    

    @WatchПервый параметр представляет слушателяpath, второй параметр представляет

    { immediate: true, deep: true } //是否立即触发、深度侦听
    
  • @Provideа также@injectЭти два обычно используются при разработке компонентов.Проще говоря, переменные предоставляются в родительских компонентах через провайдер, а затем переменные вводятся в дочерние компоненты через инъекцию в стиле класса.

    import { Component, Inject, Provide, Vue } from 'vue-property-decorator'
     
    const symbol = Symbol('baz')
     
    @Component
    export class MyComponent extends Vue {
      @Inject() readonly foo!: string
     
      @Provide() foo = 'foo'
    }
    

Обратите внимание, что это общийdataне отвечает

  • @ProvideReactiveтак же как@InjectReactiveТо же использование, что и выше, но общее значение, если оно изменено в родительском компоненте, будет зафиксировано в дочернем компоненте.

  • @refЧасто используется для прямого доступа к компонентам

    import { Vue, Component, Ref } from 'vue-property-decorator'
     
    import AnotherComponent from '@/path/to/another-component.vue'
     
    @Component
    export default class YourComponent extends Vue {
      @Ref() readonly anotherComponent!: AnotherComponent
      @Ref('aButton') readonly button!: HTMLButtonElement
    }
    # 等同于
    export default {
      computed() {
        anotherComponent: {
          cache: false,
          get() {
            return this.$refs.anotherComponent as AnotherComponent
          }
        },
        button: {
          cache: false,
          get() {
            return this.$refs.aButton as HTMLButtonElement
          }
        }
      }
    }
    
  • @ModelЧасто используется для реализацииv-modelТакая директива в предыдущем компоненте vue

    export default {
      model: {
        prop: 'checked',
        event: 'change'
      },
      props: {
        checked: {
          type: Boolean
        }
      }
    }
    

    В стиле класса просто используйте@Modelреализовать

    import { Vue, Component, Model } from 'vue-property-decorator'
    
    @Component
    export default class YourComponent extends Vue {
      @Model('change', { type: Boolean }) readonly checked!: boolean
    }
    

Vuex — управление состоянием

При использовании Vuex раньше это в основном зависело отstate,getters,mutationsтак же какactions, и они могут быть модульными

  • stateусловие

    import { Module, VuexModule } from 'vuex-module-decorators'
    
    @Module
    export default class Vehicle extends VuexModule {
      wheels = 2
    }
    // 来替代之前的
    export default {
      state: {
        wheels: 2
      }
    }
    
  • Getterс классным стилем вcomputedаналогично, также с помощьюgettersдостигать

    import { Module, VuexModule } from 'vuex-module-decorators'
    
    @Module
    export default class Vehicle extends VuexModule {
      wheels = 2
      get axles() {
        return this.wheels / 2
      }
    }
    // 来代替之前的
    export default {
      state: {
        wheels: 2
      },
      getters: {
        axles: (state) => state.wheels / 2
      }
    }
    
  • Mutationмодифицироватьstate, как и Vuex в js, можно использовать только для реализации синхронных операций

    import { Module, VuexModule, Mutation } from 'vuex-module-decorators'
    
    @Module
    export default class Vehicle extends VuexModule {
      wheels = 2
    
      @Mutation
      puncture(n: number) {
        this.wheels = this.wheels - n
      }
    }
    // 来替代之前的
    export default {
      state: {
        wheels: 2
      },
      mutations: {
        puncture: (state, payload) => {
          state.wheels = state.wheels - payload
        }
      }
    }
    
  • actionsИспользуется для реализации асинхронных операций

    const request = require('request')
    export default {
      state: {
        wheels: 2
      },
      mutations: {
        addWheel: (state, payload) => {
          state.wheels = state.wheels + payload
        }
      },
      actions: {
        fetchNewWheels: async (context, payload) => {
          const wheels = await request.get(payload)
          context.commit('addWheel', wheels)
        }
      }
    }
    // 来替代之前的
    const request = require('request')
    export default {
      state: {
        wheels: 2
      },
      mutations: {
        addWheel: (state, payload) => {
          state.wheels = state.wheels + payload
        }
      },
      actions: {
        fetchNewWheels: async (context, payload) => {
          const wheels = await request.get(payload)
          context.commit('addWheel', wheels)
        }
      }
    }
    

Выше мы просто реализовали модуль Vuex, так как же его использовать? Разберите приведенный выше код и настройте его в Vuex, в частности

import { Module, VuexModule } from 'vuex-module-decorators'

@Module({ name: 'Vehicle',  namespaced: true, stateFactory: true })
export default class Vehicle extends VuexModule {
    public wheels = 2;
    get axles() {
        return this.wheels / 2;
    }
    @Mutation
    public puncture(n: number): void {
        this.wheels = this.wheels - n;
    }
}

осторожностьModuleНужная нам конфигурация должна бытьnamespaced: true, назовите пространство и импортируйте его в проект

import Vue from 'vue'
import Vuex from 'vuex'
import Vehicle from './Vehicle'

Vue.use(Vuex)

export default new Vuex.Store({
    modules: {
        Vehicle,
    },
})

затем используйтеvuex-class, чтобы получить статус и методы в пространстве имен

import { Component, Vue } from 'vue-property-decorator';
import {
    Getter,
    Mutation,
    namespace,
} from 'vuex-class';

const Vehicle = namespace('Vehicle');

@Component
export default class HelloWorld extends Vue {
	// 引入 Vechicle 下的 Getters
    @Vehicle.Getter('axles') public axles: number | undefined;
    // 引入 Vechicle 下的 puncture
    @Vehicle.Mutation('puncture')
    public mutationPuncture!: (n: number) => void;

    private message: string = 'world';

    public render() {
        const { message, axles, mutationPuncture }: HelloWorld = this;

        return (
            <div onClick={ () => mutationPuncture(1) }>
                <h5 ref='quickEntry'>Hello {message} { axles }</h5>
            </div>
        );
    }
}

использоватьnamespaceВы можете легко получить модуль Vehicle Vuex, а затем сотрудничать сGetter,MutationВведение можно завершить, да и другие способы введения аналогичны, поэтому не буду вдаваться в подробности.

В заключение

В целом, весь процесс опыта очень похож на ощущения от написания Mobx и React на Typescript раньше, очень похоже. Если использовать его в реальных проектах, ts принесет некоторое удобство, но он также кажется немного надуманным Другое продолжение 3.0, давайте посмотрим, сможет ли он произвести замечательную химическую реакцию с ts.