vue面经

生命周期
ES6中对象的简写,允许对象中只写属性名不写属性值,这时属性值=属性名所代表的变量
vue面经_第1张图片
生命周期:
1.又名:生命周期回调函数、生命周期函数、生命周期钩子.
2.是什么: Vue在 关健时刻帮我们调用的些特殊名称的函数.
3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
4.生命周期函数中的this指向是vm或组件实例对象。
beforeCreate:
此时vue实例只有生命周期函数和一些默认的事件。
--------------------属性赋值、computed计算等
created:
此时已经有data等内容了,但还没有真实dom,对数据等进行修改不会触发update。在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。
例如:数据的初始值就来自于后端,可以发送ajax,或者fetch请求获取数据,但是,此时不会触发updated函数

---------------解析模板的过程:render > template > el
beforeMount:
解析了但是还在内存中,this.$el也有值,但是没有渲染到页面中,即模板中的 {undefined{xx}}还没有内容。
--------------模板编译:用vm中的内容替换模板
mounted:
实例创建期间的最后一个生命周期函数。执行完了mounted函数表示整个Vue实例已经初始化完毕,脱离了创建阶段,进入到了运行阶段。在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted。所以mounted只执行一次。一般来说,我们在此处发送异步请求(ajax,fetch,axios等)、启动定时器、绑定自定义事件、订阅消息等[初始化操作]。
beforeUpdate:
在数据更新之前被调用,发生在虚拟DOM重新渲染和打补丁之前,可以在该钩子中进一步地更改状态,不会触发附加地重渲染过程。数据更新了但是还没修改dom即没更新模板。
updated:
beforeDestroy:
清除定时器、解绑自定义事件、取消订阅消息等[收尾工作]。
destroyed:
关于销毁Vue实例
1.销毁后借助Vue开发者工具看不到任何信息。
2.销毁后自定义事件会失效,但原生DOM事件依然有效。
3.一般不会再beforeDestroy操作数据。因为即便操作数据。也不会再触发更新流程了。

keep-alive

activated
deactivated

父子组件的生命周期

父beforeCreate-> 父create -> 子beforeCreate-> 子created -> 子mounted -> 父mounted
加载渲染过程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

双向数据绑定的原理

Vue 则采用的是数据劫持与发布订阅相结合的方式实现双向绑定,数据劫持主要通过 Object.defineProperty() 来实现,它里面的getter、setter属性。

在编译 HTML 的过程中,会为每个与数据绑定相关的节点生成一个订阅者 watcher,watcher 会将自己添加到相应属性的 dep 容器中。

1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。(数据劫持)在setter函数里面,如果数据变化,就会去通知所有订阅者,订阅者们就会去执行对应的更新的函数。

2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。是Observer和Compile之间通信的桥梁,主要做的事情是:

(1)在自身实例化时往属性订阅器(dep)里面添加自己

(2)自身必须有一个update()方法

(3)待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调。

3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图。(直接固定某个节点进行替换数据的,所以接下来需要实现一个解析器Compile来做解析和绑定工作。)

vue面经_第2张图片

render

createElement有三个参数,第一个参数是创建的标签用字符串,可以是引入的组件,第二个参数是一个对象,属性是attrs(给标签添加属性值)、on(给标签添加事件),或者是传给组件等;第三个参数的标签里面的内容,如文本,它用vue文件中的变量时,用模板字符串。

render: h => h(App) 是下面内容的缩写:

render: function (createElement) {
    return createElement(App);
}
template和虚拟dom
  • 将模板解析为AST(抽象语法树)—— 解析器parse
  • 遍历AST,对 AST 进行静态节点标记,主要用来做虚拟DOM的渲染优化 —— 优化器optimize
  • 使用AST生成render函数代码字符串—— 代码生成器
AST

第一步,词法分析,也叫做扫描scanner。它读取我们的代码,然后把它们按照预定的规则合并成一个个的标识tokens。同时,它会移除空白符,注释,等。最后,整个代码将被分割进一个tokens列表(或者说一维数组)。当词法分析源代码的时候,它会一个一个字母地读取代码,所以很形象地称之为扫描-scans;当它遇到空格,操作符,或者特殊符号的时候,它会认为一个话已经完成了。

第二步,语法分析,也解析器。它会将词法分析出来的数组转化成树形的表达形式。同时,验证语法,语法如果有错的话,抛出语法错误。

创建虚拟结点

img

vue面经_第3张图片

vue面经_第4张图片

  • 在首次渲染时,将虚拟DOM转为真实的DOMvm._update 会调用 __patch__方法,而 patch 方法实际上则是封装了 createPatchFunction方法。

  • createPatchFunction 方法中,先获取旧节点的父元素,然后将虚拟DOM转为真实DOM,插入到旧节点的父元素下。

  • 在将虚拟DOM转为真实DOM时,又将虚拟DOM分为组件类型的虚拟DOM、和普通的虚拟DOM。不同类型的DOM,处理方式也不一样。组件类型的

  • 而普通的虚拟DOM,处理方式如上图。

    vue面经_第5张图片

patch

比较:oldV是否是虚拟节点,是:判断新旧是不是同一结点;不是:把oldV封装成虚拟

​ 是同一节点:判断是否在同一片内存;否:删除旧增加新

​ 是同一个内存对象:不操作;否:判断newV有无text值

​ new有text值:更新oldV的innerText值;无:说明newV有children,判断old

​ oldV有text值:删旧的text,更新DOM用newV的children

​ oldV有children(即两个都有):进行diff算法

diff 比较只会在同层级进行, 不会跨层级比较。
  1. 对newV和oldV都有两个指针指向第一个元素和最后一个元素,
  2. 从前往后扫,命中就后移否则从后往前直到后指针索引<前指针索引
  3. 前不匹配,从后开始,若不匹配,新后与旧前命中则先patch处理,再把旧节点移动都旧后后面
  4. 新前与旧后,命中,放到旧前处
