一、 v-model
v-model
<input>
、<textarea>
<select>
元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。
尽管有些神奇,但v-model
本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
v-model
在内部为不同的输入元素使用不同的属性并抛出不同的事件:
- text 和 textarea 元素使用
value
input
事件; - checkbox 和 radio 使用
checked
change
事件; - select 字段将
value
change
作为事件。
vue的核心:声明式的指令和数据的双向绑定。
什么是数据的双向绑定?另外,大家一定要知道vue的设计模式:MVVM
M是Model的简写,V是View的简写,VM就是ViewModel。
单向绑定和双向绑定的区别?
单向绑定非常简单,就是把Model绑定到View,当我们用JavaScript代码更新Model时,View就会自动更新。
有单向绑定,就有双向绑定。
如果用户更新了View,Model的数据也自动被更新了,这种情况就是双向绑定。
什么情况下用户可以更新View呢?填写表单就是一个最直接的例子。当用户填写表单时,View的状态就被更新了,如果此时MVVM框架可以自动更新Model的状态,那就相当于我们把Model和View做了双向绑定。
其实单项数据也有双向绑定的意思,不过页面变动后数据的变化不会发生自动更新。
可以这样认为:双向数据绑定=单向数据绑定+UI事件监听。
先看个vue中双向数据绑定的例子:
<body>
<div id="app">
<input type="text" v-model="msg">
<p>{{msg}}</p>
</div>
<script src="./lib/vue.js"></script>
<script>
var app = new Vue({
el:'#app',
data :{
msg:''
}
})
</script>
</body>
页面效果如下
效果显示,当我们在input输入框中输入内容的时候,下面p标签同步显示内容。这就是最典型的双向数据绑定的例子。vue里使用v-model实现此想法。
浅析理解v-model实现过程
<body>
<div id="app">
<!-- v-model这个指令很好理解,其实它就是一个语法糖 (甜甜的) 它是oninput事件和绑定value的实现 -->
<input type="text" :value = 'text' @input = 'valueChange'>
<h3>{{ text }}</h3>
</div>
<script type="text/javascript" src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
new Vue({
el:"#app",
template:``,
data(){
return {
text:'hello'
}
},
methods:{
valueChange(event){
console.log(event.target.value);
this.text = event.target.value
}
}
});
</script>
</body>
分析:v-mode指令是v-bind:vlaue 和v-on:input的结合体。针对input标签使用v-bind绑定value属性,使得input标签实时接收data的数据驱动,h3标签直接采用模板插值法,由data直接驱动,这是model对view
的单向绑定。
针对input标签绑定valuechange,数值变动事件,当input的数值发生变动时,由valueChange函数针对this.text的数值进行处理,这是view对model的单向绑定。由此构成了双向绑定。
用v-model实现简易计算器
<body>
<div id="app">
<input type="text" v-model='num1'>
<select v-model='opt'>
<option value="+">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value="/">/</option>
</select>
<input type="text" v-model='num2'>
<button value='=' @click='calc'>=</button>
<input type="text" v-model='res'>
</div>
<script src="./lib/vue.js"></script>
<script>
var app = new Vue({
el:'#app',
data :{
num1:0,
num2:0,
res:0,
opt:'+'
},
methods: {
calc(){
var calcStr = 'parseInt(this.num1)'+ this.opt + 'parseInt(this.num2)';
this.res = eval(calcStr);
}
}
})
</script>
</body>
二、指令系统总结
1、注意v-bind和v-on的简写
: 替代:
<img :src="imgSrc" :title="time"> <==对应==>
<img v-bind:src="imgSrc" v-bind:title="time">
@ 替代:
<button @click = "clickHandler">切换</button> <==对应==>
<button v-on:click="clickHandler">切换</button>
2、对页面的dom进行赋值的运算
v-test、v-html、{{}}都是对页面的dom进行赋值,相当于js中的 innnerText 和 innerHTML。
3、对页面的dom进行条件渲染
(1)v-if的内在过程
v-if = 'true':
<!--创建-->
var oP = document.createElement('p');
oDiv.appendChild(op);
v-if= 'false':
<!--销毁-->
oDiv.removeChild(op);`
(2)v-show的内在过程
v-show = 'true':
oDiv.style.display = 'block';
v-show = 'false':
oDiv.style.display = 'none'
(3)v-bind内在过程
v-bind:class:
oDiv.className += ' active'
4、v-if与v-show的区别
实现方式区别:v-if是根据后面数据的真假值判断直接从Dom树上删除或重建元素节点;v-show只是在修改元素的css样式,也就是display的属性值,元素始终在Dom树上。
编译过程区别:v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件; v-show只是简单的基于css切换。
编译条件区别:v-if是惰性的,如果初始条件为假,则什么也不做,只有在条件第一次变为真时才开始局部编译; v-show是在任何条件下(首次条件是否为真)都被编译,然后被缓存,而且DOM元素始终被保留。
性能消耗区别:v-if有更高的切换消耗,不适合做频繁的切换; v-show有更高的初始渲染消耗,适合做频繁的额切换。
5、vue的思想
vue是渐进式的JavaScript框架。大多数人觉得做加法比做减法简单。
vue这个框架将做减法的事情(困难的部分)都帮忙做了,人们只需要简单的部分就能实现复杂的dom操作。
三、指令系统之轮播图实现
1、轮播图简单示例
<style type="text/css">
ul{
list-style: none;
overflow: hidden;
width: 400px;
}
ul li{
float: left;
width: 100px;
height: 40px;
line-height: 40px;
background-color: purple;
color: #fff;
text-align: center;
}
ul li.active{
background-color: green;
}
</style>
</head>
<body>
<div id="slider">
<img :src='currentImgSrc' alt="">
<ul>
<li v-for = '(item,index) in imgArr' :class="{active:currentIndex==index}" @click='clickHandler(index)'>
{{ index +1 }}
</li>
</ul>
</div>
<script src="./lib/vue.js"></script>
<script type="text/javascript">
// 数据驱动视图
var imgArr = [
{id:1,imgSrc:'./images/1.jpg'},
{id:2,imgSrc:'./images/2.jpg'},
{id:3,imgSrc:'./images/3.jpg'},
{id:4,imgSrc:'./images/4.jpg'}
];
new Vue({
el:'#slider',
template:``,
data(){
return{
imgArr:imgArr,
currentIndex:0,
currentImgSrc:'./images/1.jpg'
}
},
methods:{
clickHandler(index){
this.currentIndex = index;
this.currentImgSrc = this.imgArr[index].imgSrc;
}
}
});
</script>
</body>
页面效果如下
2、轮播图进阶
(1)增加点击下一张
<style type="text/css">
ul{
list-style: none;
overflow: hidden;
width: 400px;
}
ul li{
float: left;
width: 100px;
height: 40px;
line-height: 40px;
background-color: purple;
color: #fff;
text-align: center;
}
ul li.active{
background-color: green;
}
</style>
</head>
<body>
<div id="slider">
<img :src='currentImgSrc' alt="">
<ul>
<li v-for = '(item,index) in imgArr' :class="{active:currentIndex==index}" @click='clickHandler(index)'>
{{ index +1 }}
</li>
</ul>
<button class="btn" @click='nextImg'>下一张</button>
</div>
<script src="./lib/vue.js"></script>
<script type="text/javascript">
// 数据驱动视图
var imgArr = [
{id:1,imgSrc:'./images/1.jpg'},
{id:2,imgSrc:'./images/2.jpg'},
{id:3,imgSrc:'./images/3.jpg'},
{id:4,imgSrc:'./images/4.jpg'}
];
new Vue({
el:'#slider',
template:``,
data(){
return{
imgArr:imgArr,
currentIndex:0,
currentImgSrc:'./images/1.jpg'
}
},
methods:{
clickHandler(index){
this.currentIndex = index;
this.currentImgSrc = this.imgArr[index].imgSrc;
},
nextImg(){
if(this.currentIndex==this.imgArr.length-1){
this.currentIndex = -1;
}
this.currentIndex++;
this.currentImgSrc = this.imgArr[this.currentIndex].imgSrc;
}
}
});
</script>
</body>
页面显示效果
(2)实例生命周期钩子
生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
created 钩子可以用来在一个实例被创建之后执行代码:
<style type="text/css">
ul{
list-style: none;
overflow: hidden;
width: 400px;
}
ul li{
float: left;
width: 100px;
height: 40px;
line-height: 40px;
background-color: purple;
color: #fff;
text-align: center;
}
ul li.active{
background-color: green;
}
</style>
</head>
<body>
<div id="slider">
<img :src='currentImgSrc' alt="">
<ul>
<li v-for = '(item,index) in imgArr' :class="{active:currentIndex==index}" @click='clickHandler(index)'>
{{ index +1 }}
</li>
</ul>
<button class="btn" @click='nextImg'>下一张</button>
</div>
<script src="./lib/vue.js"></script>
<script type="text/javascript">
// 数据驱动视图
var imgArr = [
{id:1,imgSrc:'./images/1.jpg'},
{id:2,imgSrc:'./images/2.jpg'},
{id:3,imgSrc:'./images/3.jpg'},
{id:4,imgSrc:'./images/4.jpg'}
];
new Vue({
el:'#slider',
template:``,
data(){
return{
imgArr:imgArr,
currentIndex:0,
currentImgSrc:'./images/1.jpg'
}
},
created(){
// 生命周期方法
setInterval(this.nextImg, 2000)
// setInterval()方法可按照指定的周期(以毫秒计)来调用函数或计算表达
},
methods:{
clickHandler(index){
this.currentIndex = index;
this.currentImgSrc = this.imgArr[index].imgSrc;
},
nextImg(){
if(this.currentIndex==this.imgArr.length-1){
this.currentIndex = -1;
}
this.currentIndex++;
this.currentImgSrc = this.imgArr[this.currentIndex].imgSrc;
}
}
});
</script>
</body>
设置created生命周期方法后,页面上自动轮播图片
也有一些其它的钩子,在实例生命周期的不同阶段被调用,如 mounted、updateddestroyed。生命周期钩子的 this
上下文指向调用它的 Vue 实例。
created方法还可以用来获取cookie和session。
(3)鼠标 移入/移出 时 停止/开启 定时器
<style type="text/css">
ul{
list-style: none;
overflow: hidden;
width: 400px;
}
ul li{
float: left;
width: 100px;
height: 40px;
line-height: 40px;
background-color: purple;
color: #fff;
text-align: center;
}
ul li.active{
background-color: green;
}
</style>
</head>
<body>
<div id="slider">
<img :src='currentImgSrc' alt="" @mouseover='stoptimer' @mouseleave='opentimer'>
<ul>
<li v-for = '(item,index) in imgArr' :class="{active:currentIndex==index}" @click='clickHandler(index)'>
{{ index +1 }}
</li>
</ul>
<button class="btn" @click='nextImg'>下一张</button>
</div>
<script src="./lib/vue.js"></script>
<script type="text/javascript">
// 数据驱动视图
var imgArr = [
{id:1,imgSrc:'./images/1.jpg'},
{id:2,imgSrc:'./images/2.jpg'},
{id:3,imgSrc:'./images/3.jpg'},
{id:4,imgSrc:'./images/4.jpg'}
];
new Vue({
el:'#slider',
template:``,
data(){
return{
imgArr:imgArr,
currentIndex:0,
currentImgSrc:'./images/1.jpg',
timer:null
}
},
created(){
// 生命周期方法
this.timer = setInterval(this.nextImg, 500)
// setInterval()方法可按照指定的周期(以毫秒计)来调用函数或计算表达
},
methods:{
clickHandler(index){
this.currentIndex = index;
this.currentImgSrc = this.imgArr[index].imgSrc;
},
nextImg(){
if(this.currentIndex==this.imgArr.length-1){
this.currentIndex = -1;
}
this.currentIndex++;
this.currentImgSrc = this.imgArr[this.currentIndex].imgSrc;
},
stoptimer(){
clearInterval(this.timer); //停止定时器
},
opentimer(){
this.timer = setInterval(this.nextImg, 500)
//开启定时器
}
}
});
</script>
</body>
页面显示效果
注意开启定时器时不能直接调用this.created生命周期方法,必须直接开启定时器。
四、指令系统之跑马灯效果实现
<body>
<!-- 2. 创建一个要控制的区域 -->
<div id="app">
<input type="button" value="浪起来" @click="lang">
<input type="button" value="低调" @click="stop">
<h4>{{ msg }}</h4>
</div>
<script>
// 注意:在 VM实例中,如果想要获取 data 上的数据,或者 想要调用 methods 中的 方法,必须通过 this.数据属性名 或 this.方法名 来进行访问,这里的this,就表示 我们 new 出来的 VM 实例对象
var vm = new Vue({
el: '#app',
data: {
msg: '猥琐发育,别浪~~!',
intervalId: null // 在data上定义 定时器Id
},
methods: {
lang() {
// console.log(this.msg)
// 获取到头的第一个字符
// this
if (this.intervalId != null) return;
this.intervalId = setInterval(() => {
var start = this.msg.substring(0, 1)
// 获取到 后面的所有字符
var end = this.msg.substring(1)
// 重新拼接得到新的字符串,并赋值给 this.msg
this.msg = end + start
}, 400)
// 注意: VM实例,会监听自己身上 data 中所有数据的改变,只要数据一发生变化,就会自动把 最新的数据,从data 上同步到页面中去;【好处:程序员只需要关心数据,不需要考虑如何重新渲染DOM页面】
},
stop() { // 停止定时器
clearInterval(this.intervalId)
// 每当清除了定时器之后,需要重新把 intervalId 置为 null
this.intervalId = null;
}
}
})
// 分析:
// 1. 给 【浪起来】 按钮,绑定一个点击事件 v-on @
// 2. 在按钮的事件处理函数中,写相关的业务逻辑代码:拿到 msg 字符串,然后 调用 字符串的 substring 来进行字符串的截取操作,把 第一个字符截取出来,放到最后一个位置即可;
// 3. 为了实现点击下按钮,自动截取的功能,需要把 2 步骤中的代码,放到一个定时器中去;
</script>
</body>
页面显示效果
五、在Vue中使用样式(v-bind补充)
使用class样式
- 数组
<h1 :class="['red', 'thin']">这是一个邪恶的H1</h1>
- 数组中使用三元表达式
<h1 :class="['red', 'thin', isactive?'active':'']">这是一个邪恶的H1</h1>
- 数组中嵌套对象
<h1 :class="['red', 'thin', {'active': isactive}]">这是一个邪恶的H1</h1>
- 直接使用对象
<h1 :class="{red:true, italic:true, active:true, thin:true}">这是一个邪恶的H1</h1>
使用内联样式
- 直接在元素上通过
:style
的形式,书写样式对象
<h1 :style="{color: 'red', 'font-size': '40px'}">这是一个善良的H1</h1>
- 将样式对象,定义到
data
:style
中
- 在data上定义样式:
data: {
h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' }
}
- 在元素中,通过属性绑定的形式,将样式对象应用到元素中:
<h1 :style="h1StyleObj">这是一个善良的H1</h1>
- 在
:style
data
上的样式对象
- 在data上定义样式:
data: {
h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' },
h1StyleObj2: { fontStyle: 'italic' }
}
- 在元素中,通过属性绑定的形式,将样式对象应用到元素中:
<h1 :style="[h1StyleObj, h1StyleObj2]">这是一个善良的H1</h1>