mustache语法与部分指令
语法
mustache语法中,不仅仅可以直接写变量,也可以写简单的表达式 ;
mustache用{{}}表示;
<div id="app">
<h2>{{message}}</h2>
<h2>{{message}},kjk</h2>
<!-- mustache语法中,不仅仅可以直接写变量,也可以写简单的表达式 -->
<h2>{{firstName + lastName}}</h2>
<h2>{{firstName + " " + lastName}}</h2>
<h2>{{firstName}} {{lastName}}</h2>
<h2>{{counter * 2}}</h2>
</div>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: "hello",
firstName: "kobe",
lastName: "bryant",
counter: 1000
},
})
</script>
指令
v-once
1、该指令表示元素和组件只渲染一次,不会跟随数据的改变而改变。
2、该指令后面不需要跟任何表达式(v-for则需要)。
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<h2 v-once>{{message}}</h2>
<!-- v-once不随数据改变而改变 -->
<h2>{{message}}</h2>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
message: "hello vscode"
},
methods: {}
});
</script>
</body>
在控制台输入以下代码改变message的数据,可发现v-once所在行数据不会重新渲染。


v-text v-html
v-text与v-html指令都可以更新页面元素的内容,不同的是,v-text会将数据以字符串文本的形式更新,而v-html则是将数据以HTML标签的形式更新。需要注意的是,v-text、v-html指令会替换掉页面整个的内容。
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<h2>************{{message}}************</h2>
<h2>************{{html}}************</h2>
<h2 v-text="message">************</h2>
<h2 v-text="html">************</h2>
<h2 v-html="html">************</h2>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
message: "hello",
html: "<h2 style='color:blue'>Vue是现在最流行的框架之一</h2>"
},
methods: {}
});
</script>
</body>
分析:
1、页面内容******在有v-text v-html俩个指令的时候都会被替换成相应内容;
2、v-text会将数据以字符串文本的形式更新,而v-html则是将数据以HTML标签的形式更新。
v-pre
类似于HTML的一个标签pre,指定显示的内容的格式;
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<h2>{{message}}</h2>
<h2 v-pre>{{message}}</h2>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
message: "hello",
},
methods: {}
});
</script>
</body>
分析:即使data里面定义了message这里仍然是显示的{{message}}
v-cloak( 需要配合css使用(解决:插值表达式闪烁的问题))
在某些情况下,对于vue.js的引用因为某些原因没有加载完成,或者是加载有延迟时,未编译的Mustache标签就无法正常显示。我们可以用v-cloak,这个指令保持在元素上直到关联实例结束编译。和 CSS : [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。
当我正常代码运行时,在页面加载时会闪烁,然后才会加载完成,如下:
![]()
为了更明显,我在下面的例子中,模拟将网页加载速度变慢,此时就可以看见,页面最先开始会显示出插值表达式,只有vue.js加载完成后,才会渲染成正确的数据。
setTimeout(() => {
new Vue({
el: '#app',
data: {
msg: 'Vue'
}
})
}, 2000)

这个问题加入CSS可以被很好地解决: [v-cloak] { display: none } ,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。

同样的,在 html 中的加载点加上 v-cloak,也可以解决这一问题
<body>
<div id="app" v-cloak>
<p>{{message}}</p>
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'hello world!'
}
});
</script>
但是,也会发生失效的案例:那就只好在样式CSS写上[v-cloak] { display: none };
v-bind
1、前面几个指令主要作用都是插入模板内容中,而v-bind可以用来动态绑定属性(比如 img 的 src、title 属性和 a 元素的 href 属性等等)和样式(可以用style的形式进行内联样式的绑定,也可以通过指定 class 的形式指定样式);
2、缩写是 :
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<input type="button" value="按钮" v-bind:title="Title" v-bind:style="{color:Color,width:Width+'px'}">
</div>
<!--v-bind:可以用来在标签上绑定标签的属性和样式,对于绑定的内容,可以对该内容进行编写合法的 JavaScript表达式-->
<script src="../vue.js"></script>
<script>
// 创建实例对象
new Vue({
// 指定控制的区域
el: '#app',
data: {
Title: "这是我自定义的title属性",
Color: "red",
Width: "120"
},
methods: {}
});
</script>
</body>效果如下:

