vue 的渲染流程?

  1. new vue

这中间vue开始按顺序注册,调用钩子函数, initLifecircle, initEvents, initRender, callhook(vm, ‘beforeCreate’) initState(vm), 注册初始化数据,第一步使用observe将数据对象转化为响应式数据,第二部使用defineReactive,设置拦截功能,拦截数据获取,实现依赖收集,拦截数据属性更新,实现数据的订阅notify

  1. vue自动执行$mount
  2. 利用compile函数

parse() 将template模板进行编译,转换成AST语法树, AST内包含parseHTML,v-model,v-if,v-for等解析,词法解析,钩子函数,slot预处理等 optimize() 标记AST语法树中的静态节点,方便patch时,diff算法直接绕过静态节点,提升patch的性能 generate() 将上两步处理的AST语法树,转换成render function字符串,由render函数生成虚拟dom

  1. new watcher

创建数据监测实例

  1. update

在watch实例创建之前改变数据不会触发update 在beforeUpdate之前修改数据不会触发渲染

  1. patch

当数据更新变化时候会触发,响应式数据的订阅,也就是watcher的notify,通知当前的subs进行vnode的更新 执行patch方法,通过diff算法对比新旧vnode,更新、删除、修改真是dom

  1. destory

实例的销毁主要是做收尾工作,例如清楚监听器,清楚定时器,清空data对象,移除数据响应式,注销实例

compile的编译原理?

compile的编译原理就是一个字符转换成机器可识别语法断句的过程, 主要是词法分析 -> 语法分析 -> 语义分析 -> 生成中间代码 -> 优化中间代码 -> 生成目标代码 词法分析:是编译过程的第一步,主要是吧从左到右的字符一个个扫描读入源程序,根据构词规则识别单词 语法分析:编译过程的第二部逻辑处理,将词法分析的单词,组合成各类语法断句 语义分析: 编译过程的最后一个逻辑处理,验证语法断句的上下文,以及类型审查。

AST语法树有哪些东西?

  1. Token
  2. parseHtml
  3. 特殊标签解析
  4. v-if、v-for命令解析
  5. 钩子函数
  6. 词法解析
  7. slot插槽作用域处理

computed和watcher的却别?

computed内部有一个惰性的watcher,且有一个订阅实例dep,有缓存机制,通过this.dirty来判断是否需要重新计算,当内部的依赖关系发生变化时,会通知内部的watcher,computed会通过dep实例查找是否有订阅,有的话重新计算,没有的话,将this.dirty = true, 等待有订阅的时候再重新计算 computed主要是有一个缓存机制,当computed的依赖数据变化时候才会重新计算,并将值缓存 watch主要是用于响应式数据更新之后的复杂操作

vue生命周期?

  1. beforeCreate

vue实例化之后执行的第一个内部钩子函数,当前阶段data、method、computed、watch都没法使用

  1. created

实例化之后创建成功才会被调用,当前阶段完成data的响应式数据,可以使用数据,更改数据,此阶段更改数据不会触发update,可以用于初始数据定义获取,无法dom操作,如果非要做的话要使用nextTick函数异步执行

  1. beforeMount

模板渲染即将开始,挂载之前,在这之前已经将template进行compile编译,生成虚拟dom,即将渲染组件,这部更改数据不会触发update

  1. mounted

虚拟dom挂在成功完成,实现了数据的双向绑定,可以进行dom操作,例如$refs

  1. beforeUpdate

数据更新之前,响应式数据发生更新,虚拟dom重新渲染之前,被触发,此阶段可以更改数据,但是不会触发重新渲染

  1. Updated

数据更新之后,当前dom视图已完成重新渲染,此阶段不能设置数据,不然会造成死循环

  1. beforeDestory

组件实例销毁之前主要是用于收尾工作,例如监听器销毁,移除定时器等

  1. destoryed

组件实例完成销毁,数据监听被移除,数据绑定接触

为什么组件的data必须是一个函数返回对象?

因为组件可能会被多处使用,那么data如果是对象的话,对象是引用类型,可能会造成,组件内数据的问题,例如我在组件A修改了数据data的属性,影响了组件B, 所以必须使用函数返回一个新对象,来避免这种情况的发生

v-for和v-if为什么不建议一起使用?

因为v-for的优先级高于v-if,一起使用的话会造成每一个节点都有v-if,造成性能的问题

vue.$nextTick是什么?

vue异步方法,用于渲染的最后一步执行,与JS的事件循环紧密相连,核心就是使用了微任务、宏任务,定义了一个异步方法。 多次使用nextTick会将方法存在任务队列内,异步执行完之后推入执行栈

使用异步组件的优点是什么?

  1. 使用异步组件节省打包时间,异步组件是分开打包,利用jsonp的方式加载,有效的解决打包过大的问题
  2. 核心就是把组件引用定义为了一个函数,依赖import引入,实现了文件的分割

父子组件传值有哪几种方式?

  1. props、$emit
  2. v-model/ computed:value set 使用this.$emit(‘input’)
  3. provide/inject
  4. vuex
  5. eventBus
  6. 父子关系$paren / $children
  7. this.$refs
  8. attrs/ listeners

keep-alive是什么?

vue-router的一个组件,可以实现组件的缓存,当组件切换的时候,组件不会被销毁,有三个入参include正则匹配要缓存的路由 exclude正则匹配不需要缓存的路由,max设置最大缓存数量,超出的话,使用LRU最久未使用算法,更新掉最久未使用的组件

vue的事件绑定原理?

