Vue 朝花夕拾(基础与组件)

# 在本文中,笔者又提炼了以下几个重点

  1. 补偿双向数据绑定 Vue.$set
  2. 数据侦听 Vue.$watch
  3. 表单绑定修饰符
  4. 动态组件
  5. 基础组件的自动化全局注册
  6. Vue.$emit参数,及与 v-on 事件命名规范
  7. Prop传递数据时防脏
  8. 插槽及高复用组件

# 补偿双向数据绑定 Vue.$set

  官网说受JS限制,笔者觉得讲的太浅了。相信了解双向数据绑定原理的朋友都知道,Vue 2是依赖原生JS中Object.defineProperty()方法的存取操作符set即数据劫持实现数据实时更新。然而对于一些引用类型的数据,如果操作不是发生在已经定义好的数据结构本身,Vue 无法探测普通的新增属性 (比如 this.myObject.newProperty = 'hi'),我们称它为不是响应式的。如:

(1) 对象属性的添加和删除时: 见案例
(2) 利用索引直接设置数组的一个项时: vm.items[2] = 'red'
(3) 修改数组长度时: vm.items.length = newLen

data () {
  return {
    user: { name: 'zfs', age: 25  }
  }
},
mounted: {
  this.user = { name: 'borui' }  // 改变对象本身,触发setter
  this.user.tall = 178  // 新增属性,未触发setter,视图不更新
}

  为了解决这个问题,尤大大重写了set方法,提供了$setAPI接口。注意不要写成vm.$set(key, value)形式,这种错误就略低级了。对比一下原生和API接口

原生方法:Object.defineProperty(object, 'key’, descriptor)
API 接口: vm.$set(vm.Obj, 'key', 'value')

  很容易发现,该接口原理将描述符descriptor设置为set, 将输入的新值value作为参数传递给set调用从而手动触发数据劫持。因此,上述案例需要改成:

mounted () {
  this.$set(this.student, "tall",  178)
}

  但是问题又来了,如果需要一次性增加多个新的响应式属性,显然多次调用$set方法不是个很好的选择。通常我们会使用Object.assign()_extend()来实现。Vue建议创建一个新的对象来存放两个合并对象的所有属性(通常用空对象{}),然后再赋值给目标元素。而不是直接合并到目标元素上。做法即:

vm.user = Object.assign({}, vm.user, {
  hobby: 'basketball',
  favoriteColor: 'Green'
})

# 数据侦听 Vue.$watch

watch提供了观察和响应实例上数据变动的办法,当有一些数据需要跟随其他数据变化而变化时,如子组件某个数据依赖来自于父组件的prop计算。很直观的会想到计算这功能和计算属性十分类似。Vue建议用户使用计算属性,除非如下情况:
(1)当要执行的操作是异步操作时,
(2)相应事件是开销较大的操作时。

watch: {
  question: function (newVal, oldVal) {
    this.answer = 'Waiting for you to stop typing...'
    axios.get('https://yesno.wtf/api')
        .then(function (response) {
          vm.answer = _.capitalize(response.data.answer)
        })
        .catch(function (error) {
          vm.answer = 'Error! Could not reach the API. ' + error
        })
    }
  }
}

  当观察的值发生改变时, 观察者会接收到两个参数:(1) 新值,(2)原先的值。 值得注意的是, watch在组件第一次被挂载时不会触发, 只有值被改变时才触发。

watch: {
  selectedVal ( newVal, oldVal ) {
    console.log(newVal)
  }
}

# data选项为什么是一个函数?

  Vue官网第一课描述的data选项就是一个对象,为什么在编写组件的时候却要定义成一个函数?

  我们知道对象是引用类型,而组件最大的特性就是可复用性,当一个组件被多次复用却指向同一个引用类型数据,组件间将无独立性而言。因此,将data选项定义成一个函数,是为了利用函数的私有作用域特性实现不同组件间数据私有的效果

# 计算属性缓存 及 get()/set()方法

  一个需要计算的数据,通常有: (1)计算属性获取,(2)定义一个方法实现。虽然实现结果相同,但前者优势在于计算属性是基于它们的依赖进行缓存的。也就是说:

(1)计算属性依赖不改变,计算就不会触发,改变了才重新触发计算;而调用方法总会再次执行函数
(2)当依赖不是响应式依赖时, 计算属性将永远不会触发计算。如

computed: {
  now () {
    return Date.now()
  }
}

  计算属性默认只有 getter,常规用法其实是调用了计算属性的getter方法。
什么情况下使用setter?一般计算属性都是根据依赖来计算自身的值,如果计算属性自身需要手动传入值时,就需要提供一个setter。例如:将一个计算属性绑定给v-model
  提供get()set()的计算属性, 需要调整为一个对象。


computed: {
 reserve : {
    get () {
      return this. $store.state.name
    },
    set (val) {
      return this.$store.state.name = `李${val}`
    }
  }
}

# v-if 惰性、缓存 及 使用