MVVM模型
MVVM是Model-View-ViewModel的缩写。
- Model:代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑;
- View:代表UI 组件,它负责将数据模型转化成UI 展现出来;
- ViewModel:是一个同步View 和 Model的对象。
在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
Vue.js的优点
- 低耦合:视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
- 可重用性:可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
- 独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
- 可测试性:界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
指令和事件
指令(Directives)是Vue模板中最常用的一项功能,它带有前缀v-,能帮我们快速完成DOM操作、循环渲染、显示影藏。
-
v-text:解析文本,作用与
{{}}
一样 - v-html:解析html
-
v-bind:基本用途是动态更新HTML元素上的属性,如id、class等,可用
:
代替 -
v-on:用来绑定事件监听器,可用
@
代替
v-on具体介绍
在普通元素中,v-on可以监听原生的DOM事件,除了click以外,还有dbclick、keyup、mousemove等。表达式可以是一个方法名,这些方法都写在Vue实例的methods属性内,并且是函数的形式,函数内的this指向的是当前Vue实例本身,因此可以直接使用this.xxx的形式来访问或修改数据。
代码实战
要求:
- 渲染文本到页面
- 渲染HTML到页面
- 动态绑定属性(任意属性均可)
- 绑定一个事件
- 必须使用到过滤器
Vue计算属性
一、字符翻转实例
创建以一个vue实例,并使用计算属性和方法实现一个字符串翻转的操作,写出核心代码:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<p>初始字符:{{number}}</p>
<p>方法反转:{{reverseNumber2()}}</p>
<p>计算属性反转:{{reverseNumber}}</p>
<p>模板内表达式反转:{{number.split(', ').reverse().join(', ')}}</p>
</div>
<script>
var app1 = new Vue({
el: '#app',
data: {
number: '123, 456, 789',
lastName: 'He',
fullName: 'Wang He'
},
computed: {
reverseNumber: function(){
return this.number.split(', ').reverse().join(', ')
}
},
methods: {
reverseNumber2: function(){
return this.number.split(', ').reverse().join(', ')
}
}
})
</script>
</body>
</html>
二、何时使用模板内表达式,何时使用计算属性?
- 模板内表达式:只支持单行表达式,适用于简单计算
- 计算属性:适用于复杂逻辑计算
三、计算属性与方法(methods)的区别
计算属性与方法呈现的最终结果相同,然而不同的是,计算属性是基于它们的依赖进行缓存的,只有相关依赖发生变化时它们才会重新执行,否则返回已缓存的之前的计算结果;而方法只要触发重新渲染就会再次执行。
代码实战
实例化一个vue实例,在data中定义一个firstName和lastName,还有一个全称叫fullName。
提示:fullName = firstName + lastName。
需求:分别使用watch监听器和计算属性来实现以下功能实现,只要firstName和lastName中的任意一个改变,全称fullname就会改变
代码预览
v-bind以及class与style的绑定
v-bind
通常用来绑定属性,可以动态地绑定一些class类名或者style样式:
-
变量语法:
v-bind:class="变量"
,这里变量的值通常是在CSS定义好的类名; -
数组语法:
v-bind:class="[变量1, 变量2]"
,形式与变量语法差不多,但可以同时绑定多个类名; -
对象语法:
v-bind:class="{classname1:boolean, classname2:boolean}"
,这里的classname就是样式表的类名,boolean通常是一个变量,也可以是常量、计算属性等,这种方法也是绑定class最常用的方式。
代码实战
Vue的内置指令
一、v-model有什么作用
v-model
可以在表单<input>、<textarea>及<select>元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但v-model的本质不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
<input type="text" v-model="msg"/> {{msg}}
二、各指令的作用
-
v-if:根据表达式的值的真假条件渲染元素。在切换时元素及其数据绑定/组件被销毁并重建。如果元素是
<template>
,将提出他的内容作为条件块。当条件变化时该指令触发过渡效果。当和v-if一起使用时,v-for的优先级更高,只渲染变化的元素。提供key值可以决定是否复用该元素。 - v-else:必须紧跟在v-if或v-else-if后,否则不能被识别。
-
v-show:根据条件显示元素。与v-if不同的是,带有v-show的元素始终会被渲染并保留在DOM中,v-show只是简单地切换元素CSS的display属性。
注意:v-show不支持<template>元素,也不支持v-else - v-once:只渲染元素和组件一次。元素和组件及其所有子节点会将随后的重新渲染视为静态内容并跳过。这可以用于优化更新性能。
-
v-cloak:解决初始化慢导致的页面闪动。
[v-cloak]:{display:none}
一起使用,可以隐藏未编译的Mustache标签指导失利准备完毕。
代码实例
- 使用v-for循环写出一个demo:预览链接
- 使用v-bind,v-on写出一个demo:预览链接
- 在输入框运用v-model:预览链接
- 在复选框运用v-model:预览链接
- 在单选框运用v-model:预览链接
Vue组件
一、全局注册和局部注册组件
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<p>全局注册组件:</p>
<my-component></my-component>
<hr>
<p>局部注册组件:</p>
<app-component></app-component>
</div>
<script>
Vue.component('my-component',{
template:'<div class="quanju">我是全局注册的组件</div>'
})
var app = new Vue({
el: '#app',
data: {
},
components: {
'app-component': {
template: '<div class="jubu">我是局部注册的组件</div>'
}
}
})
</script>
</body>
</html>
二、父组件给子组件传递信息的代码
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<a-component msg="我是父组件传递的数据"></a-component>
<hr>
<p>提示:通过输入的数据改变正方形的边长</p>
<input type="text" v-model="length">
<b-component :length="length"></b-component>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
length: '10'
},
components: {
'a-component': {
props: ['msg'],
template: '<div class="jubu">{{count}}</div>',
data: function(){
return {
count: this.msg
}
}
},
'b-component': {
props: ['length'],
template: '<div :style="style"></div>',
computed: {
style: function(){
return {
width: this.length + 'px',
height: this.length + 'px',
background: 'red',
marginTop: '10px'
}
}
},
data: function(){
return {}
}
}
}
})
</script>
</body>
</html>
三、子组件给父组件传递信息的代码
1. 自定义事件:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<p>通过按钮给父组件传递数据</p>
<div class="content">
<p class="btn-title">您现在的银行卡余额是:{{total}}元</p>
<app-component @change="handleTotal"></app-component>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
total: 2000
},
components: {
'app-component': {
template: '<div class="btn">\
<button @click="increase">+100</button>\
<button @click="decrease">-100</button>\
</div>\
</div>',
methods: {
increase: function(){
this.count += 100
this.$emit('change', this.count)
},
decrease: function(){
this.count = this.count>0? this.count-100:0
this.$emit('change',this.count)
}
},
data: function(){
return {
count: 2000
}
}
}
},
methods: {
handleTotal: function(value){
this.total = value
}
}
})
</script>
</body>
</html>
预览链接
2. v-model:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<p>通过按钮给父组件传递数据</p>
<div class="content">
<p class="btn-title">您现在的银行卡余额是:{{total}}元</p>
<app-component v-model="total"></app-component>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
total: 2000
},
components: {
'app-component': {
template: '<div class="btn">\
<button @click="increase">+100</button>\
<button @click="decrease">-100</button>\
</div>\
</div>',
methods: {
increase: function(){
this.count += 100
this.$emit('input', this.count)
},
decrease: function(){
this.count = this.count>0? this.count-100:0
this.$emit('input',this.count)
}
},
data: function(){
return {
count: 2000
}
}
}
}
})
</script>
</body>
</html>
五、非父子组件之间传值
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<a-component></a-component>
<b-component></b-component>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
bus: new Vue()
},
components: {
'a-component': {
template: '<div class="a">a组件<br><button @click="handle">点击向b组件传值</button></div>',
data: function(){
return {
msg: '我是来自a组件的内容'
}
},
methods: {
handle: function(){
this.$root.bus.$emit('hello', this.msg)
}
}
},
'b-component': {
template: '<div class="b">b组件</div>',
data: function(){
return {
msg2: '我是来自b组件的内容'
}
},
created: function(){
this.$root.bus.$on('hello', function(value){
alert(value)
})
}
}
}
})
</script>
</body>
</html>