二. Vue基础语法
二. Vue基础语法
代码规范: 平时我们就是缩进4个空格,但是更多大型项目是2个空格.
CLI -> .editconfig 针对当前代码做了一个简单的规范,要求你缩进2个空格.
封装模板,直接输入前缀生成代码
记得勾选
这样以后只要输入vue之后,按template就会直接生成代码
1. 插值语法
Mustache
1.可以在后面直接跟文本
<h2>{{message}},李银河</h2>
2.也可以进行简单的运算
如果不拼接字符串的话,想把这几个拼在一起,可以写两个然后中间敲一个空格
<!-- mustache语法中,不仅仅可以直接写变量,而且也可以写简单的表达式-->
<h2>{{firstName +' '+ lastName}}</h2>
<h2>{{firstName}} {{ lastName}}</h2>
3.还可以直接进行计算
<h2>{{counter * 2}}</h2>
v-once
<h2>{{message}}</h2>
<h2 v-once>{{message}}</h2>
v-html
p如果我们直接通过{{}}来输出,会将HTML代码也一起输出。
p但是我们可能希望的是按照HTML格式进行解析,并且显示对应的内容。
v-text
v-text作用和Mustache比较相似:都是用于将数据显示在界面中
<div>{{message}}</div>
<div v-text="message"></div>
这样显示数据都一样,但是如果我们要是想
<div id="app">
<div>{{message}},李银河</div>
<div v-text="message">,李银河</div>
</div>
v-pre
我们之前学习过这样一个标签<pre>,这个标签的作用就是把<pre>标签之间的东西原封不动的展示出来。
希望vue不要给我解析{{}}里的内容,直接输出{{}}
{{message}}
<h2 v-pre>{{message}}</h2>
v-cloak
如果我们把script里面的赋值语句都给注释掉,或者执行到下面的script卡住了,等到代码真正运行的时候,才会将message替换到我们这里面,但在这之前上面的{{message}}就会原样输出,用户只能看到{{message}},转换为我们赋值的瞬间,闪动的效果。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
[v-cloak]{
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak>
{{message}}
</div>
<script src = "../js/vue.js">
</script>
<script>
//在vue解析之前,div中有一个属性v-cloak
//在vue解析之后,div中就没有一个属性v-cloak
setTimeout(function () {
const app = new Vue({
el:'#app',
data:{
message:'你好啊'
}
})
},1000)
</script>
</body>
</html>
上面的几个标签都是动态的往{{message}}里面传入东西,但是我们想要动态的想标签内传入东西的话,
2.绑定属性
v-bind基础
mustache语法只能在标签的内容中使用,像<img src = "{{imgURL}}">这样是不允许的
在src前面加一个v-bind:
,这样vue就会解析这个指令,动态的把src绑定我们的变量
<!-- 正确的做法:使用v-bind指令-->
<img v-bind:src="imgURL" alt="">
也可以用来动态绑定超链接
html
<a v-bind:href="aHref">百度一下</a>
v-bind:
可以缩写为:
<!-- 语法糖的写法-->
<img :src="imgURL" alt="">
<br>
<a :href="aHref">百度一下</a>
绑定class
用法一:直接通过{}绑定一个类
<h2 :class="active">{{message}}</h2>
用法二:也可以通过判断,传入多个值
<h2 :class="{'active': isActive, 'line': isLine}">Hello World</h2>
通过对象的方式,后面跟上boolean值,通过修改布尔值来决定我们是否要加进去
<h2 v-bind:class="{key1:value1,key2:value2}">{{message}}</h2>
<h2 v-bind:class="{类名1:boolean,类名2:boolean}">{{message}}</h2>
用法三:和普通的类同时存在,并不冲突
注:如果isActive和isLine都为true,那么会有title/active/line三个类
我们在写了v-bind:class之外还可以写普通的class,会将其合并
<h2 class = "title "v-bind:class="{active:isActive,line:isLine} " v-on:click="isActive = !isActive" >{{message}}</h2>
用法四:如果过于复杂,可以放在一个methods或者computed中
<h2 class = "title "v-bind:class="getClasses()">{{message}}</h2>
getClasses:function () {
return {
active:this.isActive,line:this.isLine
}
}
注:getClasses()是一个计算属性
数组绑定:
这样写其实和我们之前在class里面写多个一样
<h2 class="title" v-bind:class="['active','line']">{{message}}</h2>
<h2 class="title active line " >{{message}}</h2>
但是我们也可以这样敲:
<h2 class="title" v-bind:class="[active,line]">{{message}}</h2>
<h2 class="title" v-bind:class="getClasses()">{{message}}</h2>
data:{
message :'你好啊',
active:'aaaaaa',
line:'bbbbbb'
},
methods:{
getClasses:function () {
return [this.active,this.line]
}
}
作业需求:点击列表中的哪一项,那么这一项的文字变成红色
在这里首先给每一个item绑定一个值:
changeColor:function (index) {
this.currentIndex = index
}
设置初值为-1,表示都不选中
currentIndex : -1
通过函数调用,传入参数
v-on:click="changeColor(index)"、
动态绑定class ,class的第二个boolean值判断当前的index是否是我们想选中的
:class="{color_item:currentIndex == index}"
完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.color_item{
color: pink;
}
</style>
</head>
<body>
<!--作业需求:点击列表中的哪一项,那么这一项的文字变成红色 -->
<div id="app">
<ul>
<li v-for="(m,index) in movies" v-on:click="changeColor(index)" :class="{color_item:currentIndex == index}">{{index}}-{{m}} </li>
</ul>
</div>
<script src = "../js/vue.js">
</script>
<script>
const app = new Vue({
el:'#app',
data:{
message :'你好啊',
movies: ['海王','海尔兄弟','火影忍者','进击的巨人'],
currentIndex : -1
},
methods:{
changeColor:function (index) {
this.currentIndex = index
}
},
})
</script>
</body>
</html>
动态绑定style:
1.对象语法
如果想把value当做字符串解析,就加一个''
,如果只是单纯的写上属性,比如{fontSize:50px},它在这里会将50px看为变量,到data里面找,找 50px: xxx
<h2 :style = "{fontSize:'50px'}">{{message}}</h2>
value一定要加单引号!!!
finalSize :'100px',
<!-- <h2 :style = "{key(属性名):value(属性值)}">{{message}}</h2>-->
<!-- '50px'必须加上一个单引号,要不然会按照变量去解析-->
<h2 :style = "{fontSize:'50px'}">{{message}}</h2>
finalSize :100,
<!-- finalSize当成一个变量来正常使用-->
<h2 :style = "{fontSize:finalSize}">{{message}}</h2>
<h2 :style = "{fontSize:finalSize + 'px',color:finalColor}">{{message}}</h2>
2.数组语法:
<h2 :style="[baseStyle,baseStyle1]"> {{message}}</h2>
baseStyle:{backgroundColor:'red'},
baseStyle1:{fontSize:'100px'}
要想输出字符可以用函数
<h2>{{getFullName()}}</h2>
methods:{
getFullName(){
return this.firstName + ' ' +this.lastName;
}
}
3.计算属性
想对数据进行一定的变化或者其他的操作,返回变化之后的数据可以使用compute
计算属性本质是一个属性,不要加小括号
<h2>{{fullName}}</h2>
//computed:计算属性,最好使用属性的方式起名字
computed:{
fullName() {
return this.firstName + ' ' +this.lastName;
}
}
计算属性只会调用一次,而方法内的属性你执行几次就会调用几次。methods是没有缓存的,他的性能会更低一点。
举个例子:计算所有书的金额总数
<div id="app">
<h2>总价格:{{totalPrice}}</h2>
</div>
<script>
const app = new Vue({
el:'#app',
data:{
books:[
{id:110,name:'Unix编程技术',price:119},
{id:111,name:'代码大全',price:105},
{id:112,name:'深入理解计算机原理',price:98},
{id:113,name:'现代操作系统',price:97}
]
},
computed:{
totalPrice:function () {
//这里可以直接用高阶函数 filter maps reduce
// return this.books.reduce();
let sum = 0;
// let是有块级作用域的,所以用let比较好
for (let i = 0; i < this.books.length; i++) {
sum = this.books[i].price+sum;
console.log(this.books[i].price)
}
return sum;
}
}
})
</script>
计算的时候除了最基本的for 语法之外,还可以用
for ( let i in this.books){
sum +=this.books[i].price;
}
或者这样写
for (let book of this.books){
sum += book.price;
}
计算属性本质上:
fullName:{
set:function () {
},
get:function () {
return this.lastName + " " +this.lastName
}
}
我们在使用属性的时候本质上是调用这个属性里面的get方法,一般情况下我们只需要实现get方法就可以了。
计算属性一般是没有set方法的,没有set的时候就是一个只读属性
平常我们写的其实就是这个的简写
fullName:{
get:function () {
return this.lastName + " " +this.lastName
}
}
当我们对这个计算属性重新赋值的时候,就会调用set方法
其实这个set方法会接受一个值,我们可以把新的值打印出来看一下:
set:function (newValue) {
console.log('set方法被执行了~'+newValue)
}
还可以对字符串进行操作:
fullName:{
set:function (newValue) {
console.log('set方法被执行了~'+newValue);
const names = newValue.split('');//这里通过split分割字符串,得到names数组
//然后将拿到的names数组进行分割拼接
this.firstName = names[0];
this.lastName = names[1];
},
get:function () {
return this.lastName + " " +this.lastName
}
}
计算属性和methods的对比
分别调用四次,computed这个会对结果进行缓存,每次的时候他会去看这里面的变量有没有发生变化,如果发现没有任何变化的话,会把原来的结果返回回去。
但是methods里的方法会重复计算,性能比较低
再次重新赋值computed中用到的属性,fullName会再次调用
因为检查到其中发生了变化
4.ES补充
var/let
let:当变量需要改变的时候
const : 当变量不需要改变的时候
var:没有块级作用域
let:有块级作用域
这里都可以访问:
<script>
//1.变量作用域:变量在什么范围是可用的,
{
var name = 'why';
console.log(name);
}
console.log(name);
</script>
没有块级作用域引起的问题 (if的块级)
但是这样是有缺陷的,比如说这个if的代码块是没有限制作用的,在哪都能访问到,无论if的条件是否正确
if (true){
var name = 'why';
}
console.log(name);
我们在方法里面定义了一个属性,但有的人在外面调用了,就修改了这个值,并没有打印出这个方法里面的内容。
//2.没有块级作用域引起的问题
var func;
if (true){
var name = 'why';
func =function () {
console.log(name)
}
// func()
}
console.log(name);
name = 'kobe'
//本想打印why,但是因为之后修改了所以打印了kobe
func()
没有块级作用域引起的问题 (for的块级)
首先在页面上面定义五个按钮
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
这里会产生一个现象:就是我们无论点击哪一个按钮,到最后都会打印输出,点击了第五个按钮
// 3.没有块级作用域引起的问题 for的块级
var btns = document.getElementsByClassName('button');
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener('click',function () {
console.log('第' + i + '个按钮被点击');
})
}
这里的原因是因为,但是我们的
进行的是这个函数
但我们使用的是函数外面的这个i
这个i第一次传入,到传递到第五次之后。这个i已经变成5,我们运行function的时候一直引用的是外面这个变量。
这里这个console打算用的是最初的i = 0,但是已经因为多次循环的时候,i++最终i已经被改成5了
对于解决这种问题,我们可以创建一个闭包,首先需要把这个i传入闭包中,最后还需要调用一下这个闭包
这个闭包之所以可以解决这个问题,是因为你之后修改的变量都只是在你的变量定义域范围内,改属于你自己的变量。函数内部有属于我们自己的name
因为在ES5之前if和for都没有块级作用域的概念,所以在很多时候,我们都必须借助于function的作用域来解决我们引用外面变量的问题,ES6中加入了let,let存在作用域
const btns = document.getElementsByTagName('button');
for (let i = 0; i < btns.length; i++) {
btns[i].addEventListener('click',function (){
console.log('第' + i + '个按钮被点击');
})
}
执行过程: 因为let有自己的执行的作用域,这样我在改属于我自己的i的时候不会把别人的i改掉,因为之前没有作用域,是共用了i
{ i = 0
btns[i].addEventListener('click',function (){
console.log('第' + i + '个按钮被点击');
})
}
{ i = 1
btns[i].addEventListener('click',function (){
console.log('第' + i + '个按钮被点击');
})
}
{ i = 2
btns[i].addEventListener('click',function (){
console.log('第' + i + '个按钮被点击');
})
}
{ i = 3
btns[i].addEventListener('click',function (){
console.log('第' + i + '个按钮被点击');
})
}
{ i = 4
btns[i].addEventListener('click',function (){
console.log('第' + i + '个按钮被点击');
})
}
块级作用域(es5没闭包-有闭包-let对比)
// 1.情况1:es5中没有使用闭包(错误的方式)
const btns = document.getElementsByTagName('button');
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener('click',function (){
console.log('第' + i + '个按钮被点击');
})
}
// 执行过程:
// i = 0
var i = 0
{
btns[i].addEventListener('click',function (){
console.log('第' + i + '个按钮被点击');
})
}
var i = 1
{
btns[i].addEventListener('click',function (){
console.log('第' + i + '个按钮被点击');
})
}
var i = 2
{
btns[i].addEventListener('click',function (){
console.log('第' + i + '个按钮被点击');
})
}
这三个没有作用域,所以这三个相当于使用了同一个i
当执行到最后一个的时候,i = 2
我们点击按钮之后执行函数,首先去回调函数,比如点击了第一个按钮,当它回调的时候只能找到刚开始的 i = 2,但是还有一个 i++,所以我们最终找到的是i = 3
//2.情况二:ES5中的闭包
var btns = document.getElementsByClassName('button');
for (var i = 0; i < btns.length; i++) {
(function (i) {
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
})(i)
}
(function (i) { //i = 0
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}) (i)
(function (i) { //i = 1
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}) (i)
(function (i) { //i = 2
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
}) (i)
这里的是前面一个函数 ,后面的(i)
是指执行这个函数,这个函数执行的时候有自己的i,并不会去找外面的i
这个函数的执行过程也是回调
btns[i].addEventListener('click', function () {
console.log('第' + i + '个按钮被点击');
})
当它执行的时候,这里没有i,所以到最外层去找i
(function (i) { //i = 2
(function (i) { //i = 2
xxx
})
最外层最初的时候我已经给它传了一个i了 (function (i) { //i = 2
块级作用域有自己的i,无论改成多少都没有关系
const
使用const修饰的标识符为常量, 不可以再次赋值.
什么时候使用const呢?
当我们修饰的标识符不会被再次赋值时, 就可以使用const来保证数据的安全性.
建议: 在ES6开发中,优先使用const, 只有需要改变某一个标识符的时候才使用let.
<script>
//注意1:一旦给const修饰的标识符赋值后,不能被修改
// const name = 'why';
// name = 'abc';
// 注意2:在使用const定义标识符,必须进行赋值
// const name;
//注意3:常量含义是指向的对象不能修改,但是可以改变对象内部的属性
const obj = {
name:'why',
age:18,
height:1.88
}
console.log(obj)
obj.name = 'kobe';
obj.age = 44;
obj.height = 1.80;
console.log(obj)
</script>
对象的增强写法
<script>
// const obj = new Object();
// 这里的{}就叫做对象的字面量
// const obj = {
// name:'why',
// age:18,
// run:function () {
// console.log('在奔跑');
// },
// eat:function () {
// console.log('在吃东西');
// }
// }
//1.属性的增强写法:
const name = 'why';
const age = 18;
const height = 1.88;
//ES5的写法
// const obj = {
// name:name,
// age:age,
// height:height
// }
const obj = {name,age,height}
//2.函数的增强写法
//ES5的写法
// const obj = {
// run:function () {
//
// },
// eat:function () {
//
// }
// }
const objj = {
run(){
},
eat(){
}
}
</script>
把变量的名称作为key,把变量的具体的值作为value
5.事件监听
v-on介绍
<button v-on:click="counter++">+</button>
<button v-on:click="counter--">-</button>
可以写到方法里面
<button v-on:click="counter++">+</button>
<button v-on:click="counter--">-</button>
v-on参数
简写:v-on:
可以简写为@
<button @click="increment">+</button>
<button @click="decrement">-</button>
这里没有参数传入,方法调用的时候可以省略()
我们调用这个有参的方法btn2Click的时候,如果我们写括号但是不传入参数的话
<button @click="btn2Click()">按钮2</button>
会输出这个方法undefined
但是如果我们传入参数但是不写括号的话,因为在事件定义的时候,写方法的时候省略了小括号,但是方法本身是需要一个参数的,这个时候Vue会默认将浏览器生产的event事件对象作为参数传入到方法中
methods:{
btn1Click(){
console.log("btn1被点击了");
},
btn2Click(event){
console.log(event);
}
}
此时输出:
还有方法定义时,我们需要event对象,同时又需要其他参数
btn2Click(abc,event){
console.log(abc,event);
}
- 如果写了括号,但是啥都不传入,输出
<button @click = "btn3Click()">按钮4</button>
- 如果不写括号,啥都不传入,输出
<button @click = "btn3Click">按钮4</button>
会输出一个event一个undefined,因为会把默认的第一个参数传入event
- 但是如果我们想要第二个参数传入event对象,就必须给它赋值
假如我们这样写
<button @click = "btn3Click(123,event)">按钮4</button>
vue内部会把第二个参数event看是data里面的一个属性,找不到。
我们应该$event
这样写
<button @click = "btn3Click(123,$event)">按钮4</button>
我们应该这样写:
这里第一个参数要写123,不能写abc,因为会将abc看为变量,这里写123可以,是因为变量是不能以数字开头的,所以判定它是一个常量。
v-on修饰符
1. .stop - 调用 event.stopPropagation()
<div>
<div @click = "divClick">
<button @click="btnClick">按钮</button>
</div>
</div>
我们这时候可以使用.stop
,阻止事件冒泡,这样在点击button的时候就不会激活div的事件了
<button @click.stop="btnClick">按钮</button>
2 .prevent - 调用 event.preventDefault()。
当我们点击提交按钮的时候,会默认提交,如果我们想要阻止他们默认事件的发生,就可以用.prevent
<!--2. .prevent修饰符的使用 -->
<form action="baidu">
<input type="submit" value="提交" @click.prevent = "submitClick">
</form>
submitClick(){
console.log("submitClick")
}
这里可以看出并没有跳转
3.{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
<!-- 3. .监听键盘的键帽的点击-->
<input type="text" @keyup ="keyUp">
keyUp(){
console.log('keyup');
}
这里keyup是指按下某个键盘才会调用方法
如果想指定特定的案件的监听事件,比如说只想要监听enter键的输入 可以用.enter
<input type="text" @keyup.enter ="keyUp">
4. .native - 监听组件根元素的原生事件
如果我们定义了自己的组件想要进行监听的时候,要在后面加.active
<cpn @click.native ="cpnClick"></cpn>
5 .once - 只触发一次回调。
<!--4.once修饰符的使用-->
<button @click.once="btn2Click">按钮2</button>
6.条件和循环
条件渲染
div id="app">
<h2 v-if="isShow">
<div>abc</div>
<div>abc</div>
<div>abc</div>
<div>abc</div>
<div>abc</div>
<div>abc</div>
</h2>
<h1 v-else>isShow为false的时候,显示我</h1>
</div>
v-if的原理:
v-if后面的条件为false时,对应的元素以及其子元素不会渲染。
也就是根本没有不会有对应的标签出现在DOM中。
简单的案例演示:根据分数显示对应的评价
- 利用v-if、v-else-if、v-else:
<div id="app">
<h2 v-if="score>=90">优秀</h2>
<!-- 这里()不用写&& score<90,因为这是else if 是在上面不满足的情况下才执行的-->
<h2 v-else-if="score>=80">良好</h2>
<h2 v-else-if="score>=60">及格</h2>
<h2 v-else>不及格</h2>
</div>
- 利用computed属性显示,更复杂的逻辑建议更使用
<h1>{{result}}</h1>
computed: {
result() {
let showMessage = '';
if (this.score >= 90) {
showMessage = '优秀'
}else if(this.score>=80){
showMessage = '良好'
}else if (this.message >=60){
showMessage = '及格'
}else{
showMessage = '不及格'
}
return showMessage;
}
}
简单的小案例:
用户再登录时,可以切换使用用户账号登录还是邮箱地址登录。
<div id="app">
<div v-if="isByName">
<label for="userName">用户账号:</label>
<input type="text" placeholder="用户账号" id="userName">
</div>
<div v-else>
<label for="userEmail">用户邮箱:</label>
<input type="text" placeholder="用户邮箱" id="userEmail">
</div>
<button @click="isByName=!isByName">切换类型</button>
</div>
这里有一个问题,在我们输入账号的输入框中输入一部分之后我们这时候切换类型,这个时候输入框的内容依然存在,这是因为虚拟DOM。
虚拟dom是真实dom的一个映射,他会帮助我们做一些事情,虚拟DOM会把你要渲染的东西先给你放到内存里面
这是因为Vue在进行DOM渲染时,出于性能考虑,会尽可能的复用已经存在的元素,而不是重新创建新的元素。
上面的案例中,Vue内部会发现原来的input元素不再使用,直接作为else中的input来使用了。
如何解决这个问题?
可以在每一个input上面加一个key,这样就不会把两个input看做是一个input,就不会复用,会创建一个新的input
<div v-if="isByName">
<label for="userName">用户账号:</label>
<input type="text" placeholder="用户账号" id="userName" key="username">
</div>
<div v-else>
<label for="userEmail">用户邮箱:</label>
<input type="text" placeholder="用户邮箱" id="userEmail" key="email">
</div>
v-show
<div id="app">
<h2 v-if="isShow" id="aaa"> {{message}}</h2>
<h2 v-show="isShow" id="bbb"> {{message}}</h2>
</div>
<script src = "../js/vue.js">
</script>
<script>
const app = new Vue({
el:'#app',
data:{
message :'你好啊',
isShow :true
}
})
</script>
这里更改v-show的值之后,这两个h2都会消失,但是消失的方式不同
我们可以看到这里的这个
v-if当条件为false时,压根不会有对应的元素在DOM中。
v-show当条件为false时,仅仅是将元素的display属性设置为none而已。
当需要在显示与隐藏之间切片很频繁时,使用v-show
当只有一次切换时,通过使用v-if
v-for指令
v-for遍历数组:
<!-- 1.在遍历的过程中,没有使用索引值(下标值)-->
<ul>
<li v-for="item in names">{{item}}</li>
</ul>
<!-- 2.在遍历的过程中,获取索引值-->
<ul>
<li v-for ="(item,index) in names">{{index+1}} - {{item}}</li>
</ul>
v-for遍历对象:
<!-- 1.在遍历对象的过程中,如果只是获取一个值,那么获取的是value-->
<ul>
<li v-for = "item in info">{{item}}</li>
</ul>
//data:
info:{
name:'why',
age:18,
height:1.88
}
<!-- 2.获取key和value-->
<!-- 格式 (value,key)-->
<ul>
<li v-for="(value,key) in info"> {{key}} - {{value}} </li>
</ul>
<!-- 3.获取key value和index-->
<ul>
<li v-for="(value,key,index) in info"> {{index}} - {{key}} - {{value}} </li>
</ul>
官方推荐我们在使用v-for时,给对应的元素或组件添加上一个:key属性。
v-for使用过程添加key
<div id="app">
<ul>
<li v-for="item in letters">{{item}}</li>
</ul>
</div>
</script>
<script>
const app = new Vue({
el:'#app',
data:{
letters:['A','B','C','D','E']
}
})
</script>
数组渲染之后在数组元素前面插入元素的过程。
如果我们想在C之前插入一个F,如果没有添加key的时候我们这里就是会将后面的元素全部删除,然后再把这个F添加到C前面
为了提高效率我们给它添加一个key
有key之后,会对比每一个key和他对应的元素是否发生了变化如果没有发生变化,就不需要改变原来的元素,继续利用这一个。
这里的key一定要是item,如果是index的话因为index一直变化,效率会很低。
但是这个加key的前提条件:保证内容的唯一性
检测数组更新
<ul>
<li v-for="item in letters" >{{item}}</li>
</ul>
<button @click = "btnClick">按钮</button>
data:{
letters: ['a','b','c','d']
}
1.push方法
btnClick(){
this.letters.push('aaa')
}
这个时候修改之后,页面就可以自动刷新出现我们新添加的aaa字母
特别的,这里可以一次性添加多个元素
this.letters.push('aaa','bbb','ccc')
2.通过索引值修改数组中的元素(不是响应式的)
btnClick(){
this.letters[0] = 'bbbb';
}
这个时候修改之后,页面并没有刷新更改第一个元素的内容,但是我们使用命令查看这个数组的内容,第一个元素的值已经被改变了
可以通过这两种方式修改数据,就可以在页面中动态渲染了:
this.letters.splice(0,1,'bbbbb');
//set(要修改的对象,索引值,修改后的值)
Vue.set(this.letters,0,'bbbbb')
3.pop():删除数组中的最后一个元素
btnClick(){
this.letters.pop();
}
4.shift():删除数组中的第一个元素
btnClick(){
this.letters.shift();
}
5.unshift():在数组最前面添加元素
btnClick(){
this.letters.unshift("xxx");
}
同样,这个unshift函数也可以一次添加多个
this.letters.unshift("xxx","aaa",'ddd');
可变参数方法书写:这样可以随便在调用的时候写多个参数,在参数数量不确定的时候使用,这个方法。
function sum(...num) {
}
sum(20,30,40,50,60)
这里的实质,是将多个传入的参数放入数组num中。
6.splice()
这里如果不传入参数的话,会把从一开始的所有的元素都删除掉
// 删除元素:第二个参数传入你要删除几个元素(如果不传入,就删除所有的)
this.letters.splice(1);
//this.letters.splice(1,this.letters.length-1);
// 替换元素:第二个参数,表示我们要替换几个元素,后面是用于替换前面的元素
this.letters.splice(1,3,'m','n','i')
//插入元素:第二个参数传入0后面跟上我们要插入的参数就可以了
this.letters.splice(1,0,'x','y','z')
7.sort()
this.letters.sort();
8.reverse()
this.letters.reverse()
点击变色小案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.active{
color: red;
}
</style>
</head>
<body>
<div id="app">
<ul>
<li v-for="(item,index) in movies"
:class="{active:currentIndex === index}"
@click = "liClick(index)">{{item}}</li>
<!-- 实际上是依次判断:-->
<!-- <li :class = "{active:currentIndex === 0}"></li>-->
<!-- <li :class = "{active:currentIndex === 1}"></li>-->
<!-- <li :class = "{active:currentIndex === 2}"></li>-->
<!-- <li :class = "{active:currentIndex === 3}"></li>-->
</ul>
</div>
<script src = "../js/vue.js">
</script>
<script>
const app = new Vue({
el:'#app',
data:{
//用于记录谁是显示active的
currentIndex : -1,
movies:['海王','海贼王','加勒比海盗','海尔兄弟']
},
methods:{
// 点击哪一个就把哪一个的值赋给currentIndex
liClick(index){
this.currentIndex = index
}
}
})
</script>
</body>
</html>
7. 阶段案例
7.1实现购物车案例
实现代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="style.css">
<style>
li{
list-style: none;
}
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>
</head>
<body>
<div id="app">
<div v-if="list.length">
<table>
<thead>
<tr>
<th></th>
<th>书籍名称</th>
<th> 出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in list ">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.date}}</td>
<!-- <td>{{// getFinalPrice(item.price)}}</td>-->
<td>{{item.price | showPrice}}</td>
<td>
<button @click="item.count++">+</button>
{{item.count }}
<button @click="subCount(index)" :disabled="item.count<=1">-</button>
</td>
<td>
<button @click="removeBook(index)">移除</button>
</td>
</tr>
</tbody>
</table>
<span>总价:{{getFinalPrice(totalPrice)}}</span>
</div>
<div v-else> 购物车为空</div>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el:'#app',
data:{
// bookNames:['算法导论','UNIX编程艺术','编程珠玑','代码大全']
list:[
{id:0,name:'算法导论',date:'2006-9',price:85.00,count:1},
{id:1,name:'UNIX编程艺术',date:'2006-2',price:59.00,count:1},
{id:2,name:'编程珠玑',date:'2008-10',price:39.00,count:1},
{id:3,name:'代码大全',date:'2006-3',price:128.00,count:1},
],
curBooksNum:4,
},
computed:{
totalPrice () {
let sum = 0;
for (let i = 0; i < this.list.length; i++) {
sum += this.list[i].price * this.list[i].count ;
}
// for(let i in this.list){
// sum += i.price*i.count;
// }
return sum;
},
},
methods:{
subCount(index){
if (this.list[index].count>1){ this.list[index].count--;}
},
removeBook(index){
this.list.splice(index,1);
this.curBooksNum--;
console.log(this.curBooksNum)
},
getFinalPrice(price){
return '¥' + price;
}
},
filters: {
showPrice(price) {
return '¥' + price.toFixed(2)
}
}
})
</script>
</body>
</html>
1.固定小数点:
<td>{{item.price.toFixed()</td>
这里页面自动渲染价格的时候会自动的把小数点后面的两位去省略了,为了不省略我们可以在这个数字后面加上.toFixed(2)
,这个方法就可以固定小数点后面两位数字了。
toFixed() 方法可把 Number 四舍五入为指定小数位数的数字。
2.固定人民币格式:
对于我们数字前面的¥我们可以使用计算属性,来固定这个格式,在计算总的价格的时候也可以使用这种方式。
getFinalPrice(price){
return '¥' + price;
}
但是我们可以使用一种更加简便的方法:使用过滤器
filters: {
showPrice(price) {
return '¥' + price.toFixed(2)
}
}
调用:
<td>{{item.price | showPrice}}</td>
3.数量减到了1就不能再减了。
disable和src、href一样都是属性,我们可以动态绑定:disabled="item.count<=1"
<button @click="subCount(index)" :disabled="item.count<=1">-</button></td>
4.在移除全部的数据之后显示购物车为空
可以给上面这一部分的代码设置一个div,然后设置<div v-if="list.length">
下面的购物车为空:
<div v-else> 购物车为空</div>
5.计算这个总价格一共有四种方式
1.最普通的for循环
for (let i = 0; i < this.list.length; i++) {
sum += this.list[i].price * this.list[i].count ;
}
2.for(let i in this.list)
for(let i in this.list){
const book = this.list[i];
sum += book.price*book.count;
// console.log(i);//这里会输出 0 1 2 3,这个i就是我们的索引值
sum += this.list[i].price * this.list[i].count ;
}
3.for(let item of this.list) 直接拿到我们的book
其实上面的第二种方式的目的也是为了拿到我们的每一个book,相当于
const book = list[i];
sum += book.price*book.count;
for (let item of this.list){
sum+= item.count*item.price;
}
in 是索引,of是拿到每一个对象
编程范式:命令式编程,函数式编程
面向对象编程(第一公民:对象) /函数式编程(第一公民:函数)
7.2三个高阶函数:filter/map/reduce
filter是一个高阶函数,可能它本身的参数也是一个高阶函数
他这里有一个回调函数,在每一次遍历的时候都会来回调这个函数,每一次会从数组里面取出来一个值来回调函数。
比如说求解这个问题,如果用for循环的话,需要这样写:
// 1.取出所有小于100的数字
let newNums = []
for (let i of nums){
if (i<100){
newNums.push(i)
}
}
//2.将所有小于100的数字进行转化,全部乘以2
let new2Nums = []
for (let j of newNums){
new2Nums.push(j*2);
}
console.log(new2Nums)
//需求:3.将new2Nums中的数字全部相加,得到最终的结果
let total = 0;
for (let n of new2Nums){
total+=n;
}
console.log(total);
采用三个高阶函数:
//三个高阶函数:filter/map/reduce
// filter这个回调函数有一个要求:必须返回一个布尔值
// true:当返回true的时候,函数内部会自动的将这次回调的n加入到新的数组中
// false:当返回false的时候,函数内部会过滤掉这个n
let newNums = nums.filter(
function (n) {
return n<100;
}
)
console.log(newNums)
// 2.map函数的使用
let new2Nums = newNums.map(function (n) {
return n*2;
})
console.log(new2Nums);
//3.reduce 函数的使用
let total = new2Nums.reduce(function (preValue,n) {
return preValue + n;
},0);
console.log(total);
简洁函数:
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);
更简洁的写法:
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);
那么,我们之前求价格的函数就可以这样写:
totalPrice () {
let sum = 0;
return this.list.reduce(function (preValue,list) {
return preValue +list.count*list.price;
},0);
},
8. 表单绑定
基本使用
<div id="app">
<input type="text" v-model = "message">
{{message}}
</div>
<script>
const app = new Vue({
el:'#app',
data:{
message :'你好啊'
}
})
</script>
我们之前的mustache语法只是将数据渲染上去
但是这个v-model可以双向数据绑定,你输入的数据会影响vue原本的message
v-model原理:
v-model其实是一个语法糖,它的背后本质上是包含两个操作:
1.v-bind绑定一个value属性
2.v-on指令给当前元素绑定input事件
<input type="text" v-bind:value="message" v-on:input="message = $event.target.value">
其他类型
1.只有加上name属性才能互斥,才能在二者之间只选择一个
<div id="app">
<label for="male">
<input type="radio" id="male" value="男" v-model="sex">
男
</label>
<label for="male">
<input type="radio" id="female" value="女" v-model="sex">
女
</label>
你选的性别是: {{sex}}
</div>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
sex:'男'
}
})
</script>
2.一旦这里v-model绑定的是同一个sex,这个name属性可以没有
3.如果需要默认值,就直接在这个sex
v-model结合checkbox类型:
<div id="app">
<!-- 1.checkBox单选框 对应布尔值-->
<label for="agree">
<input type="checkbox" id="agree" v-model="isAgree">同意协议
</label>
<h2>您选择是:{{isAgree}}</h2>
<button :disabled = "!isAgree">下一步</button>
<br>
<!-- 2.checkbox多选框 对应数组-->
<input type="checkbox" value="篮球" v-model="hobbies" >篮球
<input type="checkbox" value="足球" v-model="hobbies">足球
<input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球
<input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球
您的爱好是: {{hobbies}}
</div>
<script src = "../js/vue.js">
</script>
<script>
const app = new Vue({
el:'#app',
data:{
message :'你好啊',
isAgree:false,
hobbies:[]
}
})
</script>
1.实现下一步勾选功能:
<label for="agree">
<input type="checkbox" id="agree" v-model="isAgree">同意协议
</label>
可以在data里面给默认值,这里是false,isAgree:false,
2.实现不勾选协议,下一步按钮无法点亮:
<button :disabled = "!isAgree">下一步</button>
3.多选多个显示,需要用到一个数组
<!-- 2.checkbox多选框 对应数组-->
<input type="checkbox" value="篮球" v-model="hobbies" >篮球
<input type="checkbox" value="足球" v-model="hobbies">足球
<input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球
<input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球
v-model结合select类型:
1.选择一个
<select name ="abc" id="" v-model="fruit">
<option value="苹果" >苹果</option>
<option value="香蕉" >香蕉</option>
<option value="榴莲" >榴莲</option>
<option value="葡萄" >葡萄</option>
</select>
<br>
您选择的水果是{{fruit}}
<br>
这里可以设置data的初值:fruit:'香蕉',这样会在页面加载的时候默认选择香蕉
2.选择多个
<select name ="abc" id="" v-model="fruits" multiple>
<option value="苹果" >苹果</option>
<option value="香蕉" >香蕉</option>
<option value="榴莲" >榴莲</option>
<option value="葡萄" >葡萄</option>
</select>
<br>
您选择的水果是{{fruits}}
要是想实现可以选择多个的话,给select添加一个multiple属性,这样就可以按住ctrl键,选择多个了~
<div id="app">
</div>
<script src = "../js/vue.js">
</script>
<script>
const app = new Vue({
el:'#app',
data:{
message :'你好啊',
fruits:[]
}
})
</script>
值绑定
<label v-for="item in originHobbies" ></label>
<input type="checkbox" :value="item" v-model="originHobbies">{{item}}
data:{
message :'你好啊',
isAgree:false,
hobbies:[],
originHobbies:['篮球','足球','乒乓球','羽毛球','台球','高尔夫球']
}
修饰符
.lazy
可以让失去焦点或者敲回车的时候,才让输入框中的内容渲染到下面的区域中
- 当我们想要输入框的内容是数字的时候,我们可以设置input的type是number,我们输入绑定age变量,
<input type="number" v-model="age">
给它一个初值
age:''
查看age的类型
<h2>{{typeof age}}</h2>
typeof是查看数据类型的指令,不是函数,所以不需要在后面加括号
可以看到是string类型的
这里是因为我们设置初值的原因,我们把初值改为0
age:0
首次打开网页的时候,这个页面显示数据类型是number
但是只要我们向里面输入一些内容,我们就可以看到类型变为string,并不符合我们预期的想法
v-model绑定过去的东西,都会被默认为string类型
这时,我们可以给v-model加一个.number
修饰符,这样之后输入数据的时候,v-model都会给变量赋值为number类型
-
.trim
去掉输入的空格如果不加trim的话
<!-- 3.修饰符: trim--> <input type="text" v-model="name"> 您输入的名字: <h2>{{name}}</h2>
打开控制台,可以看到很多空格,但是浏览器默认帮我们已经去掉了
加上trim,打开控制器之后可以看到,已经把变量的空格去除了