8.计算属性

189 阅读5分钟

面试题:计算属性和方法有什么区别?

计算属性本质上是包含getter和setter的方法 当获取计算属性时,实际上是在调用计算属性的getter方法。vue会收集计算属性的依赖,并缓存计算属性的返回结果。只有当依赖变化后才会重新进行计算。特别需要注意这句话,只有依赖发生改变的时候才会重新计算。只有依赖发生改变的时候才会重新计算。

方法没有缓存,每次调用方法都会导致重新执行。 计算属性的getter和setter参数固定,getter没有参数,setter只有一个参数。而方法的参数不限。 由于有以上的这些区别,因此计算属性通常是根据已有数据得到其他数据,并在得到数据的过程中不建议使用异步、当前时间、随机数等副作用操作。 实际上,他们最重要的区别是含义上的区别。计算属性含义上也是一个数据,可以读取也可以赋值;方法含义上是一个操作,用于处理一些事情。

完整的计算属性书写:

computed: {
  propName: {
    get(){
      // getter
    },
    set(val){
      // setter
    }
  }
}

只包含getter的计算属性简写:

computed: {
  propName(){
    // getter
  }
}

下面来看具体的例子 app.vue

<template>
  <div>
    <h1>App组件</h1>
    <h2>{{ count }}</h2>
    <p>姓:{{ firstName }}</p>
    <p>名:{{ lastName }}</p>
    <p>全名:{{ fullName }}</p>
    <p>全名:{{ fullName }}</p>
    <p>全名:{{ fullName }}</p>
    <p>全名:{{ fullName }}</p>
    <p>全名:{{ fullName }}</p>
    <button @click="fullName = '王麻子'">修改姓名为王麻子</button>
    <button @click="count++">count++</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      firstName: "亮",
      lastName: "哥",
      count: 0,
    };
  },
  computed: {
    fullName: {
      get() {
        console.log("fullname called");
        return this.firstName + " " + this.lastName + Date.now();
      },
      set(val) {
        console.log("正在设置全名", val);
        this.firstName = val[0];
        this.lastName = val.substr(1);
      },
    },
  },
  methods: {
    getFullName() {
      console.log("method called");
      return this.firstName + " " + this.lastName;
    },
  },
};
</script>

1.上面计算属性的简单写法就是

fullName(){
return this.firstName + " " + this.lastName
}

因为是计算属性,可以直接使用{{fullName}}就拿到了属性的值。而不是使用{{fullName()}}

2.计算属性的完整写法其实和常规的属性写法一样。计算属性,也只有当前属性的依赖发生改变的时候才会重新计算。在当前的例子里面就是firstName和lastName发生改变的时候才会重新计算,而如果你用上面的getFullName这样的方法去获取属性的时候,不论firstName和lastName是否发生了改变,只要调用方法,都会运行一遍,性能会差一些。

3.计算属性里面不能加上Date.now(),随机数这些东西,因为并不是计算属性获取的方法并不是每次都运行的,只有依赖发生改变的时候才运行。

4.注意一点计算属性收集的依据只能是响应式数据。比方说下面的例子Date.now()并不是响应式数据。他的改变并不会导致计算属性重新计算。下面只有firtName 和 lastName改变才会重新计算属性。

fullName(){
return this.firstName + " " + this.lastName + Date.now(),
}

 fullName: {
      get() {
        console.log("fullname called");
        return this.firstName + " " + this.lastName;
      },
      set(val) {
        console.log("正在设置全名", val);
        this.firstName = val[0];
        this.lastName = val.substr(1);
      },
    },

5.计算属性强调的是属性,而方法强调操作。

6.其实计算属性都可以改成方法的形式,只不过计算属性比方法多了一层数据的缓存,即当依赖项不发生改变的时候,不会重新计算。

7.计算属性可以 $route 作为依赖

计算属性的完整写法

完整的计算属性书写:

computed: {
  propName: {
    get(){
      // getter
    },
    set(val){
      // setter
    }
  }
}

只包含getter的计算属性简写:

computed: {
  propName(){
    // getter
  }
}

icon 组件的创建

app.vue 中的代码

<template>
  <div>
    <h1>App组件</h1>
    <Icon type="arrowDown" />
  </div>
</template>

<script>
import Icon from "./components/Icon";
export default {
  components: {
    Icon,
  },
};
</script>

<style scoped>
.iconfont {
  font-size: 26px;
  color: red;
}
</style>

1.默认style加上 scoped,在当前类写的选择器只能对当前的组件有效,vue为了解决在父组件中可以改变子组件的样式,内部进行了处理,可以改变子组件的根元素。可以利用选择器子组件的根元素。

icon.vue

<template>
  <i class="iconfont" :class="fontClass"></i>
</template>

<script>
const classMap = {
  home: "iconzhuye",
  success: "iconzhengque",
  error: "iconcuowu",
  close: "iconguanbi",
  warn: "iconjinggao",
  info: "iconxinxi",
  blog: "iconblog",
  code: "iconcode",
  about: "iconset_about_hov",
  weixin: "iconweixin",
  mail: "iconemail",
  github: "icongithub",
  qq: "iconsign_qq",
  arrowUp: "iconiconfonticonfonti2copy",
  arrowDown: "iconiconfonticonfonti2",
  empty: "iconempty",
  chat: "iconliuyan",
};
export default {
  props: {
    type: {
      type: String,
      required: true,
    },
  },
  computed: {
    // 图标类样式
    fontClass() {
      return classMap[this.type];
    },
  },
};
</script>

<style scoped>
@import "//at.alicdn.com/t/font_2164449_nalfgtq7il.css";
.iconfont {
  color: inherit;
  font-size: inherit;
}
</style>

  1. 关于字体图标的使用,需要在字体图标网站新建一个项目,然后在style 里面导入iconfont的地址。
  2. 这里就是用到了计算属性,因为字体图标本身的使用使用到的真实类的类名不太明显,所以这里的做法从外部传入一个好分辨的类型type,然后根据classMap计算出fontClass的属性,最后动态绑定到fontClass上面就完成了。
  3. class 可以动态绑定的数据结构包括 字符串,对象,数组。

组件内部的样式由父组件决定的问题

我们开发的过程中有时会遇到内部的组件样式由外部决定,而且这个样式可能比较多,或者是内部不太好处理,需要外部去处理,该如何解决呢?

按照之前说由于样式的后面都加了scoped,按理说我们不能从外部去通过选择器去直接控制组件内部的样式,vue 为了解决这个问题,让父组件的最外层可以去控制子组件的最高一层,详见下面的图例。原理其实就是在内部的最高一层标签上也加了外层的类样式。

image.png

image.png

如何测试组件

1.每个组件建一个文件夹,比方说pager文件夹,下面index.vue 放的是组件的代码,test.vue里面放的是测试代码。

image.png

2.每次测试时候要先 cd 到当前的项目文件夹,就是package.json 所在文件夹,然后执行 vue serve ./src/components/test.vue 即可进行测试。

  1. 还可以在package里面配置脚本 "test:Avatar": "vue serve ./src/components/Avatar/test.vue" ,运行的时候只要输入 npm run test:Avatar 即可。