Vue基础
1. Vue版本
二. 第一个Vue示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue实例</title>
</head>
<body>
<div id="app">
{{ msg }} <!-- 此处为用于渲染的代码 -->
</div>
<!-- 引入开发版本包:在全局环境就有了Vue构造函数 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el : '#app', // 通过el配置选择器,指定Vue管理的是哪个盒子
data : {
msg : "hello, Vue" // 通过data提供数据
}
})
</script>
</body>
</html>
3. 插值表达式
插值表达式是一种Vue的模板语法(表达式是可以被求值的代码,JS引擎会将其计算出一个结果)
作用:利用表达式进行插值,渲染到页面中
语法:{{表达式}}
注意点:
- 使用的数据必须在data内存在
- 支持的是表达式,而不是语句,如if, for…
- 不能在标签属性中使用{{ }}插值(如**<p title=“{{11}}”>这是p标签</p>**)
4. 响应式特性
响应式:数据改变,视图自动更新
修改或访问数据:
- 访问:实例.属性名
- 修改:实例.属性名 = 值
5. Vue指令
5.1 v-html
作用: 设置元素的innerHTML 语法:v-html=“表达式”
示例:
<body>
<div id="app" v-html="url"> <!-- 更新标签的innerHTML -->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el : '#app',
data : {
url : '<a href="https://www.jd.com/">京东官网</a>'
}
})
</script>
</body>
5.2 v-show和v-if
指令名 | v-show | v-if |
---|---|---|
作用 | 控制元素显示隐藏 | 控制元素显示隐藏(条件渲染) |
语法(表达式值为true 显示,false 隐藏) | v-show=“表达式” | v-if=“表达式” |
区别 | 切换display:none控制显示隐藏 | 基于条件判断,是否创建或移除元素节点 |
场景 | 频繁切换显示/隐藏的地方 | 显示或隐藏,不频繁切换 |
示例:
<body>
<div id="app">
<div class="box" v-show="flag">v-show控制的盒子</div>
<div class="box" v-if="flag">v-if控制的盒子</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el : '#app', // 通过el配置选择器,指定Vue管理的是哪个盒子
data : {
flag : false
}
})
</script>
</body>
5.3 v-else和v-else-if
作用: 辅助v-if进行判断渲染(必须要与v-if一起使用)
语法: v-else/v-else-if=“表达式”
5.4 v-on
作用: 注册事件 = 添加监听 + 提供处理逻辑
语法( v-on:可以简写为@):
- v-on:事件名 = “内联语句”
- v-on:事件名 = “methods中的函数名”
v-on:事件名 = "内联语句"示例:
<body>
<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', // 通过el配置选择器,指定Vue管理的是哪个盒子
data : {
count : 100
}
})
</script>
</body>
v-on:事件名 = "methods中的函数名"示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-on</title>
<style>
.box{
width: 300px;
height: 200px;
line-height: 50px;
border: 3px solid black;
border-radius: 10px;
}
button{
width: 100px;
height: 30px;
border: 1px solid black;
border-radius: 5px;
margin-left: 30px;
}
.title{
text-align: center;
}
</style>
</head>
<body>
<div id="app">
<div class="box">
<p class="title">饮料自动贩卖机</p>
<button @click="buy(3)">可乐3元</button> <!-- 内联语句 -->
<button @click="buy(6)">咖啡6元</button>
</div>
<p>余额{{count}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el : '#app', // 通过el配置选择器,指定Vue管理的是哪个盒子
data : {
count : 100
},
methods : {
buy(price){
this.count -= price
}
}
})
</script>
</body>
</html>
5.5 v-bind
作用: 动态设置html的标签属性(如src,url,title等)
语法:v-bind:属性名=“表达式”(简写::属性名=“表达式”)
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-bind</title>
</head>
<body>
<div id="app">
<img :src="imgUrl" alt="">
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
const app = new Vue({
el : '#app', // 通过el配置选择器,指定Vue管理的是哪个盒子
data : {
count : 100,
imgUrl : '../imgs/10-01.png'
},
})
</script>
</body>
</html>
5.6 v-for
作用: 基于数据循环,多次渲染整个元素(数据:数组、对象、数字等)
语法: v-for=“(item, index) in 数据” 其中item为每一项的具体值,index为下标
v-for中的key:给元素添加唯一的标识(不加keyv-for的默认行为会尝试原地修改元素)
- key只能是字符串或者数字类型
- key的值必须具有唯一性
- 一般使用id作为key
示例:
<body>
<div id="app">
<h3>小黑的书架</h3>
<ul>
<li v-for="item in booksList" :key="item.id">
<span>{{item.name}}</span>
<span>{{item.author}}</span>
<button @click="del(item.id)">删除</button>
</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>
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 => {return item.id !== id}) // filter会得到一个新数组
}
}
})
</script>
</body>
5.7 v-model
作用: 给表单使用,双向数据绑定(快速获取或设置表单元素内容)
双向数据绑定
- 数据变化—>视图自动更新
- 视图变化—>数据自动更新
语法: v-model=‘变量’
示例:
<body>
<div id="app">
账号: <input type="text" v-model="username"> <br><br>
密码: <input type="password" v-model="password"> <br><br>
<button @click="login">登录</button>
<button @click="reset">重置</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 : {
username : '',
password : ''
},
methods : {
login(){
console.log(this.username, this.password);
},
reset(){
this.username = '',
this.password = ''
}
}
})
</script>
</body>
6. 指令修饰符
通过 “.” 指明一些指令后缀,不同后缀封装了不同的操作(这样写可以简化代码)
- 按键修饰符
- @keyup.enter 键盘回车监听
- v-model修饰符
- v-model.trim 去除首位空格
- v-model.number 转数字
- 事件修饰符
- @事件名.stop 阻止冒泡
- @事件名.prevent 阻止默认行为
7. v-bind对于样式控制的增强
7.1 操作class
语法: v-bind:class=“对象/数组” 或 :class=“对象/数组”
- 对象—>键就是类名,值是布尔值。若值为true,就有这个类,否则没有这个类(频繁切换类的场景)
<div class="box" :class="{类名1:布尔值, 类名2:布尔值}"></div>
- 数组—>数组中所有的类,都会添加到盒子上,本质就是一个class列表(批量添加,删除类的场景)
<!-- 类名须为字符串 -->
<div class="box" :class="[类名1, 类名2]"></div>
7.2 操作style
语法: v-bind:style=“样式对象” 或 :style=“样式对象”
<!-- CSS属性值须为字符串 -->
<div class="box" :style="{CSS属性名1:CSS属性值, CSS属性名2:CSS属性值}"></div>
8.v-model应用于其他表单
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-model应用到其他表单元素</title>
</head>
<body>
<div id="app">
用户名:<input type="text" v-model="username">
<br><br>
是否单身:<input type="checkbox" v-model="isSingle">
<br><br>
性别:
<!-- 给单选框加上name属性,可以分组,且同一组会相互互斥
给单选框加上value属性,用于提交数据给后台
-->
<input type="radio" name="gender" value="1" v-model="gender">男
<input type="radio" name="gender" value="2" v-model="gender">女
<br><br>
<!-- option需要设置value值,提交给后台
select的value值关联了选中的option的值 -->
所在城市:
<select v-model="cityID">
<option value="101">北京</option>
<option value="102">上海</option>
<option value="103">重庆</option>
<option value="104">广州</option>
</select>
<br><br>
个人简介:
<br>
<textarea v-model="desc"></textarea>
<button>立即注册</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: {
username : '',
isSingle : true,
gender : '1',
cityID : '101',
desc : '我是小蓝'
}
})
</script>
</body>
</html>
9. 计算属性
9.1 什么是计算属性
定义:基于现有的数据,计算出来新的属性。 依赖的数据变化,自动重新计算 \color{red}依赖的数据变化,自动重新计算 依赖的数据变化,自动重新计算。
语法:
声明在computed配置项中,一个计算属性对应一个函数
使用起来和普通属性一样使用 {{ 计算属性名 \color{red}计算属性名 计算属性名}}
computed : {
计算属性名(){
基于现有数据,编写求值逻辑
return 结果
}
}
9.2 computed 计算属性 vs methods方法
computed | methods | |
---|---|---|
作用 | 封装了一段处理数据的函数,返回一个结果 | 给实例提供一个方法,用以处理业务逻辑 |
语法 | 1、写在computed配置项中 2、作为属性,直接使用(this.计算属性 {{计算属性}}) | 1、写在methods配置项中 2、作为方法,需要调用(this.方法名 {{方法名}} @事件名=‘方法名’) |
备注 | 计算属性具有缓存特性(对计算出的结果缓存,再次使用时直接读取) | 方法不具备缓存特性,调用一次,执行一次 |
9.3 计算属性的完整写法
computed : {
计算属性名 : {
get(){
一段逻辑代码(计算逻辑)
reutrn 结果
}
set(修改的值){
一段代码逻辑(修改逻辑)
}
}
}
10. watch 侦听器(监听器)
作用: 监听数据变化,执行一些业务逻辑或异步操作 \color{red}监听数据变化,执行一些业务逻辑或异步操作 监听数据变化,执行一些业务逻辑或异步操作。
10.1 简单写法
场景:简单数据类型,直接监听
data : {
words : 'apple',
obj : {
words : 'apple'
}
},
watch : {
数据属性名 (newValue, oldValue){
一些业务逻辑 或 异步操作
}
'对象.属性名' (newValue, oldValue){
一些业务逻辑 或 异步操作
}
}
10.2 完整写法
场景:添加额外配置项
-
deep : true 对复杂类型深度监视
-
immediate : true 初始化立刻执行一次handler方法
data : {
obj : {
words : 'apple',
lang : 'italy'
},
},
watch : {
数据属性名(obj) : { // 数据属性名
deep : true, // 深度监视
immediate : true, // 立刻执行,页面打开即执行一次handler方法
handler(newValue){
一些业务逻辑 或 异步操作
}
}
}
11. Vue的生命周期
Vue四个生命周期: 创建(create)、挂载(mount)、更新(update)、销毁(destroy)
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue四个生命周期</title>
</head>
<body>
<div id="app">
<h3>{{ title }}</h3>
<div>
<button @click="count--">-</button>
<span>{{ count }}</span>
<button @click="count++">+</button>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
count: 100,
title: '计数器'
},
beforeCreate(){
console.log('创建数据之前');
},
created(){
console.log('创建数据之后');
},
beforeMount(){
console.log('模板渲染之前');
},
mounted(){
console.log('模板渲染之后');
},
beforeUpdate(){
console.log('数据更新之前');
},
updated(){
console.log('数据更新之后');
},
beforeDestroy(){
console.log('销毁实例之前');
},
destroy(){
console.log('销毁实例之后');
}
})
</script>
</body>
</html>
12. 工程化开发
12.1 脚手架Vue CLI搭建过程
- 全局安装(新设备安装一次即可): yarn global add @vue/cli 或 npm i @vue/cli -g
- 查看Vue版本: vue --version
- 创建项目架子: vue create project-name (项目名–不能是中文)
- 启动项目:yarn server 或 **npm run server **(找package.json)
12.2 脚手架文件目录
12.3 组件化&根组件
组件化: 一个页面可以拆分成一个个组件,每个组件有着自己独立的结构、样式、行为。(优点:便于维护、复用-----提升开发效率)
根组件: 整个应用最上层的组件,包裹所有其他组件。
12.4 普通组件的注册
局部注册: 只能在注册的组件内使用
- 在components文件夹中创建.vue文件
- 在使用的组件内导入并注册
- 示例:
<script>
import YouHeader from './components/YouHeader.vue';
export default {
name: 'App',
components: {
YouHeader,
}
}
</script>
使用:当成html标签使用 ‘<组件名></组件名>’
note:组件名通常采用大驼峰命名(如:YouHeader)
全局注册: 所有组件内都能使用
- 在components文件夹中创建.vue文件
- 然后在main.js中进行全局注册
- 示例:
import YouButton from './components/YouButton.vue'
// 调用 Vue.component 进行全局注册
Vue.component('YouButton', YouButton)
13. 解决组件间的样式冲突----scoped
默认情况下,写在组件中的样式会全局生效,因此很容易造成多个组件之间样式的冲突
- 全局样式: 默认组件中的样式会作用到全局
- 局部样式: 给组件的style加上scoped属性,可以让样式只作用于当前组件
scoped原理:
- 当前组件内的标签都被添加上 data-v-hash值 的属性
- CSS选择器都被添加上[ data-v-hash值] 的属性选择器
- 因此必须是当前组件的元素,才会有这个自定义属性,才会被这个样式用到
14. 组件内的数据
一个组件的data选项必须是一个函数,这样的设计保证了每个组件实例维护独立的一份数据对象。
// 示例
export default {
data() {
return {
count: 100,
}
},
}
14.1 组件内通信方案
14.1.1 父子关系
一、通过 props和$emit 进行通信
- 父组件通过props将数据传递给子组件
- 子组件利用$emit通知父组件,进行数据更新
子组件代码:
<template>
<div class="son" style="border:3px solid #000;margin:10px">
<!-- 3.直接使用props的值 -->
我是Son组件 {{ title }}
<button @click="changeTitle">修改</button>
</div>
</template>
<script>
export default {
name: 'Son-Child',
// 2.通过props来接受
props : ['title'],
methods : {
changeTitle(){
this.$emit('changeT', '学JAVA更有趣')
}
}
}
</script>
<style>
</style>
父组件代码:
<template>
<div class="app" style="border: 3px solid #000; margin: 10px">
我是APP组件
<!-- 1.给组件标签,添加属性方式 赋值 -->
<Son :title="myTitle" @changeT="updateTitle"></Son>
</div>
</template>
<script>
import Son from './components/Son.vue'
export default {
name: 'App',
data(){
return {
myTitle : '前端真有趣'
}
},
components: {
Son,
},
methods : {
updateTitle(newTitle){
this.myTitle = newTitle
}
}
}
</script>
<style>
</style>
二、什么是 prop?
定义: 组件上注册的一些自定义属性
作用: 向子组件传递数据
特点: 可以传递 任意数量和任意类型 \color{red}任意数量和任意类型 任意数量和任意类型的prop
三、props校验
为组件的prop指定校验要求,不符合要求,控制台就会有错误提示
// 只校验类型格式
props : {校验属性名 : 类型} // Number,String,Boolean ...
// 完整校验格式
props : {
校验属性名 : {
type : 类型, // Number,String,Boolean ...
required : true, // 是否必填
default : 默认值, // 默认值
validator(value){
// 自定义校验逻辑
return 是否通过校验(true or false)
}
}
}
四、prop&data、单向数据流
共同点: prop&data都可以给组件提供数据流
区别:
- data的数据是自身的,可以自己修改
- prop的数据是外部的,不能直接修改,要遵循单向数据流
单向数据流: 父级的prop数据更新,会向下流动,影响子组件
14.1.2 非父子关系
(1) provide & inject
作用:用于跨层级之间的数据共享
- 父组件使用 provide 提供数据
export default {
provide(){
return {
userName : this.name, // 普通类型数据,是非响应式的
userInfo : {age : this.age, hobby : 'basketball'} // 复杂类型数据,是响应式的
}
}
}
- 子/孙组件使用 inject 取值使用
export default {
inject : ['userName','userInfo']
}
(2) event bus
作用:非父子组件之间,进行简易消息传递
创建一个都能访问到的事件总线(空 Vue 实例)----utils/EventBus.js
import Vue from 'vue'
const Bus = new Vue()
export default Bus
A 组件(接收方),监听 Bus 实例 的事件
import Bus from '../utils/EventBus.js'
created(){
Bus.$on('sendMsg', (msg) => {
this.msg = msg
})
}
B 组件(发送方),触发 Bus 实例 的事件
import Bus from '../utils/EventBus.js'
Bus.$emit('sendMsg', '这是B组价发送的信息')
通用方案:Vuex(适合复杂业务场景)
15 V-model详解
v-model本质上是一个语法糖,用于提供数据的双向绑定。如应用在输入框上,就是 value属性 和 input事件 的合写
- 数据变,视图跟着变 :value
- 视图变,数据跟着变 @input
<!-- 如下两个标签的功能一样 -->
<input type="text" v-model="msg">
<input type="text" :value="msg" @input="msg = $event.target.value">
note: $event 用于在模板中,获取事件的形参
1、封装表单类基础组件(实现子组件与父组件之间数据的双向绑定)
- 父传子: 父组件动态传递prop数据,拆解v-model,绑定数据
- 子传父: 监听输入,子组件传值给父组件修改
2、v-model简化代码步骤
- 子组件中: props 通过 value 接收,事件触发input
- 父组件: v-model 给组件直接绑数据
示例:
<!-- 父组件 -->
<template>
<div id="app">
<SonCom v-model="CityId"></SonCom>
</div>
</template>
<script>
import SonCom from './components/SonCom.vue'
export default {
components: {
SonCom
},
data () {
return {
CityId : '102'
}
},
}
</script>
<style>
</style>
<!-- 子组件 -->
<template>
<select :value="value" @change="handleC">
<option value="101">北京</option>
<option value="102">上海</option>
<option value="103">广州</option>
<option value="104">深圳</option>
</select>
</template>
<script>
export default {
name: 'SonCom',
props : {value : String},
components: {},
methods : {
handleC(e) {
this.$emit('input', e.target.value)
}
}
}
</script>
<style scoped>
</style>
16. 其他知识点补充
16.1 .sync修饰符
作用: 可以实现子组件与父组件的双向数据绑定,简化代码
特点: 可以自定义prop属性名,不用固定为value
使用场景: 封装弹框类的基础组件:visible属性—true显示 ,false隐藏
本质: 属性名 和 @update:属性名的合写
note:子组件向父组件传值时必须使用如下格式
this.$emit('update:属性名',false)
16.2 ref 和 refs
作用:用于获取 DOM元素 或 组件实例
特点:查找范围只限于 当前组件内
获取DOM元素:
-
给目标标签添加ref属性
<div ref="chartRef"> 渲染图表的容器 </div>
-
恰当时机,通过this.$refs.xxx,获取目标标签
mounted(){ console.log(this.$refs.chartRef) }
获取组件实例:
-
给目标组件添加ref属性
<BaseDialog ref="baseDialog"></BaseDialog>
-
恰当时机,通过this.$refs.xxx,获取目标组件,调用组件对象内的方法
this.$refs.baseDialog.组件方法()
16.3 $nextTick
Vue是异步更新DOM的:即数据更新之后,DOM(视图)不会立即更新
如果想要在DOM更新完成后做某件事,则需要使用$nextTick
this.$nextTick(() => {
// 业务逻辑
})
17. 自定义指令
自定义指令可以 封装一些DOM操作 ,扩展额外的功能。
全局注册:
Vue.directive('focus',{ // 自定义指令全局注册
inserted (el){ // el是指令绑定的DOM元素
// 自定义逻辑操作
}
})
局部注册:
directives : { // 自定义指令局部注册
"focus" : {
inserted (el) { // el是指令绑定的DOM元素
// 自定义逻辑操作
}
}
}
指令的值:
语法:在绑定指令时,通过"="为指令绑定 具体的参数值
<p v-color="color1">指令的值1测试</p>
通过binding.value可以拿到指令值,指令修改会触发 update函数 。
<!-- 示例 -->
<template>
<div>
<p v-color="color1">指令的值111</p>
<p v-color="color2">指令的值222</p>
</div>
</template>
<script>
export default {
data () {
return {
color1 : 'blue',
color2 : 'green'
}
},
directives : {
"color" : {
inserted(el, binding) {
el.style.color = binding.value
},
update(el, binding) {
el.style.color = binding.value
},
}
}
}
</script>
<style>
</style>
18. 插槽
18.1 默认插槽
语法:
- 组件内需要定制的结构位置,改用 <slot> </slot> 占位
- 使用组件时,<MyBody> ( 内部 ) </MyBody> 标签内部传入结构替换slot
插槽后备内容( 默认值 ):在 <slot> </slot> 标签内放置内容,作为默认显示内容
18.2 具名插槽
语法:
-
多个slot使用name属性区分名字
<div class="Header"> <slot name="head"></slot> </div> <div class="Body"> <slot name="body"></slot> </div>
-
template配合v-slot:名字来分发对应的标签(v-slot:也可以简写为#)
<template v-slot:head>Header</template> <!-- v-slot:head相当于#head --> <template v-slot:body>Body</template>
18.3 作用域插槽
使用步骤:
-
以添加属性的方式给slot标签 传值
-
所有添加的属性都会被收集到一个对象中
-
在template中,通过’ #插槽名=“obj” '接收,默认插槽名为 default