Vue学习目录
1.概述
1.介绍
Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的渐进式 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。
- 构建用户界面:把数据通过某种办法变成用户界面
- 渐进式:Vue可以自底向上逐层的应用,简单应用只需要一个轻量小巧的核心库,复杂应用可以引入各式各样的Vue插件
- 尤雨溪开发
2.特点
- 遵循MVVM模式
- 编码简洁,体积小,运行效率高,适合移动/PC端开发
- 它本身只关注 UI,可以引入其它第三方库开发项目
- 采用组件化模式,提高代码复用率、且让代码更好维护
- 声明式编码,让编码人员无需直接操作DOM,提高开发效率
- 使用虚拟DOM 和 Diff算法,尽量复用DOM节点
3.Diff算法
真实DOM:
浏览器渲染的过程主要包括以下五步:
- 浏览器获取到 HTML 文档并解析 DOM 树
- 解析 CSS 构建层叠样式表模型CSSOM(CSS Object Model)
- 将 DOM Tree 和 CSSOM 合并成一个 Render Tree
- 有了Render Tree,浏览器便能获取到每个节点的 CSS 定义和从属关系,从而可以计算出每个节点的现实位置
- 通过上一步的计算规则进行绘制页
虚拟DOM指的就是将真实的DOM树构造为js对象的形式,从而解决浏览器操作真实DOM的性能问题。
虚拟DOM就是利用js运行速度快的这一优点对操作DOM进行优化的,用js模拟DOM树,在js中处理DOM的操作再渲染,简单概括分为以下三点:
- 用javascript对象模拟DOM树并且渲染DOM树;
- 通过 diff算法 比较新旧DOM树,得到差异的对象;
- 将差异的对象应用到渲染的DOM树中。
diff 算法是一种通过同层的树节点进行比较的高效算法。
- 比较只会在同层级进行, 不会跨层级比较
- 在diff比较的过程中,循环从两边向中间比较
4.搭建环境
5.简单使用
Vue实例化对象挂载到根元素后,生成全局Vue对象实例,Vue对象在实例化过程中会传入配置对象(options),options中包括data、methods、computed、watch等等
- 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象
- root 容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法
- root 容器里的代码被称为Vue模板
- Vue 实例与容器是一一对应的,后面的不会代入vue。
- 真实开发中只有一个Vue实例,并且会配合着组件一起使用
- {{xxx}}中的 xxx 要写 js 表达式,且 xxx 可以自动读取到data中的所有属性
- 一旦data中的数据发生变化,那么模板中用到该数据的地方也会自动更新。
2.语法
1.模板语法
模板语法分两类:文本插值语法与指令语法。
- 插值语法:用于解析标签内容
<div>{{xxx}}</div>
//xxx 是 js 表达式,可以直接读取到 data 中的所有区域
- 指令语法:用于解析标签(包括:标签属性、标签体内容、绑定事件…)
v-html
:插入为html;
<span v-html="xxx"></span>
v-bind
:单向数据绑定,数据只能从data流向页面;
<div v-bind:id="dynamicId"></div>
简写为:
<div :id="dynamicId"></div>
动态绑定多个值:
<div v-bind="objectOfAttrs"></div>
data() {
return {
objectOfAttrs: {
id: 'container',
class: 'wrapper'
}
}
}
v-model
:双向数据绑定,数据不仅能从data流向页面,也能从页面流向data;把v-model:value
简写为v-model
;常用于表单类组件。
<input type="text" v-model:value="name"><br/>
简写为
<input type="text" v-model="name">
v-if
: 该指令会基于表达式的值的真假来移除/插入该元素
<p v-if="seen">Now you see me</p>
v-on
:监听DOM事件,缩写为@
字符
<a v-on:click="doSomething"> ... </a>
<!-- 简写 -->
<a @click="doSomething"> ... </a>
动态参数:
同样在指令参数上也可以使用一个 JavaScript 表达式,需要包含在一对方括号内:
<a v-bind:[attributeName]="url"> ... </a>
<!-- 简写 -->
<a :[attributeName]="url"> ... </a>
- 动态参数中表达式的值应当是一个字符串,或者是 null;
- 空格和引号,在 HTML attribute 名称中都是不合法的;
- 当使用 DOM 内嵌模板 (直接写在 HTML 文件里的模板) 时,我们需要避免在名称中使用大写字母,因为浏览器会强制将其转换为小写。
2.响应式基础
data:选用选项式 API 时,会用 data 选项来声明组件的响应式状态。此选项的值应为返回一个对象的函数。Vue 将在创建新组件实例的时候调用此函数,并将函数返回的对象用响应式系统进行包装。此对象的所有顶层属性都会被代理到组件实例 (即方法和生命周期钩子中的 this) 上。
也就是,data是在创建vue实例时调用的函数,该函数返回响应式组件中要响应的值构成的对象,该对象的顶层属性与组件实例挂钩。
data有2种写法:
- 对象式:data: { }
- 函数式:data() { return { } }
如何选择:目前哪种写法都可以,以后到组件时,data必须使用函数,否则会报错。
由Vue管理的函数,一定不要写箭头函数,否则 this 就不再是Vue实例了。
el:
el有2种写法
- 创建Vue实例对象的时候配置el属性
- 先创建Vue实例,随后再通过vm.$mount(‘#root’)指定el的值
3.MVVM模型 数据代理
MVVM模型
- M:模型 Model,data中的数据
- V:视图 View,模板代码
- VM:视图模型 ViewModel,Vue实例
data中所有的属性,最后都出现在了vm身上;
vm身上(即Vue实例)所有的属性 及Vue原型身上所有的属性,在 Vue模板(页面的大双括号)中都可以直接使用。
数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)
Object.defineproperty(给谁加属性,属性名,{配置项})
配置项:
{
value:xx,
enumerbale:true,//是否可枚举,默认false
writable:true,// 是否可以被修改,默认值是false
configurable:true// 是否可以被删除,默认值是false
get(){}
//当有人读取对象的该属性时,get函数(getter)就会被调用,且返回值就是该属性的值
set(value){}
//当有人修改对象的该属性时,set函数(setter)就会被调用,且会收到要修改的值
}
Vue中的数据代理:
- Vue中的数据代理通过vm对象来代理data对象中属性的操作(读/写)
- Vue中数据代理的好处:更加方便的操作data中的数据
基本原理 - 通过object.defineProperty()把data对象中所有属性添加到vm上
- 为每一个添加到vm上的属性,都指定一个 gettersetter
- 在gettersetter内部去操作(读/写)data中对应的属性
也就是说,我们创建vue实例时传入配置项,其中data函数返回了一组数据,该数据被拷贝到实例(vm)中的_data
中,然后再用data函数中的数据代理_data
中的数据。_data
为了实现数据劫持,实现数据更改监听,通知vue去渲染。
Vue2中写到:
当一个 Vue 实例被创建时,它将 data 对象中的所有的 property 加入到 Vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
data有两种写法,但他到底是对象还是函数呢
4.事件处理
- 使用v-on:xxx或@xxx绑定事件,其中 xxx 是事件名
- 事件的回调需要配置在methods对象中,最终会在vm上
- methods中配置的函数,不要用箭头函数,否则 this 就不是vm了而是指向window
- methods中配置的函数,都是被 Vue所管理的函数,this 的指向是vm或组件实例对象
- @click="demo"和@click="demo($event)"效果一致,但后者可以传参。
事件修饰符:
修饰符可以连用:@click.prevent.stop=dosome
键盘事件:
5.计算属性
属性:data里的数据,前者为属性名,后者为属性值。
定义:要用的属性不存在,需要通过已有属性计算得到。
原理:底层借助了Objcet.defineproperty()方法提供的getter和setter。
getter在初次读值和依赖的数据改变时会调用。
setter在该属性被修改时会调用。
优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便 。
计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。否则立即返回先前的计算结果,而不用重复执行 getter 函数。方法调用总是会在重渲染发生时再次执行函数。
- 计算属性最终会出现在vm上,直接读取使用即可
- 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变
- 如果计算属性确定不考虑修改,可以使用计算属性的简写形式
语法:
computed{
计算属性名:
{ get(){},
set(){}}
}
new Vue({
el:'#root',
data:{
n:12
},
computed: {
//完整写法
fun: {
get(){
return n++;
},
set(value){
this.n=value
}
// 简写
fun2() {
return this.n++
}
}
})
6.监视属性
计算属性是直接从已有属性计算得到“新属性”,但如果需要对已有属性进行一些衍生操作,就无法完成。
监视属性是每次响应式属性发生变化时触发一个函数。在要监视的已有属性发生变化时,触发函数进行一些操作。
watch监视属性
- 当被监视的属性变化时,回调函数handler自动调用,进行相关操作
- 监视的属性必须存在,才能进行监视,既可以监视data,也可以监视计算属性
- 配置项属性默认为immediate:false,改为 true,则初始化时调用一次 handler(newValue,oldValue)
监视有两种写法
- 创建Vue时传入watch: {}配置
- 通过vm.$watch()监视
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(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}
} */
})
// 方式二
vm.$watch('isHot', {
immediate: true, // 初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时
handler(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue)
}
})
多级监测:
监测对象中某个值,直接使用引号'对象名.属性名'
const vm = new Vue({
el: '#root',
data: {
number:{
a:1,
b:2
}
},
watch:{
"number.a":{
immediate:true,
handler(newValue,oldValue){
}
}
}
})
深层监测器:
watch 默认是浅层的:被侦听的属性,仅在被赋新值时,才会触发回调函数——而嵌套属性的变化不会触发。如果想侦听所有嵌套的变更,你需要深层侦听器。
- Vue中的watch默认不监测对象内部值的改变(一层)
- 在watch中配置
deep:true
可以监测对象内部值的改变(多层)
注意
- Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
- 使用watch时根据监视数据的具体结构,决定是否采用深度监视
简写:
如果监视属性除了handler没有其他配置项的话,可以进行简写。
watch: {
//简写
isHot(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue, this)
}
计算属性 VS 侦听属性
-
computed能完成的功能,watch都可以完成
-
watch能完成的功能,computed不一定能完成,例如watch可以进行异步操作
-
所有被Vue管理的函数,最好写成普通函数,这样 this 的指向才是vm或组件实例对象
-
所有不被Vue所管理的函数(定时器的回调函数、ajax 的回调函数等、Promise 的回调函数),最好写成箭头函数,这样 this 的指向才是vm或组件实例对象
7.类与样式绑定
数据绑定的一个常见需求场景是操纵元素的 CSS class 列表和内联样式,使用v-bind
。
- 写法::class=“xxx”,xxx 可以是字符串、数组、对象
- :style="[a,b]"其中a、b是样式对象
- :style="{fontSize: xxx}"其中 xxx 是动态值
- 字符串写法适用于:类名不确定,要动态获取
- 数组写法适用于:要绑定多个样式,个数不确定,名字也不确定
- 对象写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
绑定数组
<div :class="[activeClass, errorClass]"></div>
绑定对象isActive
<div :class="{ active: isActive }"></div>
<!-- 绑定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>
8.条件渲染
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回真值时才被渲染。一个 v-else
元素必须跟在一个 v-if
或者 v-else-if
元素后面,否则它将不会被识别。
想要切换不止一个元素:在这种情况下我们可以在一个 元素上使用 v-if,这只是一个不可见的包装器元素,最后渲染的结果并不会包含这个 元素。
v-show
也会条件显示一个元素。
<h1 v-show="ok">Hello!</h1>
v-if 与 v-for:
- v-if 是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。
- v-if 也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。
- 相比之下,v-show 简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display 属性会被切换。
- 总的来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show 较好;如果在运行时绑定条件很少改变,则 v-if 会更合适。
9.列表渲染
使用 v-for 指令基于一个数组来渲染一个列表。
- 用于展示列表数据
- 语法:
<li v-for="(item, index) of items" :key="index">
,这里key可以是index,更好的是遍历对象的唯一标识 - 可遍历:数组、对象、字符串(用的少)、指定次数(用的少)
“(item, index) of items” :key=“index”:
遍历对象:第一个是属性值,第二个属性名,第三个是位置索引;
遍历数组:item in items
<li v-for="item in items">
{{ item.message }}
</li>
<!-- 遍历数组 -->
<h3>人员列表(遍历数组)</h3>
<ul>
<li v-for="(p,index) of persons" :key="index">{{ p.name }}-{{ p.age }}</li>
</ul>
<!-- 遍历对象 -->
<h3>汽车信息(遍历对象)</h3>
<ul>
<li v-for="(value,k) of car" :key="k">{{ k }}-{{ value }}</li>
</ul>
<!-- 遍历字符串 -->
<h3>测试遍历字符串(用得少)</h3>
<ul>
<li v-for="(char,index) of str" :key="index">{{ char }}-{{ index }}</li>
</ul>
<!-- 遍历指定次数 -->
<h3>测试遍历指定次数(用得少)</h3>
<ul>
<li v-for="(number,index) of 5" :key="index">{{ index }}-{{ number }}</li>
</ul>
v-for 可以直接接受一个整数值。在这种用例中,会将该模板基于 1…n 的取值范围重复多次。
<span v-for="n in 10">{{ n }}</span>
注意此处 n 的初值是从 1 开始而非 0。
在 标签上使用 v-for 来渲染一个包含多个元素的块。
不建议同时使用v-for和v-if,因为无法区分优先级。
可以直接在组件上使用 v-for,但不会自动将任何数据传递给组件,因为组件有自己独立的作用域。
key管理:
Vue 默认按照“就地更新”的策略来更新通过 v-for 渲染的元素列表。当数据项的顺序改变时,Vue 不会随之移动 DOM 元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染。
默认模式是高效的,但只适用于列表渲染输出的结果不依赖子组件状态或者临时 DOM 状态 (例如表单输入值) 的情况。
所以需要为每个元素对应的块提供一个唯一的 key,以便它可以跟踪每个节点的标识,从而重用和重新排序现有的元素。
数组管理:
变更方法:更改原数组
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
替换数组:返回一个新数组
- filter()
- concat()
- slice()
数据监视:
这个链接中讲解得很清楚:Vue数据监控
简单来说,就是
- data对象加工成_data对象:添加响应式的get、set方法(数据代理),实现数据劫持,并存入Vue实例;
- Vue全局实例对象代理_data对象,实现对_data中属性的直接操作;
- 数据变化监测效果
每个具有reactive setter的数据发生变化时,都会调用这个reactive setter,而这个reactive setter被调用时,会触发重新解析模板、生成新的虚拟DOM,、新旧虚拟DOM对比,更新内容映射到真实DOM、重新渲染这一套流程。
动态新增的属性:Vue.set(targetObject,attributeName,attributeValue)方法或this.$set(targetObject,attributeName,attributeValue)进行响应式属性的动态添加。
Vue不会为数组元素添加响应式的getter和setter,所以通过下标更改数组数据是无法被Vue所监测到的。
针对数组,只有通过调用push、pop、shift、unshift、splice、reverse、sort这7个改变原数组本身的API,才会引起Vue的响应。