美文网首页Typescriptvue
vue2+ts+vue-property-decorator

vue2+ts+vue-property-decorator

作者: 硅谷干货 | 来源:发表于2022-05-18 22:54 被阅读0次

    前言

    其实看 Vue2 的官方文档,对 Typescript 的支持还是相对比较少的,有两种方式定义组件

    • 使用 Vue.componentVue.extend 定义Vue组件
    • 使用 vue-class-component 装饰器来定义基于类的Vue组件

    其实市面上还有一种实现方式,那就是由社区基于 vue-class-component 装饰器的二次封装 vue-property-decorator, 它也是今天我们要分享主要内容

    npm vue-property-decorator 文档地址

    搭建 webpack5 + Vue2 + Ts

    安装 node, vue-cli, webpack ts 这些在这里我就不过多描述了,不懂的小伙伴可以看我前几篇文章,有关于环境搭建的,那搭建项目也比较简单,参考 vue-cli 搭建项目

    进入需要创建工程的目录,cmd 执行命令

    vue create vue-ts-demo 
    复制代码
    

    回车,我们选择第三个自定义选项

    image.png

    然后,根据自己的选择来勾选,记住勾选 typescript ,如下是我都选的

    image.png

    回车之后,选择 2.x 因为我们分享的是 vue 2.x + ts

    image.png

    紧跟着是一些里的描述于配置,yes/no 自己决定,当然也可以一系列 yes 到底,然后就到了这里

    image.png

    很熟悉,让我们进入工程目录,然后启动项目,接下来我们可以简单看一下此时的工程目录结构,目录结构大多数都是很熟悉的东西,就是有一点小小的改变

    image.png
    • 添加了 tsconfig.json (ts 配置文件)
    • shims-tsx.d.ts (支持 tsx)
    • shims-vue.d.ts (支持vue使用 ts)

    其它的不同就是打开文件后发现 vue 都是用 ts 编写的,拿一个 Home.vue 展示一下吧

    <template>
      <div class="home">
        <img alt="Vue logo" src="../assets/logo.png">
        <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
      </div>
    </template>
    
    <script lang="ts">
    import { Component, Vue } from 'vue-property-decorator';
    import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src
    
    @Component({
      components: {
        HelloWorld,
      },
    })
    export default class Home extends Vue {}
    </script>
    
    复制代码
    

    看起来很不一样哈,跟我们之前写的 vue 模板语法都不一样,不要着急,我们今天就是来分享vue2 如何使用 ts 的,好了,进入整体,我们直接开始撸代码

    如何编写组件

    这里呢,我不动目前所有的工程结构,直接拿 Aboud.vue 组件来演示如何使用 vue-property-deractor 来创建基于类的 vue 组件

    @Component 创建组件

    @Component 装饰器可以接收一个对象作为参数,可以在对象中声明 components ,filters,directives等未提供装饰器的选项,(下文会做一些演示)

    基础模板语法

    <template>
      <div class="about">
        <h1>我是about.vue组件</h1>
      </div>
    </template>
    
    <script lang="ts">
    import { Component, Vue } from "vue-property-decorator";
    
    @Component
    export default class about extends Vue {}
    </script>
    
    复制代码
    

    @Prop 接收参数

    @Prop装饰器接收一个参数,常常这样用

    • @Prop('String') : 指定 prop 的类型,字符串 String,Number,Boolean
    • @Prop({ default: 1, type: Number }) : 对象,指定默认值 {default:'1'}
    • @Prop([String,Number]) : 数组,指定 prop 的可选类型 [String, Boolean]
    // 父组件:
    <template>
      <div class="Props">
        <Child :name="name" :age="age" :sex="sex"></Child>
      </div>
    </template>
     
    <script lang="ts">
    import {Component, Vue,} from 'vue-property-decorator';
    import Child from './Child.vue';
     
    @Component({
      components: {Child}, // 上边有说 @Component 可接受的参数
    })
    export default class PropsPage extends Vue {
      private name = 'Hs';
      private age = 18;
      private sex = 1;
    }
    </script>
     
    // 子组件:
    <template>
      <div>
        name: {{name}} | age: {{age}} | sex: {{sex}}
      </div>
    </template>
     
    <script lang="ts">
    import {Component, Vue, Prop} from 'vue-property-decorator';
     
    @Component
    export default class Child extends Vue {
       @Prop(String) readonly name!: string | undefined;
       @Prop({ default: 20, type: Number }) private age!: number;
       @Prop([String, Number]) private sex!: string | number;
    }
    </script>
    复制代码
    

    @Propsync 不一样的@Prop

    @PropSync装饰器与@prop用法基本类似,只是多了一个参数, 父组件在传递的时候需要配合 .sync

    • 第一个参数是父组件传递过来的属性名
    • 第二个参数与@Prop的第一个参数一样
    • @PropSync 会生成一个新的计算属性 , 可逆向修改父组件传递过来的属性,父组件会同步修改
    // 父组件:
    <template>
      <div class="Props">
        <Child :name.sync="name"></Child>
      </div>
    </template>
     
    <script lang="ts">
    import {Component, Vue,} from 'vue-property-decorator';
    import Child from './Child.vue';
     
    @Component({
      components: { Child }, // 上边有说 @Component 可接受的参数
    })
    export default class PropsPage extends Vue {
      private name = 'Hs';
    }
    </script>
     
    // 子组件:
    <template>
      <div>
        name: {{name_copy}}
        <button @click="setProp">修改prop</button>
      </div>
    </template>
     
    <script lang="ts">
    import {Component, Vue, PropSync} from 'vue-property-decorator';
     
    @Component
    export default class Child extends Vue {
       @PropSync("name",String) name_copy!: string | undefined;
       setProp(){
           this.name_copy = "abcd" // 父组件会同步修改
       }
    }
    </script>
    复制代码
    

    @Watch 监听

    @Watch 装饰器接收两个参数:

    • 被监听的属性名
    • 可选属性: {immediate?:boolean 监听开始之后是否立即调用该回调函数,deep?:boolean 是否深度监听}
    <template>
      <div class="about">
        <h3> {{age}}</h3>
      </div>
    </template>
    
    <script lang="ts">
    import { Component, Vue, Watch } from "vue-property-decorator";
    
    @Component
    export default class About extends Vue {
      private age = 18;
    
      @Watch("age")
      // 可选参数 @Watch('age', {immediate: true, deep: true})
      onChangeAge(v: number, o: number): void {}
    
    }
    </script>
    
    复制代码
    

    @Emit 广播事件

    @Emit 装饰器接收一个可选参数,广播事件名,如果没有定义这个参数,则是以回调方法名为广播事件名

    • 回调函数的返回值默认为第二个参数,如果返回是 promise ,则会默认为 resolve 之后触发回调
    // 父组件
    <template>
      <div class="about">
        <ChildComp @childEmit="chileEmit" />
      </div>
    </template>
    
    <script lang="ts">
    import { Component, Vue } from "vue-property-decorator";
    import ChildComp from "./Child.vue";
    
    @Component
    export default class About extends Vue {
      chileEmit(n: string): void {
        console.log("子组件 emit 触发,参数:", n);
      }
    }
    </script>
    
    //子组件
    <template>
      <div>
        <h3>我是子组件</h3>
        <button @click="customClickName"> @emit age+1 </button>
      </div>
    </template>
    
    <script lang="ts">
    import { Component, Emit, Vue } from "vue-property-decorator";
    
    @Component
    export default class Child extends Vue {
      @Emit("childEmit")
      customClickName(): string {
        return "hs"
      }
    }
    </script>
    
    复制代码
    

    @ref 句柄

    @Ref 装饰器接收一个可选参数,用来指向元素或子组件的引用信息,即 ref="这个值"

    <template>
      <div class="about">
        <button @click="getRef"
                ref="child_btn">age++</button>
        <hr>
        <ChildComp :name="name"
                   :age="age"
                   ref="child_c"
                   @childEmit="chileEmit" />
      </div>
    </template>
    
    <script lang="ts">
    import { Component, Provide, Ref, Vue, Watch } from "vue-property-decorator";
    import ChildComp from "./Child.vue";
    
    @Component({
      components: { ChildComp }
    })
    export default class About extends Vue {
      @Ref("child_c") readonly child_comp!: ChildComp;
      @Ref("child_btn") readonly child_btn_dom!: HTMLButtonElement;
      getRef() {
        console.log(this.child_comp, this.child_btn_dom);
      }
    }
    </script>
    
    复制代码
    

    @Provide / @Inject 和 @ProvideReactive / @InjectReactive`

    提供/注入装饰器,key可以为string或者symbol类型,使用方式都一样

    • 相同: Provide/ProvideReactive提供的数据,在子组件内部使用Inject/InjectReactive都可取到
    • 不同: ProvideReactive 的值被父组件修改,子组件可以使用 InjectReactive 捕获
    // 顶层组件
    <template>
      <div class="about">
        <ChildComp />
      </div>
    </template>
    
    <script lang="ts">
    import { Component, Provide,Vue } from "vue-property-decorator";
    import ChildComp from "./Child.vue";
    
    @Component({
      components: { ChildComp },
    })
    export default class About extends Vue {
      @Provide("provide_value") private p = "from provide";
    }
    </script>
    
    // 子组件
    <template>
      <div>
        <h3>我是子组件</h3>
        <h3>provide/inject 跨级传参 {{provide_value}}</h3>
    
      </div>
    </template>
    
    <script lang="ts">
    import { Component, Inject, Vue } from "vue-property-decorator";
    
    @Component
    export default class Child extends Vue {
      @Inject() readonly provide_value!: string;
    }
    </script>
    
    复制代码
    

    如何使用 tsx

    其实 tsx 用起来会让我们有 react 的感觉,写过 react 的都知道是使用 jsx javascript + xml, 那么 tsx 基本上跟 jsx 差不多,等同于 typescript + xml,用一个实例来体现一下 现有的工程不变,我们搭建环境的时候已经支持了 tsx

    创建一个 demo.tsx ,键入如下简单内容,大致看下来,基本上跟我们上边分享的类组件一样,唯一有一点不一样的就是模板变成 render 函数,这样可以让我们更加灵活。

    import { Component, Emit, Prop, PropSync, Vue, Watch } from "vue-property-decorator";
    
    @Component
    export default class Demo extends Vue {
    
      public name = "Hs"
      public str = "hello tsx"
      public data = [1, 2, 3, 4]
    
      // Prop
      @Prop() demo_name!: string
      @Prop(Number) demo_age!: number
    
      // Propsync
      @PropSync("propsync", Number) propsync_copy!: number | undefined
    
      // Computed
      get _age(): number {
        this.str = this.str + "-x"
        return this.demo_age * 10
      }
    
      //watch
      @Watch("str")
      onhangeStr(v: string, o: string) {
        console.log(v, o)
      }
    
      //emit
      @Emit("tsx_emit")
      clickEvent() { return "params 123" }
    
      // 渲染函数    
      render() {
        return (
          <div>
            <h2>data属性: {this.name}-{this.str}</h2>
            <h2>prop: {this.demo_name}</h2>
            <h2>计算属性: {this._age}</h2>
            <h2>prop-sync: {this.propsync_copy}</h2>
            <h2>遍历</h2>
            {
              this.data.map(c => {
                return <span>{c} - </span>
              })
            }
            <button onClick={() => this.clickEvent()}>emit</button>
          </div>
        )
      }
    
    }
    
    复制代码
    

    ok,tsx 的使用基本上跟 ts 差不多,这里都是简单的例子和使用方式,便于理解和学习,更重要的是如何学习好 ts, 它真的很枪手。

    贴出我的package.json 文件,仅供版本差异的参考:

    {
      "name": "vue2-ts-demo2",
      "version": "0.1.0",
      "private": true,
      "scripts": {
        "serve": "vue-cli-service serve",
        "build": "vue-cli-service build",
        "lint": "vue-cli-service lint"
      },
      "dependencies": {
        "vue": "^2.6.11",
        "vue-class-component": "^7.2.3",
        "vue-property-decorator": "^9.1.2",
        "vue-router": "^3.2.0",
        "vuex": "^3.4.0"
      },
      "devDependencies": {
        "@typescript-eslint/eslint-plugin": "^4.18.0",
        "@typescript-eslint/parser": "^4.18.0",
        "@vue/cli-plugin-eslint": "^4.0.5",
        "@vue/cli-plugin-router": "^4.0.5",
        "@vue/cli-plugin-typescript": "^4.0.5",
        "@vue/cli-plugin-vuex": "^4.0.5",
        "@vue/cli-service": "^4.0.5",
        "@vue/eslint-config-prettier": "^6.0.0",
        "@vue/eslint-config-typescript": "^7.0.0",
        "eslint": "^6.7.2",
        "eslint-plugin-prettier": "^3.3.1",
        "eslint-plugin-vue": "^6.2.2",
        "node-sass": "^4.12.0",
        "prettier": "^2.2.1",
        "sass-loader": "^8.0.2",
        "typescript": "~4.1.5",
        "vue-template-compiler": "^2.6.11"
      },
      "eslintConfig": {
        "root": true,
        "env": {
          "node": true
        },
        "extends": [
          "plugin:vue/essential",
          "eslint:recommended",
          "@vue/typescript/recommended",
          "@vue/prettier",
          "@vue/prettier/@typescript-eslint"
        ],
        "parserOptions": {
          "ecmaVersion": 2020
        },
        "rules": {}
      },
      "browserslist": [
        "> 1%",
        "last 2 versions",
        "not dead"
      ]
    }
    
    

    参考链接:资料

    相关文章

      网友评论

        本文标题:vue2+ts+vue-property-decorator

        本文链接:https://www.haomeiwen.com/subject/fljzurtx.html