分析:
1、值得关注的是,v-bind绑定的属性值中,可以对该内容进行编写合法的 JavaScript表达式;如:{color:Color,width:Width+'px'}
2、注意此处px要加引号
3、上面代码可以简写成<input type="button" value="按钮" :title="Title" :style="{color:Color,width:Width+'px'}">
- v-bind动态绑定class(对象语法)也就是class后面跟着是一个对象
<style>
.active {
color: red;
}
.line {
background-color: royalblue;
width: 100px;
}
</style>设置激活类的样式如上
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<!-- v-bind:class="{类名1:属性值1,类名2:属性值2}" -->
<h2 v-bind:class="{active:isActive,line:isLine}">{{msg}}</h2>
<button v-on:click="btnClick">按钮</button>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
msg: "kk",
isActive: true,
isLine: true
},
methods: {
btnClick: function () {
this.isActive = !this.isActive
this.isLine = !this.isLine
}
}
});
</script>
</body>代码结果如下:

分析:
1、先给不同类名赋予样式,用v-bind动态绑定类名时,样式也会随之切换;
2、对象语法:v-bind:class="{类名1:属性值1,类名2:属性值2}"
3、active,line俩个类先在实例中的data默认布尔值为true,确保能够被应用。再给按钮一个点击事件(v-on:click),实现点击的时候,类的布尔值取反也就是实现了点击实现再点击取消的效果,原则上就是类的显示和隐藏
总结:
还有以下用法:
1、直接通过{}绑定一个类 : <h2 :class = "{'active : isActive'}">hello world</h2>,此时isActive是布尔值
2、也可以通过判断,传入多个值 : <h2 :class = "{'active : isActive', 'line' : isLine}">hello world</h2>此时isActive、 isLine是布尔值
3、和普通的类同时存在,并不冲突,注:如果俩者都为真,则会同时存在三个类
<h2 class="title" ; :class = "{'active : isActive', 'line' : isLine}">hello world</h2>
4、如果过于复杂,可以放在一个methods或者computed中,注:下面的classes是一个计算属性
<h2 class="title" ; :class = "classes">hello world</h2>
{'active : isActive', 'line' : isLine}则是放在了计算属性中,以下是放在方法中的案例
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<!-- v-bind:class="{类名1:属性值1,类名2:属性值2}" -->
<h2 v-bind:class="{active:isActive,line:isLine}">{{msg}}</h2>
<h2 v-bind:class="classes()">{{msg}}</h2>
<button v-on:click="btnClick">按钮</button>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
msg: "kk",
isActive: true,
isLine: true
},
methods: {
btnClick: function () {
this.isActive = !this.isActive
this.isLine = !this.isLine
},
classes: function () {
return {
active: this.isActive,
line: this.isLine
}
}
}
});
</script>
</body>- v-bind动态绑定class(数组语法)class后面跟得是数组
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<h2 :class="[active,line]">{{msg}}</h2>
<h2 :class="classes()">{{msg}}</h2>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
msg: "hello",
active: "a",
line: "b"
},
methods: {
classes: function () {
return [this.active, this.line]
}
}
});
</script>
</body>
作业1:v-for与v-bind的结合--作业需求:点击列表中的哪一项,则该项的文字变红色
1、CSS部分
<style>
.active {
color: red;
}
</style>2、vue部分
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<ul>
<li v-for="(item,index) in movies" :class="{active: currentIndex === index}" @click="liClick(index)">
{{item}}</li>
</ul>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
movies: ["海王", "海贼王", "火影忍者", "钢琴师"],
currentIndex: 0
},
methods: {
// 跟下标有关,参数必须写index,不然运行不了
liClick(index) {
this.currentIndex = index
}
}
});
</script>
</body>
分析:
1、先把基础的列表显示完成
2、再把第一行默认为红色(赋予CSS激活类,先写死)
3、第二部,通过遍历的下标,与自己给定的data中变量currentIndex(默认为0)比较,若相等,则为true,也就是激活CSS类
4、绑定点击事件,并给定方法liClick(index),把点击的事件的下标赋予currentIndex,因为与index有关,所以方法的参数需要写上index,调用的时候也需要。
5、一开始写也不太熟悉,多看几次,理解原理就好了。
- v-bind动态绑定style(对象语法)style后面跟得是对象
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<!-- 语法格式: :style="{key:value}" -->
<h2 :style="{fontSize : size + 'px',backgroundColor : color}">{{msg}}</h2>
<h2 :style="getStyle()">{{msg}}</h2>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
msg: "hello",
size: "100",
color: "red"
},
methods: {
getStyle: function () {
return {
fontSize: this.size + 'px',
backgroundColor: this.color
}
}
}
});
</script>
</body>分析:
1、<h2 :style="{fontSize : size + 'px',backgroundColor : color}">{{msg}}</h2>
2、style后面跟得是一个对象类型,对象的value是具体赋的值,可以来自data中的属性;
- v-bind动态绑定style(数组语法)style后面跟得是数组
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<h2 :style="[style1,style2]">{{msg}}</h2>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
msg: "hello",
style1: {
backgroundColor: "red"
},
style2: {
fontSize: "100px"
}
},
methods: {}
});
</script>
</body>分析:
1、<h2 :style="[style1,style2]">{{msg}}</h2>
2、style后面跟得是一个数组,多个值以逗号隔开
v-on
1、缩写是:@,v-on:click = @click
2、在使用v-on指令对事件进行绑定时,需要在标签上指明v-on:event(click、mousedown、mouseup等)绑定的事件。
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<button v-on:click="Alert()">按钮</button>
<button @click="Alert()">按钮</button>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {},
methods: {
Alert: function () {
alert("vue中的事件绑定")
}
}
});
</script>
</body>v-on的参数问题
1、在methods定义方法供@click使用时,如果方法不需要额外参数,那么方法后的()可以不添加。
注:但是,如果该方法本身有一个参数,那么会默认将原生事件event参数传递进去
2、如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件
例如:1、事件调用的方法没有参数
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<!-- 1.事件调用的方法没有参数 -->
<button @click="btnClick()">按钮1</button>
<button @click="btnClick">按钮2</button>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {},
methods: {
//不用function是因为前面说的字面量增强
btnClick() {
console.log("btnClick");
}
}
});
</script>
</body>可以发现,加不加()都可以调对象中的方法!
例如:2、事件调用的方法有参数 ,分为三种情况:1、正常使用;2、不加参数;3、不加括号
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<!-- 2.在事件定义的时候,写法省略了小括号,
但方法本身是需要一个参数的,这时候vue会默认
将浏览器生产的event事件作为对象传入到方法 -->
<button @click="btnClick2(123)">按钮2</button>
<!-- 如果函数需要参数却没有参数传入,则函数的形参为undefined -->
<button @click="btnClick2()">按钮2</button>
<!-- 如果直接省略小括号,则vue会默认
将浏览器生产的event事件作为对象传入到方法 -->
<button @click="btnClick2">按钮2</button>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
abc: 123
},
methods: {
btnClick2(abc) {
console.log("btnClick2", abc);
}
}
});
</script>
</body>
分别点击三个按钮得到上图:1、正常;2、undefined;3、event事件
所以,当你需要获得even对象时,可以将对象中的方法参数改成event,并且调用方法时不加小括号即可
<button @click="btnClick2">按钮2</button>
methods: {
btnClick2(event) {
console.log("btnClick2", event);
}
}
例如:3、如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<!-- 3、如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件 -->
<!-- 不加括号的时候会把event对象传给第一个参数abc,而第二个参数event为undefined -->
<button @click="btnClick3">按钮3</button>
<!-- 当括号内参数都写了以下格式,会报错,----- 123 undefined,
显示找不到event方法,因为在调用方法时,手动获取
浏览器参数的event对象:$event-->
<!-- <button @click="btnClick3(123,event)">按钮3</button> -->
<button @click="btnClick3(123,$event)">按钮3</button>
<!-- 当第一个 参数为abc的时候,加单引号不报错,不加就报错-->
<button @click="btnClick3(abc,$event)">按钮3</button>
<!-- 因为第一个参数不加引号会被认为 是一个变量,会去data里面查找,没有则报错-->
<button @click="btnClick3('abc',$event)">按钮3</button>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {},
methods: {
btnClick3(abc, event) {
console.log("-----", abc, event);
}
}
});
</script>
</body>注:分析在代码中注释好了!
v-on的修饰符(后面v-for也会涉及,也就是冒泡事件)
- .stop阻止事件冒泡( 调用的是event.stopPropagation() )
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<div @click="divClick">
<p>这是一段div文字!</p>
<button @click="btnClick">按钮</button>
</div>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {},
methods: {
divClick() {
console.log("divClick");
},
btnClick() {
console.log("btnClick");
}
}
});
</script>
</body>
分析:
可看出,当点击文字时会触发divClick方法,而当点击按钮时,俩个方法都同时被触发!这时我们可以用.stop来阻止事件冒泡,也就是点击按钮时不触发div的方法。
<div @click="divClick">
<p>这是一段div文字!</p>
<button @click="btnClick">按钮</button>
<button @click.stop="btnClick">按钮</button>
</div>
- .prevent阻止默认行为-调用event.preventDefault()
例如:当我们想要手动提交而不是通过提交按钮自动提交时,就可以用.prevent
<div id="app">
<form action="baidu">
<input type="submit" value="提交" @click="submitClick">
<input type="submit" value="提交" @click.prevent="submitClick">
</form>
</div>

