英文官网:
- Vue2: https://vuejs.org/
- Vue3:https://vuejs.org/
中文官网
- Vue2: https://v2.cn.vuejs.org/
- Vue3: https://cn.vuejs.org/
用来构建用户界面的渐进式的js库
与其它前端JS框架的关联
- 借鉴Angular的模板和数据绑定技术
- 借鉴React的组件化和虚拟DOM技术
声明式
无需亲自操作DOM,利用模板描述界面
只要更新data数据,Vue就会自动更新DOM
组件化
将功能界面拆分成多组件组合使用,提高代码复用率,更好维护
虚拟DOM+DIff算法
实现最小化DOM更新
MVVM(Model-View-ViewModel)是一种软件架构设计模式
Vue是MVVM模式的一个实现库
MVVM支持双向绑定,当M层数据进行修改时,VM层会监测到变化,并且通知V层进行相应的修改,反之修改V层则会通知M层数据进行修改
M: Model 模型
V: View 视图
VM: ViewModel 视图模型
V与M沟通的桥梁,负责监听M或者V的修改,是实现MVVM双向绑定的要点
ViewModel是Vue.js的核心,是Vue的实例对象,简称vm对象
作用:model到view和view到model的双向数据绑定
什么是diff算法
diff算法就是比较新旧虚拟DOM树,寻找差异的算法。在源码中是通过patch函数来完成的,所以也称为patch算法
diff算法比较思路:深度优先,同级比较
深度优先即针对一个元素一路深挖下去,广度优先的反面
同级比较只进行同级比较,不进行父子级等比较
_render
方法生成新的虚拟DOM,把虚拟DOM传递给_update
方法,并调用_update
方法_update
函数中,首先定义一个变量保存旧的虚拟DOM(在vm._vnode_属性上),然后再把_update
接受的新的虚拟DOM vNode放在vm的_vnode_属性上,此时在_update
函数中就有了新旧虚拟DOM,最后使用patch方法对新旧虚拟DOM进行比较function Vue(){
const updateComponent = ()=>{
this._update(this._render())
}
}
function _update(vNode){
//将旧虚拟DOM存下来,以便新虚拟DOM赋值vm._vnode
const oldVnode = vm._vnode;
vm._vnode = vNode;
//使用patch函数进行新旧虚拟DOM比较
patch(oldVnode, vNode)
}
patch函数首先使用sameVnode方法专门比较两个节点是否相同(相同:两个虚拟DOM节点的标签类型相同[input还要比较type类型]
,并且key的值也要相同,如果没有写key,则key的值是undefined)
如果sameVnode比较两个节点相同,则直接进入更新流程
更新流程:
如果sameVnode比较两个不相同,则直接遍历新的节点创建新的元素,并直接删除旧的元素
Vetur
vue-helper
<head>
<script src="../js/vue.js">script>
head>
<body>
<div id="app">{
{msg}}div>
<script>
/* 3. 创建vue实例对象 */
new Vue({
// 页面根元素
el: '#app',
// 定义响应式数据
data: {
msg: 'Hello Vue!'
},
// 要解析显示的模板页面, 模板页面中可以读取data数据
// template会将页面挂载的根元素替换掉
template: `
{
{ msg }}
`
})
script>
body>
{ { js表达式 }}
React中的JSX表达式
标签体文本和标签属性值上使用
{表达式}
作用: 动态显示表达式的值
el: 用来指定模板的根元素
el: 'selector'
vue.$mount('selector')
挂载后,vue内部才会进行模板的解析/编译生成虚拟DOM,进而生成真实DOM显示
// 创建vue对象
const vm = new Vue({
// 1.自动挂载 => 内部会调用$mount进行挂载
// el: '#root',
data: {
content: 'abc'
}
})
// 2.手动挂载
vm.$mount('#root')
⚠vm的函数必须在挂载之前定义
const vm = new Vue({ data: { content: 'abc' } }) vm.fn = ()=> 'fn函数' vm.$mount('#root');//挂载
data: 用来指定可变的数据,模板可以直接读取,对应react的state
⚠vue3中所有数据类型直接修改都有响应式,无需注意以下
直接赋值
vue能监听到数据发生的变化,并作出修改
Vue内部会对data中所有层次属性都通过defineProperty添加getter/setter实现对data数据的深度劫持(监视)
但defineProperty不能监视添加和删除属性,只能监视读取和修改属性
修改对象已有属性 => 响应式 => 界面自动更新
添加新属性/删除属性 => 不是响应式 => 界面不会自动更新
直接修改,为响应式
this.obj.yyy = value
Vue.set( target, propertyName, value )
vm.$set( target, propertyName, value )
Vue.delete( target, propertyName )
vm.$delete( target, propertyName )
Vue实现数组数据的响应式,不是通过defineProperty给元素添加getter/setter
而是对数组更新元素的7个方法进行包装:
先调用原生方法对数组进行更新,再去更新DOM
对于其他未实现响应式的数组方法,可以用新数组替换旧数组
template: 用来生成真实DOM的模板字符串
methods:包含n个方法的对象
⚠methods中的方法不要用箭头函数
function Vue(options){
this._data = options.data;
this._initData();
this._initMethods(options.methods)
}
Vue.prototype = {
_initData(){
},
_initMethods(methods){
Object.keys(methods).forEach(key => {
this[key] = methods[key].bind(this)
})
}
}
计算属性监视内部使用的属性的变化,一旦发生变化,计算属性就要重新计算
⚠computed属性在使用时同data属性,无需带上小括号调用,{ { fullName }}
⚠什么时候使用计算属性
- 如果一个数据仅需直接展示,直接展示
- 如果一个数据要进行处理之后才能展示,可以考虑使用计算属性
- 如果模板页面中表达式较长,可以使用计算属性完成
computed: {
fullName () {
// setTimeout(() => 计算的结果)
// 进行特定的计算
return 同步计算的结果 => 专门用于显示
}
}
computed: {
fullName () {
get () {
return 计算后的结果
},
// 监视计算属改变, 当fullName被指定了新的值时调用,
set (value) {
// 可以在其中更新其它数据/发请求/保存数据
}
}
}
computed | methods | |
---|---|---|
调用情况 | 1.初始显示 2.依赖数据发生变化 |
1.data中的数据发生变化 (只要data中的数据发生变化,vue需要重新解析模板,就会重新调用methods) |
多次读取 | 一次 计算属性有缓存,多次读取只计算一次(只在相关响应式依赖发生改变时才会重新求值) |
methods多次读取需要执行多次 |
watch | computed | |
---|---|---|
是否可异步 | 可以执行异步操作 监视数据变化后,做一些特定工作, 比如:更新其它数据/保存数据/发请求提交数据 |
只能同步返回结果,不能异步返回计算结果 |
分别适用场景
- method: 事件的回调或者我们封装一些功能函数(更新数据/提示界面/发请求, 一般不用来返回一个要显示的数据)
- computed: 当要显示的数据是由已有n个数据来共同确定(需要有计算过程)的
- watch: 用于当某个数据发生改变时就需要做一些相应工作(比如: 更新其它数据/发请求/保存数据)
被监听的属性可以是属性(data)也可以是计算属性(computed)
⚠监听属性首先需要保证有该属性(data)或计算属性(computed)
watch配置写法
watch: {
firstName: {
handler (newVal, oldVal) {
console.log('watch2 firstName', newVal, oldVal)
// 更新fullName4
setTimeout(() => {
this.fullName4 = newVal + '-' + this.lastName
}, 1000);
}
}
}
$watch方法
vm.$watch('lastName', function (value) {
this.fullName4 = this.firstName + '-' + value
})
watch配置简写
watch: {
firstName(newVal, oldVal){
console.log(oldVal)
}
}
说明 | |
---|---|
立即监听 immediate: true |
初始化时就执行一次监听 |
深度监听 deep: true |
watch默认不监测对象内部值的改变(监视一层) 配置deep:true可以监测对象内部值改变(监视多层) |
⚠要使用监听配置,必须用一般写法1
{ handler (value, oldValue) { } // 监视的回调 deep: true, // 深度监视 immediate: true, // 初始化执行一次, 后面数据时会再次执行 }
⚠**
immediate
&mounted
**如果某个功能既需要watch监听执行, 又需要初始化(mounted)时执行, 直接给watch设置immediate立即监听
有两种写法:
["xx.xx"](){}
"xx.xx"(){}
watch:{
"person.name"(){
}}
data(){
return{
person:{
name:"asa",
age:22
}
}
}
watch本身不能同时监听多个属性,只能利用computed实现
computed可以同时监听多个属性的变化,并重新计算值
computed:{
nameAge(){
return{
name:this.name,
age:this.age
}
}
}
watch:{
nameAge(){
}}
vue指令:vue中自定义的一些标签属性,都以v-开头
作用:对所在标签进行特定操作
:
v-bind可以给给任意标签属性,指定动态的属性值
v-bind:属性名="表达式"
//可简写为
:属性名="表达式"
<body>
<div id="test">
<img :src="imgSrc" yyy="xxx" />
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: "#test",
data: {
imgSrc: "https://v2.cn.vuejs.org/images/logo.svg",
xxx: "123",
},
});
setTimeout(() => {
vm.imgSrc = "https://sponsors.vuejs.org/images/aircode.png";
}, 1000);
</script>
</body>
<p :style="{fontSize: '20px', 'backgroun-color': 'pink' color: val}">str</p>
v-bind:style
是一个包含样式属性的对象React的style写法
<p style={ { fontSize: 20, 'background-color': 'pink'}}>style动态绑定p>
:class="xxx"
,xxx可以是字符串、对象、数组
hasA、hasB: boolean,
myClass: 'classA',
myClass2: ['classA', 'classB']
类名确定,且只有一个,但不确定有没有 => 三元表达式写法
<p :class="hasA ? 'classA' : ''">情况1</p>
类名确定,且有多个,但不确定有没有 => 对象写法
<p :class="{classA: hasA, classB: hasB}">情况2</p>
**类名只有一个,类名不确定时 => 字符串写法**
```js
情况3
**类名有多个,类名不确定时 => 数组写法**
```js
<p :class="myClass2">情况4</p>
**有固定/静态类名,也有动态类名 => 可分开写,会自动合并它们**
```js
<p class="classC" :class="myClass">aaaa</p>
React的class写法
- 在react中,静态类名和动态类名不能分开,需写在一起
<p class={ hasA ? 'classA' : ''}>情况1</p> <p class={ hasA ? 'classC classA' : 'classC'}>情况1</p>
v-bind="包含多个属性的对象"
<div id="test">
<p v-bind="obj">aaaaaaap>
div>
<script src="../js/vue.js">script>
<script>
const vm = new Vue({
el: '#test',
data: {
obj: {
a: 1,
b: 2
}
}
})
script>
<div id="test">
<input type="text" v-model:value="msg" />
<p>{
{msg}}p>
div>
<script src="../js/vue.js">script>
<script>
const vm = new Vue({
el: "#test", data: {
msg: 123 } });
script>
监听用户的输入事件,从而更新数据
也就是双向数据绑定:data数据 和 模板页面
对于checkbox、radio、option,需要设置value值,选中后会自动将绑定数据更新为选中项的value值
<span>性别: </span>
<input type="radio" id="female" value="女" v-model="gender">
<label for="female">女</label>
<input type="radio" id="male" value="男" v-model="gender">
<label for="male">男</label><br>
<span>爱好: </span>
<input type="checkbox" id="basket" value="basket" v-model="hobbies">
<label for="basket">篮球</label>
<input type="checkbox" id="foot" value="foot" v-model="hobbies">
<label for="foot">足球</label>
<select v-model="city">
<option value="">未选择</option>
<option value="芬兰">芬兰</option>
</select><br>
将value值设置为初始值即可
<input type="button" value="重置" @click="reset">
reset () {
this.user = {
username: '',
gender: '女',
hobbies: ['foot'],
city: '',
}
v-model.xxx="事件函数"
说明 | |
---|---|
.lazy | 转为change事件进行同步,失焦时触发同步 (v-model默认为input事件,输入即触发同步) |
.number | 将输入值转为数值类型 |
.trim | 过滤输入值的首尾空白字符 |
@
一般写法:v-on:事件类型="事件函数"
简写:@:事件类型="事件函数"
事件函数默认接收一个event对象
<button @click="fn">默认传递event</button>
fn(e) {
...code }
传递自定义参数
<button @click="fn2('abc')">传递自定义参数</button>
fn2(msg) {
}
传递自定义参数和event对象
<button @click="fn3('abc', $event)">传递自定义参数+event</button>
fn3(msg, e) {
}
原理
vue在解析模板会自动在事件函数外层包裹一个函数
($event) => { 事件函数 }
在@事件名后, 加.xxx
来对事件监听进行进一步处理
@click.stop="fn"
说明 | |
---|---|
.stop | 停止事件传播 |
.self | 事件只有绑定事件的元素能触发,点击子元素不能触发 |
.prevent | 阻止事件默认行为 |
.once | 只执行一次 |
.capture | 默认绑定的事件都是在冒泡阶段执行,添加capture修饰符可以设置捕获阶段执行 |
@按钮事件后, 加.xxx
来事件监听进行进一步处理
@keyup.enter="fn"
说明 | |
---|---|
.enter | 监视enter键 |
.keyCode | 监视keyCode对应的按钮,不推荐使用(enter键: 13 |
条件性地渲染内容
这块内容只会在表达式为true时才渲染
v-if="表达式1"
v-else-if="表达式2"
v-else
v-else-if 和 v-else 注意点
v-else-if必须用在紧跟在带有v-if的元素后面的元素上,中间不能有其他元素
v-else必须用在紧跟在带有v-if或v-else-if的元素后面的元素上,中间不能有其他元素
可在上使用 v-if 条件渲染,最终渲染结果不包含
<template v-if="表达式"></template>
v-show="表达式"
通过切换display样式来控制渲染内容的显示和隐藏
带有 v-show 的元素始终会被渲染并保留在 DOM 中
v-if | v-show | |
---|---|---|
false隐藏 | 删除标签 | 修改display为none |
true重新显示 | v-if重新创建 | 修改diplay的样式(不需要创建标签) |
更新显示效率 | 相较慢(需要创建标签) | 更快 |
占用内存 | 相较小 | 相较大 利用空间换时间:隐藏时占用更大的空间,来换取重新显示时更快 |
适用场景 | 1.界面切换频繁 2.DOM较大 |
在模板中初始读取空对象或空数组内部对象中的数据 {a:{}} -> a.b.c (空对象或空数组内部对象为undefined,再向下读取即为读取undefined的数据,会报错) |
⚠v-for 和 v-if/v-show不能同时用在同一个元素上
问题
- v-for的优先级大于v-if,在编译阶段先v-for生成多个虚拟DOM,任何在v-if去掉多个虚拟DOM,造成性能浪费
- v-for和v-if一起使用,可能结果预期不明确
解决方案
遍历时如果部分DOM条件渲染,使用计算属性解决
定义一个计算属性,返回过滤后的列表,再v-for遍历
遍历时如果全部DOM同时一个条件渲染,把v-if写在外层容器上
避免列表渲染和完全隐藏同步执行导致预期不明确
item:数组项
index:下标
v-for="item in 数组"
v-for="(item, index) in 数组"
item:键值
key:键名
v-for="item in 对象"
v-for="(item, key) in 对象"
v-for="item in 字符串"
v-for="(item, index) in 字符串"
数值为多少,就遍历多少次
如10,即从item为从1到10,index为从0到9
v-for="item in 数值"
v-for="(item, index) in 数值"
说明 | |
---|---|
v-text=“content” | 将content设置为标签的innerText |
v-html=“content” | 将content设置为标签的innerHTML |
插值语法相对于v-text更灵活:可以是标签体部分文本
说明 | |
---|---|
v-once | 初始显示后,不再更新,用于不变的数据显示 => 优化内部解析的性能 (v-once显示后不会再更新,即使引用了动态值,动态值更新,也不会变) |
v-pre | 不解析指令/插值语法,直接按结构HTML显示 |
v-cloak存在于元素上直到关联实例结束编译
和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 插值 标签直到实例准备完毕
<div v-cloak>...div>
<style>
div [v-cloak]{
display:
}
style>
ECMAScript5新增的一个静态方法,用来定义一个实例对象
该方法可以指定对象的原型和对象特性,使用现有对象来提供新创建对象的__proto__
Object.create({
对象原型 }, {
对象配置项... })
//对象配置项详写
{
键名: {
value:键值/(get&set),
configurable: true,
//键值是否可以被配置,默认是false,不可被配置(删除操作);
enumerable: true,
//是否可枚举,是否可以被循环,默认值是false
}, 对象配置项2}
对象原型-必选——指定原型对象,可以为null
对象配置项-可选参数
包含一个或多个属性描述符的 JavaScript对象:
创建一个对象的原型
var obj = Object.create({
myname: "张三" });
console.log(obj);
//两种创建方法结果相同
var obj = {
};
obj.__proto__ = {
myname:"张三"};
console.log(obj);
创建一个可控对象
相当于{myname: '张三', age: 20}
var obj = Object.create(
{
},
{
myname: {
// 配置项
value: "张三", // 键值 ;
configurable: true,
//键值是否可以被配置,默认是false,不可被配置(删除操作);
enumerable: true,
//是否可枚举,是否可以被循环,默认值是false
},
age: {
value: 20 },
}
);
console.log(obj);
delete obj.myname
for(var key in obj){
console.log(key);
}
value值属性可以被拆分成get和set
⚠value 与 get&set两者不可同时存在,一个对象里只能有其中一个
get:当访问、获取属性时触发
set:当设置属性时触发
⚠get&set均为异步执行函数
var str = "张三";
var obj = Object.create(
{
},
{
myname: {
// value:"张三", // 不要和get set 一起写 ;
get: function () {
// 当获取myname属性的时候触发;
// 访问 myname属性的时候就是get操作
console.log("get函数被触发了");
return str;
},
set: function (newvalue) {
// 当设置myname的时候会触发set函数;