Vue interview knowledge points

Vue data two-way binding principle

The principle of Vue's implementation of two-way data binding is mainly: using data hijacking combined with publish and subscribe design patterns, intercepting the getter/setter method of data (Object.defineProperty or Proxy), subscribing in the getter method, and subscribing in the setter method Post a notification and let all subscribers complete the response.

In vue2.0, when an ordinary Javascript object is passed to a Vue instance as its data option, Vue will traverse its properties, collect dependencies through the getter method, and trigger the setter method when the data changes, thereby completing the release notification of the property , notify all subscribers to update, then calculate virtual dom, and re-render dom.

In a responsive system, Vue will create a new subscription center for each attribute of the data model data as the publisher, while the three roles of the listener watch, computed attribute computed, and view rendering template/render serve as subscribers at the same time. For the listener watch , will directly subscribe to the observed and monitored properties. For the computed properties and view rendering template/render, if the internal execution obtains a certain property of data, the getter method of the property will be executed, and then the subscription to the property will be automatically completed. When When the property is modified, the setter method of the property will be executed, so as to complete the publishing notification of the property and notify all subscribers to update.

Vue component communication method

  • Parent-child component communication

    Parent Component -> Child Component: props, provide/inject

    child component -> parent component: $on/$emit

    v-model: Subcomponents use props.valueand inputevents to complete two-way binding

    Get component instance: use $parent/ $children,$refs.xxx

  • Sibling communication

    Event Bus: Every Vue instance is an Event Bus, all support on / on/o n / emit, you can create a new Vue instance between instances of sibling components, and communicate as an Event Bus.

    Vuex: Extract state and methods to Vuex, complete sharing

  • Cross-level component communication

    Parent component -> grandson component: use provide/inject

    Event Bus: Communicate with the sibling component Event Bus

    Vuex: Extract state and methods to Vuex, complete sharing

    $attrs / $listeners

Introducing vuex

The state that is only used for reading is concentrated in the store; the way to change the state is to submit mutations, which is a synchronous thing; asynchronous logic should be encapsulated in action

state: Vuex uses a single state tree, that is, each application will only contain one store instance, but a single state tree does not conflict with modularization. The state of the stored data cannot be directly modified.

mutations: The method defined by mutations dynamically modifies the state or data in the store of Vuex.

Getters: Computational properties similar to vue, mainly used to filter some data.

Action: Actions can be understood as a method of asynchronously processing data by changing the method of processing data in mutations, simply speaking, asynchronously operating data. The view layer triggers actions through store.dispath.

What is Virtual DOM

Virtual DOM is an abstract data structure of DOM nodes in JavaScript. The reason why virtual DOM is needed is that the operation of DOM in the browser is expensive, and frequent operation of DOM will cause performance problems. The role of the virtual DOM is that every time the responsive data changes and the page is re-rendered, Vue compares the virtual DOM before and after the update, and matches to find as few real DOMs that need to be updated as possible, so as to achieve the purpose of improving performance.

Introduce the Diff algorithm in vue

When comparing the old and new virtual DOM

  • First, compare the node itself to determine whether it is the same node, if not, delete the node and recreate the node to replace it
  • If it is the same node, perform patchVnode to determine how to process the child nodes of the node. First, determine the situation where one party has child nodes and the other has no child nodes (if the new children have no child nodes, remove the old child nodes)
  • Compare if there are child nodes, updateChildren is performed to determine how to operate on the child nodes of these old and new nodes (diff core).
  • When matching, find the same child node, recursively compare the child nodes

In diff, only the child nodes of the same layer are compared, and the cross-level node comparison is abandoned, so that the time complexity is reduced from O(n^3) to O(n), that is, only when the old and new children are multiple children Nodes need to use the core Diff algorithm for same-level comparison.

keep-alive implementation principle

