Vue入门实战教程(四)—— 模型层:数据

此学习教程是对官方教程的解析,

本章节主要涉及到的官方教程地址:

Vue实例—— Vue.js计算属性和侦听器——Vue.js

上一章 :vue入门实战教程(三)—— 视图层:模板及指令

四. 数据

1. 数据的特性:响应式

1.1 响应式属性

当一个 Vue 实例被创建时,它将 data 对象中的所有的属性加入到 Vue 的响应式系统中。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。

当这些数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时就已经存在于 data 中的属性才是响应式的。

理解:

根据官方教程,响应式属性可以定义如下:

响应式属性Vue实例里面的data 对象中的属性(当实例被创建时就已经存在) 的值发生改变时, 视图将会产生“响应”,即匹配更新为新的值。

根据上面的定义,响应式属性必须是Vue实例里面的data 对象中的属性。但是真的是这样吗?我们看下如下例子:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
<div id="app">
</div>

<script>
var Comp = Vue.extend({
  props: ['msg'],
  template: '<div>comp.msg:{{ msg }}<Br/>comp.msg2:{{ msg2 }}</div>'
})


var comp = new Comp({
  el: "#app",
  propsData: {
    msg: 'hello'
  },
  data:{
    msg2: 'hello2'
  }
})
</script>
</body>
</html>

这个页面访问后,可以发现对comp.msg2更改,视图也会响应,说明外部传入属性propsData也是响应式属性。而这个局部注册的Comp组件是Vue的子类,本质上也是Vue实例。

所以我们可以重新定义下:

响应式属性: Vue实例里面的data/propsData对象中的属性(当实例被创建时就已经存在) 的值发生改变时, 视图将会产生“响应”,即匹配更新为新的值。

       (1) 必须是Vue实例里面的data/propsData对象中的属性

       (2) 前提是当实例被创建时就已经存在

       (3)“响应", 指的是data的属性一但改变了,就会发出通知给响应式系统,系统再反馈给视图,视图得到通知反映这种改变。所以,响应主要是指不需要人为干预的、自动化地处理了这种改变,一般是反映速度很快、即时的,但并不是立即、不需要时间的。

1.2 非响应式属性

1.2.1 当实例被创建时就不存在,而在实例创建后赋值的属性

当实例被创建时就已经存在是响应式属性的前提,所以如果这个前提条件不符合,当然就不是响应式属性了。

直接看简单的例子:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
<div id="app">
  <p>a_value: {{ a }}</p>
  <p>b_value: {{ b }}</p>
</div>

<script>
var vm = new Vue({
  el:"#app",
  data: { a: 1 }
})
vm.b = '2'
</script>
</body>
</html>

运行结果:

可以看到,实例化后赋值的b属性对模板没有任何效果。

1.2.2 使用 Object.freeze()来包装的data对象的属性

使用Object.freeze()会阻止修改现有的 property,也意味着响应系统无法再追踪变化。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
<div id="app">
  <p>a_value: {{ a }}</p>
</div>

<script>
var vm = new Vue({
  el:"#app",
  data: Object.freeze({ a: 1 })
})
</script>
</body>
</html>

运行结果:

2.计算属性

看一下官方教程的例子:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
<div id="example">
  <p>Original message: {{ message }}</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>

<script>
var vm = new Vue({
  el: '#example',
  data: {message: 'Hello'},
  //data: Object.freeze({foo: 'bar'})
  computed: {
    // 计算属性
    reversedMessage: function () {
        return this.message.split('').reverse().join('')
    }
  }
})
</script>
</body>
</html>

官方教程:

这里我们声明了一个计算属性 reversedMessage。我们提供的函数将用作 property vm.reversedMessage 的 getter 函数。

你可以打开浏览器的控制台,自行修改例子中的 vm。vm.reversedMessage 的值始终取决于 vm.message 的值。

你可以像绑定普通 property 一样在模板中绑定计算属性。Vue 知道 vm.reversedMessage 依赖于 vm.message,因此当 vm.message 发生改变时,所有依赖 vm.reversedMessage 的绑定也会更新。而且最妙的是我们已经以声明的方式创建了这种依赖关系:计算属性的 getter 函数是没有副作用 (side effect) 的,这使它更易于测试和理解。

我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。

理解:

计算属性:依赖响应式属性,并对响应式属性进行计算并对计算结果进行缓存(除非依赖的响应式属性变化才重新计算)的声明式绑定模板的属性

(1) 声明式绑定模板:计算属性与响应式属性一样,都是Vue应用的属性,可与模板进行声明式绑定

(2) 依赖响应式属性, 对响应式属性进行计算:计算属性不能对自身直接赋值, 它的值是响应式属性的计算值

(4) 对计算结果进行缓存:响应式属性的值随依赖的响应式属性变化而变化,所以依赖的响应式属性不变的话,它也不变,所以没必要重新计算了,只要响应式属性不变,就把计算值缓存起来。所以计算属性可以放一些复杂的计算而不对性能造成很大影响。

 

计算属性默认是一个getter函数,所以上面的例子等同于:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
<div id="example">
  <p>Original message: {{ message }}</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>

<script>
var vm = new Vue({
  el: '#example',
  data: {message: 'Hello'},
  //data: Object.freeze({foo: 'bar'})
  computed: { 
    reversedMessage: {
		// 计算属性的 getter
		get:function () {
		  // `this` 指向 vm 实例
		  return this.message.split('').reverse().join('')
		},
		// 虽然你可以设置setter,但不建议这样做, 因为对自身的直接赋值将会抛出错误
		// 虽然你可做其它逻辑,但显然会让逻辑混乱。 
		/*set: function (newValue) {
		  this.reversedMessage = newValue;//会抛出错误
		}*/
	}
  }
})
</script>
</body>
</html>

计算属性也可以用方法/过滤器来实现,它们的区别在于计算属性有缓存而方法/过滤器没有缓存。

看下面的例子来看下同样效果的不同实现方式:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
<div id="example">
  <p>Original message: {{ message }}</p>
  <p>reversed message: "{{ reversedMessage }}"</p>
  <p>reversed message: "{{ reverseMessage(message) }}"</p>
  <p>reversed message: "{{ message | reverseMessageFilter }}"</p>
</div>

<script>
var vm = new Vue({
  el: '#example',
  data: {message: 'Hello'},
  //data: Object.freeze({foo: 'bar'})
  computed: { 
    reversedMessage: function () {
        return this.message.split('').reverse().join('')
    }
  },
  methods: {
    reverseMessage: function (message) {
        return message.split('').reverse().join('')
    }
  },
  filters: {
    reverseMessageFilter: function (message) {
        return message.split('').reverse().join('')
    }
  }
})
</script>
</body>
</html>

另一个实现同样的效果使用侦听器的例子:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
<div id="example">
  <p>Original message: {{ message }}</p>
  <p>reversed message: "{{ reversedMessage }}"</p>
</div>

<script>
var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello',
    reversedMessage: 'olleH'
  },
  watch: {
    message: function (val) {
      this.reversedMessage = this.message.split('').reverse().join('')
    }
  }
})
</script>
</body>
</html>

可以看到侦听器不创建新的属性,它只是对现有响应式属性进行侦听,如响应式属性发生变化,再实现相应的逻辑。

那什么时候用侦听器而不使用计算属性?当需要在数据变化时执行异步或开销较大的操作时。这个可以参考官网的例子,就不赘述了。

 

本章节教程结束。

全部教程地址:Vue入门实战教程 | 寒于水学习网

下一章:Vue入门实战教程(五)—— 控制层:方法及过滤器

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值