分析:
可看出加了修饰符后,页面不会自动提交,而没有修饰符则根据表单的action给定的“baidu”自动跳转。
- .{keyCode | keyAlias}--只当事件是从指定键触发时才触发回调(@keyup=“”监听键盘按键按下的时候,@click监听鼠标点击),当只要监听enter键的时候可以在@keyup后面加.enter
<div id="app">
<!-- 监听按键点击事件 -->
<input type="text" @keyup="keyUp">
<input type="text" @keyup.enter="keyUp">
<input type="text" @keyup.13="keyUp">
</div>- .native-监听组件根元素的原生事件(后面会细讲)
<cpn @click.native="cpnClick"></cpn>- .once-只能触发一次回调(相关应用比如点击发送验证码60s一次)
<button @click.once="btnClick">按钮</button>
v-if、v-else、v-else-if
1、v-if指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回truthy值的时候被渲染。
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
我正在学习
<h3 v-if="value">vue.js</h3>
<h3 v-if="!value">javascript</h3>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
value: true,
},
methods: {}
});
</script>
</body>
2、可以使用v-else指令来表示v-if的“else块”,类似于JavaScript中的if…else逻辑语句
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
我正在学习
<h3 v-if="!value">Vue.js</h3>
<h3 v-else>Angular.js</h3>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
value: true,
},
methods: {}
});
</script>
</body>
3、v-else-if指令类似于条件语句中的“else-if块”,可以与v-if连续使用。
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
type: 'C',
},
methods: {}
});
</script>
</body>4、如果想切换多个元素,此时可以把一个<template>元素当做不可见的包裹元素,并在上面使用v-if,最终的渲染结果将不包含<template>元素。
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<template v-if="value">
<h3>马云想要学习</h3>
<p>Vue.js</p>
<p>Angular.js</p>
</template>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
value: true,
},
methods: {}
});
</script>
</body>5、用key管理可复用的元素
Vue会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使Vue变得非常快之外,还有其它一些好处。例如,允许用户在不同的登录方式之间切换。
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<template v-if="loginType === 'userName'">
<label for="">姓名</label>
<input type="text" placeholder="输入用户名" >
</template>
<template v-else>
<label for="">邮箱</label>
<input type="text" placeholder="输入邮箱" >
</template>
<button @click="toggleLoginType">切换</button>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
loginType: "userName",
},
methods: {
toggleLoginType: function () {
return this.loginType = this.loginType === "userName" ? "email" : "userName"
}
}
});
</script>
</body>可以优化如下:
<div id="app">
<template v-if="isUser">
<label for="username">姓名</label>
<input type="text" id="username" placeholder="输入用户名" >
</template>
<template v-else>
<label for="email">邮箱</label>
<input type="text" id="email" placeholder="输入邮箱" >
</template>
<button @click="isUser=!isUser">切换</button>
</div> data: {
isUser: true,
},分析:
1、其中标签属性的for关联输入框的id以点击标签文字的时候可以实现对应id输入框的聚焦
2、<button @click="isUser=!isUser">切换</button>切换按钮运用取反会更简单