每一个vue实例都可以被看作一个EvenBus,当组件被创建的时候,父组件将事件传递给子组件,子组件初始化的时候,使用$on将事件注册到内部,在需要的时候使用$emit触发函数, 对原生的native事件,使用addeventlisnter绑定到原生dom上

  1. 原生dom绑定,vue在创建组件的时候会调用updatedomlistener方法,通过内部的add方法添加事件
  2. 组件事件绑定,以及自定义事件,通过vue的$on进行绑定,在使用的时候通过4emit调用

vue的模板渲染原理?

vue的template是不符合浏览器标准的,无法被浏览器解析,通过vue内部的compile生成一个render function的字符串,转化为可被浏览器识别的html元素,这个过程叫做编译。 编译主要有三个方法: parse:将template字符串解析,提取指令、事件、标签、属性等转译成AST语法树 optimize: 将转译好的AST字符串,标记静态节点,方便diff算法对比的时候直接绕过静态节点,优化patch的性能 generate: 将优化处理的AST字符串,转换成render function字符串,生成vnode

模板的预编译是什么?

所谓的预编译就是在打包的时候提前将组建编译成render function字符串,当组件被使用的时候直接渲染,大大节省了组件的加载性能

vue的响应式数据是怎么实现的?

响应式数据的核心就是Object.defineProperty对data进行所有属性的重新定义,Object.defineProperty可以设置拦截,在数据获取的时候设置拦截,收集依赖,这个时候dep实例addSub添加依赖, 在属性更新的setter内设置拦截,进而尽心数据的订阅通知,notify 响应式数据在vue实例化的时候,initState内完成,通过observe将数据转换为响应式数据,内部调用defineReactive循环定义响应式变化,进行依赖的收集和订阅。

vue是如何检测数组变化的?

通过重写数组的原型方法进行数据更新拦截,主要是更改数组的sort,splice, shift, unshift, push, pop, reverse这几种方法

vue的diff算法?

vue的diff算法主要是优化了个数算法的时间复杂度,

  1. 最小量更新,做到最小量更新,节点key很重要主要是,这个唯一标识告诉diff算法,这个节点在修改前后是一个节点
  2. v-for使用key的话,会减少dom操作,否则的话将会暴力覆盖,全部替换节点
  3. 同一个虚拟节点会进行精细化对比,否则直接暴力替换
  4. 只会进行同层对比,不会跨层对比

vue diff算法的优化策略?

  1. 先比较开头,一般是后插入或者删除的情况
  2. 对比末尾,前插入或者删除的情况
  3. 字符串头部和尾部对比,
  4. 尾部和旧的头部对比

描述vue组件的渲染以及更新流程?

组件开始渲染的时候会调用vue.extend方法构建子组件的构造函数,完成组件的实例化,最后调用$mount进行挂载, 更新组件的时候会进行patch方法,通过diff方法对比新旧vnode,进行虚拟dom更新

说说vue2.x和vue3.0的区别?

  1. vue3.0使用proxy替换了defineProperty,重构了响应式数据系统
  • proxy可以直接监听数组的变化
  • 数据对象不需要进行轮循收集依赖,性能提升很多
  • 可拦截has、apply、ownkey等多种方法
  • 可以实现对数据对象的属性的新增删除
  1. 重构虚拟dom
  • 优化compile将静态节点设置为常量
  • 优化slot,将slot编译为lazy函数,slot的渲染交给子组件
  • 模板的内联事件可以被复用,vue2.x都是重建
  1. 代码重构更有利于tree-shaking,减少未使用代码的移除,大大减少代码体积
  2. 使用了ts

vue的$set原理是什么?

  1. 如果目标是数组,则直接使用splice进行新的依赖的收集和发布
  2. 如果目标是对象,则检查是否只存在该属性以及该对象是否是响应式,最终通过defineReactive对数据进行响应式处理

vue computed的实现原理?

computed是一个惰性的观察者,内部有一个watcher和一个dep实例,computed的watcher并不会立刻求值, computed通过this.dirty属性判断是否需要重新计算,当依赖状态发生改变的时候,会通知内部的watcher, computed通过this.dep.length来判断是否有订阅,有的话更新,没有的话,将this.dirty=true,等待订阅时重新计算

vue-router的keep-alive的实现原理?

  1. 获取keep-alive下的第一个组件的对象以及组件名称,根据组件是有设置include、exclude正则匹配,来决定是否缓存,未匹配到直接返回实例
  2. 根据组件的ID和tag生成缓存key,并在缓存对象中查找是否命中缓存,如果命中则直接去除缓存,并更新这个key在this.keys的位置,进行LRU的计算
  3. this.cache对象中存储该组件实例并保存key值之后检查缓存实例是否超过max设置的缓存最大值,如果查过了则进行LRU最久未使用算法,删除this.cache中最久未使用的那个组件
  4. 最后设置实例的keep-alive属性为true,该属性将在组件的内部钩子函数使用例如actived,dectived

LRU就是最久未使用算法,从缓存中找出最久未使用的数据,=用于置换新的数据

vue的优化

编码优化:

  1. 路由懒加载,使用异步组件
  2. 使用keep-alive减少组件的重新挂载
  3. 使用事件代理
  4. 拆分组件
  5. 使用v-for使用必须使用唯一的key
  6. 使用防抖节流,重复请求取消

加载性能优化:

  1. UI组件、插件按需加载
  2. 图片懒加载
  3. 使用多路CDN
  4. 减少重绘重排
  5. 开启GZIP
  6. 开启浏览器缓存
  7. 开启http2
  8. 使用webpack优化代码,uglifityPlugin,splitChuck,image-loader,externals

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注