Vue
1、基本概念
- 渐进式框架
渐进式意味着你可以将Vue作为你应用的一部分嵌入其中,带来更丰富的交互体验 - 特点
- 解耦视图和数据
- 可复用的组件
- 前端路由技术
- 状态管理
- 虚拟DOM
2、安装Vue.js
1. 直接CDN引入
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
2. 下载后项目中引入js文件
3. 使用npm安装
3、 初体验
<div id="app">
<h2>{{message}}</h2>
<ul>
<li v-for="item in movies">{{item}}</li>
</ul>
<h2>{{count}}</h2>
<button type="button" @click="add">+</button>
<button type="button" @click="sub">-</button>
</div>
//数据和视图分离 引入vue的js文件
<script src="../js/vue.js"></script>
<script>
//let(ES6定义变量)/const(常量)
let app=new Vue({
el:'#app',//用于挂载要管理的元素,id的方式就用#
data:{//定义数据
message:'Hello Vue',
// 数组
movies:['Hello','World','VUE']
},
methods:{
add:function(){
console.log('add执行');
this.count++;
},
sub:function(){
console.log('sub执行');
this.count--;
}
}
});
</script>
-
演示
4、什么是MVVM
- View层
视图层
在我们前端开发中,通常就是DOM层。
主要的作用是给用户展示各种信息。 - Model层
数据层
数据可能是我们固定的死数据,更多的是来自我们服务器,从网络上请求下来的数据。
在我们计数器的案例中,就是后面抽取出来的obj,当然,里面的数据可能没有这么简单。 -
VueModel层
视图模型层
视图模型层是View和Model沟通的桥梁。
一方面它实现了Data Binding,也就是数据绑定,将Model的改变实时的反应到View中
另一方面它实现了DOM Listener,也就是DOM监听,当DOM发生一些事件(点击、滚动、touch等)时,可以监听到,并在需要的情况下改变对应的Data。
6、Vue实例的Options
- el:
类型:string | HTMLElement
作用:决定之后Vue实例会管理哪一个DOM - data:
类型:Object | Function (组件当中data必须是一个函数)
作用:Vue实例对应的数据对象 - methods:
类型:{ [key: string]: Function }
作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用
7、Vue的生命周期
-
官方解释
8、语法
8.1、插入操作
8.1.1、Mustache
- 就是两个{{}},并且可以在里面写入简单的表达式
<h2>{{firstName + lastName}}</h2>
8.1.2、v-once
- 有时候我们想一些界面数据不跟随其他的进行改变,该指令表示元素和组件(组件后面才会学习)只渲染一次,不会随着数据的改变而改变。
<h2 v-once>{{message}}</h2>
8.1.3、v-html
- 某些情况下,我们从服务器请求到的数据本身就是一个HTML代码
- 如果我们直接通过{{}}来输出,会将HTML代码也一起输出
- 但是我们可能希望的是按照HTML格式进行解析,并且显示对应的内容
<h2 v-html="url"></h2>
//则显示的时候是相当于正常使用a标签显示一样
url: '<a href="http://www.baidu.com">百度一下</a>'
8.1.4、v-text
- v-text作用和Mustache比较相似:都是用于将数据显示在界面中
<h2 v-text="message"></h2>
8.1.5、v-pre
- v-pre用于跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法
<h2 v-pre>{{message}}</h2>
- 直接显示出{{message}}
8.1.6、v-cloak
- 在某些情况下,我们浏览器可能会直接显然出未编译的Mustache标签
- 防止闪烁..防止htlml先显示{{message}}之类的
<div id="app" v-cloak>
<h2>{{message}}</h2>
</div>
8.2、绑定属性
8.2.1、v-bind
- v-bind用于绑定一个或多个属性值,或者向另一个组件传递props值
<div id="app">
<a v-bind:href="ahref">百度</a>
</div>
<script>
let app=new Vue({
el:'#app',//用于挂载要管理的元素
data:{//定义数据
ahref:'http://www.baidu.com'
}
});
</script>
//简写 : <a :href="ahref">xxx</a>
8.2.2、v-bind绑定class-对象语法
- 直接通过{}绑定一个类
<h2 :class="{'active': isActive}">Hello World</h2>
- 也可以通过判断,传入多个值
<h2 :class="{'active': isActive, 'line': isLine}">Hello World</h2>
- 和普通的类同时存在,并不冲突
注:如果isActive和isLine都为true,那么会有title/active/line三个类
<h2 class="title" :class="{'active': isActive, 'line': isLine}">Hello World</h2>
- 如果过于复杂,可以放在一个methods或者computed中
注:classes是一个计算属性
<h2 class="title" :class="classes">Hello World</h2>
8.2.3、v-bind绑定class-数组语法
- 直接通过{}绑定一个类
<h2 :class="['active']">Hello World</h2>
- 也可以通过判断,传入多个值
<h2 :class=“[‘active’, 'line']">Hello World</h2>
- 和普通的类同时存在,并不冲突
注:如果isActive和isLine都为true,那么会有title/active/line三个类
<h2 class="title" :class=“[‘active’, 'line']">Hello World</h2>
- 如果过于复杂,可以放在一个methods或者computed中
注:classes是一个计算属性
<h2 class="title" :class="classes">Hello World</h2>
8.2.4、v-bind绑定style
- 在写CSS属性名的时候,比如font-size
- 我们可以使用驼峰式 (camelCase) fontSize
- 或短横线分隔 (kebab-case,记得用单引号括起来) ‘font-size’
8.2.5、v-bind绑定style--对象语法
:style="{color: currentColor, fontSize: fontSize + 'px'}"
style后面跟的是一个对象类型
- 对象的key是CSS属性名称
- 对象的value是具体赋的值,值可以来自于data中的属性
8.2.5、v-bind绑定style--数组语法
<div v-bind:style="[baseStyles, overridingStyles]"></div>
style后面跟的是一个数组类型
- 多个值以,分割即可
8.3、计算属性
- 我们知道,在模板中可以直接通过插值语法显示一些data中的数据。
- 但是在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示
比如我们有firstName和lastName两个变量,我们需要显示完整的名称。 - 但是如果多个地方都需要显示完整的名称,我们就需要写多个{{firstName}} {{lastName}},重复的东西太多这在开发当中是非常忌讳的
<div id="app">
<h2>{{firstName + ' ' + lastName}}</h2>
<h2>{{firstName}} {{lastName}}</h2>
<h2>{{getFullName()}}</h2>
<h2>{{fullName}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
firstName: 'Lebron',
lastName: 'James'
},
// computed: 计算属性()
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
//上面的可以简写成如下
fullName() {
return this.firstName + ' ' + this.lastName
}
},
//同样上述的可以写成方法进行实现,记住调用的时候有些不一样
methods: {
getFullName() {
return this.firstName + ' ' + this.lastName
}
}
})
</script>
- Tips
计算属性是基于它们的依赖进行缓存的。只有依赖值改变时,计算属性才重新求值。依赖不变时,多次访问计算属性,会直接返回之前的计算结果,不必再次执行函数。
而每次访问调用方法时,它都会再次执行函数。即使当前页面已经调用过一次了,新调用时还会重新计算
计算属性只有在改变的时候才会再次执行对应函数,方法则每次都会再次执行函数
计算属性会进行缓存,如果多次使用时,计算属性只会调用一次(内容没修改的前提下)
8.3.1、复杂操作
8.3.2、计算属性的setter、getter
// 计算属性一般是没有set方法, 只读属性.
fullName: {
set: function(newValue) {
// console.log('-----', newValue);
const names = newValue.split(' ');
this.firstName = names[0];
this.lastName = names[1];
},
get: function () {
return this.firstName + ' ' + this.lastName
}
}
//一般还是直接简写成上面的那种形式
- 使用set的情况:如果我在控制台修改:fuuName="xxx xxx",默认会调用set方法。
8.4、事件监听
- 在前端开发中,我们需要经常和用于交互。
- 这个时候,我们就必须监听用户发生的时间,比如点击、拖拽、键盘事件等等
8.4.1、v-on的使用
- 绑定事件监听器
- 缩写:@
<button v-on:click="increment">+</button>
//简写
<button @click="decrement">-</button>
8.4.2、v-on参数
- 如果该方法不需要额外参数,那么方法后的()可以不添加
<button @click="decrement()">-</button>
//直接写成如下形式
<button @click="decrement">-</button>
<!--2.在事件定义时, 写方法时省略了小括号,
但是方法本身是需要一个参数的, 这个时候, Vue会默认将
浏览器生产的event事件对象作为参数传入到方法-->
<button @click="btn2Click">按钮2</button>
<!--3.方法定义时, 我们需要event对象, 同时又需要其他参数-->
<!-- 在调用方式, 如何手动的获取到浏览器参数的event对象: $event-->
<button @click="btn3Click(abc, $event)">按钮3</button>
//对应的方法
btn2Click(event) {
console.log('--------', event);
},
btn3Click(abc, event) {
console.log('++++++++', abc, event);
}
-
演示
9.1.3、v-on修饰符
8.5、条件判断
- v-if后面的条件为false时,对应的元素以及其子元素不会渲染。
- 也就是根本没有不会有对应的标签出现在DOM中
<div id="app">
<h2 v-if="score>=90">优秀</h2>
<h2 v-else-if="score>=80">良好</h2>
<h2 v-else-if="score>=60">及格</h2>
<h2 v-else>不及格</h2>
//也可以写在计算属性当中
<h1>{{result}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
score: 99
},
computed: {
result() {
let showMessage = '';
if (this.score >= 90) {
showMessage = '优秀'
} else if (this.score >= 80) {
showMessage = '良好'
}
return showMessage
}
}
})
</script>
8.5.1、小案例
- 用户再登录时,可以切换使用用户账号登录还是邮箱地址登录
<div id="app">
<span v-if="isUser">
<label for="username">用户账号</label>
<input type="text" id="username" placeholder="用户账号">
</span>
<span v-else>
<label for="email">用户邮箱</label>
<input type="text" id="email" placeholder="用户邮箱">
</span>
<button @click="isUser = !isUser">切换类型</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
isUser: true
}
})
</script>
- 我们可以发现随便测试能够实现我们的功能,但是存在一个问题,如果我们在用户账号的输入框输入内容的时候,我们再点击用户邮箱时,会发现输入框里面的内容也任然是存在的,文字依然显示之前的输入的内容。
Q:为什么会存在这种问题么?另一个input原本是没有内容的。
A:这是因为Vue在进行DOM渲染时,出于性能考虑,会尽可能的复用已经存在的元素,而不是重新创建新的元素,任然会复用原本的input - 解决
这时候我们需要保证key的不同,避免复用,将上述代码添加各自的key,确保唯一!
<input type="text" id="username" placeholder="用户账号" key="username">
<input type="text" id="email" placeholder="用户邮箱" key="email">
8.6、v-show
- v-show的用法和v-if非常相似,也用于决定一个元素是否渲染
v-if和v-show对比
- v-if当条件为false时,压根不会有对应的元素在DOM中。
- v-show当条件为false时,仅仅是将元素的display属性设置为none而已
如何选择?
- 当需要在显示与隐藏之间切片很频繁时,使用v-show
- 当只有一次切换时,通过使用v-if
<!--v-show: 当条件为false时, v-show只是给我们的元素添加一个行内样式: display: none-->
<h2 v-show="isShow" id="bbb">{{message}}</h2>
8.7、循环遍历--数组
- 格式:item in items的形式
//不需要索引值
<!--1.在遍历的过程中,没有使用索引值(下标值)-->
<ul>
<li v-for="item in names">{{item}}</li>
</ul>
<!--2.在遍历的过程中, 获取索引值-->
//其中的index就代表了取出的item在原数组的索引值
<ul>
<li v-for="(item, index) in names">
{{index+1}}.{{item}}
</li>
</ul>
8.7.1、循环遍历--对象
<div id="app">
<!--1.在遍历对象的过程中, 如果只是获取一个值, 那么获取到的是value-->
<ul>
<li v-for="item in info">{{item}}</li>
</ul>
<!--2.获取key和value 格式: (value, key) -->
<ul>
<li v-for="(value, key) in info">{{value}}-{{key}}</li>
</ul>
<!--3.获取key和value和index 格式: (value, key, index) -->
<ul>
<li v-for="(value, key, index) in info">{{value}}-{{key}}-{{index}}</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
info: {
name: 'why',
age: 18,
height: 1.88
}
}
})
</script>
8.7.2、组件的key属性
- 官方推荐我们在使用v-for时,给对应的元素或组件添加上一个:key属性
- 这其实和Vue的虚拟DOM的Diff算法有关系
- 可以这么理解,好比有一个数组,我们得我往中间插入一个数据,那么就需要插入位置后面的数据全部往后面移动一位,显然这是很没有效率的
- 所以就需要通过key给每一个节点做一个唯一标识,通过Diff算法就可以正确的识别此节点了,并且找到正确的位置区插入新的节点
- key的作用主要是为了高效的更新虚拟DOM
<div id="app">
<ul>
<li v-for="item in letters" :key="item">{{item}}</li>
</ul>
</div>
8.8、检测数组更新
- vue是响应式的,当数据发生改变,Vue检测到data数据改变就会重新渲染dom
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
-
reverse()
8.9、小案例--图书购物车
<div id="app">
<div v-if="books.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 books">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.date}}</td>
<td>{{item.price | showPrice}}</td>
<td>
<button @click="decrement(index)" v-bind:disabled="item.count <= 1">-</button>
{{item.count}}
<button @click="increment(index)">+</button>
</td>
<td><button @click="removeHandle(index)">移除</button></td>
</tr>
</tbody>
</table>
<h2>总价格: {{totalPrice | showPrice}}</h2>
</div>
<h2 v-else>购物车为空</h2>
</div>
<script>const app = new Vue({
el: '#app',
data: {
books: [
{
id: 1,
name: '《算法导论》',
date: '2006-9',
price: 85.00,
count: 1
},
{
id: 2,
name: '《UNIX编程艺术》',
date: '2006-2',
price: 59.00,
count: 1
}
]
},
methods: {
increment(index) {
this.books[index].count++
},
decrement(index) {
this.books[index].count--
},
removeHandle(index) {
this.books.splice(index, 1)
}
},
computed: {
totalPrice() {
let totalPrice = 0
for (let i = 0; i < this.books.length; i++) {
totalPrice += this.books[i].price * this.books[i].count
}
return totalPrice
}
},
filters: {
showPrice(price) {
return '¥' + price.toFixed(2)
}
}
})
</script>
9、表单绑定v-model
- Vue中使用v-model指令来实现表单元素和数据的双向绑定
- 当我们在输入框输入内容时
- 因为input中的v-model绑定了message,所以会实时将输入的内容传递给message,message发生改变。
- 当message发生改变时,因为上面我们使用Mustache语法,将message的值插入到DOM中,所以DOM会发生响应的改变。
- 所以,通过v-model实现了双向的绑定。
<div id="app">
<input type="text" v-model="message">
{{message}}
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
}
})
</script>
9.1、v-model的原理
- v-model其实是一个语法糖,它的背后本质上是包含两个操作:
- v-bind绑定一个value属性
- v-on指令给当前元素绑定input事件
<input type="text" v-model="message">
等同于
<input type="text" v-bind:value="message" v-on:input="message = $event.target.value">
9.1.1、v-model:radio
- 单选框
<div id="app">
<label for="male">
<input type="radio" id="male" value="男" v-model="sex">男
</label>
<label for="female">
<input type="radio" id="female" value="女" v-model="sex">女
</label>
<h2>您选择的性别是: {{sex}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
sex: '女'
}
})
</script>
9.1.1、v-model:checkbox
- 多选框
单个勾选框:
- v-model即为布尔值。
- 此时input的value并不影响v-model的值。
多个复选框:
- 当是多个复选框时,因为可以选中多个,所以对应的data-中属性是一个数组。
- 当选中某一个时,就会将input的value添加到数组中。
<!--1.checkbox单选框-->
<label for="agree">
<input type="checkbox" id="agree" v-model="isAgree">同意协议
</label>
<h2>您选择的是: {{isAgree}}</h2>
<button :disabled="!isAgree">下一步</button>
<!--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">羽毛球
<h2>您的爱好是: {{hobbies}}</h2>
<label v-for="item in originHobbies" :for="item">
<input type="checkbox" :value="item" :id="item" v-model="hobbies">{{item}}
</label>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
isAgree: false, // 单选框
hobbies: [], // 多选框,
originHobbies: ['篮球', '足球', '乒乓球', '羽毛球', '台球', '高尔夫球']
}
})
</script>
9.2.3、v-model:select
单选:只能选中一个值。
- v-model绑定的是一个值。
- 当我们选中option中的一个时,会将它对应的value赋值到mySelect中
多选:可以选中多个值。
- v-model绑定的是一个数组。
- 当选中多个值时,就会将选中的option对应的value添加到数组mySelects中
<div id="app">
<!--1.选择一个-->
<select name="abc" v-model="fruit">
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="榴莲">榴莲</option>
<option value="葡萄">葡萄</option>
</select>
<h2>您选择的水果是: {{fruit}}</h2>
<!--2.选择多个值 增加multiple-->
<select name="abc" v-model="fruits" multiple>
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="榴莲">榴莲</option>
<option value="葡萄">葡萄</option>
</select>
<h2>您选择的水果是: {{fruits}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
fruit: '香蕉',
fruits: []
}
})
</script>
9.3、值绑定
9.4、修饰符
- lazy修饰符:
- 默认情况下,v-model默认是在input事件中同步输入框的数据的。
- 也就是说,一旦有数据发生改变对应的data中的数据就会自动发生改变。
- lazy修饰符可以让数据在失去焦点或者回车时才会更新:
<input type="text" v-model.lazy="message">
- number修饰符:
- 默认情况下,在输入框中无论我们输入的是字母还是数字,都会被当做字符串类型进行处理。
- 但是如果我们希望处理的是数字类型,那么最好直接将内容当做数字处理。
- number修饰符可以让在输入框中输入的内容自动转成数字类型:
<input type="number" v-model.number="age">
- trim修饰符:
- 如果输入的内容首尾有很多空格,通常我们希望将其去除
- trim修饰符可以过滤内容左右两边的空格
<input type="text" v-model.trim="name">