vue面试知识点

2021-02-22  本文已影响0人  Moon_f3e1

vue 数据双向绑定原理

vue实现数据双向绑定原理主要是:采用数据劫持结合发布订阅设计模式的方式,通过对data的getter/setter方法进行拦截(Object.defineProperty或者Proxy),在getter方法中进行订阅,在setter方法中发布通知,让所有订阅者完成响应。

在vue2.0,当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,通过 getter 方法收集依赖,数据改变时触发 setter 方法, 从而完成该属性的发布通知,通知所有订阅者进行更新,并重新渲染 dom。

在响应式系统中,Vue会为数据模型data的每一个属性新建一个订阅中心作为发布者,而监听器watch、计算属性computed、视图渲染template/render三个角色同时作为订阅者,对于监听器watch,会直接订阅观察监听的属性,对于计算属性computed和视图渲染template/render,如果内部执行获取了data的某个属性,就会执行该属性的getter方法,然后自动完成对该属性的订阅,当属性被修改时,就会执行该属性的setter方法,从而完成该属性的发布通知,通知所有订阅者进行更新。

vue组件通信方式

  1. 父子组件通信
  1. 兄弟组件通信
  1. 跨级组件通信

介绍vuex

只用来读取的状态集中放在store中; 改变状态的方式是提交mutations,这是个同步的事物; 异步逻辑应该封装在action中

state:Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据。

mutations:mutations定义的方法动态修改Vuex 的 store 中的状态或数据。

getters:类似vue的计算属性,主要用来过滤一些数据。

action:actions可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。view 层通过 store.dispath 来分发 action。

什么是Virtual DOM

Virtual DOM 是 DOM 节点在 JavaScript 中的一种抽象数据结构,之所以需要虚拟DOM,是因为浏览器中操作DOM的代价比较昂贵,频繁操作DOM会产生性能问题。虚拟DOM的作用是在每一次响应式数据发生变化引起页面重渲染时,Vue对比更新前后的虚拟DOM,匹配找出尽可能少的需要更新的真实DOM,从而达到提升性能的目的。

介绍 vue 中的 Diff 算法

在新老虚拟DOM对比时

在diff中,只对同层的子节点进行比较,放弃跨级的节点比较,使得时间复杂从O(n^3)降低值O(n),也就是说,只有当新旧children都为多个子节点时才需要用核心的Diff算法进行同层级比较。

keep-alive实现原理

Vue内部将DOM节点抽象成了一个个的VNode节点,keep-alive的缓存是基于VNode节点的而不是直接存储DOM结构。keep-alive 会将需要缓存的VNode节点保存在this.cache中/在render时,如果VNode的name符合在缓存条件(可以用include以及exclude控制),则会从this.cache中取出之前缓存的VNode实例进行渲染。

keep-alive 组件代码:

type VNodeCache = { [key: string]: ?VNode };

const patternTypes: Array<Function> = [String, RegExp]

/* 获取组件名称 */
function getComponentName (opts: ?VNodeComponentOptions): ?string {
  return opts && (opts.Ctor.options.name || opts.tag)
}

/* 检测name是否匹配 */
function matches (pattern: string | RegExp, name: string): boolean {
  if (typeof pattern === 'string') {
    /* 字符串情况,如a,b,c */
    return pattern.split(',').indexOf(name) > -1
  } else if (isRegExp(pattern)) {
    /* 正则 */
    return pattern.test(name)
  }
  /* istanbul ignore next */
  return false
}

/* 修正cache */
function pruneCache (cache: VNodeCache, current: VNode, filter: Function) {
  for (const key in cache) {
    /* 取出cache中的vnode */
    const cachedNode: ?VNode = cache[key]
    if (cachedNode) {
      const name: ?string = getComponentName(cachedNode.componentOptions)
      /* name不符合filter条件的,同时不是目前渲染的vnode时,销毁vnode对应的组件实例(Vue实例),并从cache中移除 */
      if (name && !filter(name)) {
        if (cachedNode !== current) {
          pruneCacheEntry(cachedNode)
        }
        cache[key] = null
      }
    }
  }
}