Vue internally abstracts DOM nodes into individual VNode nodes, and the keep-alive cache is based on VNode nodes instead of directly storing the DOM structure. keep-alive will save the VNode node that needs to be cached in this.cache/When rendering, if the name of the VNode meets the cache conditions (can be controlled by include and exclude), it will take out the previously cached VNode from this.cache instance to render.

keep-alive component code:

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
  }
}

The role of the key attribute

In the process of diffing nodes, an important condition for judging whether they are the same node is whether the keys are equal. If they are the same node, the original DOM node will be reused as much as possible. So the key attribute is provided for the framework to use when diffing, not the developer.

vue-router principle

When the routing url changes, the routing listening event will be triggered to change the current variable in vue-router, so that the listener of the current variable will get the new component and render it.

Vue-router has hash and history modes:

  • Hash mode: In the browser, the symbol "#", # and the characters behind # are called hash, read with window.location.hash, and monitor hash changes through onhashchange. Features: Although the hash is in the URL, it is not included in the HTTP request; it is used to guide the browser's actions, and is useless for server-side security, and the hash will not reload the page.
  • History mode: the normal path, read with window.location.pathname, adopts the new features of HTML5, and provides two new methods: pushState(), replaceState() can modify the browser history stack, and popState Event listener for state changes.

Why component data must be a function

A component may be used in many places, that is, many instances will be created. If data is an object, the object is a reference type. Modifying data by one instance will affect other instances, so data must use functions to create for each instance A data that belongs to itself, so that different instances of the same component do not affect each other.

computed principle

  • When the component is initialized, computed and data will establish their own response systems, and Observer traverses each attribute in data to set get/set data interception

  • Initializing computed calls the initComputed function

    • Register a watcher instance, and instantiate a Dep message subscriber inside as a follow-up collection dependency (such as the watcher of the rendering function or other watchers that observe the change of the computed property)
    • Calling a computed property triggers its Object.defineProperty get accessor function
    • Call the watcher.depend() method to add a watcher with other attributes to the subs of its own message subscriber dep
    • Call the watcher's evaluate method (and then call the watcher's get method) to make itself a subscriber of other watcher's message subscribers, first assign the watcher to Dep.target, and then execute the getter evaluation function, when accessing the properties in the evaluation function (such as from data, props or other computed), their get accessor functions will also be triggered to add the watcher of the computed property to the message subscriber dep of the watcher of the property in the evaluation function. When these operations are completed, finally Turns off Dep.target assignment to null and returns the evaluation function result.
  • When a property changes, trigger the set interception function, then call the notify method of its own message subscriber dep, traverse the subs array of all subscriber wathcers in the current dep, and call the update method of the watcher one by one to complete the response update.

The similarities and differences between computed and watched

  • Both
    computed and watched are based on Vue's dependency tracking mechanism. When a dependent data changes, all related data or functions that depend on this data will automatically change or be called.

  • the difference

    • Computed is to calculate a new attribute and mount it on vm (Vue instance), while watch is to monitor the data that already exists and has been mounted on vm, so watch can also monitor the change of computed attribute ( Others include data, props)
    • Computed is essentially a lazy evaluation observer with cacheability. Only when the computed property is accessed for the first time after the dependency changes, the new value will be calculated, while watch will call the execution function when the data changes
    • In terms of usage scenarios, computed applies to one data being affected by multiple data, while watch applies to one data affecting multiple data;

vue white screen optimization

  • asynchronous loading
    • Routing lazy loading (vue asynchronous component)
    • vuex asynchronous loading
    • import() in ES6
    • webpack 的 require.ensure
  • webpack subpackage strategy CommonsChunkPlugin
  • Third-party library cdn import