问题:会出现 输入框输入文字之后切换输入框类型文字不清零怎么解决?
答:用可复用的属性key,并且保证key的不同
<div id="app">
<template v-if="isUser">
<label for="username">姓名</label>
<input type="text" id="username" placeholder="输入用户名" key="username">
</template>
<template v-else>
<label for="email">邮箱</label>
<input type="text" id="email" placeholder="输入邮箱" key="email">
</template>
<button @click="isUser=!isUser">切换</button>
</div>
v-show
1、用法与v-if大致一样,不同的是带有v-show的元素始终会被渲染并保留在DOM中。
2、v-show只是简单地切换元素的CSS属性display,当模板属性为true的时候,控制台显示为display:block;属性值为false的时候,控制台显示display: none。
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<h2 v-show="value">vue</h2>
<h2 v-show="!value">js</h2>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
value: true,
},
methods: {}
});
</script>
</body>得出结果是vue
v-show与v-if的区别:
- v-if与v-show指令都是根据表达式的真假值判断元素的显示与隐藏。
- v-if是“真正”的条件渲染,因为它会确保在切换过程中,条件块内的事件监听器和子组件适当地被销毁和重建。
- v-if也是惰性的:如果在初始渲染时条件为假,则什么也不做,直到条件第一次变为真时,才会开始渲染条件块。
- 相比之下,v-show就简单得多,不管初始条件是什么,元素总是会被渲染,并且只是简单地基于CSS进行切换。
- 一般来说,v-if有更高的切换开销,而v-show有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用v-show较好;如果在运行时条件很少改变,则使用v-if较好。
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<button @click="btnClick">切换</button>
<br>
<!--v-if 指令控制-->
<img v-if="msg" src="VIP-3.png" alt="" width="200">
<!-- v-show 指令控制-->
<img v-show="msg" src="VIP-4.png" alt="" width="200">
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
msg: true,
},
methods: {
btnClick: function () {
this.msg = !this.msg;
}
}
});
</script>
</body>切换前:

