javascript前端知识体系(2)vue篇个人文章
首先你得说说相同点,两个都是MVVM框架,数据驱动视图,无争议。如果说不同,那可能分为以下这么几点:
MVVM的核心是数据驱动即ViewModel,ViewModel是View和Model的关系映射。
MVVM本质就是基于操作数据来操作视图进而操作DOM,借助于MVVM无需直接操作DOM,开发者只需编写ViewModel中有业务,使得View完全实现自动化。
SPA(single-pageapplication)即一个web项目就只有一个页面(即一个HTML文件,HTML内容的变换是利用路由机制实现的。
仅在Web页面初始化时加载相应的HTML、JavaScript和CSS。一旦页面加载完成,SPA不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现HTML内容的变换,UI与用户的交互,避免页面的重新加载。
优点:
缺点:
前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
SEO难度较大:由于所有的内容都在一个页面中动态替换显示,所以在SEO上其有着天然的弱势。
注意:浏览器有8个钩子,但是node中做服务端渲染的时候只有beforeCreate和created
加载渲染过程父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子组件更新过程父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程父beforeUpdate->父updated
销毁过程父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
它的生命周期中有多个事件钩子,让我们控制Vue实例过程更加清晰。
第一次页面加载时会触发beforeCreate,created,beforeMount,mounted这几个钩子
v-if
v-show
不管初始条件是什么,元素总是会被渲染,并且只是简单地基于CSS的“display”属性进行切换。
所以:
背景:
所有的prop都使得其父子prop之间形成了一个单向下行绑定:父级prop的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向变的混乱。
每次父级组件发生更新时,子组件中所有的prop都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变prop。如果你这样做了,Vue会在浏览器的控制台中发出警告。子组件想修改时,只能通过$emit派发一个自定义事件,父组件接收到后,由父组件修改。
有两种常见的试图改变一个prop的情形:
在第2情况下,最好定义一个本地的data属性并将这个prop用作其初始值:
props:['initialCounter'],data:function(){return{counter:this.initialCounter//定义本地的data属性接收prop初始值}}这个prop以一种原始的值传入且需要进行转换。
在这种情况下,最好使用这个prop的值来定义一个计算属性
props:['size'],computed:{normalizedSize:function(){returnthis.size.trim().toLowerCase()}}6.异步请求适合在哪个生命周期调用?官方实例的异步请求是在mounted生命周期中调用的,而实际上也可以在created生命周期中调用。
本人推荐在created钩子函数中调用异步请求,有以下优点:
1.父子props,on
//子组件
//父组件
//A子组件
exportdefault{data(){return{title:'a组件'}},methods:{sayHello(){alert('Hello');}}}//父组件
3.attrs,listeners
listeners::包含了父作用域中的(不含.native修饰器的)v-on事件监听器。它可以通过v-on="$listeners"传入内部组件
//index.vue
浪里行舟
//childCom1.vueboo:{{boo}}
childCom2:{{$attrs}}
//childCom3.vue
childCom3:{{$attrs}}
4.Provide、inject的使用:父组件
子组件
8.什么是SSRSSR也就是服务端渲染,也就是将Vue在客户端把标签渲染成HTML的工作放在服务端完成,然后再把html直接返回给客户端。服务端渲染SSR的优缺点如下:
(1)服务端渲染的优点:
(2)服务端渲染的缺点:
vue-router有3种路由模式:hash、history、abstract,对应的源码如下所示:
switch(mode){case'history':this.history=newHTML5History(this,options.base)breakcase'hash':this.history=newHashHistory(this,options.base,this.fallback)breakcase'abstract':this.history=newAbstractHistory(this,options.base)breakdefault:if(process.env.NODE_ENV!=='production'){assert(false,`invalidmode:${mode}`)}}路由模式的说明如下:
(1)hash模式的实现原理
早期的前端路由的实现就是基于location.hash来实现的。其实现原理很简单,location.hash的值就是URL中#后面的内容。
比如下面这个网站,它的location.hash的值为'#search':
(2)history模式的实现原理
HTML5提供了HistoryAPI来实现URL的变化,其中做最主要的API有以下两个:
这两个API可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,如下所示:
window.history.pushState(null,null,path);window.history.replaceState(null,null,path);history路由模式的实现主要基于存在下面几个特性:
key是为Vue中vnode的唯一标记,通过这个key,我们的diff操作可以更准确、更快速。
Vue的diff过程可以概括为:
oldCh和newCh各有两个头尾的变量oldStartIndex、oldEndIndex和newStartIndex、newEndIndex,它们会新节点和旧节点会进行两两对比,即一共有4种比较方式:newStartIndex和oldStartIndex、newEndIndex和oldEndIndex、newStartIndex和oldEndIndex、newEndIndex和oldStartIndex,如果以上4种比较都没匹配,如果设置了key,就会用key再进行比较,在比较的过程中,遍历会往中间靠,一旦StartIdx>EndIdx表明oldCh和newCh至少有一个已经遍历完了,就会结束比较。
所以Vue中key的作用是:key是为Vue中vnode的唯一标记,通过这个key,我们的diff操作可以更准确、更快速
更快速:利用key的唯一性生成map对象来获取对应节点,比遍历方式更快,源码如下:
虚拟DOM的实现原理主要包括以下3部分:
缺点:
Proxy的优势如下:
Object.defineProperty的优势如下:
Proxy是ES6中新增的功能,它可以用来自定义对象中的操作。
letp=newProxy(target,handler)target代表需要添加代理的对象handler用来自定义对象中的操作,比如可以用来自定义set或者get函数。下面来通过Proxy来实现一个数据响应式:
letonWatch=(obj,setBind,getLogger)=>{lethandler={get(target,property,receiver){getLogger(target,property)returnReflect.get(target,property,receiver)},set(target,property,value,receiver){setBind(value,property)returnReflect.set(target,property,value)}}returnnewProxy(obj,handler)}letobj={a:1}letp=onWatch(obj,(v,property)=>{console.log(`监听到属性${property}改变为${v}`)},(target,property)=>{console.log(`'${property}'=${target[property]}`)})p.a=2//监听到属性a改变为2p.a//'a'=2在上述代码中,通过自定义set和get函数的方式,在原本的逻辑中插入了我们的函数逻辑,实现了在对对象任何属性进行读写时发出通知。
当然这是简单版的响应式实现,如果需要实现一个Vue中的响应式,需要在get中收集依赖,在set派发更新,之所以Vue3.0要使用Proxy替换原本的API原因在于Proxy无需一层层递归为每个属性添加代理,一次即可完成以上操作,性能上更好,并且原本的实现有一些数据更新不能监听到,但是Proxy可以完美监听到任何方式的数据改变,唯一缺陷就是浏览器的兼容性不好。
Vue框架是通过遍历数组和递归遍历对象,从而达到利用Object.defineProperty()也能对对象和数组(部分方法的操作)进行监听。
vue2:
数组就是使用object.defineProperty重新定义数组的每一项,能引起数组变化的方法为pop、push、shift、unshift、splice、sort、reverse这七种,只要这些方法执行改了数组内容,就更新内容
vue3:
改用proxy,可直接监听对象数组的变化。
Vue数据双向绑定主要是指:数据变化更新视图,视图变化更新数据
输入框内容变化时,Data中的数据同步变化。即View=>Data的变化。Data中的数据变化时,文本节点的内容同步变化。即Data=>View的变化。
其中,View变化更新Data,可以通过事件监听的方式来实现,所以Vue的数据双向绑定的工作主要是如何根据Data变化更新View。
Vue主要通过以下4个步骤来实现数据双向绑定的:
v-model指令在表单input、textarea、select等元素上创建双向数据绑定,v-model本质上是语法糖,会在内部为不同的输入元素使用不同的属性并抛出不同的事件:
以input表单元素为例:
相当于
18.组件中data为什么是一个函数?为什么组件中的data必须是一个函数,然后return一个对象,而newVue实例里,data可以直接是一个对象?
//data
data(){return{message:"子组件",childName:this.name}}//newVue
newVue({el:'#app',router,template:'',components:{App}})一个组件被复用多次的话,也就会创建多个实例,本质上,这些实例用的都是同一个构造函数。
如果data是对象的话,对象属于引用类型,会影响到所有的实例,所以为了保证组件不同的实例之间data不冲突,data必须是一个函数。
而newVue的实例,是不会被复用的,因此不存在引用对象的问题。
keep-alive是Vue内置的一个组件,可以使被包含的组件保留状态,避免重新渲染,其有以下特性:
keep-alive的生命周期
比如有父组件Parent和子组件Child,如果父组件监听到子组件挂载mounted就做一些逻辑处理,可以通过以下写法实现:
//Parent.vue
//Child.vue
mounted(){this.$emit("mounted");}以上需要手动通过$emit触发父组件的事件,更简单的方式可以在父组件引用子组件时通过@hook来监听即可,如下所示:
doSomething(){console.log('父组件监听到mounted钩子函数...');},//Child.vue
mounted(){console.log('子组件触发mounted钩子函数...');},//以上输出顺序为://子组件触发mounted钩子函数...//父组件监听到mounted钩子函数...当然@hook方法不仅仅是可以监听mounted,其它的生命周期事件,例如:created,updated等都可以监听。
由于JavaScript的限制,Vue不能检测到以下数组的变动:
为了解决第一个问题,Vue提供了以下操作方法:
//Vue.set
Vue.set(vm.items,indexOfItem,newValue)//vm.$set(Vue.set的一个别名)
vm.$set(vm.items,indexOfItem,newValue)//Array.prototype.splice
vm.items.splice(indexOfItem,1,newValue)为了解决第二个问题,Vue提供了以下操作方法:
//Array.prototype.splice
vm.items.splice(newLength)22.vue2.x中如何监测数组变化使用了函数劫持的方式,重写了数组的方法,Vue将data中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样当调用数组api时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样就实现了监测数组变化。
简单来说,diff算法有以下过程
同级比较,再比较子节点,先判断一方有子节点一方没有子节点的情况(如果新的children没有子节点,将旧的子节点移除)
比较都有子节点的情况(核心diff)递归比较子节点
Vue3.x借鉴了ivi算法和inferno算法在创建VNode时就确定其类型,以及在mount/patch的过程中采用位运算来判断一个VNode的类型,在这个基础之上再配合核心的Diff算法,使得性能上较Vue2.x有了提升。该算法中还运用了动态规划的思想求解最长递归子序列。
简单说,Vue的编译过程就是将template转化为render函数的过程。会经历以下阶段:
首先解析模版,生成AST语法树(一种用JavaScript对象的形式来描述整个模板)。
编译的最后一步是将优化后的AST树转换为可执行的代码。
computed:
watch:
小结:
在下次DOM更新循环结束之后执行延迟回调。在这里面的代码会等到dom更新以后再执行。
点击具体的过程:
Vue3.x改用Proxy替代Object.defineProperty。因为Proxy可以直接监听对象和数组的变化,并且有多达13种拦截方法。并且作为新标准将受到浏览器厂商重点持续的性能优化。
Proxy只会代理对象的第一层,那么Vue3又是怎样处理这个问题的呢?
判断当前Reflect.get的返回值是否为Object,如果是则再通过reactive方法做代理,这样就实现了深度观测。
监测数组的时候可能触发多次get/set,那么如何防止触发多次呢?
我们可以判断key是否为当前被代理对象target自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行trigger。
不能同名因为不管是计算属性还是data还是props都会被挂载在vm实例上,因此这三个都不能同名
找到config/index.js配置文件,找build打包对象里的assetsPublicPath属性默认值为/,更改为./就好了
因为动态添加src被当做静态资源处理了,没有进行编译,所以要加上require。
30.使用vue渲染大量数据时应该怎么优化?说下你的思路!Object.freeze适合一些bigdata的业务场景。尤其是做管理后台的时候,经常会有一些超大数据量的table,或者一个含有n多数据的图表,这种数据量很大的东西使用起来最明显的感受就是卡。但其实很多时候其实这些数据其实并不需要响应式变化,这时候你就可以使用Object.freeze方法了,它可以冻结一个对象(注意它不并是vue特有的api)。
当你把一个普通的JavaScript对象传给Vue实例的data选项,Vue将遍历此对象所有的属性,并使用Object.defineProperty把这些属性全部转为getter/setter,它们让Vue能进行追踪依赖,在属性被访问和修改时通知变化。
使用了Object.freeze之后,不仅可以减少observer的开销,还能减少不少内存开销。
使用方式:
this.item=Object.freeze(Object.assign({},this.item))30.vue自定义指令先了解一下,在vue中,有很多内置的指令.
比如:
所以,关于指令,我们可以总结下面几点:
Vue自定义指令案例1
例如:我们需要一个指令,写在某个HTML表单元素上,然后让它在被加载到DOM中时,自动获取焦点.
//和自定义过滤器一样,我们这里定义的是全局指令Vue.directive('focus',{inserted(el){el.focus()}})
先总结几个点:
指令的生命周期
用指令我们需要:
当一个指令绑定到一个元素上时,其实指令的内部会有五个生命周期事件函数.
Vue.directive('gqs',{bind(){//当指令绑定到HTML元素上时触发.**只调用一次**console.log('bindtriggerd')},inserted(){//当绑定了指令的这个HTML元素插入到父元素上时触发(在这里父元素是`div#app`)**.但不保证,父元素已经插入了DOM文档.**console.log('insertedtriggerd')},updated(){//所在组件的`VNode`更新时调用.console.log('updatedtriggerd')},componentUpdated(){//指令所在组件的VNode及其子VNode全部更新后调用。console.log('componentUpdatedtriggerd')},unbind(){//只调用一次,指令与元素解绑时调用.console.log('unbindtriggerd')}})HTML
那么剩下的三个什么时候触发呢
unbind触发.gif
v-show设置元素的display:block|none,会触发componentUpdated事件
一个把元素从DOM删除触发unbind().--->仅仅是删除.一个显示设置元素的隐藏和显示的时候触发componentUpdated()--->block|none都触发.31.vue实例挂载的过程是什么32.组件和插件有什么区别组件(Component)是用来构成你的App的业务模块,它的目标是App.vue。插件(Plugin)是用来增强你的技术栈的功能模块,它的目标是Vue本身。33.使用vue过程中可能会遇到的问题(坑)有哪些34.动态给vue的data添加一个新的属性时会发生什么根据官方文档定义:如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。
Vue不允许在已经创建的实例上动态添加新的根级响应式属性(root-levelreactiveproperty)。
然而它可以使用Vue.set(object,key,value)方法将响应属性添加到嵌套的对象上。
一个混入对象可以包含任意组件选项。同一个生命周期,混入对象会比组件的先执行。
//暴露两个mixins对象
exportconstmixinsTest1={methods:{hello1(){console.log("hello1");}},created(){this.hello1();},}exportconstmixinsTest2={methods:{hello2(){console.log("hello2");}},created(){this.hello2();},}home
hello2hello1121237.vue的核心是什么数据驱动专注于View层。它让开发者省去了操作DOM的过程,只需要改变数据。组件响应原理数据(model)改变驱动视图(view)自动更新组件化扩展HTML元素,封装可重用的代码。38.vue常用的修饰符有哪些v-model:.trim.numberv-on:.stop.prevent
40.template编译的理解41.axios是什么,如何中断axios的请求42.如何引入scss?安装scss依赖包:
npminstallsass-loader--save-devnpminstallnode-sass--save-dev在build文件夹下修改webpack.base.conf.js文件,在module下的rules里添加配置,如下:
{test:/\.scss$/,loaders:['style','css','sass']}应用:
created():在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测(dataobserver),property和方法的运算,watch/event事件回调。然而,挂载阶段还没开始,$elproperty目前尚不可用。
activated():是在路由设置时,才会有这个生命周期。在被keep-alive缓存的组件激活时调用。
因为在插入数据或者删除数据的时候,会导致后面的数据的key绑定的index变化,进而导致从新渲染,效率会降低
动态组件使用方法
使用标签保存状态,即切换组件再次回来依然是原来的样子,页面不会刷新,若不需要可以去掉。通过事件改变is绑定的isWhich值即可切换成不同的组件,isWhich的值为组件名称。47.vue中怎么重置data使用场景:
比如,有一个表单,表单提交成功后,希望组件恢复到初始状态,重置data数据。
使用Object.assign(),vm.$data可以获取当前状态下的data,vm.$options.data可以获取到组件初始化状态下的data
初始状态下设置data数据的默认值,重置时直接bject.assign(this.$data,this.$options.data())
说明:
...49.style加scoped属性的用途和原理用途:防止全局同名CSS污染原理:在标签加上v-data-something属性,再在选择器时加上对应[v-data-something],即CSS属性选择器,以此完成类似作用域的选择方式50.在vue项目中如何配置favicon将favicon图片放到static文件夹下然后在index.html中添加:
举例来说,ES6在Array对象上新增了Array.from方法。Babel就不会转码这个方法。如果想让这个方法运行,必须使用babel-polyfill,为当前环境提供一个垫片。
强制重新渲染
this.$forceUpdate()强制重新刷新某组件
//模版上绑定key//选项里绑定datadata(){return{theKey:0}}//刷新key达到刷新组件的目的
theKey++;54.vue给组件绑定自定义事件无效怎么解决加入.native修饰符
报错"Method'xxx'hasalreadybeendefinedasadataproperty"
键名优先级:props>data>methods
实例创建之后,可以通过vm.$data访问原始数据对象。Vue实例也代理了data对象上所有的属性,因此访问vm.a等价于访问vm.$data.a。
以_或$开头的属性不会被Vue实例代理,因为它们可能和Vue内置的属性、API方法冲突。可以使用vm.$data._property的方式访问这些属性。
v-model默认的触发条件是input事件,加了.lazy修饰符之后,v-model会在change事件触发的时候去监听
diff算法要求,源码中patch.js中的patchVnode也是根据树状结构进行遍历
生命周期的钩子函数不能使用箭头函数,否者this不能指向vue实例
包裹嵌套其它元素,使元素具有区域性,自身具有三个特点:
解析和转换.vue文件,提取出其中的逻辑代码script、样式代码style、以及HTML模版template,再分别把它们交给对应的Loader去处理。
设置path:'*',并且放在最后一个
为什么要响应参数变化?
解决方案:
使用watch监听
watch:{$route(to,from){if(to!=from){console.log("监听到路由变化,做出相应的处理");}}}向router-view组件中添加key
$route.fullPath是完成后解析的URL,包含其查询参数信息和hash完整路径
在路由实例中配置scrollBehavior(ro,form,savedPosition){//滚动到顶部return{x:0,y:0}//保持原先的滚动位置return{selector:falsy}}74.路由懒加载75.MVVM全称:Model-View-ViewModel,Model表示数据模型层,view表示视图层,ViewModel是View和Model层的桥梁,数据绑定到viewModel层并自动渲染到页面中,视图变化通知viewModel层更新数据。
事件绑定有几种?
解释下这2种的区别:
Vue在更新DOM时是异步执行的,只要侦听到数据变化,将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更,如果同一个watcher被多次触发,只会被推入到队列中一次,这种在缓冲时去除重复数据对于减少不必要的计算和DOM操作是非常重要的.
然后,在下一个的事件循环tick中,Vue刷新队列并执行实际(已去重的)工作,Vue在内部对异步队列尝试使用原生的Promise.then、MutationObserver和setImmediate,如果执行环境不支持,则会采用setTimeout(fn,0)代替。
描述
对于Vue为何采用异步渲染,简单来说就是为了提升性能,因为不采用异步更新,在每次更新数据都会对当前组件进行重新渲染,为了性能考虑,Vue会在本轮数据更新后,再去异步更新视图,举个例子,让我们在一个方法内重复更新一个值。
this.msg=1;this.msg=2;this.msg=3;事实上,我们真正想要的其实只是最后一次更新而已,也就是说前三次DOM更新都是可以省略的,我们只需要等所有状态都修改好了之后再进行渲染就可以减少一些性能损耗。
假设这里是同步更新队列,this.msg=1,大致会发生这些事:
msg值更新->触发setter->触发Watcher的update->重新调用render->生成新的vdom->dom-diff->dom更新这里的dom更新并不是渲染(即布局、绘制、合成等一系列步骤),而是更新内存中的DOM树结构,之后再运行this.msg=2,再重复上述步骤,之后的第3次更新同样会触发相同的流程,等开始渲染的时候,最新的DOM树中确实只会存在更新完成3,从这里来看,前2次对msg的操作以及Vue内部对它的处理都是无用的操作,可以进行优化处理。
如果是异步更新队列,会是下面的情况:
运行this.msg=1,并不是立即进行上面的流程,而是将对msg有依赖的Watcher都保存在队列中,该队列可能这样[Watcher1,Watcher2...],当运行this.msg=2后,同样是将对msg有依赖的Watcher保存到队列中,Vue内部会做去重判断,这次操作后,可以认为队列数据没有发生变化,第3次更新也是上面的过程。
当然,你不可能只对msg有操作,你可能对该组件中的另一个属性也有操作,比如this.otherMsg=othermessage,同样会把对otherMsg有依赖的Watcher添加到异步更新队列中,因为有重复判断操作,这个Watcher也只会在队列中存在一次,本次异步任务执行结束后,会进入下一个任务执行流程,其实就是遍历异步更新队列中的每一个Watcher,触发其update,然后进行重新调用render->newvdom->dom-diff->dom更新等流程,但是这种方式和同步更新队列相比,不管操作多少次msg,Vue在内部只会进行一次重新调用真实更新流程。
此外,组件内部实际使用VirtualDOM进行渲染,也就是说,组件内部其实是不关心哪个状态发生了变化,它只需要计算一次就可以得知哪些节点需要更新,也就是说,如果更改了N个状态,其实只需要发送一个信号就可以将DOM更新到最新,如果我们更新多个值。
this.msg=1;this.age=2;this.name=3;此处我们分三次修改了三种状态,但其实Vue只会渲染一次,因为VIrtualDOM只需要一次就可以将整个组件的DOM更新到最新,它根本不会关心这个更新的信号到底是从哪个具体的状态发出来的。
而为了达到这个目的,我们需要将渲染操作推迟到所有的状态都修改完成,为了做到这一点只需要将渲染操作推迟到本轮事件循环的最后或者下一轮事件循环,也就是说,只需要在本轮事件循环的最后,等前面更新状态的语句都执行完之后,执行一次渲染操作,它就可以无视前面各种更新状态的语法,无论前面写了多少条更新状态的语句,只在最后渲染一次就可以了。
将渲染推迟到本轮事件循环的最后执行渲染的时机会比推迟到下一轮快很多,所以Vue优先将渲染操作推迟到本轮事件循环的最后,如果执行环境不支持会降级到下一轮,Vue的变化侦测机制(setter)决定了它必然会在每次状态发生变化时都会发出渲染的信号,但Vue会在收到信号之后检查队列中是否已经存在这个任务,保证队列中不会有重复,如果队列中不存在则将渲染操作添加到队列中,之后通过异步的方式延迟执行队列中的所有渲染的操作并清空队列,当同一轮事件循环中反复修改状态时,并不会反复向队列中添加相同的渲染操作,所以我们在使用Vue时,修改状态后更新DOM都是异步的。
当数据变化后会调用notify方法,将watcher遍历,调用update方法通知watcher进行更新,这时候watcher并不会立即去执行,在update中会调用queueWatcher方法将watcher放到了一个队列里,在queueWatcher会根据watcher的进行去重,若多个属性依赖一个watcher,则如果队列中没有该watcher就会将该watcher添加到队列中,然后便会在$nextTick方法的执行队列中加入一个flushSchedulerQueue方法(这个方法将会触发在缓冲队列的所有回调的执行),然后将$nextTick方法的回调加入$nextTick方法中维护的执行队列,flushSchedulerQueue中开始会触发一个before的方法,其实就是beforeUpdate,然后watcher.run()才开始真正执行watcher,执行完页面就渲染完成,更新完成后会调用updated钩子。
$nextTick
在上文中谈到了对于Vue为何采用异步渲染,假如此时我们有一个需求,需要在页面渲染完成后取得页面的DOM元素,而由于渲染是异步的,我们不能直接在定义的方法中同步取得这个值的,于是就有了vm.$nextTick方法,Vue中$nextTick方法将回调延迟到下次DOM更新循环之后执行,也就是在下次DOM更新循环结束之后执行延迟回调,在修改数据之后立即使用这个方法,能够获取更新后的DOM。简单来说就是当数据更新时,在DOM中渲染完成后,执行回调函数。
通过一个简单的例子来演示$nextTick方法的作用,首先需要知道Vue在更新DOM时是异步执行的,也就是说在更新数据时其不会阻塞代码的执行,直到执行栈中代码执行结束之后,才开始执行异步任务队列的代码,所以在数据更新时,组件不会立即渲染,此时在获取到DOM结构后取得的值依然是旧的值,而在$nextTick方法中设定的回调函数会在组件渲染完成之后执行,取得DOM结构后取得的值便是新的值。
Js是单线程的,其引入了同步阻塞与异步非阻塞的执行模式,在Js异步模式中维护了一个EventLoop,EventLoop是一个执行模型,在不同的地方有不同的实现,浏览器和NodeJS基于不同的技术实现了各自的EventLoop。浏览器的EventLoop是在HTML5的规范中明确定义,NodeJS的EventLoop是基于libuv实现的。
在浏览器中的EventLoop由执行栈ExecutionStack、后台线程BackgroundThreads、宏队列MacrotaskQueue、微队列MicrotaskQueue组成。
当Js执行时,进行如下流程:
实例#
//Step1console.log(1);//Step2setTimeout(()=>{console.log(2);Promise.resolve().then(()=>{console.log(3);});},0);//Step3newPromise((resolve,reject)=>{console.log(4);resolve();}).then(()=>{console.log(5);})//Step4setTimeout(()=>{console.log(6);},0);//Step5console.log(7);//StepN//...//Result/*1475236*/分析#
在了解异步任务的执行队列后,回到中$nextTick方法,当用户数据更新时,Vue将会维护一个缓冲队列,对于所有的更新数据将要进行的组件渲染与DOM操作进行一定的策略处理后加入缓冲队列,然后便会在$nextTick方法的执行队列中加入一个flushSchedulerQueue方法(这个方法将会触发在缓冲队列的所有回调的执行),然后将$nextTick方法的回调加入$nextTick方法中维护的执行队列,在异步挂载的执行队列触发时就会首先会首先执行flushSchedulerQueue方法来处理DOM渲染的任务,然后再去执行$nextTick方法构建的任务,这样就可以实现在$nextTick方法中取得已渲染完成的DOM结构。
在测试的过程中发现了一个很有意思的现象,在上述例子中的加入两个按钮,在点击updateMsg按钮的结果是321,点击updateMsgTest按钮的运行结果是231。
首先对有数据更新的updateMsg按钮触发的方法进行debug,断点设置在Vue.js的715行,版本为2.4.2,在查看调用栈以及传入的参数时可以观察到第一次执行$nextTick方法的其实是由于数据更新而调用的nextTick(flushSchedulerQueue)语句,也就是说在执行this.msg="Update";的时候就已经触发了第一次的$nextTick方法,此时在$nextTick方法中的任务队列会首先将flushSchedulerQueue方法加入队列并挂载$nextTick方法的执行队列到Promise对象上,然后才是自行自定义的Promise.resolve().then(()=>console.log(2))语句的挂载,当执行微任务队列中的任务时,首先会执行第一个挂载到Promise的任务,此时这个任务是运行执行队列,这个队列中有两个方法,首先会运行flushSchedulerQueue方法去触发组件的DOM渲染操作,然后再执行console.log(3),然后执行第二个微队列的任务也就是()=>console.log(2),此时微任务队列清空,然后再去宏任务队列执行console.log(1)。
接下来对于没有数据更新的updateMsgTest按钮触发的方法进行debug,断点设置在同样的位置,此时没有数据更新,那么第一次触发$nextTick方法的是自行定义的回调函数,那么此时$nextTick方法的执行队列才会被挂载到Promise对象上,很显然在此之前自行定义的输出2的Promise回调已经被挂载,那么对于这个按钮绑定的方法的执行流程便是首先执行console.log(2),然后执行$nextTick方法闭包的执行队列,此时执行队列中只有一个回调函数console.log(3),此时微任务队列清空,然后再去宏任务队列执行console.log(1)。
简单来说就是谁先挂载Promise对象的问题,在调用$nextTick方法时就会将其闭包内部维护的执行队列挂载到Promise对象,在数据更新时Vue内部首先就会执行$nextTick方法,之后便将执行队列挂载到了Promise对象上,其实在明白Js的EventLoop模型后,将数据更新也看做一个$nextTick方法的调用,并且明白$nextTick方法会一次性执行所有推入的回调,就可以明白其执行顺序的问题了,下面是一个关于$nextTick方法的最小化的DEMO。
THE END
1.全身经络疏通接待流程与操作流程操作流程: 有一个k核心,话在手前。 要做什么 先要告知客人。之后再着手做。 a、客人平躺在床上。技师站于客人右侧, 告知客人 项目服务的时长及大概流程。 今天咱们做的全身经络疏通,预计20分钟左右。 做身体的正面背面 从头到脚的穴位按摩 全身的拍打放松。 https://www.jianshu.com/p/b5462acc0c1b
2.18道流程做的很仔细的男士spa店18道流程做的很仔细的男士spa店 各位男同胞们,我要把私藏了很久的这家专业男士SPA店分享出来! 这是今年以来我去过的觉得最赞的SPA店了!集服务+环境+技师+手法与一体的精品店,错过就要后悔莫及了!店里的环境那叫一个气派优雅,等待区很舒适,有很多不同的饮料可以选,随心喝都免费~~ 里面房间超多的,都是...https://m.dianping.com/ugcdetail/226718514?sceneType=0&bizType=29&msource=baiduappugc
3.www.kzjiancai.cn/mmmj31139287.shtml对标管理特别强调持续改进的观念、有循环再生特性的流程。它不是一个短期的活动,也不是一次就完成的活动。通过对对标企业的不断对照和学习,了解人力资源领域最先进的作业技术及管理方式,激发企业人力资源的创意,推动企业的持续改进和不断创新。 (4)打造特色。融合历史文化、国情和实践,探索中国企业人力资源管理的特色...http://www.kzjiancai.cn/mmmj31139287.shtml
4.用于治疗法布里病的方法和组合物与流程21.在一些实施方案中,所述表达构建体以约5.0e+12至1.0e+14个载体基因组/千克(vg/kg)的剂量施用于受试者。 22.在一些实施方案中,将所述表达构建体施用于受试者的肝脏。在其它实施方案中,所述表达载体通过静脉内输注施用于受试者。在还有其它实施方案中,仅将一剂表达构建体施用于所述受试者。 http://xjishu.com/zhuanli/05/202080010090.html
1.体验给起飞降落spa术语起飞降落SPA术语解密:航空知识问答 --- 起飞篇 问题1:什么是V1速度? 回答:V1速度是指在起飞过程中,如果达到该速度后发生引擎故障,飞行员会继续起飞而不中断,保证飞机的安全性能。 问题2:起飞速度中的VR是什么意思? 回答:VR是指旋转速度,即飞机在起飞过程中,飞行员将飞机的机头抬起,使飞机离开地面。 问题3:...https://www.yeeper-dairy.com/news/news_detail/1411n.jpgl?wap=View&aid=802088
2.完整的HOMESPA流程来咯~FORVIL品牌主理人@大忙人施老师从听觉、嗅觉、触觉等五感六觉的角度手把手为大家讲解头部HOME SPA洗前、洗中、洗后的养护流程宅家也能享受一次身心的疗愈快收藏起来,给自己安排一场 #头部按摩 #按摩头皮 #给头部做个护理 #改善头痛偏头痛失眠 #头部护理 #按摩头部仪器 #养发好物 #梳头的好处和功效与作用...https://m.dewu.com/note/trend/details?id=246085997
3.堪称2024最强的前端面试嘲题,让419人成功拿到offer32. 为什么 SPA 应?都会提供?个 hash 路由,好处是什么? 33.[React] 如何进?路由变化监听 34. 单点登录是是什么, 具体流程是什么 35. web 如何禁?别?移除?印 36. 访问?屏了, 原因是啥, 如何排查? https://maimai.cn/article/detail?fid=1835361421&efid=oa5JXyziIhwZpL8_jbOwrw
4.www.nccode.cn/apfccd58852505.shtmlRoblox过去四个季度的现金收入增长超过22%,达到9.552亿美元,同期产生了5.76亿美元的自由现金流。公司发言人强调,这些数据是Roblox一贯向投资者强调的指标,并坚信Roblox是一个安全的平台。 《TheBearCave》是一个专门给那些喜欢做空股票的人看的财经新闻通讯。该网站的运营者EdwinDorsey在2022年的时候发表了一份该平台的...http://www.nccode.cn/apfccd58852505.shtml
5.arab.hbqx.cn/fancai73952922有一个男的把鸡鸡插进美女屁股里的视频软件下载 伊人影院在线观看 免费在黄色视频在哪里 「王者荣耀伽罗太华被×哭还流东西漫画」衍生小说免费阅读_中青网 婷婷五月开心亚洲综合在线 av久草 寡妇门全特级毛片大抽插 美女高潮呻吟久久久极品APP xxx69视频 pr九天狐正能量视频在线观看高清 午夜综合社 cao18 ...http://arab.hbqx.cn/fancai73952922
6.vue面试题51CTO博客18、axios同时请求多个接口,如果当token过期时,怎么取消后面的请求? 答:javascript:void(0)19、从0到1自己构架一个vue项目,说说有哪些步骤、哪些重要插件、目录结构你会怎么组织 答:https://github.com/haizlin/fe-interview/issues/98320、你知道vue的模板语法用的是哪个web模板引擎...https://blog.51cto.com/u_15726982/5690015
7.手边酒店小程序yyfhotel25.0(1).18.rar酒店小程序资源js:50个 需积分: 5148 浏览量2023-06-09上传2.05MBRAR 【手边酒店小程序 yyf_hotel 25.0(1).18.rar】是一个针对酒店行业的微信小程序应用,主要用于提供便捷的酒店预订、管理和服务功能。这个版本号为25.0.18的更新包,包含了安装和升级的所有必要文件,确保用户能够顺利地在现有版本基础上进行升级或全新安装。 https://download.csdn.net/download/2301_78600126/87884082
8.pseinu.cn/xxxr/768887.shtml2023年5月,克劳斯公告披露,KM集团2022年度和2023年第一季度持续亏损。为进一步降低运营成本,KM集团拟实施以组织结构优化、组织流程优化、工作岗位精简、降本提质增效为核心的调整计划和效率计划项目。在此次调整中,KM集团拟在全球范围内精简不超过790个工作岗位,在德国精简不超过490个工作岗位。 https://pseinu.cn/xxxr/768887.shtml
9.www.huojuyude.com/yyys40650094上海少妇丁字裤SPA按摩 伦理片免费 日韩经典久久久 嫩白臀 春药痉挛HDvideosex 久久久婷婷精品人妻8888 前面两个小兔兔被?的上下抖 欧美赛三A大片免费看 日本操逼网站 男人把鸡巴插进女人的那个朱竹清 大陆偷拍 兰氏牝户 你把我片儿操逼视频 91久久工口网站 中国小臊逼丁香按摩推油按...http://www.huojuyude.com/yyys40650094
10.美容院活动方案(精选20篇)一、邀宴客人:请老顾客带2个以上朋友参加,国庆节发廊活动方案。 二、会议流程:(18:30——21:00) 1、主持人公布晚会开始。 2、美容师表演《感恩的心》。 3、老板代表致感谢词。 4、颁奖。 A、感谢20xx年对xx美容院给予的最大支持、消费最高的忠实朋友xxx特奖大礼一份。 https://www.unjs.com/huodongfangan/202211/5866702.html
11.美容院活动方案(通用20篇)一、邀请客人:请老顾客带2个以上朋友参加。(4.15-4.19) 二、会议流程:(18:30——21:00) (1)主持人宣布晚会开始 (2)美容师表演《感恩的心》 (3)老板代表致感谢词 (4)颁奖A感谢20xx年对xxx美容院给予的支持、消费的'忠实朋友xxxxxxxxx特奖大礼一份xxxxxxxx,凡是xx美容院新老顾客均有一份精美礼品xxxxxxxxx(...https://www.oh100.com/kaidian/3968050.html