本篇博客主要记录自己学习vue的过程,对主要知识点及问题做一个记录。
Vue是什么
Vue是一套用于构建用户界面的渐进式JavaScript框架。
Vue的特点
- 采用组件化模式,提高代码复用率、且让代码更好维护。
- 声明式编码,让编码人员无需直接操作DOM,提高开发效率。
- 使用虚拟DOM+优秀的Diff算法,尽量复用DOM节点。
Vue的模板语法
Vue的模板语法有两类:
1. 插值语法:
功能:用于解析标签体内容。
写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性。
<div id="root">
<h1>插值语法</h1>
<h3>Hello, {{name}}</h3>
</div>
<script>
new Vue({
el: '#root',
data: {
name: 'Jack'
}
})
</script>
2. 指令语法:
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件…)。
举例:v-bind:href=“xxx” 或 简写为 :href=“xxx”,xxx同样要写js表达式,且可以直接读取到data中的所有属性。(此处拿v-bind举个例子)
<div id="root">
<h1>指令语法</h1>
<!-- v-bind: 可以简写为 : -->
<a v-bind:href="school.url">点我去{{school.name}}1</a>
<a :href="school.url">点我去{{school.name}}2</a>
</div>
<script>
new Vue({
el: '#root',
data: {
name: 'Jack',
school: {
name: '百度',
url: 'http://www.baidu.com'
}
}
})
</script>
Vue中的数据绑定
Vue中有两种数据绑定的方式
1. 单向绑定(v-bind):数据只能从data流向页面。
2. 双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data。
备注:
1.双向绑定一般都应用在==表单类==元素上(如:input、select等)
2.v-model:value 可以简写为 v-model,因为v-model默认收集的就是value值。
<div id="root">
<!-- 普通写法 -->
单向数据绑定:<input type="text" v-bind:value="name"><br/>
双向数据绑定:<input type="text" v-model:value="name"><br/>
<!-- 简写 -->
单向数据绑定:<input type="text" :value="name"><br/>
双向数据绑定:<input type="text" v-model="name"><br/>
</div>
<script>
new Vue({
el:'#root',
data:{
new_name: '小花'
}
})
</script>
data与el的两种写法
- el的2种写法
(1).new Vue时候配置el属性。
(2).先创建Vue实例,随后再通过vm.$mount(‘#root’)指定el的值。 - data的2种写法
(1).对象式
(2).函数式 - 如何选择:在组件中,data必须使用函数式,否则会报错。
// el的两种写法
const v = new Vue({
el: '#root', // 第一种写法
data: {
name: 'hahaha',
}
})
v.$mount('#root') // 第二种写法
// data的两种写法
new Vue({
el: '#root',
// data的第一种写法:对象式
data: {
name: 'hahaha',
}
// data的第二种写法:函数式
// data: function (){}
data() {
return {
name: 'hahaha'
}
}
})
事件代理
事件的基本使用:
1. 使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名;
2. 事件的回调需要配置在methods对象中,最终会在vm上;
3. methods中配置的函数,不要用箭头函数!否则this就不是vm了;
4. methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;
5. @click="demo" 和 @click="demo($event)" 效果一致,但后者可以传参;
<div id="root">
<h2>欢迎来到{{name}}</h2>
<button v-on:click="showInfo">点我提示信息</button>
<button @click="showInfo1">点我提示信息1(不传参)</button>
<button @click="showInfo2($event, 66)">点我提示信息2(传参)</button>
</div>
<script>
new Vue({
el: '#root',
data: {
name: '一二三',
},
methods: {
showInfo1(event){
alert('同学你好!')
},
showInfo2(event, number){
console.log(number)
}
}
})
</script>
Vue中的事件修饰符:
1. prevent:阻止默认事件(常用);
2. stop:阻止事件冒泡(常用);
3. once:事件只触发一次(常用);
4. capture:使用事件的捕获模式;
5. self:只有event.target是当前操作的元素时才触发事件;
6. passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
计算属性
1. 定义:要用的属性不存在,要通过已有属性计算得来。
2. 原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
3. get函数什么时候执行?
(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会被再次调用。
4. 优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
5. 备注:
1. 计算属性最终会出现在vm上,直接读取使用即可。
2. 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
<div id="root">
姓:<input type="text" v-model=firstName><br><br>
名:<input type="text" v-model=secondName><br><br>
全名:<span>{{fullName}}</span>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
firstName: '张',
secondName:'三'
},
computed:{
// 完整写法
fullName:{
get(){
return this.firstName + '-' + this.secondName
},
set(value){
const arr = value.split('-')
this.firstName = arr[0]
this.secondName = arr[1]
}
}
// 简写
// 只有 只读不改(只有get时) 时才能用简写
fullName(){
return this.firstName + '-' + this.secondName
}
}
})
</script>
监视属性
1.当被监视的属性变化时, 回调函数自动调用, 进行相关操作
2.监视的属性必须存在,才能进行监视!!
3.监视的两种写法:
(1).new Vue时传入watch配置
(2).通过vm.$watch监视
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
isHot: true
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.isHot = !this.isHot
}
},
// 第一种写法
watch:{
isHot:{
immediate:true, // 初始化时让handler调用一下
// 当idHot的值改变时,调用handler
handler(newValue, oldValue){
console.log('isHot的值被修改了', newValue, oldValue)
}
}
}
})
// 第二种写法
vm.$watch('isHot', {
immediate: true, // 初始化时让handler调用一下
// 当idHot的值改变时,调用handler
handler(newValue, oldValue) {
console.log('isHot的值被修改了', newValue, oldValue)
}
})
</script>
深度监视:
1.Vue中的watch默认不监测对象内部值的改变(一层)。
2.配置deep:true可以监测对象内部值改变(多层)。
备注:
1.Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!
2.使用watch时根据数据的具体结构,决定是否采用深度监视。
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
<hr>
<h2>a的值是:{{numbers.a}}</h2>
<button @click="numbers.a++">点我让a+1</button><br>
<h2>b的值是:{{numbers.b}}</h2>
<button @click="numbers.b++">点我让b+1</button>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
isHot: true,
numbers:{
a:1,
b:1
}
},
watch:{
isHot:{
immediate:true, // 初始化时让handler调用一下
// 当idHot的值改变时,调用handler
handler(newValue, oldValue){
console.log('isHot的值被修改了', newValue, oldValue)
}
},
// 监视多级结构中某个属性的变化
'numbers.a':{
handler(){
console.log('a被改变了')
}
},
// 监视多级结构中所有属性的变化
numbers:{
deep:true,
handler(){
console.log('numbers改变了')
}
}
}
})
</script>
watch: {
// 完整写法
isHot: {
// immediate:true, // 初始化时让handler调用一下
// deep: true, // 深度监视
handler(newValue, oldValue) {
console.log('isHot的值被修改了', newValue, oldValue)
}
},
// 简写
// 当只有handler,没有其他配置项的时候可以采用简写形式
isHot(newValue, oldValue) {
console.log('isHot的值被修改了', newValue, oldValue)
}
}
// 完整写法
vm.$watch('isHot', {
// immediate:true, // 初始化时让handler调用一下
// deep: true, // 深度监视
handler(newValue, oldValue) {
console.log('isHot的值被修改了', newValue, oldValue)
}
})
// 简写
vm.$watch('isHot', function (newValue, oldValue) {
console.log('isHot的值被修改了', newValue, oldValue)
})
绑定样式
1. class样式
写法:class="xxx" xxx可以是字符串、对象、数组。
字符串写法适用于:类名不确定,要动态获取。
对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
2. style样式
:style="{fontSize: xxx}"其中xxx是动态值。
:style="[a,b]"其中a、b是样式对象。
<div id="root">
<!-- 绑定class样式--字符串写法, 适用于:样式的类名不确定,需要动态指定 -->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div><br><br>
<!-- 绑定class样式--数组写法, 适用于:要绑定的样式个数不确定、名字也不确定 -->
<div class="basic" :class="classArr">{{name}}</div><br><br>
<!-- 绑定class样式--对象写法, 适用于:要绑定的样式个数不定、名字也确定,但要动态决定用不用 -->
<div class="basic" :class="classObj">{{name}}</div><br><br>
<!-- 绑定style样式--对象写法 -->
<div class="basic" :style="styleObj">{{name}}</div><br><br>
<!-- 绑定style样式--数组写法 -->
<div class="basic" :style="styleArr">{{name}}</div>
</div>
<script>
new Vue({
el: '#root',
data: {
name: '一二三',
mood: 'normal',
classArr: ['atguigu1', 'atguigu2', 'atguigu3'],
classObj: {
atguigu1: true,
atguigu2: false
},
styleObj: {
fontSize: '40px',
backgroundColor: 'orange'
},
styleArr: [
{
fontSize: '40px',
backgroundColor: 'orange'
},
{
color:'red'
}
]
},
methods: {
changeMood() {
const arr = ['happy', 'sad', 'normal']
const index = Math.floor(Math.random() * 3)
this.mood = arr[index]
}
}
})
</script>
条件渲染
1.v-if
写法:
(1).v-if="表达式"
(2).v-else-if="表达式"
(3).v-else="表达式"
适用于:切换频率较低的场景。
特点:不展示的DOM元素直接被移除。
注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。
2.v-show
写法:v-show="表达式"
适用于:切换频率较高的场景。
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
3.备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。
<div id="root">
<!-- 使用v-show做条件渲染 -->
<h2 v-show="false">欢迎来到{{name}}</h2>
<h2 v-show="1 === 1">欢迎来到{{name}}</h2>
<!-- 使用v-if做条件渲染 -->
<h2 v-if="false">欢迎来到{{name}}</h2>
<h2 v-if="1 === 1">欢迎来到{{name}}</h2>
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
<div v-show="n===1">html</div>
<div v-show="n===2">css</div>
<div v-show="n===3">javascript</div>
<!-- v-else和v-else-if -->
<div v-if="n === 1">Angular</div>
<div v-else-if="n === 2">React</div>
<div v-else-if="n === 3">Vue</div>
<div v-else>哈哈</div>
<!-- v-if与template的配合使用 -->
<template v-if="n === 1">
<h2>你好</h2>
<h2>尚硅谷</h2>
<h2>北京</h2>
</template>
</div>
<script>
new Vue({
el: '#root',
data: {
name: '一二三',
n: 0
}
})
</script>
Vue监视数据
1. vue会监视data中所有层次的数据。
2. 如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value) 或
vm.$set(target,propertyName/index,value)
3. 如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1).调用原生对应的方法对数组进行更新。
(2).重新解析模板,进而更新页面。
4.在Vue修改数组中的某个元素一定要用如下方法:
1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
2.Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!
<div id="root">
<h1>学生信息</h1>
<button @click="student.age++">年龄+1岁</button><br><br>
<button @click="addSex">添加性别属性,默认值:男</button><br><br>
<button @click="student.sex = '未知' ">修改性别</button><br><br>
<button @click="addFriend">在列表首位添加一个朋友</button><br><br>
<button @click="updateFirstFriendName">修改第一个朋友的名字为:张三</button><br><br>
<button @click="addHobby">添加一个爱好</button><br><br>
<button @click="updateHobby">修改第一个爱好为:开车</button><br><br>
<button @click="removeSleep">过滤掉爱好中的睡觉</button><br><br>
<h2>姓名:{{student.name}}</h2>
<h2>年龄:{{student.age}}</h2>
<h2 v-if="student.sex">性别:{{student.sex}}</h2>
<h2>爱好</h2>
<ul>
<li v-for="(h, index) in student.hobby" :key="index">
{{h}}
</li>
</ul>
<h2>朋友们</h2>
<ul>
<li v-for="(f, index) in student.friends" :key="index">
{{f.name}}--{{f.age}}
</li>
</ul>
</div>
<script>
new Vue({
el: '#root',
data: {
student: {
name: '小明',
age: 18,
hobby: ['吃饭', '睡觉', '打豆豆'],
friends: [
{ name: '小花', age: 20 },
{ name: '小草', age: 30 },
]
}
},
methods: {
addSex() {
Vue.set(this.student, 'sex', '男')
},
addFriend() {
this.student.friends.unshift({ name: '小东', age: 19 })
},
updateFirstFriendName() {
this.student.friends[0].name = '张三'
},
addHobby() {
this.student.hobby.push('逛街')
},
updateHobby() {
// this.student.hobby.splice(0, 1, '开车')
Vue.set(this.student.hobby, 0, '开车')
},
removeSleep() {
this.student.hobby = this.student.hobby.filter((h) => {
return h != '睡觉'
})
}
}
})
</script>
收集表单数据
若:<input type="text"/>,则v-model收集的是value值,用户输入的就是value值。
若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。
若:<input type="checkbox"/>
1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
2.配置input的value属性:
(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
(2)v-model的初始值是数组,那么收集的的就是value组成的数组
备注:v-model的三个修饰符:
lazy:失去焦点再收集数据
number:输入字符串转为有效的数字
trim:输入首尾空格过滤
<div id="root">
<form @submit.prevent="demo">
账号: <input type="text" v-model.trim="userInfo.account"><br><br>
密码: <input type="password" v-model="userInfo.password"><br><br>
性别:
男<input type="radio" name="sex" v-model="userInfo.sex" value="male">
女<input type="radio" name="sex" v-model="userInfo.sex" value="female"><br><br>
年龄: <input type="number" v-model.number="userInfo.age"><br><br>
爱好:
吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat">
睡觉<input type="checkbox" v-model="userInfo.hobby" value="sleep">
打豆豆<input type="checkbox" v-model="userInfo.hobby" value="hit"><br><br>
所属校区:
<select v-model="userInfo.city">
<option value="">请选择校区</option>
<option value="bj">北京</option>
<option value="ush">上海</option>
<option value="hz">杭州</option>
</select><br><br>
其他信息:
<textarea v-model.lazy="userInfo.other"></textarea><br><br>
<input type="checkbox" v-model="userInfo.agree"> 阅读并接受 <a href="https://www.baidu.com">《用户协议》</a><br><br>
<button>提交</button>
</form>
</div>
<script>
new Vue({
el: '#root',
data: {
userInfo: {
account: '',
password: '',
sex: '',
age: '',
hobby: [],
city: '',
other: '',
agree: ''
}
},
methods: {
demo() {
console.log(JSON.stringify(this.userInfo))
}
}
})
</script>
过滤器
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
语法:
1.注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}
2.使用过滤器:{{ xxx | 过滤器名}} 或 v-bind:属性 = "xxx | 过滤器名"
备注:
1.过滤器也可以接收额外参数、多个过滤器也可以串联
2.并没有改变原本的数据, 是产生新的对应的数据
<div id="root">
<h2>显示格式化后的时间</h2>
<h3>现在是:{{time}}</h3>
<!-- 计算属性实现 -->
<h3>格式化后的时间: {{fmtTime}}</h3>
<!-- methods实现 -->
<h3>格式化后的时间: {{getFmtTime()}}</h3>
<!-- 过滤器实现 -->
<h3>格式化后的时间: {{time | timeFormater}}</h3>
<!-- 过滤器实现(传参) -->
<h3>格式化后的时间: {{time | timeFormater('YYYY_MM_DD')}}</h3>
<h3>格式化后的时间: {{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3>
<h3 :x="msg | mySlice">你好</h3>
</div>
<div id="root2">
<h2>{{msg | mySlice}}</h2>
</div>
<script>
// 全局过滤器
Vue.filter('mySlice', function (value) {
return value.slice(0, 4) // 截取前四位
})
new Vue({
el: '#root',
data: {
time: 1698668089147, // 时间戳
msg:'西南石油大学'
},
computed: {
fmtTime() {
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
},
methods: {
getFmtTime() {
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
},
// 局部过滤器
filters: {
timeFormater(value, str = 'YYYY-MM-DD HH:mm:ss') {
return dayjs(value).format(str)
},
// mySlice(value) {
// return value.slice(0, 4) // 截取前四位
// }
}
})
new Vue({
el: '#root2',
data: {
msg: 'hello'
}
})
</script>
内置指令
v-text指令:
1. 作用:向其所在的节点中渲染文本内容。
2. 与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。
v-html指令:
1.作用:向指定节点中渲染包含html结构的内容。
2.与插值语法的区别:
(1).v-html会替换掉节点中所有的内容,{{xx}}则不会。
(2).v-html可以识别html结构。
3.严重注意:v-html有安全性问题!!!!
(1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
(2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
v-cloak指令(没有值):
1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。
v-once指令:
1.v-once所在节点在初次动态渲染后,就视为静态内容了。
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
v-pre指令:
1.跳过其所在节点的编译过程。
2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
自定义指令
一、定义语法:
(1).局部指令:
new Vue({ new Vue({
directives:{指令名:配置对象} 或 directives{指令名:回调函数}
}) })
(2).全局指令:
Vue.directive(指令名,配置对象) 或 Vue.directive(指令名,回调函数)
二、配置对象中常用的3个回调:
(1).bind:指令与元素成功绑定时调用。
(2).inserted:指令所在元素被插入页面时调用。
(3).update:指令所在模板结构被重新解析时调用。
三、备注:
1.指令定义时不加v-,但使用时要加v-;
2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。
<div id="root">
<h2>当前的n值是: <span v-text="n"></span></h2>
<!-- <h2>放大10倍后的n值是: <span v-big-number="n"></span></h2> -->
<h2>放大10倍后的n值是: <span v-big="n"></span></h2>
<button @click="n++">点我n+1</button>
<hr>
<input type="text" v-fbind:value="n">
</div>
<div id="root2">
<input type="text" v-fbind:value="x">
</div>
<script>
// 定义全局指令
Vue.directive('fbind', {
// 指令与元素成功绑定时(一上来)
bind(element, binding) {
element.value = binding.value
},
// 指令所在元素被插入页面时
inserted(element, binding) {
element.focus()
},
// 指令所在的模板被重新解析时
update(element, binding) {
element.value = binding.value
}
})
Vue.directive('big', function (element, binding) {
element.innerText = binding.value * 10
console.log('big', this)
})
new Vue({
el: '#root',
data: {
n: 1
},
directives: {
'big-number'(element, binding) {
// element是v-big绑定的元素span, binding是绑定对象, binding.value是v-big用到的n值
// big函数何时会被调用?
// 1.指令与元素成功绑定时(一上来)。2.指令所在的模板被重新解析时
element.innerText = binding.value * 10
},
big(element, binding) {
// element是v-big绑定的元素span, binding是绑定对象, binding.value是v-big用到的n值
// big函数何时会被调用?
// 1.指令与元素成功绑定时(一上来)。2.指令所在的模板被重新解析时
element.innerText = binding.value * 10
console.log('big', this)
},
fbind: {
// 指令与元素成功绑定时(一上来)
bind(element, binding) {
console.log('fbind-bind', this)
element.value = binding.value
},
// 指令所在元素被插入页面时
inserted(element, binding) {
console.log('fbind-inserted', this)
element.focus()
},
// 指令所在的模板被重新解析时
update(element, binding) {
console.log('fbind-update', this)
element.value = binding.value
}
}
}
})
new Vue({
el: '#root2',
data: {
x: 10
}
})
</script>
生命周期
<script>
new Vue({
beforeCreate() {},
created() {},
beforeMount() { },
mounted() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
})
<script/>
常用的生命周期钩子:
1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
2.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。
关于销毁Vue实例
1.销毁后借助Vue开发者工具看不到任何信息。
2.销毁后自定义事件会失效,但原生DOM事件依然有效。
3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。
一个重要的内置关系
1.一个重要的内置关系:VueComponent.prototype.proto === Vue.prototype
2.为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。