切换后:

分析:
v-if直接不渲染,v-show修改了CSS属性
使用v-for指令遍历元素
其中items是源数据数组,而item则是被迭代的数组元素的别名,data: {items: ['马云', '马化腾', '蔡徐坤', '刘强东']},
<ul>
<li v-for="item in items">
{{ item }}
</li>
</ul>也可以同时遍历下标
<ul>
<li v-for="(item,index) in items">
{{ index }}-{{ item }}
</li>
</ul>还可以遍历对象
<ul>
<!-- 获得key和value 格式:(value,key) -->
<li v-for="(value,key,index) in object">
{{index}}--{{ key }}:{{value}}
</li>
</ul><script>
new Vue({
el: '#app',
data: {
//定义对象
object: {
姓名: '蔡徐坤',
性别: '男',
出生日期: '2019-10-10'
}
}
})
</script>

v-for使用过程添加key
1、v-for遍历数组
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<ul>
<li v-for="item in letters">{{item}}</li>
</ul>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
letters: ["A", "B", "C", "D", "E"]
},
methods: {}
});
</script>
</body>
2、此时插入一个元素a [ app.letters.splice(1,0,"a") ] 如下

3、此时数组是怎么更新的?
分析:这里我们依靠vue的虚拟DOM的diff算法来解释。
(1)当我们希望在某一层相同的节点中(列表结点)插入一个新的节点,比如我们希望把a插入ABCDE的AB之间,diff算法执行过程是把B更新为a、C更新为B、D更新为C、E更新为D,最后再插入E,这样看起来是不是很没效率?

(2)所以我们需要一个唯一的key,并且key跟{{}}中的值一一对应,来给每个节点做一个唯一的标识,因此diff算法就可以正确识别此节点,找到正确位置并插入新节点