The difference between vue2.0 and vue3.0

  • Refactor the responsive system, use Proxy to replace Object.defineProperty, and use Proxy advantages:
    • The target of monitoring is the object itself, and there is no need to traverse each property like Object.defineProperty, which has a certain performance improvement
    • Object.defineProperty is implemented by changing the original object property label, while proxy is implemented by generating a new proxy object without changing the original object, and the js engine prefers stable objects
    • Can directly monitor data changes of array type
    • Add/delete object properties directly
    • Can intercept 13 methods such as apply, ownKeys, has, etc., but Object.defineProperty cannot
  • Refactor Virtual DOM
    • Redefine the virtual dom comparison idea, distinguish between dynamic and static nodes, and only compare dynamic data nodes
    • Slot optimization, compile slots into lazy functions, and hand over the decision of slot rendering to subcomponents
    • Extraction and reuse of inline events in templates (originally regenerate inline functions for each rendering)
  • Functional programming, that is, the new Composition API, which is convenient for combining logic. For example, multiple mixins are prone to naming conflicts and data sources are not clear. It is difficult to see which mixin a property comes from
  • Code structure adjustment, more convenient for tree shaking, shaking out unused methods, object functions cannot be shaken, making the volume smaller
  • ts support

ssr principle and advantages and disadvantages

Server-side rendering (SSR): The browser requests a URL. After receiving the URL request, the front-end server finds the corresponding components according to different URLs and requests data from the back-end server. After the request is completed, the front-end server generates a The HTML text that carries the specific data is returned to the browser. After the browser gets the HTML, it starts rendering the page. At the same time, the browser loads and executes the JavaScript script, binds events to the elements on the page, and makes the page interactive. When the user interacts with the browser page, such as jumping to the next page, the browser will execute the JavaScript script, request data from the back-end server, and then execute the JavaScript code to dynamically render the page after obtaining the data.
insert image description here

And when the client gets the HTML and data rendered by the server, since the data already exists, the client does not need to request the data again, but only needs to synchronize the data to the component or inside Vuex. In addition to the data accident, the HTML structure already exists. When the client renders the component, it only needs to map the HTML DOM node to the Virtual DOM. There is no need to recreate the DOM node. This process of synchronizing data and HTML, Also known as client activation.

advantage:

  • Good for SEO: In fact, it is good for crawlers to crawl your pages, because some page crawlers do not support JavaScript execution, and the non-SSR pages captured by crawlers that do not support JavaScript execution will be an empty HTML page, while With SSR, these crawlers can obtain the data of the complete HTML structure, and then include it in the search engine.
  • Shorter white screen time: Compared with client-side rendering, server-side rendering has already obtained an HTML text with data after the browser requests the URL. The browser only needs to parse the HTML and directly build the DOM tree. For client-side rendering, you need to get an empty HTML page first. At this time, the page has entered a white screen, and then you need to load and execute JavaScript, request the back-end server to obtain data, and JavaScript render the page to see the final result. page. Especially in complex applications, since JavaScript scripts need to be loaded, the more complex the application, the more and larger the JavaScript scripts that need to be loaded, which will lead to a very long loading time for the first screen of the application, thereby reducing the sense of experience.
  • more secure
  • Cross-system, cross-terminal
  • Avoid repeated filling and verification at the front and back ends
  • Can handle data logic

shortcoming:

  • Code complexity increases. In order to achieve server-side rendering, the application code needs to be compatible with both server-side and client-side running conditions, but some external extension libraries that depend on it can only run on the client side, and require special handling to be able to be used in server-side rendering applications. run.
  • Need more server load balancing. Due to the server's increased demand for rendering HTML, the nodejs service that originally only needs to output static resource files has added data acquisition IO and CPU usage for rendering HTML. If the traffic suddenly increases, it may cause the server to go down. Therefore, it is necessary to Use a responsive caching strategy and prepare the server load accordingly.
  • More requirements covering build setup and deployment. Unlike fully static single-page applications (SPAs), which can be deployed on any static file server, server-rendered applications require a Node.js server runtime.

Guess you like

Origin blog.csdn.net/Moonoly/article/details/113928132