初识Vue
vue是什么?
在vue官网的简介是:用于 构建用户界面1 的 渐进式2 框架3
构建用户界面:基于数据动态渲染出用户看到的页面
渐进式:循序渐进逐步学习,使用
框架:一套完整的项目解决方案,可以极大地提高开发的效率
既然那么好用,让我们来盘一盘这个vue
vue中的MVVM
- M指的是model(模型),对应data中的数据。
- V指的是view(视图),一个展示用户界面的模板,可以简单的理解为HTML标签页面。
- MV指的是ViewModel(视图模层),它是Vue的实例对象,一个连接view和model的桥梁,负责把model对象封装成可以显示和接受输入的界面对象。
vue的基本使用
核心步骤(4步):
-
准备容器
-
引包(官网) — 开发版本/生产版本
-
创建Vue实例 new Vue()
-
指定配置项,渲染数据
- el:指定挂载点
- data提供数据
创建vue实例,初始化渲染
来到vue2的官网看看该如何使用
依照上面的顺序
//准备容器 (Vue所管理的范围)
<div id="app">{{ message }}</div>
//引包 (开发版本包 / 生产版本包) 官网
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
//创建实例
const box = new Vue({
// 制定 vue 托管的区域
el: '#app',
// 渲染的数据
data: {
message: 'Hello Vue!'
}
})
这样的话data里面的数据就被渲染到页面上了
只能渲染由vue托管的区域,没有托管的区域不会动态渲染
插值表达式{{ }}
数据绑定最常见的形式就是使用“Mustache
”语法 (小胡子语法) 的文本插值
作用:利用表达式进行插值,渲染到页面上
语法:{{ 表达式}}
<span>Message: {{ msg }}</span>
所谓的表达式,就是一段可以被求值的代码,js引擎会将其计算并最终得出一个结果
money + 100
money - 100
money * 10
money / 10
price >= 100 ? '真贵':'还行'
obj.name
arr[0]
fn()
obj.fn()
以上这些都是表达式
可以简单的直接渲染数据,既然支持表达式那么也可以往里面写一些复杂的计算表达式,就像这样
<h3>{{title}}<h3>
<p>{{nickName.toUpperCase()}}</p>
<p>{{age >= 18 ? '成年':'未成年'}}</p>
<p>{{obj.name}}</p>
<p>{{fn()}}</p>
当msg里面的数据发生变化的时候,对应页上的信息也会进行动态的渲染和更新
通过使用 v-once 指令
,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定:
<span v-once>这个将不会改变: {{ msg }}</span>
注意:
数据必须存在
插值表达式,只能支持表达式,而非语句: if ,for...
不能够在标签中使用
响应式特性
当data里面的数据发生变化的时候,对应页上的信息也会进行动态的渲染和更新
data中的数据, 最终会被添加到实例上
① 访问数据: "实例.属性名"
② 修改数据: "实例.属性名"= "值"
vue指令
v-html
作用:设置元素的innerHTML
语法: v-html = 表达式
双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html
指令
<div id="app">
<div v-html="msg"></div>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
msg: '<h1>燕子,你别走!!!没了你我可怎么活啊!</h1>'
}
})
</script>
v-if
v-if 底层原理:通过创建 / 删除 dom 元素来实现盒子的显示隐藏,当表达式的值为 true 时就显示,为 false 时就隐藏
适用于初始状态
就决定了显示隐藏的场景
v-show
v-show 底层原理: 通过设置 display:none
,来改变盒子的显示隐藏
当表达式的值为 true 时就显示,为 false 时就隐藏
适用于频繁切换
显示隐藏的场景
v-else ,v-else-if
- 作用:辅助v-if进行判断渲染
- 语法:v-else v-else-if="表达式"
- 需要紧接着v-if使用
<div id="app">
<p v-if="gender===0">性别:♂ 男</p>
<p v-else>性别:♀ 女</p>
<hr>
<p v-if="score>=90">成绩评定A:奖励电脑一台</p>
<p v-else-if="score>=70">成绩评定B:奖励周末郊游</p>
<p v-else-if="score>=60">成绩评定C:奖励零食礼包</p>
<p v-else>成绩评定D:惩罚一周不能玩手机</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
// 目标:
// 1. 根据条件渲染姓名
// 2. 根据成绩来渲染奖励
const app = new Vue({
el: '#app',
data: {
gender: 0,
score: 95
}
})
</script>
v-on
作用: 注册事件 = 添加监听 + 处理逻辑
语法:
- v-on :事件名 = "内联语句"
- v-on: 事件名 = "methods中的函数名"
简写形式
@事件名
<div id="app">
<button @click={{count--}}>-</button>
<span>{{ count }}</span>
<button @click={{count++}}>+</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
// 目标: 点击 - 让数字减一, 点击 + 让数字加一
const app = new Vue({
el: '#app',
data: {
count: 100
}
})
</script>
methods 中存放函数
只要是定义在 methods 中的函数,最终都会放到实例对象上,所以内部的 this 指向实例对象
<div id="app">
<button @click="isShow = !isShow">切换显示隐藏</button>
<button @click="change">切换显示隐藏</button>
<h1 v-show="isShow">我又出来了</h1>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
/**
* 目标:
* 点击切换 h1 标签显示隐藏
*/
const app = new Vue({
el: '#app',
data: {
isShow: true
},
methods: {
change() {
this.isShow = !this.isShow
}
}
})
v-on调用传参
<div id="app">
<div class="box">
<h3>小黑自动售货机</h3>
<button @click="buy(5)">可乐5元</button>
<button @click="buy(10)">咖啡10元</button>
</div>
<p>银行卡余额:{{ money }}元</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
/**
* 目标:
* 点击不同按钮, 银行卡余额减少对应的金额
*/
const app = new Vue({
el: '#app',
data: {
money: 100
},
methods: {
buy(price) {
this.money -= price
}
}
})
v-bind
作用:动态设置标签的属性值
语法: v-bind:属性名="表达式"
简写:
:属性名 = "表达式"
以下是一些常见的HTML标签属性
1.src 属性(用于图像、音频、视频等):
<img :src="imageSource">
<audio :src="audioSource"></audio>
<video :src="videoSource"></video>
2.href 属性(用于链接):
<a :href="externalLink">Visit Website</a>
3.title 和 alt 属性(用于提示信息):
<img :title="imageTitle" :alt="imageAlt" src="imageSource">
4.disabled 属性(用于禁用表单元素):
<button :disabled="isDisabled">Submit</button>
5.placeholder 属性(用于表单元素的占位符文本):
<input :placeholder="inputPlaceholder">
6.value 属性(用于表单元素的值):
<input :value="inputValue">
7.readonly 属性(用于设置表单元素为只读):
<input :readonly="isReadOnly">
8.checked 属性(用于复选框和单选按钮):
<input type="checkbox" :checked="isChecked">
<input type="radio" :checked="isSelected">
9.data-属性(用于存储自定义数据):
<div :data-custom-value="customData"></div>
<div id="app">
<img :src="imgUrl" :title="msg" alt="">
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
/**
* 目标:
* 使用 v-bind 指令动态绑定标签属性
*/
const app = new Vue({
el: '#app',
data: {
imgUrl: './imgs/10-02.png',
msg: 'hello'
}
})
v-bind 操作 class 类名
语法 :class ="{对象}"
设置对象使用布尔值控制是否使用这个类名,比较常用动态设置类名
<div id="app">
<div class="box" :class="{pink:flag}">键盘敲烂,月薪过万</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
flag: true
}
})
</script>
语法 :class="[数组]"
使用数组一次性设置多个类名,或删除多个类名
<div id="app">
<div class="box" :class="['pink','big']">今天天气真不挫!</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
flag: true
}
})
</script>
v-bind 操作 style 类名
语法 :style = "{ 样式属性 }"
<div id="app">
<div class="box" :style="{width: '50rem',height: '18.75rem'}"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
}
})
</script>
v-for
作用:基于数组循环,多次整个渲染整个元素
语法: v-for ="(item,index) in list"
- item 是数组中的每一项
- index 是每一项对应的索引,不需要可以省略
- list是被遍历的数组
const app = new Vue({
el: '#app',
data: {
booksList: [
{ id: 1, name: '《红楼梦》', author: '曹雪芹' },
{ id: 2, name: '《西游记》', author: '吴承恩' },
{ id: 3, name: '《水浒传》', author: '施耐庵' },
{ id: 4, name: '《三国演义》', author: '罗贯中' }
]
},
methods: {
del(id) {
this.booksList =this.booksList.filter(item=> {
this.booksList = item.id !== id
})
}
}
})
key属性
当使用 v-for
来渲染一个列表时,Vue 需要一个唯一的 key
值来追踪每个循环项。这有助于 Vue 在更新列表时识别出哪些项被添加、删除或移动了,从而提供更快速的 DOM 更新以及更精准的变化追踪。
<template>
<ul>
<li v-for="(item, index) in items" :key="item.id">
{{ item.name }}
</li>
</ul>
</template>
v-modal
作用:双向数据绑定,快速获取或设置表单元素内容
什么是双向数据绑定?
Vue是一个MV VM框架, 即数据双向绑定, 即当数据发生变化的时候, 视图也就发生变化, 当视图发生变化的时候,数据也会跟着同步变化。这也算是Vue的精髓之处了。
v-model 会根据控件类型自动选取正确的方法来更新元素。
<div id="app">
账户:<input type="text"> <br><br>
密码:<input type="password"> <br><br>
<button>登录</button>
<button>重置</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
username: '',
password: ''
},
})
</script>
复选框的数据绑定
复选框如果是一个为逻辑值
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<p>单个复选框:</p>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
</div>
<script>
new Vue({
el: '#app',
data: {
checked : false,
}
})
</script>
</body>
</html>
如果是多个则绑定到同一个数组,会把选中的选项里面的 value 值,添加到数组中
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<p>多个复选框:</p>
<input type="checkbox" id="runoob" value="苹果" v-model="checkedNames">
<label for="runoob">苹果</label>
<input type="checkbox" id="google" value="香蕉" v-model="checkedNames">
<label for="google">香蕉</label>
<input type="checkbox" id="taobao" value="荔枝" v-model="checkedNames">
<label for="taobao">荔枝</label>
<br>
<span>选择的值为: {{ checkedNames }}</span>
</div>
<script>
new Vue({
el: '#app',
data: {
checkedNames: []
}
})
</script>
</body>
</html>
单选框的数据绑定
<div id="app">
<!-- 1.name: 给单选框加上 name 属性可以分组,同一组互相会互斥 -->
<!-- 2.value: 给单选框加上 value 属性,用于提交给后台的数据 -->
性别:
<input v-model="gender" name="gender" value="male" type="radio">男
<input v-model="gender" name="gender" value="female" type="radio">女
<br><br>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.7.14/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
gender: 'female',
}
})
</script>
下拉菜单的数据绑定
<div id="app">
<!-- 1. option 需要设置 value 值,提交给后台 -->
<!-- 2. select 的 value 值,关联了选中的 option 的 value 值 -->
<!-- 注意: 下拉选择框使用 v-model 要绑定在 select 上 -->
所在城市:
<select v-model="city">
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
</select>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.7.14/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
city: 'beijing',
}
})
</script>
指令修饰符
1.v-model修饰符
.lazy
在默认情况下, v-model 在 input 事件中同步输入框的值与数据,但你可以添加一个修饰符 lazy ,从而转变为在 change 事件中同步:
<!-- 在 "change" 而不是 "input" 事件中更新 -->
<input v-model.lazy="msg" >
.number
如果想自动将用户的输入值转为 Number 类型(如果原值的转换结果为 NaN 则返回原值),可以添加一个修饰符 number 给 v-model 来处理输入值:
<input v-model.number="age" type="number">
.trim
如果要自动过滤用户输入的首尾空格,可以添加 trim 修饰符到 v-model 上过滤输入:
<input v-model.trim="msg">
2.按键修饰符
Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:
<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">
记住所有的 keyCode 比较困难,所以 Vue 为最常用的按键提供了别名:
<!-- 同上 -->
<input v-on:keyup.enter="submit">
<!-- 缩写语法 -->
<input @keyup.enter="submit">
全部的按键别名:
.enter
.tab
.delete
(捕获 "删除" 和 "退格" 键).esc
.space
.up
.down
.left
.right
系统修饰键:
.ctrl
.alt
.shift
.meta
鼠标按钮修饰符:
.left
.right
.middle
3.事件修饰符
Vue 为 v-on 提供了事件修饰符来处理 DOM 事件细节,如:event.preventDefault() 或 event.stopPropagation()。
Vue 通过由点 . 表示的指令后缀来调用修饰符。
.stop
- 阻止冒泡.prevent
- 阻止默认事件.capture
- 阻止捕获.self
- 只监听触发该元素的事件.once
- 只触发一次.left
- 左键事件.right
- 右键事件.middle
- 中间滚轮事件
<!-- 阻止单击事件冒泡 -->
<a @click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form @submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a @click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form @submit.prevent></form>
<!-- 添加事件侦听器时使用事件捕获模式 -->
<div @click.capture="doThis">...</div>
<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div @click.self="doThat">...</div>
<!-- click 事件只能点击一次,2.1.4版本新增 -->
<a @click.once="doThis"></a>
计算属性 computed
计算属性,字如其名,首先它是属性,其次有计算的“功能”
说的官方一点:计算属性就是当其依赖属性的值发生变化时,这个属性的值会自动变化,与之相关的DOM部分也会同步自动更新。
在computed配置项中添加我们的计算属性,在属性里面写我们的逻辑代码
注意:计算属性其实是属性,在页面上使用只需要用属性值,不要带(),不然就变成方法了
<div id="app">
<h3>小黑的礼物清单</h3>
<table>
<tr>
<th>名字</th>
<th>数量</th>
</tr>
<tr v-for="(item, index) in list" :key="item.id">
<td>{{ item.name }}</td>
<td>{{ item.num }}个</td>
</tr>
</table>
<!-- 目标:统计求和,求得礼物总数 -->
<p>礼物总数:{{totalCount}} 个</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
// 现有的数据
list: [
{ id: 1, name: '篮球', num: 1 },
{ id: 2, name: '玩具', num: 2 },
{ id: 3, name: '铅笔', num: 5 },
]
},
computed: {
totalCount() {
return this.list.reduce((sum,item)=>sum+item.num,0)
}
}
})
计算属性computed
和methods
的区别
computed是一个方法,而methods里面也是一个方法,那么他们之间的区别是什么呢?
我们可以使用 methods 来替代 computed,效果上两个都是一样的,但是 computed 是基于它的依赖缓存,只有相关依赖发生改变时才会重新取值。而使用 methods ,在重新渲染的时候,函数总会重新调用执行。
我们可以通过多次调用,打印出来看看结果有什么不一样
<div id="app">
<h3>小黑的礼物清单🛒<span>?</span></h3>
<table>
<tr>
<th>名字</th>
<th>数量</th>
</tr>
<tr v-for="(item, index) in list" :key="item.id">
<td>{{ item.name }}</td>
<td>{{ item.num }}个</td>
</tr>
</table>
<p>礼物总数:{{ getNum() }} 个</p>
<p>礼物总数:{{ getNum() }} 个</p>
<p>礼物总数:{{ getNum() }} 个</p>
<p>礼物总数:{{ getNum() }} 个</p>
<p>礼物总数:{{ getNum() }} 个</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
// 现有的数据
list: [
{ id: 1, name: '篮球', num: 3 },
{ id: 2, name: '玩具', num: 2 },
{ id: 3, name: '铅笔', num: 5 },
]
},
computed: {
totalCount () {
console.log('我是 computed 里的求和属性');
let total = this.list.reduce((sum, item) => sum + item.num, 0)
return total
}
},
methods: {
getNum() {
console.log('我是 methods 里的求和属性');
return this.list.reduce((sum, item) => sum + item.num, 0)
}
}
})
可以清晰地看到,用methods方法数据每更新一次就会被调用一次,而computed方法只被调用了一次
优点:计算属性最重要的特性: 带缓存
在第一次使用了该属性时进行计算,计算完了后他就会把结果存起来,后面有用到会直接在缓存里面把结果取出来
只在相关响应式依赖发生改变(相关的属性变化)时它们才会重新求值
这样做的主要目的也是为了提高性能
计算属性的完整写法
计算属性都包含有一个get和一个set,计算属性会默认使用 get函数,如果你要使用 set函数,那么你必须要手动写出 set 函数
const app = new Vue({
el: '#app',
data: {
firstName: '李',
lastName: '雷',
},
methods: {
change() {
this.fullName = '韩梅梅'
}
},
computed: {
fullName: {
// 当访问这个计算属性时, get 函数会自动执行,并将返回值作为计算属性的值
get() {
return this.firstName + this.lastName
},
// 当修改这个计算属性时, set 函数会自动执行, 并将修改的新值作为参数传递过来
set(val) {
this.firstName = val.substring(0,1)
this.lastName = val.substring(1)
}
}
}
})
watch侦听器
作用:监视数据变化,执行业务逻辑或是异步操作
const app = new Vue({
el: '#app',
data: {
words: ''
},
watch: {
//要监听什么数据,就以什么数据命名,定义函数
// 该函数会在数据变化时自动执行,并携带两个参数
// 参数一:新值
// 参数二:旧值 ,不常用
words(newValue,oldValue) {
console.log(value)
}
}
})
完整写法:
deep: true
对复杂类型深度监视immediate: true
初始化立刻执行一次handler方法
watch: {// watch 完整写法
数据属性名: {
deep: true, // 深度监视(针对复杂类型)
immediate: true, // 是否立刻执行一次handler
handler (newValue) {
console.log(newValue)
}
}
}
生命周期
一个Vue实例从 创建 到 销毁 的整个过程
vue的生命周期分为四个阶段:
①创建 ②挂载 ③更新 ④销毁
Vue生命周期过程中,会自动运行一些函数,被称为【生命周期钩子】
这个时候我们就可以在【特定阶段】运行自己的代码
- 实例、组件通过new Vue() 创建出来之后会初始化事件和生命周期,然后就会执行beforeCreate钩子函数,这个时候,数据还没有挂载,只是一个空壳,无法访问到数据和真实的dom,一般不做操作
- 挂载数据,绑定事件等等,然后执行created函数,这个时候已经可以使用到数据,也可以更改数据,在这里更改数据不会触发updated函数,在这里可以在渲染前倒数第二次更改数据的机会,不会触发其他的钩子函数,一般可以在这里做初始数据的获取
- 接下来开始找实例或者组件对应的模板,编译模板为虚拟dom放入到render函数中准备渲染,然后执行beforeMount钩子函数,在这个函数中虚拟dom已经创建完成,马上就要渲染,在这里也可以更改数据,不会触发updated,在这里可以在渲染前最后一次更改数据的机会,不会触发其他的钩子函数,一般可以在这里做初始数据的获取
- 接下来开始render,渲染出真实dom,然后执行mounted钩子函数,此时,组件已经出现在页面中,数据、真实dom都已经处理好了,事件都已经挂载好了,可以在这里操作真实dom等事情...
- 当组件或实例的数据更改之后,会立即执行beforeUpdate,然后vue的虚拟dom机制会重新构建虚拟dom与上一次的虚拟dom树利用diff算法进行对比之后重新渲染,一般不做什么事儿
- 当更新完成后,执行updated,数据已经更改完成,dom也重新render完成,可以操作更新后的虚拟dom
- 当经过某种途径调用$destroy方法后,立即执行beforeDestroy,一般在这里做一些善后工作,例如清除计时器、清除非指令绑定的事件等等
- 组件的数据绑定、监听...去掉后只剩下dom空壳,这个时候,执行destroyed,在这里做善后工作也可以
生命周期图示
生命周期钩子 | 实例阶段 | 说明 | 能否获取到data | 能否获取到 methods |
---|---|---|---|---|
beforeCreate | 创建前 | 实例已初始化,但数据观测,watch/event 事件回调还未配置 | NO | NO |
created | 创建后 | 已完成如下配置,数据观测 (data observer),property 和方法的运算,watch/event 事件回调 | NO | OK |
beforeMount | 挂载前 | dom已初始化,但并未挂载和渲染 | OK | OK |
mounted | 挂载后 | dom已完成挂载和渲染 | OK | OK |
beforeUpdate | 更新前 | 数据已改变,但dom为更新 | OK | OK |
updated | 更新后 | dom已更新 | OK | OK |
beforeDestroy | 销毁前 | 实例销毁前,实例仍然可用 | OK | OK |
destroyed | 销毁后 | 实例已销毁,所有指令被解绑,事件监听器被移除,子实例都被销毁 | OK | OK |