<li v-for="item in letters" :key="item">{{item}}</li>
总结:key的作用就是为了高效地更新虚拟DOM,以后开发中使用v-for要加上key
类似于v-if,也可以利用带有v-for的<template>来循环渲染一段包含多个元素的内容。
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<template v-for="n in items">
<li>{{n.name}}--{{n.age}}--{{n.sex}}</li>
<hr>
</template>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
items: [{
name: '小明',
age: 15,
sex: '男'
},
{
name: '小红',
age: 14,
sex: '女'
}
]
},
methods: {}
});
</script>
</body>v-for与v-if一同使用,当 Vue 处理指令时,v-for 比 v-if 具有更高的优先级
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<h3>没有报道的学生名单:</h3>
<ul>
<li v-for="n in items" v-if="!n.value">
{{n.name}}
</li>
</ul>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
items: [{
name: "小明"
},
{
name: "小红",
value: "已报到"
},
{
name: "小洋",
value: "已报到"
},
{
name: '小思'
}
]
},
methods: {}
});
</script>
</body>
其中,v-if相当于在计算属性中利用替换数组的filter方法找出所需要的值
computed:{
student:function(){
return this.items.filter(function (n) {
return !n.value
})
}
}数组API可分为变异方法和替换数组,变异方法和替换数组有什么区别?
变异方法:顾名思义,会改变原始数组。
替换方法:则不会改变原始数组。
变异方法push、pop、shift、unshift、splice、sort、reverse的作用?(改变原始数组)
push()方法可向数组的末尾添加一个或多个元素,并返回新的长度。
pop() 方法用于删除并返回数组的最后一个元素
shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值
unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。;
splice()方法向/从数组中添加/删除项目,然后返回被删除的项目。
splice( index, len, [item]) 用来删除/替换/添加数组内某一个或者几个值(该方法会改变原始数组)。
参数:
index:数组开始下标 ;len:替换/删除的长度 ;item:替换的值,删除操作的话item为空
删除:
//删除起始下标为1,长度为1的一个值(len设置1,如果为0,则数组不变)
splice()方法始终会返回一个数组,该数组中包含从原始数组中删除的项(如果没有删除任何项,则返回一个空数组)
sort() 方法用于对数组的元素进行排序。
reverse() 方法用于颠倒数组中元素的顺序。
替换数组concat、slice、map、filter的作用?不会改变原始数组
- concat() 方法用于连接两个或多个数组。该方法不会改变现有的数组;
- slice() 方法可从已有的数组中返回选定的元素。该方法并不会修改数组,而是返回一个子数组;
- map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。map() 方法按照原始数组元素顺序依次处理元素。
是否改变原数组:否
是否对空数组进行检测:否
const arr= [4, 9, 16, 25];
const arr1 = arr.map(item => item+2)
console.log(arr) // [4, 9, 16, 25]
console.log(arr1) // [6, 11, 18, 27]- filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
是否改变原数组:否
是否对空数组进行检测:否
const arr= [32, 33, 16, 40];
const arr1 = arr.filter(item => item >= 18)
console.log(arr) // [32, 33, 16, 40]
console.log(arr1) // [32, 33, 40]以上数组的方法哪些是响应式的?
1、响应式:页面会根据代码的更新而自动更新!(vue是响应式的)
2、由上面数组代码可见得,响应式的有:push、pop() 、shift() 、unshift() 、splice() 、sort()、reverse()
3、并不是所有改变数组的方法都是响应式,比如:通过索引值改变数组元素
<div id="app">
<ul>
<li v-for="item in letters">{{item}}</li>
</ul>
<button @click="btnClick">按钮</button>
</div>
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
letters: ["A", "B", "C", "D", "E"]
},
methods: {
btnClick() {
this.letters[0] = "aaaa";
}
}
});
可见无论我怎么点击按钮页面都不会更新,但数组已经发生改变!
自定义指令
自定义指令全局注册和局部注册的写法?
(1)全局注册:通过 Vue.directive( id, [definition] ) 方式注册全局指令,第一个参数为自定义指令名称(指令名称不需要加 v- 前缀,默认是自动加上前缀的,使用指令的时候一定要加上前缀),第二个参数可以是对象数据,也可以是一个指令函数。
Vue.directive('focus',{
bind:function(){//每当指令绑定到元素上的时候,会立即执行这个 bind 函数,只执行一次},
inserted:function(el){// inserted 表示元素 插入到DOM中的时候,会执行此函数,触发一次
el.focus(); //注意:在每一个函数中,第一个参数,永远是 el ,表示被绑定了指令的 那个元素 ,这个el参数,是一个原生JS的DOM对象
},
updated:function(){}
})(2)局部注册:
directives:{
'color':{//给字体设置颜色
bind:function(el){ //这个function() 中还有第二个参数 binding ,这里不做介绍,在下边钩子函数参数中介绍
el.style.color="red";
}
}
}
- 自定义指令的各个选项(钩子函数)的执行时机?
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update:所在组件的 VNode(一个DOM上的所有属性) 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
- 自定义指令中钩子函数的第二个参数binding是一个对象,这个对象包含哪些属性?
每个钩子函数都可以接收两个参数:
El:作用该指令的 DOM 对象
binding:一个对象,包含以下属性:name:指令名,不包括 v- 前缀。value:指令的绑定值。
<div v-demo="{ color: 'white', text: 'hello!' }">123</div>
Vue.directive('demo', function (el, binding) {
console.log(binding.value.color); // => "white"
console.log(binding.value.text); // => "hello!"
})
综合案例:完成购物车的数量加减操作以及删除操作还有过滤器格式化操作

步骤1:先完成基础表格的v-for遍历,如下:
CSS部分:
<style>
table {
border: 1px solid #e9e9e9;
border-collapse: collapse;
border-spacing: 0;
}
th,
td {
padding: 8px 16px;
border: 1px solid #e9e9e9;
text-align: left;
}
th {
background-color: #f7f7f7;
color: #5c6b77;
font-weight: 600;
}
</style>body部分:
<body>
<!-- 被app实例所控制的区域 -->
<div id="app">
<table>
<thead>
<th></th>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</thead>
<tbody>
<tr v-for="item in books">
<td v-for="value in item">{{value}}</td>
</tr>
</tbody>
</table>
</div>
<script src="../vue.js"></script>
<script>
// 创建实例对象
const app = new Vue({
// 指定控制的区域
el: '#app',
data: {
books: [{
id: 1,
name: "《算法导论》",
date: "2006-9",
price: 85,
num: 1
}, {
id: 2,
name: "《UNIX编程艺术》",
date: "2006-2",
price: 59,
num: 1
}, {
id: 3,
name: "《编程珠玑》",
date: "2008-10",
price: 39,
num: 1
}, {
id: 4,
name: "《代码大全》",
date: "2006-3",
price: 128,
num: 1
}, ]
},
});
</script>
</body>效果如图:

分析:
1、分成俩部分,表头和表体,用<thead>和 <tbody>表示,表头第一行用<th>表示,注意第一个为空!
2、表体分四行,每一行的数据都是从数据中用v-for遍历出来的,因此这里<tr>需要v-for,把数据中每一个对象都遍历出来;每一列也是每个对象中的数据,因此也需要在每个对象中遍历出来每一个value值,这就是双重遍历数组填充表格。
如右:<tr v-for="item in books">
<td v-for="value in item">{{value}}</td>
</tr>3、数据CSS已经给出如上,即可得到以上效果图
4、发现:与原图还差加减号的实现、移除的功能、价格的¥符号以及俩位小数
步骤2:加减号的实现、移除的功能、价格的¥符号以及俩位小数
此时需要换种写法去填充列,不用数组中遍历对象的方法,因为加减号实现不了
<tbody>
<tr v-for="item in books">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.date}}</td>
<td>
¥{{item.price.toFixed(2)}}
<!-- {{'¥' + item.price.toFixed(2)}}
</td>
<td>
<button>-</button>
{{item.num}}
<button>+</button>
</td>
<td>
<button>移除</button>
</td>
</tr>
</tbody>
此时我们会发现价格的插值表达式写得太长,可以用到过滤器(filters)格式:{{item.price | 过滤器}}
<td>
<!-- ¥{{item.price.toFixed(2)}} -->
<!-- {{'¥' + item.price.toFixed(2)}} -->
<!-- {{item.price | 过滤器}} -->
{{item.price | showPrice}}
</td> // 过滤器一般是一个函数,与methods同级
filters: {
showPrice(price) {
// price要写,不然会错,参数要写上要过滤的变量
return "¥" + price.toFixed(2)
}
}分析:
1、同样可以得到需要的价格格式
2、要注意,filters不是写在methods中的;
3、格式:{{item.price | 过滤器}}
4、过滤器函数参数要写,写的是要过滤的变量
此时还差购物车的数量加减操作、移除操作和总价格:
1、数量加减操作
加减按钮:
<td>
<button @click="decrement(index)" :disabled="item.num <=1">-</button>
{{item.num}}
<button @click="increment(index)">+</button>
</td>加减方法:
methods: {
decrement(index) {
this.books[index].num--;
},
increment(index) {
this.books[index].num++;
},
}分析:
1、绑定点击事件,并写加减方法,根据便利的数组下标,来确定点击的数组中第几个对象中的数量;this.books[index].num--/++;当index=0时,则为数组第一个对象的数量num——/++
2、我们要设定数量小于1之后不可以使用减少的按钮不然商品会出现负数!:disabled="item.num <=1"
动态绑定v-bind:disabled
3、点击事件的括号必须有index作为参数
2、移除操作
移除按钮:
<td>
<button @click="removeClick(index)">移除</button>
</td>移除方法:
removeClick(index) {
// 删除数组的某一个对象
this.books.splice(index, 1)
}分析:
1、绑定点击移除事件,并写移除方法
2、方法根据遍历的数组下标得来,splice()方法去除,该数组下标为起始点,删除一个元素,即上面数组的一个item对象
3、点击事件括号内必须有唯一的参数index
3、总价格计算(computed)
价格页面:
<h2>总价格{{totalPrice | showPrice}}</h2>价格方法:(建议多看)
computed: {
totalPrice() {
let totalPrice = 0
for (let i = 0; i < this.books.length; i++) {
// 价格*数量=总价
totalPrice += this.books[i].price * this.books[i].num
}
// 别忘了返回总值
return totalPrice
}
},分析:
1、总价格也需要有对应的格式,因此要加管道符过滤器
2、先把总价默认为0,然后用for循环遍历每一个数组对象,使得每一个对象中的价格*数量=总价,最后返回总价格,若不返回则为无效方法,要中断该方法的执行。
最后附上:购物车案例最终效果.

