0
点赞
收藏
分享

微信扫一扫

Vuejs装饰器风格开发教程(计算属性、事件派发、侦听器)

计算属性

计算属性的设计背景:在 Vuejs 开发时我们可以在模板中通过编写表达式的方式做一系列的逻辑处理,但这就偏离的模板的概念,还会使得模板的内容变得臃肿且难以维护,所以引入了计算属性的来对不该出现在模板中的复杂逻辑处理进行重构,使用计算属性重构后的依然保持了状态的响应式。

在类组件中,通过使用类属性的访问器方法(gettersetter)来描述计算属性的读写操作。在下面的代码中就是一个应用计算属性的典型场景。

基础示例

在计算属性的 getter 中应该仅做计算而不包含任何其他的副作用,应该严厉禁止在 getter 中做异步请求或改变 DOMgetter 的触发会使得数据的变化,数据的变化会驱动 DOM 的变化,这是一个正向的流程,强行对 DOM 进行修改会导致数据与 DOM 不同步,也会造成性能问题(getter 会频繁触发)。

<script lang="ts">
  import { Component, Vue } from 'vue-facing-decorator';

  @Component
  export default class App extends Vue {
    
    firstName = '';
    lastName = '';

    // 计算属性(标记为 get 访问器):对 firstname 和 lastname 进行合并
    get fullName() {
      return this.firstName + '/' + this.lastName;
    }
    
  }
</script>

<template>
  <div>
    <p>firstName:<input v-model="firstName" /></p>
    <p>lastName:<input v-model="lastName" /></p>
    <p>fullName:{{ fullName }}</p>
  </div>
</template>

可写的计算属性

下面的代码是改造后的支持可写的计算属性的场景,在 fullName 发生变化后会重写对 firstNamelastName 进行赋值。

<script lang="ts">
  import { Component, Vue } from 'vue-facing-decorator';

  @Component
  export default class App extends Vue {
    
    firstName = '';
    lastName = '';
    
    get fullName() {
      return this.firstName + '/' + this.lastName;
    }
    
    set fullName(newValue) {
      [this.firstName, this.lastName] = newValue.split('/');
    }
  }
</script>

<template>
  <div>
    <p>firstName:<input v-model="firstName" /></p>
    <p>lastName:<input v-model="lastName" /></p>
    <p>fullName:<input v-model="fullName" /></p>
  </div>
</template>

原生访问器

在类组件中如果要使用原生的 gettersetter 时需要用 vfd 提供得 @Vanilla装饰器。

事件派发

$emitVuejs 组件开发中是使用频率较高一个 API,往往会使用 $emit 将子组件运行后的结果通知到父组件去执行后续的一些操作。在类组件中同样需要一个新的装饰器支持:@Emit

基础示例

在下面这个 Form 组件中,我们仅让它来负责数据的收集,在触发登陆按钮后会通过事件派发将 username 和 password 数据发送至它的父组件。

<script lang="ts">
  import { Component, Emit, Vue } from 'vue-facing-decorator';

  @Component({
    name: 'Form',
  })
  export default class From extends Vue {
    username = 'admin';
    password = '123456';

    @Emit('login')
    login() {
      return {
        username: this.username,
        password: this.password,
      }
    }
  }
</script>

<template>
  <p>username: <input v-model="username"></p>
  <p>password: <input v-model="password"></p>
  <p><button @click="login">登陆</button></p>
</template>
<script lang="ts">
  import Form from './components/Form.vue';
  import { Component, Vue } from 'vue-facing-decorator';

  @Component({
    components: {
      Form,
    },
  })
  export default class App extends Vue {
    toLogin(value: any) {
      console.log(`以获取登陆数据,${value.username}/${value.password},可以执行登录请求~`);
    }
  }
</script>

<template>
  <div>
    <Form @login="toLogin"></Form>
  </div>
</template>

^注: @Emit 的参数即事件派发的名称,如果装饰器所描述的方法名称和派发事件的名称一致,则可以省略装饰器内的参数。

异步事件

将上面的案例进行改造,Form 组件中的 login 方法负责请求服务器进行实际的登陆处理,将登陆的结果进行派发,在其父组件接收登录的结果。

