定义
Vue是一个用来构建用户界面的渐进式框架
创建Vue实例 初始化渲染步骤:
- 准备容器(Vue管理的范围)
- 引包(官网)——开发版本/生产版本
- 创建实例
- 指定配置项——渲染数据
- el指定挂裁点
- data 提供数据
<div id="app">
//将来会编写一些用来渲染的代码逻辑
{{msg}}
</div>
//引入开发版本的包-包含完整的注释和警告
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
<script>
//一旦引入VueJS核心包,在全局环境就有了Vue构造函数
const app = new Vue({
//通过el去配置选择器,指定Vue管理的是哪个盒子
el'#app',
//通过data提供数据
data:{
msg:'Hello 黑马'
}
</script>
vue目录
nose_modules 是通过npm instal 来安装
文本插值
最基本的数据绑定形式就是文本插值,他使用的是"Mustache"语法(即大括号)
<template>
<p>{{msg}}</p>
</template>
<script>
export default {
data(){
return{
msg:"神奇的魔法"
}
}
}
</script>
使用JavaScript表达式
每个绑定仅支持 单一表达式 ,也就是一段能求值的javaScript代码。
简单判断方法:是否可以合法的写在return后面
插值表达式
- 作用:利用表达式进行插值,渲染到页面中
- 语法:
{{表达式}}
<h3>{{title}}<h3>
<p>{{nickName.toUpperCase()}}</p>
<p>{{age >= 18 ? '成年':'未成年'}}</p>
<p>{{obj.name}}</p>
<p>{{fn()}}</p>
注意:
- 使用的数据要存在
- 支持的是表达式,不是语句 如if,for
- 不能在标签属性中使用{{}}
- 不具备解析标签的能力
Vue 核心特性:响应式
数据改变,视图自动更新
访问和修改数据:
访问数据: 实例.属性名
修改数据:实例.属性名="值"
Vue指令
指令:带有v-前缀的特殊标签属性
Vue 中的指令按照不同的用途可以分为如下 6 大类:
- 内容渲染指令(v-html、v-text)
- 条件渲染指令(v-show、v-if、v-else、v-else-if)
- 事件绑定指令(v-on)
- 属性绑定指令 (v-bind)
- 双向绑定指令(v-model)
- 列表渲染指令(v-for)
v-show VS v-if
v-show
- 作用:控制元素显示隐藏
- 语法:v-show="表达式" 表达式值 true显示,false 隐藏
- 场景:频繁切换隐藏场景
- 原理:切换
display:none来控制隐藏
v-if
- 作用:控制元素显示隐藏(条件渲染)
- 语法:v-if="表达式" 表达式值 true显示,false 隐藏
- 场景:要么显示,要么隐藏,不频繁切换的场景
- 原理:基于判断条件 控制元素的创建和移除
<div id="app">
<div class="box">我是v-show控制的盒子</div>
<div class="box">我是v-if控制的盒子</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
flag: false
}
})
</script>
区别:
-
v-show 元素无论初始条件如何始终都会被渲染 底层原理:切换CSS中的display:none来控制隐藏(简单隐藏)
-
v-show有较高的初始渲染开销
-
v-if 有较高的切换开销
-
v-if 底层原理:根据判断条件 控制元素的创建和移除(条件渲染) 只有为true时才会渲染
v-else v-else-if
-
作用:辅助v-if进行判断渲染
-
语法:v-else v-else-if = "表达式"
-
注意:需要紧挨着v-if一起使用
<div id="app">
<p>性别:♂ 男</p>
<p>性别:♀ 女</p>
<hr>
<p>成绩评定A:奖励电脑一台</p>
<p>成绩评定B:奖励周末郊游</p>
<p>成绩评定C:奖励零食礼包</p>
<p>成绩评定D:惩罚一周不能玩手机</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
gender: 2,
score: 95
}
})
</script>
v-on(内联事件处理器)
- 作用:注册事件 = 添加监听+提供处理逻辑
- 语法:
- v-on:事件名 ="内联语句"
- v-on:事件名= "methods中的函数名"
<div id="app">
<button @click="count--">-</button>
<span>{{ count }}</span>
<button v-on:click="count++">+</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
count: 100
}
})
</script>
v-on:事件名 可替换为@事件名
注:data用来提供数据,methods用来提供方法
<div id="app">
<button @click = "fn">切换显示隐藏</button>
<h1 v-show ="isShow">黑马程序员</h1>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
<script>
const app = new Vue({
el:'#app',
data:{
isShow:true
},
methods:{
fn(){
// 让提供的所有methods中的函数,this指向当前案例
// console.log('执行力fn')
// console.log(app3 === this)
this.isShow = !this.isShow
}
}
})
</script>
注:
-
内联语句可以直接访问到数据,函数不可直接拿数据
-
可通过methods,methods函数内的this指向Vue实例
-
methods与datas 同级
v-on 调用传参
<button @click="fn(参数1,参数2)">按钮</button>
示例:小黑自动售货机
.box {
border: 3px solid black;
border-radius: 10px;
padding: 20px;
margin: 20px;
width: 200px;
}
h3 {
margin: 10px 0 20px 0;
}
p {
margin: 20px;
}
<div id="app">
<div class="box">
<h3>小黑自动售货机</h3>
<button @click="buy(5)">可乐5元</button>
<button @click="buy(10)">咖啡10元</button>
<button @click="buy(8)">牛奶8元</button>
</div>
<p>银行卡余额:{{ money }}元</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
money: 100
},
methods:{
buy(price){
console.log(price)
this.money -= price
}
}
})
</script>
效果如下:
v-bind
- 作用:动态设置的html的标签属性 src url title
- 语法:v-bind:属性名= "表达式"
<img v-bind:src="url" /><img :src="url" />(v-bind可以省略)
v-bind指令知识Vue将元素的idattribute 与组建的dynamicid属性保持一致,如果绑定的的是null或者undefined,那么改attribute 将会从渲染的元素上移除
<div id="app">
<img v-bind:src="imgUrl" v-bind:title="msg" alt="">
<img :src="imgUrl" :title="msg" alt="">
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
imgUrl: './imgs/10-02.png',
msg: 'hello 波仔'
}
})
</script>
示例:波仔的学习之旅
要求:默认展示数组中的第一张图片,点击上一页下一页来回切换数组中的图片
实现思路:
1.数组存储图片路径 ['url1','url2','url3',...]
2.可以准备个下标index 去数组中取图片地址。
3.通过v-bind给src绑定当前的图片地址
4.点击上一页下一页只需要修改下标的值即可
5.当展示第一张的时候,上一页按钮应该隐藏。展示最后一张的时候,下一页按钮应该隐藏
<div id="app">
<button v-show"index >0" @click="index--">上一页</button>
<div>
<img src ="list[index]"alt="">
</div>
<button v-show <list.length -1 @click="index++" >下一页</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
index:0,
list: [
'./imgs/11-00.gif',
'./imgs/11-01.gif',
'./imgs/11-02.gif',
'./imgs/11-03.gif',
]
}
})
</script>
v-for
-
作用:基于数据循环,多次渲染整个元素
-
可遍历:数组,对象,数字……
-
语法:
v-for="(item,index) in 数组"- item 每一项,index 下标
效果图如下:
示例:
<div id="app">
<h3>小黑水果店</h3>
<ul>
<li v-for="item in list">
{{item}}
</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
list:['西瓜','苹果','椰子']
}
})
</script>
省略index : v-for="item in 数组"
效果如下:
示例:小黑的书架
核心:列表渲染和删除功能
<div id="app">
<h3>小黑的书架</h3>
<ul>
<li v-for="(item,index) in booklist" :key="item.list">
<span>{{item.name}}</span>
<span>{{item.author}}</span>
<button @click ="del(item.id)">删除</button>
</li>
<!-- 注册点击事件 通过id进行删除数组中的对应项 -->
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
booklist:[
{id:1,name:'《红楼梦》',author:'曹雪芹'},
{id:2,name:'《西游记》',author:'吴承恩'},
{id:3,name:'《水浒传》',author:'施耐庵'},
{id:4,name:'《三国演义》',author:'罗贯中'},
]
},
methods:{
del(id){
// console.log('删除',id)
// 通过id 进行删除数组中的对应项——filter(不改变原数组)
//filter:根据条件,保留满足条件的对应项,得到一个新数组
// console.log(this.booklist.filter (item => item.id!==id))
this.booklist= this.booklist.filter (item => item.id!==id)
}
}
})
</script>
效果如下: 通过删除按钮可以将这一行内容删除
v-for中的key
- 作用:给列表项添加唯一的标识,便于Vue进行列表项的正确排序复用
- 语法:
key属性 = "唯一的标识"
加key时:给元素添加了唯一标识,点击删除时 li中所有内容全部删除
未加key时:
- v-for的默认行为会尝试 原地修改元素(就地复用)
- 不加key则没有唯一标识,当删除时,会保留前三个而删除最后一个
key注意:
- key的值只能是字符串或数字类型
- key的值必须具有 唯一性
- key绑定的值期望是一个基础类型的值,如 字符串或number类型
- 推荐使用 id 作为key(唯一),不推荐使用index 作为key(会变化,不对应)
v-model
-
作用: 给 表单元素(input、radio、select)使用,双向绑定数据,可以快速 获取 或 设置 表单元素内容
-
语法:
v-model="变量" -
v-model可以让数据和视图,形成双向数据绑定
- 数据变化,视图自动更新
- 视图变化,数据自动更新
- 可以快速获取或设置表单元素的内容
示例:
需求:使用双向绑定实现以下需求
- 点击登录按钮获取表单中的内容
- 点击重置按钮清空表单中的内容
<div id="app">
账户:<input type="text"> <br><br>
密码:<input type="password"> <br><br>
<button @click ="login">登录</button>
<button @click ="reset">重置</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: ''
},
methods:{
login(){
console.log(this.username,this.password)
},
reset(){
this.username = ''
this.password = ''
}
}
})
</script>
效果如下:
指令修饰符
通过"." 指明后缀最,不同后缀封装了不同的处理操作——简化代码
1. 按键修饰符
- @keyup.enter 键盘回车监听
2. v-model 修饰符
- v-model.trim 去除首尾空格
- v-model.number 转数字
3. 事件修饰符
- @事件名.stop 阻止冒泡
- @事件名.prevent 阻止默认行为
- 在处理事件时调用
event.prenventDfault()或event.stopPropagation()很常见
4.v-bind对于样式控制的增强
(1) 操作class
- 语法:
class="对象/数组"
对象:键就是类名,值是布尔值 如果为true 则有这个类 否则没有这个类
- 适用场景:一个类名,来回切换
<div class="box":class="{类名1:布尔值,类名2:布尔值}">
</div>
数组:数组中所有的类都会添加到盒子上,本质就是一个class列表
- 适用场景:批量添加或删除类
数组和对象嵌套过程中,只能是数组嵌套对象。不能相反,中括号里嵌套大括号
<div class="box":class="[类名1,类名2,类名3]">
</div>
示例:
.box{
width:200px;
height:200px;
border:1px solid black;
font-size: 30px;
margin-top: 10px;
}
.pink{
background-color: pink;
}
.big{
width:300px;
height: 300px;
}
<div id="app">
<div class="box":class="{pink:true,big:true}">黑马程序员</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
}
})
</script>
效果如下:
此时pink与big都为true
案例:京东秒杀tab导航高亮
核心思路:
- 基于数据动态渲染tab v-for
- 准备下标记录高亮是哪一个tab activeindex
- 基于下标,动态控制class类名 v-bind;class
* {
margin: 0;
padding: 0;
}
ul {
display: flex;
border-bottom: 2px solid #e01222;
padding: 0 10px;
}
li {
width: 100px;
height: 50px;
line-height: 50px;
list-style: none;
text-align: center;
}
li a {
display: block;
text-decoration: none;
font-weight: bold;
color: #333333;
}
li a.active {
background-color: #e01222;
color: #fff;
}
<div id="app">
<ul>
<li v-for="{item,index} in list":key="item.id"@click="activeindex">
<a:class="{active:index === activeindex}"href="#">{{item.name}}</a>
</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
activeindex:0,
// 记录高亮
list:[
{ id:1,name:'京东秒杀'},
{ id:2,name:'每日特价'},
{ id:3,name:'品类秒杀'}
]
}
})
</script>
(2) 操作style
- 语法:
:style="样式对象"
<div class="box" :style="{CSS属性名1:CSS属性值,CSS属性名2:CSS属性值}">
</div>
示例:
<style>
.box {
width: 200px;
height: 200px;
background-color: rgb(187, 150, 156);
}
</style>
<div id="app">
<div class="box"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
}
})
</script>
示例:进度条
<style>
.progress {
height: 25px;
width: 400px;
border-radius: 15px;
background-color: #272425;
border: 3px solid #272425;
box-sizing: border-box;
margin-bottom: 30px;
}
.inner {
width: 50%;
height: 20px;
border-radius: 10px;
text-align: right;
position: relative;
background-color: #409eff;
background-size: 20px 20px;
box-sizing: border-box;
transition: all 1s;
}
.inner span {
position: absolute;
right: -20px;
bottom: -25px;
}
</style>
<div id="app">
<!-- 外层盒子底色 (黑色) -->
<div class="progress">
<!-- 内层盒子 进度(蓝色) -->
<div class="inner":style="{width:percent + '%'}">
<span>{{percent}}%</span>
</div>
</div>
<button @click="percent = 25">设置25%</button>
<button @click="percent = 50>设置50%</button>
<button @click="percent = 75>设置75%</button>
<button @click="percent = 100>设置100%</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
percent:30
}
})
</script>
style绑定的权重很高 ,后期修改麻烦 建议使用class
v-model应用于其他表单元素
-
作用:快速获取或设置表单元素的值
-
会根据控件类型自动灭火选取正确的方法来更新元素
示例:
<style>
*{
margin:0;
padding: 0;
}
ul{
display: flex;
background-color: red;
}
li{
list-style: none;
color: white;
width: 100px;
height:50px;
line-height: 50px;
text-align: center;
}
</style>
<body>
<div id="app">
<h3>小黑学习网</h3>
姓名:
<input type="text" v-model="useername">
<br><br>
是否单身:
<input type="checkbox" v-model="isSingle">
<br><br>
<!-- 前置理解:
1. name: 给单选框加上 name 属性 可以分组 → 同一组互相会互斥
2. value: 给单选框加上 value 属性,用于提交给后台的数据
结合 Vue 使用 → v-model
-->
性别:
<input v-model="gender"type="radio"name="gender" value="1">男
<input v-model="gender"type="radio"name="gender" value="2">女
<br><br>
<!--
前置理解:
1. option 需要设置 value 值,提交给后台
2. select 的 value 值,关联了选中的 option 的 value 值
结合 Vue 使用 → v-model
-->
所在城市:
<select v-model="cityId">
<option value="101">北京</option>
<option value="102">上海</option>
<option value="103">成都</option>
<option value="104">南京</option>
</select>
<br><br>
自我描述:
<textarea v-model="desc"></textarea>
<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:'',
isSingle:false,
gender:"1",
cityId:'102',
desc:""
}
})
</script>
效果如下:
数组变化侦测
1.变更方法
变更方法:会对调用他们的原数组进行变更
2.替换一个数组
有一些不可变的方法,如 filter() ,concat() 和slice(),这些都不会更改原数组而总是返回一个新数组。当遇到非变更方法时,我们需要将旧的数组替换为新的
计算属性
- 基于现有数据,计算出新属性,依赖的数据变化会自动重新计算
- 语法
- 声明在computed配置项中,一个计算属性对应一个函数
- 使用起来和普通属性一样使用{{计算属性名}}
<style>
table {
border: 1px solid #000;
text-align: center;
width: 240px;
}
th,td {
border: 1px solid #000;
}
h3 {
position: relative;
}
</style>
</style>
<body>
<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(){
let total = this.list.reduce((sum,item) => sum+item.num,0)
return total
}
}
})
</script>
效果如下:
computed计算属性 VS methods方法
computed计算属性
作用:封装了一段对于 数据 的处理,求得一个结果
语法:
-
写在computed配置项中
-
作为属性,直接使用
- js中使用计算属性: this.计算属性
- 模板中使用计算属性:{{计算属性}}
计算属性有缓存,一旦计算出结果就会立刻缓存 下一次读取-直接读缓存-性能高
methods计算属性
作用:给Vue实例提供一个 方法,调用以处理业务逻辑。
语法:
- 写在methods配置项中
- 作为方法调用
- js中调用:this.方法名()
- 模板中调用 {{方法名()}} 或者 @事件名=“方法名”
注意:
- computed配置项和data配置项是同级的
- computed中的计算属性虽然是函数的写法,但他依然是个属性
- computed中的计算属性不能和data中的属性同名
- 使用computed中的计算属性和使用data中的属性是一样的用法
- computed中计算属性内部的this依然指向的是Vue实例
重点区别:
-
计算属性值会基于其他响应式依赖被缓存,一个计算属性仅会在其响应式以来更新时才重新计算 多次调用的值是一次计算的
-
方法调用总是会在重渲染发生时再次执行函数 调用四次,计算属性也计算四次
-
复杂的逻辑使用计算属性更好