计算属性 computed
与data,el,methods属性一样,都是vm实例的属性(选项)
理解其大致意思即可
绑定表达式
一段绑定表达式可以由一个简单的js表达式和可选的一个或多个过滤器组成
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script src="../node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--字符串拼接-->
<p>{{ message + "----" + name }}</p>
<!--三元运算符-->
<p>{{ bool ? 1 : 0 }}</p>
<!--四则运算-->
<p>{{ num * 2 }}</p>
</div>
</body>
</html>
<!--
使用的限制是只能包含单个表达式
-->
<script>
//创建Vue对象
var app =new Vue ({
el:'#app',//将Vue对象绑定到指定的选择器
data:{
message: 'hello world ',
name:'莉莉',
bool: true,
num:10
}
})
</script>
上面已经讲解过绑定表达式的相关知识点,即在 {{}}内部支持js表达式
{{}} 内的表达式是非常便利的,但是它们实际上只用于简单的运算,并且只能够支持单个js表达式,多个就会报错。
在模板中放入太多的逻辑会让模板过重且难以维护。
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
computed
针对这些复杂的逻辑,提出了计算属性这一要求
也就是说,将我们复杂的计算逻辑提取出来
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<!--引入js-->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!--在这里加入模型数据-->
<h2>正常情况下的结果:{{ message }}</h2>
<!--正常情况下,模板结构变得复杂,特别是在多次使用复杂逻辑的时候-->
<h2>{{ message.split('').reverse().join('') }}</h2>
<!--使用计算属性,使模板更简洁-->
<h2>{{ reverseMessage }}</h2>
<input type="text" v-model="message" />
</div>
</body>
</html>
<script>
var dataModel = {
message: 'hello world!'
}
var vm = new Vue({
el: '#app',
data: dataModel,
//我们可以像绑定普通属性那样绑定计算属性:计算属性定义在computed内部,和data类似
computed: {
//在这里,我们将计算的逻辑提取出来,封装成一个函数,在函数的内部分使用return来定义返回值,返回计算的结构
reverseMessage: function() {
return this.message.split("").reverse().join("");
}
}
})
</script>
要勤于复习哦!
你可能已经注意到我们可以通过调用表达式中的 method 来达到同样的效果:
在这里存在一些区别,涉及到缓存和性能的问题,下面我们将进行详细的讨论
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
计算缓存 methods vs computed
1.使用methods: 每次使用都会执行一次函数,不存在像计算属性那样的缓存,会浪费性能
2.计算属性的优点: 不同的计算属性是会基于他们的依赖进行缓存的,只有在依赖关系发生变化时,才会重新求值
当依赖关系没有发生变化时,不会重新求值,会使用上一次计算缓存的结果
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<!--引入js-->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
<!--在这里加入模型数据-->
<h2>正常情况下的结果:{{ message }}</h2>
<h2>使用计算属性得出的结果:{{ reverseMessageComputed }}</h2>
<h2>使用methods方法得出的结果:{{ reverseMessageMethods() }}</h2>
<input type="text" v-model="message" />
</div>
</body>
</html>
<script>
var dataModel = {
message: 'hello world!'
}
var vm = new Vue({
el: '#app',
data: dataModel,
//计算属性的优点: 不同的计算属性是会基于他们的依赖进行缓存的,只有在依赖关系发生变化时,才会重新求值
//当依赖关系没有发生变化时,不会重新求值,会使用上一次计算缓存的结果
computed: {
reverseMessageComputed: function() {
return this.message.split("").reverse().join("");
}
},
//使用methods: 每次使用都会执行一次函数,不存在像计算属性那样的缓存,会浪费性能
methods: {
reverseMessageMethods: function() {
return this.message.split("").reverse().join("");
}
}
})
</script>
computed vs methods的缓存效果演示
通过这个演示,你需要知道,对于一些单只需要纯的计算获取结果的内容,使用计算属性,能够提高性能,尤其是一些性能耗费比较严重的问题
一次增加绑定的数据的个数,根据渲染的时间来判定不同的效果
computed: 在增加绑定数据的个数的时候,渲染的时间基本不会变化(依赖关系没有发生变化)
methods: 在增加绑定数据的个数的时候,渲染的时间会随着渲染的次数的增加而递增(依赖关系没有发生变化)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<!--引入js-->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
</div>
</body>
</html>
<script>
var startTime = Date.now()
var str =
'{{ reverseMessageMethods() }}{{ reverseMessageMethods() }}' +
'{{ reverseMessageMethods() }}{{ reverseMessageMethods() }}' +
'{{ reverseMessageMethods() }}{{ reverseMessageMethods() }}' +
'{{ reverseMessageMethods() }}{{ reverseMessageMethods() }}' +
'{{ reverseMessageMethods() }}{{ reverseMessageMethods() }}';
document.getElementById("app").innerHTML = str;
var dataModel = {
message: 'hello world!'
}
var vm = new Vue({
el: '#app',
data: dataModel,
//使用methods: 每次使用都会执行一次函数,不存在像计算属性那样的缓存,会浪费性能
methods: {
reverseMessageMethods: function() {
function fn(n) {
if(n === 1) {
return 1;
}
if(n === 2) {
return 1;
} else {
return fn(n - 1) + fn(n - 2);
}
}
var num = fn(35)
return num;
}
}
})
//-----------------计算属性-----缓存演示
var endTime = Date.now()
console.log(endTime - startTime)
var startTime = Date.now()
var str1 =
'{{ reverseMessageComputed }}{{ reverseMessageComputed }}' +
'{{ reverseMessageComputed }}{{ reverseMessageComputed }}' +
'{{ reverseMessageComputed }}{{ reverseMessageComputed }}' +
'{{ reverseMessageComputed }}{{ reverseMessageComputed }}' +
'{{ reverseMessageComputed }}{{ reverseMessageComputed }}';
document.getElementById("app").innerHTML = str1;
var dataModel = {
message: 'hello world!'
}
var vm1 = new Vue({
el: '#app',
data: dataModel,
//计算属性的优点: 不同的计算属性是会基于他们的依赖进行缓存的,只有在依赖关系发生变化时,才会重新求值
//当依赖关系没有发生变化时,不会重新求值,会使用上一次计算缓存的结果
computed: {
reverseMessageComputed: function() {
function fn(n) {
if(n === 1) {
return 1;
}
if(n === 2) {
return 1;
} else {
return fn(n - 1) + fn(n - 2);
}
}
var num = fn(35)
return num;
}
}
})
var endTime = Date.now()
console.log(endTime - startTime)
</script>
watch属性
vue提供的一种更通用的方式来观察和响应 Vue 实例上的数据变动的方式
当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch
通常更好的想法是使用 computed 属性而不是命令式的 watch 回调
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<!--引入js-->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>watch监视:{{ fullName }}</h2>
<input type="text" v-model="firstName" /><br />
<input type="text" v-model="lastName" /><br />
</div>
</body>
</html>
<script>
var dataModel = {
firstName: "firstName",
lastName: "lastName",
fullName: "firstName lastName"
}
var vm = new Vue({
el: '#app',
data: dataModel,
watch: {
//在变量变化的时候,会自动执行这个函数 参数val就是绑定的值
firstName: function(val) {
this.fullName = val + ' ' + this.lastName
},
//在这里:val获取的是执行该函数的元素的内容 参数val就是绑定的值
lastName: function(val) {
this.fullName = this.firstName + ' ' + val
}
}
})
</script>
getter 和 setter属性 (了解)
计算属性默认只有getter ,我们可以在需要的时候提供一个setter
也就是进行数据的读取和设置的操作
定义get和set方法,需要的时候直接调用
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<!--引入js-->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
</div>
</body>
</html>
<script>
var dataModel = {
a: 1
}
var vm = new Vue({
el: '#app',
data: dataModel,
computed: {
//默认情况下,只会进行值的读取
aDouble: function() {
return this.a * 2
},
//在需要的时候进行值的读取和设置
aPlus: {
get: function() {
return this.a + 1;
},
set: function(v) {
this.a = v - 1;
}
}
}
})
console.log(vm.aPlus) //2---------执行的是值的读取,也就是默认进行值的读取getter
vm.aPlus = 100 //------------执行的是值的设置,也就是setter
console.log(vm.a) //99
//调用computed方法,在没有参数的时候,不需要添加参数,否则的话,会报错
console.log(vm.aDouble) //4 --------------执行的是值的读取
</script>
观察者 watchers(了解)
当然啦,一项技术或者是理念的提出都是有其依据的
虽然我们提到watch是有弊端的,但是其存在还有其具体的意义
我们配合下面这个例子做作一个简单的介绍
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<!--引入js-->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<script src="https://unpkg.com/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://unpkg.com/lodash@4.13.1/lodash.min.js"></script>
</head>
<body>
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
</body>
</html>
<script>
var watchExampleVM = new Vue({
el: '#watch-example',
data: {
question: '',
answer: 'I cannot give you an answer until you ask a question!'
},
watch: {
// 如果 question 发生改变,这个函数就会运行
question: function(newQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.getAnswer()
}
},
methods: {
// _.debounce 是一个通过 lodash 限制操作频率的函数。
// 在这个例子中,我们希望限制访问yesno.wtf/api的频率
// ajax请求直到用户输入完毕才会发出
// 学习更多关于 _.debounce function (and its cousin
// _.throttle), 参考: https://lodash.com/docs#debounce
getAnswer: _.debounce(
function() {
var vm = this
if(this.question.indexOf('?') === -1) {
vm.answer = 'Questions usually contain a question mark. ;-)'
return
}
vm.answer = 'Thinking...'
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
})
},
// 这是我们为用户停止输入等待的毫秒数
500
)
}
})
</script>
在这个示例中,使用 watch 选项允许我们执行异步操作(访问一个 API), 限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这是计算属性无法做到的。
事件绑定与监听
之前我们已经讲解过v-on这个指令,可以监听dom,触发js代码,在这里我们先做一个简单的回顾
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<!--引入js-->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
<!--在这里加入模型数据-->
{{ message }}
<ul>
<button v-on:click="clickNum = clickNum + 1">click me!</button>
<li>这个按钮呗点击了 {{clickNum}} 次</li>
</ul>
</div>
</body>
</html>
<script>
var dataModel = {
message: 'hello world!',
clickNum: 0
}
var vm = new Vue({
el: '#app',
data: dataModel
})
</script>
方法和事件处理器
通过v-on可以绑定方法,方法支持传参
有时候,我们需要访问原生的dom事件,需要使用特殊变量$event将它传入方法
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<!--引入js-->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
<!--在这里加入模型数据-->
{{ message }}
<!--不带参数-->
<button v-on:click="sayHi1">不带参数</button>
<!--带参数-->
<button v-on:click="sayHi2('say what')">带参数</button>
<!--使用原生dom事件,传递$event这个特殊参数-->
<button v-on:click="sayHi3('say what',$event)">特殊参数</button>
</div>
</body>
</html>
<script>
var dataModel = {
message: 'hello world!'
}
var vm= new Vue({
el:'#app',
data:dataModel,
methods:{
sayHi1:function(){
console.log("hello")
},
sayHi2:function(msg){
console.log(msg)
},
sayHi3:function(msg,event){
console.log(msg,event.target)
}
}
})
//通过js调用处理,和上面得到的结果是相同的
//区别是: 点击执行(事件处理函数)与直接执行
vm.sayHi1("test")
</script>
修饰符
之前我们提到过一些指令的修饰符,和这里的修饰符都是类似的东西,帮助我们解决一些实际的问题
事件修饰符
在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。 尽管我们可以在 methods 中轻松实现这点,但更好的方式是:methods 只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
为了解决这个问题, Vue.js 为 v-on 提供了 事件修饰符。通过由点(.)表示的指令后缀来调用修饰符。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<!--引入js-->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
<!--在这里加入模型数据-->
{{ message }}
<!-- 阻止单击事件冒泡 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件侦听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div v-on:click.self="doThat">...</div>
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
</div>
</body>
</html>
<!--
.stop: event.stopPropagation()
.prevent event.preventDefault()
.capture : 捕获
.self : 只有当该元素是事件本身时触发回调
.once : 只会触发一次
-->
<script>
var dataModel = {
message: 'hello world!'
}
var vm = new Vue({
el: '#app',
data: dataModel
})
</script>
按键修饰符
同样的,在按键事件中也给我们提供了一些较为便利的操作(修饰符)
.enter
.tab
.delete (捕获 “删除” 和 “退格” 键)
.esc
.space
.up
.down
.left
.right
-------------------------------------------------支持链式写法
2.1.0新增
.ctrl
.alt
.shift
.meta: 对应的是window键
简单演示
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<!--引入js-->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
<!--在这里加入模型数据-->
{{ message }}
<!-- 只有在keyCode的值是13时 调用vm.submit 13:回车键/enter键-->
<input v-on:keyup.13="submit"></input>
<!-- 同上 支持链式写法-->
<input v-on:keyup.enter="submit">
<!-- 缩写语法 -->
<input @keyup.enter="submit">
</div>
</body>
</html>
<script>
var dataModel = {
message: 'hello world!'
}
var vm = new Vue({
el: '#app',
data: dataModel,
methods: {
submit: function() {
console.log("submit")
}
}
})
/**
*
*可以通过全局 config.keyCodes 对象自定义按键修饰符别名:
*
*
*/
Vue.config.keyCodes.f1 =112;
</script>
与传统事件绑定的区别
你可能注意到这种事件监听的方式违背了关注点分离(separation of concern)传统理念。
不必担心,因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护上的困难。
实际上,使用 v-on 有几个好处:
1. 扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
2. 因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。
3. 当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何自己清理它们。
new Vue()和Vue.extend()
之前我们讲解到,vue的两大特性: 数据的 双向绑定和组件化
在这里,我们对组件化进行更深一步的了解
new Vue()
new Vue()创建的是vue的一个实例,之前我们提到过,vue是只关注视图层
我们可以将页面看成是一个大的根组件,里面包含的元素就是子组件,子组件可以在不同的根组件中被调用
那么,如何创建子组件呢,这个是我们需要关注的主题
Vue.extend()
在vue中给我们提供的创建组价的方式,
在这里,我们先了解一下这个方法,之后再做详细的讲解
两者的共性
构造器
每个 Vue.js 应用的起步都是通过构造函数 Vue 创建一个 Vue 的根实例(根组件)
可以扩展 Vue 构造器,从而用预定义选项创建可复用的组件构造器
TIM截图20180507164025.png
分析
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<!--引入js-->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
</div>
</body>
</html>
<!--
vue这个对象可以进行扩展
相当于在原先的vue的基础上添加新的内容,形成我们自己的,别具一格的vue
-->
<script>
//1. 扩展vue对象
var vueExtend = Vue.extend({
//----定义扩展的vue的对象的内容
template:'<div><a href="#">注册</a><a href="#">登录</a></div>'
})
//3. 创建根实例
var vm = new vueExtend({
el:'#app'
})
</script>
网友评论