<script lang="ts">
  import { Component, Emit, Vue } from 'vue-facing-decorator';

  @Component({
    name: 'Form',
  })
  export default class From extends Vue {
    username = 'admin';
    password = '123456';

    @Emit('login')
    login() {
      return new Promise((resolve) => {
        setTimeout(() => {
            resolve('登陆成功~')
        }, 1000)
      })
    }
  }
</script>

<template>
  <p>username: <input v-model="username"></p>
  <p>password: <input v-model="password"></p>
  <p><button @click="login">登陆</button></p>
</template>
<script lang="ts">
  import Form from './components/Form.vue';
  import { Component, Vue } from 'vue-facing-decorator';

  @Component({
    components: {
      Form,
    },
  })
  export default class App extends Vue {
    toLogin(value: any) {
      console.log(`登陆结果:${value}`);
    }
  }
</script>

<template>
  <div>
    <Form @login="toLogin"></Form>
  </div>
</template>

侦听器

在类组件中提供装饰器 @Watch 对需要侦听的类属性进行描述,@Watch 的参数 1 是被侦听的属性名,参数 2 是一个可选的选项列表,分别是deepflushimmediate

<script lang="ts">
  import { Component, Watch, Vue } from 'vue-facing-decorator';

  @Component
  export default class App extends Vue {
    userName = 'admin';
    passWord = '123456';

    @Watch('userName')
    userNameWatcher(newValue: string, oldValue: string) {
      console.table([
        { '': 'userName', newValue: newValue, oldValue: oldValue },
      ]);
    }

    @Watch('passWord')
    passWordWatcher(newValue: string, oldValue: string) {
      console.table([
        { '': 'passWord', newValue: newValue, oldValue: oldValue },
      ]);
    }
  }
</script>

<template>
  <div>
    <p>UserName:<input v-model="userName" /></p>
    <p>PassWord:<input v-model="passWord" /></p>
  </div>
</template>

深度监听

在面对普通的类属性时可以不指定任何的可选选项,但是对于对象类型的类属性来说,就必须用到 deep 选项了,作用同 vue option api。

<script lang="ts">
  import { Component, Watch, Vue } from 'vue-facing-decorator';

  @Component
  export default class App extends Vue {
    login = {
      userName: 'admin',
      passWord: '123456',
    };

    @Watch('login', {
      deep: true,
    })
    userNameWatcher(newValue: any, oldValue: any) {
      console.log(newValue, oldValue);
    }
  }
</script>

<template>
  <div>
    <p>UserName:<input v-model="login.userName" /></p>
    <p>PassWord:<input v-model="login.passWord" /></p>
  </div>
</template>

回调触发时机

当响应式的状态发生改变后,除了会触发侦听器以外还会触发 Vuejs 组件更新,也就是会触发 beforeUpdateupdated 生命周期。默认情况下执行的先后顺序是:Watcher => beforeUpdate => updated,但是如果需要再 Watcher 中对更新前的 DOM 进行访问和操作时,就需要指定 flush 选项值为 post,这时的执行先后顺序就变更为:beforeUpdate => Watcher => updated

<script lang="ts">
  import { Component, Watch, Vue } from 'vue-facing-decorator';

  @Component
  export default class App extends Vue {
    
    userName = 'admin';

    @Watch('userName', {
      flush: 'post'
    })
    userNameWatcher(newValue: any, oldValue: any) {
      console.log(newValue, oldValue);
    }

    beforeUpdate() {
      console.log('beforeUpdate')
    }
    
    updated() {
      console.log('updated')
    }
  }
</script>

<template>
  <div>
    <p>UserName:<input id="username" v-model="userName" /></p>
  </div>
</template>

立即执行的侦听器

侦听器默认情况下仅在响应状态发生变化时才会触发回调,但某些场景下会需要在侦听器被创建后就立即执行一次,比如说根据默认的响应状态要做一遍数据的筛选等等。

@Watch('userName', {
  immediate: true,
})
userNameWatcher(newValue: any, oldValue: any) {
  console.log(newValue, oldValue);
}
举报

相关推荐

0 条评论