一.学习vue的前知识准备
1.1DOM是什么?什么是虚拟DOM
在学js是大家肯定都知道DOM是网页中用来表示文档中对象的标准模型,通过JavaScript可以对网页中的所有DOM对象进行操作,是由万维网联盟W3C组织制定的标准编程接口。简单来说就是我们可以通过DOM控制页面中的元素做你想让它们完成的事情,无论是增删改查还是完成一个事件,这里举一个例子:
<!DOCTYPE html>
<html lang="en">
<head>
<Meta charset="UTF-8">
<Meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<script>
window.onload=function(){
// 下面的id=dd的按钮对象也是dom对象,所以我们使用getElementById方法就可以的获取到对象
var dd=document.getElementById("dd")
// 设置了一个单击的事件
dd.onclick=function(){
// 单机以后就会发生警告
alert("点了一下")
}
}
</script>
<body>
<!-- 设置了一个按钮 -->
<button id="dd">点我一下</button>
</body>
</html>
为什么要讲这个dom对象,因为在vue中有个虚拟dom,也正是因为vue2.0引入了虚拟dom,它的初始化渲染的效率大大提高了,那什么是虚拟dom呢?
Virtual DOM 其实就是一棵以 JavaScript 对象( VNode 节点)作为基础的树,用对象属性来描述节点,实际上它只是一层对真实 DOM 的抽象。最终可以通过一系列操作使这棵树映射到真实环境上。
简单来说,可以把Virtual DOM 理解为一个简单的JS对象,并且最少包含标签名( tag)、属性(attrs)和子元素对象( children)三个属性。不同的框架对这三个属性的命名会有点差别。
对于虚拟DOM,咱们来看一个简单的实例,就是下图所示的这个,详细的阐述了模板 → 渲染函数 → 虚拟DOM树 → 真实DOM
的一个过程
因为DOM操作的执行速度远不如Javascript的运算速度快,因此,把大量的DOM操作搬运到Javascript中,运用patching算法来计算出真正需要更新的节点,最大限度地减少DOM操作,从而显著提高性能。
1.2什么是MVVM模型?
MVVM简介
MVVM 是Model-View-viewmodel 的缩写,它是一种基于前端开发的架构模式,其核心是提供对View 和 viewmodel 的双向数据绑定,这使得viewmodel 的状态改变可以自动传递给 View,即所谓的数据双向绑定。
Vue.js 是一个提供了 MVVM 风格的双向数据绑定的 Javascript 库,专注于View 层。它的核心是 MVVM 中的 VM,也就是 viewmodel。 viewmodel负责连接 View 和 Model,保证视图和数据的一致性,这种轻量级的架构让前端开发更加高效、便捷。
在MVVM的架构下,View层和Model层并没有直接联系,而是通过viewmodel层进行交互。viewmodel层通过双向数据绑定将View层和Model层连接了起来,使得View层和Model层的同步工作完全是自动的。
一方面它实现了Data Binding, 也就是数据绑定, 将Model的改变实时的反应到View中另一方面它实现了DOM Listener, 也就是DOM监听, 当DOM发生一些事件(点击, 滚动, touch等)时, 可以监听到, 并在需要的情况下改变对应的Data.
<!DOCTYPE html>
<html>
<head>
<Meta charset="utf-8">
<title></title>
<script src="vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="app">
<!-- 设置两个输入框 -->
<input type="text" v-model="msg" /><br/>
<input type="text" v-model="msg" />
<!-- 设置一个p的显示为hello+data的msg内容 -->
<p>hello{{msg}}</p>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// 设置data中msg值 此时的'我是msg'可以理解为默认值
let data={
msg:'我是msg'
}
const vm= new Vue({
// 指定vue来管理页面中的id=app的div块
el:'#app',
data:data
})
</script>
</html>
1.3Vue.js 是什么
Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
1.4vue中的attribute 和 property 是什么意思
简单的说,attribute 是元素标签的属性,property 是元素对象的属性,例如:
<input id="input" value="test value">
<script>
let input = document.getElementById('input');
console.log(input.getAttribute('value')); // test value
console.log(input.value); // test value
</script>
input 的 value attribute 是通过标签里的 value=“test value” 定义的,可以通过input.getAttribute(‘value’) 获取,可以通过 input.setAttribute(‘value’, ‘New Value’) 更新
input 的 value property 可通过 input.value 获取和更新,初始值是与 attribute 中的赋值一致的
二.理解vue的钩子函数,vue的生命周期理解,什么是vue的生命周期,钩子函数
2.1vue的生命周期的理解
生命周期
用人举例说明:
生命周期就是一个人的一生。
从人的出生,到成长,到工作,到死亡,就是人的一生,也叫一个人的生命周期。
- 对象的生命周期
在程序开发中,是要使用对象的。那么,对象的生命周期就是:从对象的创建,到使用对象,到对象的消亡就是对象的生命周期。
用人和对象进行类比:
人 | 程序中的对象 |
---|---|
人出生 | New对象 |
人工作(ps:要人的目的就是为了工作,如果一个人不工作,不为国家做贡献,那就不是合格的人,活着没有意义) | 使用对象的方法和属性(ps:new的对象的目的就是为了用它,用对象主要就是使用对象的方法和属性) |
人死亡 | 对象使用完就该消亡了(没用了就不要占用内存。) |
2.2vue生命周期经历的阶段
生命周期是有不同的阶段的,就像人一样,有幼儿期,童年期,少年期,青年期,中年期,老年期。每个阶段应该做不同的事情,但是每个人做的事情又不尽相同。
Vue对象的生命周期也分不同的阶段,不同的阶段也可以做不同的事情,但是不同的vue(组件)对象在不同的阶段做的事情也不尽相同,所以,每个vue组件的代码不相同。
Vue生命周期经历哪些阶段:
总体来说:初始化、运行中、销毁
详细来说:开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、销毁等一系列过程
2.3生命周期经历的阶段和钩子函数
实例化vue(组件)对象:new Vue()
初始化事件和生命周期 init events 和 init cycle
beforeCreate
函数:
在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
即此时vue(组件)对象被创建了,但是vue对象的属性还没有绑定,如data属性,computed属性还没有绑定,即没有值。
此时还没有数据和真实DOM。
即:属性还没有赋值,也没有动态创建template属性对应的HTML元素(二阶段的createUI函数还没有执行)
- 挂载数据(属性赋值)
Created
函数:
vue对象的属性有值了,但是DOM还没有生成,$el属性还不存在。
此时有数据了,但是还没有真实的DOM
即:data,computed都执行了。属性已经赋值,但没有动态创建template属性对应的HTML元素,所以,此时如果更改数据不会触发updated函数
如果:数据的初始值就来自于后端,可以发送ajax,或者fetch请求获取数据,但是,此时不会触发updated函数
-
检查
1)检查是否有el属性
检查vue配置,即new Vue{}里面的el项是否存在,有就继续检查template项。没有则等到手动绑定调用vm.$mount()
完成了全局变量$el的绑定。
2)检查是否有template属性
检查配置中的template项,如果没有template进行填充被绑定区域,则被绑定区域的el对象的outerHTML(即整个#app DOM对象,包括
和 标签)都作为被填充对象替换掉填充区域即:如果vue对象中有 template属性,那么,template后面的HTML会替换$el对应的内容。如果有render属性,那么render就会替换template。
即:优先关系时: render > template > el
7 beforeMount
函数:
模板编译(template)、数据挂载(把数据显示在模板里)之前执行的钩子函数
此时 this.$el有值,但是数据还没有挂载到页面上。即此时页面中的{{}}里的变量还没有被数据替换
Mounted
函数:
模板编译完成,数据挂载完毕
即:此时已经把数据挂载到了页面上,所以,页面上能够看到正确的数据了。
一般来说,我们在此处发送异步请求(ajax,fetch,axios等),获取服务器上的数据,显示在DOM里。
beforeUpdate
函数:
组件更新之前执行的函数,只有数据更新后,才能调用(触发)beforeUpdate,注意:此数据一定是在模板上出现的数据,否则,不会,也没有必要触发组件更新(因为数据不出现在模板里,就没有必要再次渲染)
数据更新了,但是,vue(组件)对象对应的dom中的内部(innerHTML)没有变,所以叫作组件更新前
updated
函数:
vue(组件)对象对应的dom中的内部(innerHTML)改变了,所以,叫作组件更新之后
代码样例:
<!DOCTYPE html>
<html lang="en">
<head>
<Meta charset="UTF-8">
<Meta name="viewport" content="width=device-width, initial-scale=1.0">
<Meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vue生命周期学习</title>
</head>
<body>
<div id="app">
<h1>{{message}}</h1>
<h1>count:{{count}}</h1>
</div>
<input id="btn01" type="button" value="测试" />
</body>
<script type="text/javascript" src="js/vue.min.js" ></script>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Vue的生命周期',
age:2
},
computed:{
count:function(){
return this.age+1;
}
},
// template:"<p>vue对象中的template的内容</p>",
// render: function(createElement) {
// return createElement('h1', 'this is createElement')
// },
beforeCreate: function() {
console.group('------beforeCreate创建前状态------');
console.log("%c%s", "color:red" , "el : " + this.$el); //undefined
console.log("%c%s", "color:red","data : " + this.$data); //undefined
console.log("%c%s", "color:red","count : " + this.count); //undefined
console.log("%c%s", "color:red","message: " + this.message)
},
created: function() {
console.group('------created创建完毕状态------');
console.log("%c%s", "color:red","el : " + this.$el); //undefined
console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
console.log("%c%s", "color:red","count : " + this.count); //undefined
console.log("%c%s", "color:red","message: " + this.message); //已被初始化
},
//完成了el的绑定
beforeMount: function() {
console.group('------beforeMount挂载前状态------');
console.log("%c%s", "color:red","el : " + (this.$el)); //已被初始化
console.log(this.$el.innerHTML);
console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
console.log("%c%s", "color:red","message: " + this.message); //已被初始化
},
mounted: function() {
console.group('------mounted 挂载结束状态------');
console.log("%c%s", "color:red","el : " + this.$el); //已被初始化
console.log(this.$el);
console.log(this.$el.innerHTML);
console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
console.log("%c%s", "color:red","message: " + this.message); //已被初始化
},
beforeUpdate: function () {
console.group('beforeUpdate 更新前状态===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el.innerHTML);
console.log("%c%s", "color:red","data : " + this.$data.message);
console.log("%c%s", "color:red","message: " + this.message);
}
,
updated: function () {
console.group('updated 更新完成状态===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el.innerHTML);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message);
},
// beforeDestroy: function () {
// console.group('beforeDestroy 销毁前状态===============》');
// console.log("%c%s", "color:red","el : " + this.$el);
// console.log(this.$el);
// console.log("%c%s", "color:red","data : " + this.$data);
// console.log("%c%s", "color:red","message: " + this.message);
// },
// destroyed: function () {
// console.group('destroyed 销毁完成状态===============》');
// console.log("%c%s", "color:red","el : " + this.$el);
// console.log(this.$el);
// console.log("%c%s", "color:red","data : " + this.$data);
// console.log("%c%s", "color:red","message: " + this.message)
// }
});
document.getElementById("btn01").onclick = function(){
vm.message="改了";
}
补充:三.Promise用法
3.1.什么是Promise
Promise 是异步编程的一种解决方案,其实是一个构造函数,自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法。(ps:什么是原型:https://blog.csdn.net/qq_34645412/article/details/105997336)
Promise对象有以下两个特点。
(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
下面先 new一个Promise
let p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('执行完成Promise');
resolve('要返回的数据可以任何数据例如接口返回数据');
}, 2000);
});
刷新页面会发现控制台直接打出
其执行过程是:执行了一个异步操作,也就是setTimeout,2秒后,输出“执行完成”,并且调用resolve方法。
注意!我只是new了一个对象,并没有调用它,我们传进去的函数就已经执行了,这是需要注意的一个细节。所以我们用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数,如:
<div onClick={promiseClick}>开始异步请求</div>
const promiseClick =()=>{
console.log('点击方法被调用')
let p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('执行完成Promise');
resolve('要返回的数据可以任何数据例如接口返回数据');
}, 2000);
});
return p
}
当放在函数里面的时候只有调用的时候才会被执行
那么,接下里解决两个问题:
1、为什么要放在函数里面
2、resolve是个什么鬼
我们包装好的函数最后,会return出Promise对象,也就是说,执行这个函数我们得到了一个Promise对象。接下来就可以用Promise对象上有then、catch方法了,这就是Promise的强大之处了,看下面的代码:
promiseClick().then(function(data){
console.log(data);
//后面可以用传过来的数据做些其他操作
//......
});
这样控制台输出:
先是方法被调用起床执行了promise,最后执行了promise的then方法,then方法是一个函数接受一个参数是接受resolve返回的数据这事就输出了‘要返回的数据可以任何数据例如接口返回数据’
这时候你应该有所领悟了,原来then里面的函数就跟我们平时的回调函数一个意思,能够在promiseClick这个异步任务执行完成之后被执行。这就是Promise的作用了,简单来讲,就是能把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。
你可能会觉得在这个和写一个回调函数没有什么区别;那么,如果有多层回调该怎么办?如果callback也是一个异步操作,而且执行完后也需要有相应的回调函数,该怎么办呢?总不能再定义一个callback2,然后给callback传进去吧。而Promise的优势在于,可以在then方法中继续写Promise对象并返回,然后继续调用then来进行回调操作。
所以:精髓在于:Promise只是能够简化层层回调的写法,而实质上,Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活的多。所以使用Promise的正确场景是这样的:
promiseClick()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return runAsync3();
})
.then(function(data){
console.log(data);
});
这样能够按顺序,每隔两秒输出每个异步回调中的内容,在runAsync2中传给resolve的数据,能在接下来的then方法中拿到。
(Ps:此处执行多次是因为研究该用法的时候我在一个react的demo中进行的,该页面多个元素改变导致页面多次渲染执行所致,正常页面只渲染一次的话就所有只会执行一次)
3.2reject的用法
以上是对promise的resolve用法进行了解释,相当于resolve是对promise成功时候的回调,它把promise的状态修改为
fullfiled,那么,reject就是失败的时候的回调,他把promise的状态修改为rejected,这样我们在then中就能捕捉到,然后执行“失败”情况的回调。
(PS:此处也是执行多次所以输出多次,执行多次的原因和上次原因一致)
以上代码:调用promiseClick方法执行,2秒后获取到一个随机数,如果小于10,我们算成功,调用resolve修改Promise的状态为fullfiled。否则我们认为是“失败”了,调用reject并传递一个参数,作为失败的原因。并将状态改成rejected
运行promiseClick并且在then中传了两个参数,这两个参数分别是两个函数,then方法可以接受两个参数,第一个对应resolve的回调,第二个对应reject的回调。(也就是说then方法中接受两个回调,一个成功的回调函数,一个失败的回调函数,并且能在回调函数中拿到成功的数据和失败的原因),所以我们能够分别拿到成功和失败传过来的数据就有以上的运行结果
3.3catch的用法
与Promise对象方法then方法并行的一个方法就是catch,与try catch类似,catch就是用来捕获异常的,也就是和then方法中接受的第二参数rejected的回调是一样的,如下:
function promiseClick(){
let p = new Promise(function(resolve, reject){
setTimeout(function(){
var num = Math.ceil(Math.random()*20); //生成1-10的随机数
console.log('随机数生成的值:',num)
if(num<=10){
resolve(num);
}
else{
reject('数字太于10了即将执行失败回调');
}
}, 2000);
})
return p
}
promiseClick().then(
function(data){
console.log('resolved成功回调');
console.log('成功回调接受的值:',data);
}
)
.catch(function(reason, data){
console.log('catch到rejected失败回调');
console.log('catch失败执行回调抛出失败原因:',reason);
});
这里就不放运行结果,自己跑一遍思路就很清晰了
效果和写在then的第二个参数里面一样。它将大于10的情况下的失败回调的原因输出,但是,它还有另外一个作用:在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。如下:
function promiseClick(){
let p = new Promise(function(resolve, reject){
setTimeout(function(){
var num = Math.ceil(Math.random()*20); //生成1-10的随机数
console.log('随机数生成的值:',num)
if(num<=10){
resolve(num);
}
else{
reject('数字太于10了即将执行失败回调');
}
}, 2000);
})
return p
}
promiseClick().then(
function(data){
console.log('resolved成功回调');
console.log('成功回调接受的值:',data);
console.log(noData);
}
)
.catch(function(reason, data){
console.log('catch到rejected失败回调');
console.log('catch失败执行回调抛出失败原因:',reason);
});
在resolve的回调中,我们console.log(noData);而noData这个变量是没有被定义的。如果我们不用Promise,代码运行到这里就直接在控制台报错了,不往下运行了。但是在这里,会得到上图的结果,也就是说进到catch方法里面去了,而且把错误原因传到了reason参数中。即便是有错误的代码也不会报错了
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。