案例优化-JavaScript的高阶函数:
上文我们写到的价格方法如下:
computed: {
totalPrice() {
let totalPrice = 0
for (let i = 0; i < this.books.length; i++) {
// 价格*数量=总价
totalPrice += this.books[i].price * this.books[i].num
}
// 别忘了返回总值
return totalPrice
}
},由于for循环的可读性有点差,于是考虑对它进行优化!这就涉及到了JavaScript的高阶函数;
首先我们先看2种for方法的优化:(this.books是数组)
1、for(let i in this.books)
computed: {
totalPrice() {
// 2.for(let i in 数组(this.books))
let totalPrice = 0
for (let i in this.books) {
// i是个索引值
// console.log(i);
totalPrice += this.books[i].price * this.books[i].num
}
return totalPrice
}
},分析:
1、for循环得到的i是下标:0,1,2,3;
2、this.books是数组,用的是in
3、每次for循环,this.book[i]得到的是数组的每一个对象,也就是i是为了拿到this.book[i]
思考:上面的写法太复杂,有没有一步到位取到this.book[i]的方法?
2、for(let i of this.books)
// 3.for(let i of 数组(this.books))
let totalPrice = 0
for (let item of this.books) {
// item是个对象
//console.log(item);
totalPrice += item.price * item.num
}
return totalPrice分析:
1、item是个对象,得到的是数组的每一个对象
2、this.books是数组,用的是of
思考:更简便的方法?
可以用高阶函数来简便代码,函数式编程!
需要先介绍几个例子,然后在正式认识高阶函数!
例子1:需要取出数组中小于100的数字
const nums = [10, 20, 111, 222, 444, 40, 50]
// Array(4) [ 10, 20, 40, 50 ]
let newNums = []
for (let n of nums) {
if (n < 100) {
newNums.push(n)
}
}例子2:需要将小于100的数字转化:全部乘以2
// 例子2:需要将小于100的数字转化:全部乘以2
// Array(4) [ 20, 40, 80, 100 ]
let new2Nums = []
for (let n of newNums) {
new2Nums.push(n * 2)
}
console.log(new2Nums);例子3:将2中得到的数字相加,得到最终结果
// 3.将2中得到的数字相加,得到最终结果
//240
let total = 0
for (let n of new2Nums) {
total += n
}
console.log(total);高阶函数:filter、map、reduce
1、filter
// 1、filter
// filter中的回调函数有一个要求:必须返回一个布尔值
// true:函数内部会自动将这次回调的n加入新数组中
// false:函数内部会过滤掉这次的n
let newNums = nums.filter(function (n) {
return n < 100
// n < 100返回的是布尔值
})
console.log(newNums);
// Array(4) [ 10, 20, 40, 50 ]2、map
// 2、map
let new2Nums = newNums.map(function (n) {
return n * 2
})
console.log(new2Nums);
// Array(4) [ 20, 40, 80, 100 ]3、reduce对数组中所有内容进行汇总
// 3、reduce对数组中所有内容进行汇总
// 参数1:function(preValue,n){}
// 参数2:0;初始化为0
let total = new2Nums.reduce(function (preValue, n) {
return preValue + n
}, 0)
console.log(total);
// 240
// preValue n
// 1: 0 20
// 2: 20 40
// 3: 60 80
// 4: 140 100
// 返回240继续进行优化:
// 不感觉以上高阶函数有多方便,于是再次进行优化,将三个需求并在一起
const nums = [10, 20, 111, 222, 444, 40, 50]
let total = nums.filter(function (n) {
return n < 100
}).map(function (n) {
return n * 2
}).reduce(function (preValue, n) {
return preValue + n
}, 0)
console.log(total);
// 240还是太长,继续优化:
// 感觉代码量并不少,于是看看能不能一行代码解决?
const nums = [10, 20, 111, 222, 444, 40, 50]
let total = nums.filter(n => n < 100).map(n => n * 2).reduce((pre, n) => pre + n);
console.log(total);
// 240一行代码就搞定,运用的是箭头函数,仔细对比上下文代码的区别
看回购物车案例,进行优化:
computed: {
totalPrice() {
let totalPrice = 0
for (let i = 0; i < this.books.length; i++) {
// 价格*数量=总价
totalPrice += this.books[i].price * this.books[i].num
}
// 别忘了返回总值
return totalPrice
}
},优化如下:book相当于books中的对象item
computed: {
totalPrice() {
// 方法都是基于数组进行的操作,所以使用reduce得先得到数组
// this.books数组、book对象
return this.books.reduce(function (preValue, book) {
return preValue + book.price * book.num
}, 0)
}
},