/* 销毁vnode对应的组件实例(Vue实例) */
function pruneCacheEntry (vnode: ?VNode) {
  if (vnode) {
    vnode.componentInstance.$destroy()
  }
}

/* keep-alive组件 */
export default {
  name: 'keep-alive',
  /* 抽象组件 */
  abstract: true,

  props: {
    include: patternTypes,
    exclude: patternTypes
  },

  created () {
    /* 缓存对象 */
    this.cache = Object.create(null)
  },

  /* destroyed钩子中销毁所有cache中的组件实例 */
  destroyed () {
    for (const key in this.cache) {
      pruneCacheEntry(this.cache[key])
    }
  },

  watch: {
    /* 监视include以及exclude,在被修改的时候对cache进行修正 */
    include (val: string | RegExp) {
      pruneCache(this.cache, this._vnode, name => matches(val, name))
    },
    exclude (val: string | RegExp) {
      pruneCache(this.cache, this._vnode, name => !matches(val, name))
    }
  },

  render () {
    /* 得到slot插槽中的第一个组件 */
    const vnode: VNode = getFirstComponentChild(this.$slots.default)

    const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
    if (componentOptions) {
      // check pattern
      /* 获取组件名称,优先获取组件的name字段,否则是组件的tag */
      const name: ?string = getComponentName(componentOptions)
      /* name不在inlcude中或者在exlude中则直接返回vnode(没有取缓存) */
      if (name && (
        (this.include && !matches(this.include, name)) ||
        (this.exclude && matches(this.exclude, name))
      )) {
        return vnode
      }
      const key: ?string = vnode.key == null
        // same constructor may get registered as different local components
        // so cid alone is not enough (#3269)
        ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
        : vnode.key
      /* 如果已经做过缓存了则直接从缓存中获取组件实例给vnode,还未缓存过则进行缓存 */
      if (this.cache[key]) {
        vnode.componentInstance = this.cache[key].componentInstance
      } else {
        this.cache[key] = vnode
      }
      /* keepAlive标记位 */
      vnode.data.keepAlive = true
    }
    return vnode
  }
}

key属性的作用

在对节点进行diff的过程中,判断是否为相同节点的一个很重要的条件是key是否相等,如果是相同节点,则会尽可能的复用原有的DOM节点。所以key属性是提供给框架在diff的时候使用的,而非开发者。

vue-router 原理

当路由url改变时将触发路由监听事件改变 vue-router 里的 current 变量,从而 current 变量的监听者会获取新的组件并渲染。

vue-router 有 hash 和 history模式:

为什么组件的data必须是一个函数

一个组件可能在很多地方使用,也就是会创建很多个实例,如果data是一个对象的话,对象是引用类型,一个实例修改了data会影响到其他实例,所以data必须使用函数,为每一个实例创建一个属于自己的data,使其同一个组件的不同实例互不影响。

computed 原理

computed 和 watched 的相同点和区别

vue提升白屏时长

vue2.0和vue3.0区别

ssr 原理和优缺点

服务端渲染(SSR):浏览器请求URL,前端服务器接收到URL请求之后,根据不同的URL找到对应的组件,并向后端服务器请求数据,请求完成后,前端服务器会把数据和组件生成一个携带了具体数据的HTML文本,并且返回给浏览器,浏览器得到HTML之后开始渲染页面,同时,浏览器加载并执行 JavaScript 脚本,给页面上的元素绑定事件,让页面变得可交互,当用户与浏览器页面进行交互,如跳转到下一个页面时,浏览器会执行 JavaScript 脚本,向后端服务器请求数据,获取完数据之后再次执行 JavaScript 代码动态渲染页面。

而当客户端拿到服务器渲染的HTML和数据之后,由于数据已经有了,客户端不需要再一次请求数据,而只需要将数据同步到组件或者Vuex内部即可。除了数据意外,HTML也结构已经有了,客户端在渲染组件的时候,也只需要将HTML的DOM节点映射到Virtual DOM即可,不需要重新创建DOM节点,这个将数据和HTML同步的过程,又叫做客户端激活。

优点:

缺点:

上一篇 下一篇

猜你喜欢

热点阅读