路由守卫
全局守卫

vue-router全局有三个守卫:

  1. router.beforeEach 全局前置守卫 进入路由之前
  2. router.beforeResolve 全局解析守卫(2.5.0+) 在beforeRouteEnter调用之后调用
  3. router.afterEach 全局后置钩子 进入路由之后

使用方法

    // main.js 入口文件
    import router from './router'; // 引入路由
    router.beforeEach((to, from, next) => { 
      next();
    });
    router.beforeResolve((to, from, next) => {
      next();
    });
    router.afterEach((to, from) => {
      console.log('afterEach 全局后置钩子');
    });
to,from,next 这三个参数:

to和from是将要进入和将要离开的路由对象,路由对象指的是平时通过this.$route获取到的路由对象。

next:Function 这个参数是个函数,且必须调用,否则不能进入路由(页面空白)。

  • next() 进入该路由。

  • next(false): 取消进入路由,url地址重置为from路由地址(也就是将要离开的路由地址)。

  • next 跳转新路由,当前的导航被中断,重新开始一个新的导航。

      我们可以这样跳转:next('path地址')或者next({path:''})或者next({name:''})
      且允许设置诸如 replace: true、name: 'home' 之类的选项
      以及你用在router-link或router.push的对象选项。
    
路由独享守卫

如果你不想全局配置守卫的话,你可以为某些路由单独配置守卫:

    const router = new VueRouter({
      routes: [
        {
          path: '/foo',
          component: Foo,
          beforeEnter: (to, from, next) => { 
            // 参数用法什么的都一样,调用顺序在全局前置守卫后面,所以不会被全局守卫覆盖
            // ...
          }
        }
      ]
    })
路由组件内的守卫:
  1. beforeRouteEnter 进入路由前
  2. beforeRouteUpdate (2.2) 路由复用同一个组件时
  3. beforeRouteLeave 离开当前路由时

文档中的介绍:

  beforeRouteEnter (to, from, next) {
    // 在路由独享守卫后调用 不!能!获取组件实例 `this`,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用 可以访问组件实例 `this`
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用,可以访问组件实例 `this`
  }
复制代码

beforeRouteEnter访问this

因为钩子在组件实例还没被创建的时候调用,所以不能获取组件实例 this,可以通过传一个回调给next来访问组件实例 。

但是回调的执行时机在mounted后面,所以在我看来这里对this的访问意义不太大,可以放在created或者mounted里面。

    beforeRouteEnter (to, from, next) {
    console.log('在路由独享守卫后调用');
      next(vm => {
        // 通过 `vm` 访问组件实例`this` 执行回调的时机在mounted后面,
      })
    }

beforeRouteLeave:

导航离开该组件的对应路由时调用,我们用它来禁止用户离开,比如还未保存草稿,或者在用户离开前,将setInterval销毁,防止离开之后,定时器还在调用。

    beforeRouteLeave (to, from , next) {
      if (文章保存) {
        next(); // 允许离开或者可以跳到别的路由 上面讲过了
      } else {
        next(false); // 取消离开
      }
    }
完整的路由导航解析流程(不包括其他生命周期):
  1. 触发进入其他路由。
  2. 调用要离开路由的组件守卫beforeRouteLeave
  3. 调用局前置守卫:beforeEach
  4. 在重用的组件里调用 beforeRouteUpdate
  5. 调用路由独享守卫 beforeEnter
  6. 解析异步路由组件。
  7. 在将要进入的路由组件中调用beforeRouteEnter
  8. 调用全局解析守卫 beforeResolve
  9. 导航被确认。
  10. 调用全局后置钩子的 afterEach 钩子。
  11. 触发DOM更新(mounted)。
  12. 执行beforeRouteEnter 守卫中传给 next 的回调函数
    新增了三个组件:Fragment 支持多个根节点、Suspense 可以在组件渲染之前的等待时间显示指定内容、Teleport 可以让子组件能够在视觉上跳出父组件(如父组件overflow:hidden)
Vue3对比Vue2
  • 新增指令 v-memo,可以缓存 html 模板,比如 v-for 列表不会变化的就缓存,简单说就是用内存换时间
    支持 Tree-Shaking,会在打包时去除一些无用代码,没有用到的模块,使得代码打包体积更小
  • 新增 Composition API 可以更好的逻辑复用和代码组织,同一功能的代码不至于像以前一样太分散,虽然 Vue2 中可以用 minxin 来实现复用代码,但也存在问题,比如方法或属性名会冲突,代码来源也不清楚等
  • 用 Proxy 代替 Object.defineProperty 重构了响应式系统,可以监听到数组下标变化,及对象新增属性,因为监听的不是对象属性,而是对象本身,还可拦截 apply、has 等13种方法
  • 重构了虚拟 DOM,在编译时会将事件缓存、将 slot 编译为 lazy 函数、保存静态节点直接复用(静态提升)、以及添加静态标记、Diff 算法使用 最长递增子序列 优化了对比流程,使得虚拟 DOM 生成速度提升 200%
  • 支持在 里使用 v-bind,给 CSS 绑定 JS 变量(color: v-bind(str))
  • 用 setup 代替了 beforeCreate 和 created 这两个生命周期
  • 新增了开发环境的两个钩子函数,在组件更新时 * onRenderTracked 会跟踪组件里所有变量和方法的变化、每次触发渲染时 onRenderTriggered 会返回发生变化的新旧值,可以让我们进行有针对性调试
  • 毕竟 Vue3 是用 TS 写的,所以对 TS 的支持度更好
    Vue3 不兼容 IE11

你可能感兴趣的:(vue.js)