Sort out the vuejs interview questions. Personally, I think the framework should start from the source code principle. For data responsiveness, the source code principle of the virtual DOM should be deliberately repeated. Have seen a program especially yuxi said his learning method is to see the source code to write the source code, avenue to simple.

The life cycle

A Vue instance has a full life cycle, that is, from the start of creation, initialization of data, compilation of templates, Dom mounting -> render, update -> render, uninstall, etc. We call this the life cycle of a Vue.

Created: the component instance is completely created and properties are bound, but the real dom is not generated, $el is not available beforeMount: Mounted: the hook el is replaced by the newly created vm.$el and mounted to the instance before the hook beforeUpdate is called: Update: after component data is updated activited: keep-alive Exclusive: deadctivated when the component is activated Keep-alive exclusive, called when a component is destroyed. BeforeDestory: called before the component is destroyed. Destoryed: called after the component is destroyedCopy the code

Why is data a function in a component?

  1. Vue components may need to be reused, so there are multiple instances. If you define data in object form, they may share a single data object, and state changes will affect all component instances. This is certainly unreasonable.
  2. Therefore, it is defined in the form of a function and returns a new data object as a factory function when initData is used to effectively avoid the problem of state pollution between multiple instances.
  3. However, there is no such limitation in the Vue root instance creation process. The root instance can get the instance when merging options, so it can effectively avoid the data option verification. Because there can only be one root instance.

The difference between computed and Watch

  1. Computed attributes: Depend on other attribute values, and computed values are cached. If the attribute values it depends on change, computed values will be recalculated the next time it gets computed values.

  2. Watch listener: More of a listener, no cache, similar to some data listener callbacks that are executed for subsequent operations whenever the monitored data changes.

  3. Usage scenario: Use computed if you need to do numerical calculations and rely on other data, because you can take advantage of the caching nature of computed and avoid recalculating each time you get a value. Watch should be used if we need to perform asynchronous or expensive operations when data changes. Using the Watch option allows us to perform asynchronous operations (accessing an API), limits how often we perform that operation, and sets the intermediate state until we get the final result. These are all things you can’t do with computed properties.

V – if, v – show, the principle of v – HTML

  1. V-if is true conditional rendering because it ensures that event listeners and subcomponents within the conditional block are properly destroyed and rebuilt during the switch; Also lazy: if the condition is false during the initial render, nothing is done — the conditional block does not start rendering until the condition is true for the first time. V-if is a ternary expression in the source code, which needs to be compiled into the render function to execute, like template is compiled into the render function to execute.

  2. V-show is much simpler. Regardless of the initial conditions, elements are always rendered and simply switched based on the display property of CSS.

  3. Therefore, V-IF is suitable for scenarios where conditions are rarely changed at runtime and do not need to be switched frequently; V-show is suitable for scenarios that require very frequent switching of conditions.

Priority of V-IF and V-for

  1. V-for is resolved before V-if
  2. V-for and V-if occur at the same time, and v-for has a higher priority, which is equivalent to executing an IF judgment in each for loop.
  3. So we shouldn’t put them together, because even if we render only a small part of the list, we still have to traverse the entire list every time we re-render.
  4. We’re going to write v-if in the outer layer, and then v-for, so we’re going to go through the for loop after the if.
  5. Actual scenario:
    • To filter items in the list (e.gv-for="user in users" v-if="user.isActive"). At this point, define a calculated property (e.gactiveUsers) to return to the filtered list.
    • To avoid rendering lists that should be hidden (e.gv-for="user in users" v-if="shouldShowUsers"). At this point thev-ifMove to a container element (e.gul,ol).

Two-way data binding

  1. Two-way binding in VUE is an instruction V-model that can bind a dynamic value to a view and change the value as the view changes. V-model is syntax sugar, equivalent to :value and @input by default.

  2. Using v-Model can reduce a lot of tedious event handling code, improve development efficiency, and make the code more readable

  3. Native form items can be used directly with v-Models, and custom components need to bind values within the component and handle input events to use them

  4. After testing, the output component rendering function that contains the V-Model template is converted to a binding of the value property and an event listener that updates the corresponding variable in the event callback function.

Response principle

Vue. js adopts data hijacking combined with publishe-subscribe mode. It hijacks the setter and getter of each attribute through Object.defineProperty, releases messages to subscribers when data changes, and triggers the response listening callback

Core implementation class:

  1. Observer: It adds getters and setters to properties of an object for dependency collection and distribution of updates

  2. Dep: Collects the dependencies of the current responsive objects. Each responsive object, including its children, has a Dep instance (where subs is an array of Watcher instances). When data changes, each Watcher is notified via dep.notify().

  3. Watcher: an observer object. The instance can be divided into three types: render Watcher (render Watcher), computed attribute Watcher (computed Watcher), and listener Watcher (user Watcher)

  4. Relationship between Watcher and Dep: The Dep is instantiated in Watcher and adds subscribers to dep.subs, which notifies each Watcher of updates by traversing dep.subs with notify.

Depend on the collection

  1. InitState triggers a computed Watcher dependency collection when the computed property is initialized
  2. When initState, user Watcher dependency collection is triggered when the listener property is initialized
  3. The render() procedure triggers the Render Watcher dependency collection
  4. When re-render is executed,vm.render() removes the watcer subscriptions in all subs and reassigns them.

Distributed update

  1. The data in the response is modified in the component, triggering the logic of the setter
  2. Call dep. Notify ()
  3. Iterate through all subs (Watcher instances) and call the Update method of each Watcher.

The principle of summary

  1. Data responsiveness is a mechanism that enables data changes to be detected and responded to.

  2. One of the core issues to be solved in the MVVM framework is the connection between the data layer and the view layer. Through data-driven applications, data changes, and view updates, this requires responsive processing of data, so that when data changes, it can be updated immediately.

  3. Taking VUE as an example, through the data responsiveness plus virtual DOM and Patch algorithm, we only need to operate data without having to touch the complicated DOM operation, thus greatly improving the development efficiency and reducing the difficulty of development.

  4. The data response in VUe2 is handled differently according to the data type. If the data is an Object, we define data interception using object.defineProperty (). When the data is accessed or changed, we sense and respond. If it is an array, extend its seven change methods by overwriting the array’s prototype methods so that they can respond with additional update notifications. This mechanism solves the problem of data responsiveness well, but it also has some disadvantages in practice: for example, recursive traversal during initialization can cause performance loss; To add or delete attributes, users need to use a special API such as vue. set/delete. Data structures such as Map and Set generated in ES6 are not supported.

  5. To solve these problems, vue3 rewrote this part of the implementation: Using ES6 Proxy mechanism to Proxy reactive data, it has many benefits, the programming experience is consistent, no special API is required, initialization performance and memory consumption are greatly improved; In addition, because the reactive implementation code is extracted into a separate Reactivity package, we can use it more flexibly, and we don’t even need to introduce VUE to experience it.

Vm.$set() implementation principle

  1. First, you need to consider whether vUE can directly detect array changes. Of course it can’t. Object.defineproperty can detect changes to an array, but only changes to an existing element inside the array, not new elements or attributes. However, vue doesn’t use Object.defineProperty internally to detect this change at all (officially, it’s limited by modern JavaScript). Instead, the mechanism is tied to seven methods that change arrays to make data responsive.

  2. Restricted by modern JavaScript (and deprecated by Object.Observe), Vue cannot detect additions or deletions of Object attributes. Since Vue performs getter/setter conversions on the property when it initializes the instance, the property must exist on the data object for Vue to convert it to reactive. Vue does not allow dynamic root-level reactive attributes to be added to already created instances. However, you can add reactive properties to nested objects using the vue.set (Object, propertyName, value) method.

  3. Vue does not respond to new attributes of objects:

    (1). If the target is an array, use the vUE implementation of the variant method splice to implement the response (2). If the target is an object, the attribute is judged to exist, that is, responsivity, and the value (3) is directly assigned. If target itself is not reactive, assign (4) directly. If the property is not reactive, the defineReactive method is called for reactive processing

export function set(target: Array<any> | Object, key: any, val: any) :any {
  // Target is an array
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    // Change the length of the array so that splice() is not executed incorrectly due to index > array length
    target.length = Math.max(target.length, key);
    // Use the array splice mutation method to trigger the response
    target.splice(key, 1, val);
    return val;
  }
  // Target is an Object. Key must be on target or target.prototype and must not be directly assigned to object. prototype
  if (key intarget && ! (keyin Object.prototype)) {
    target[key] = val;
    return val;
  }
  If none of the above is true, start creating a new property for target
  // Get an Observer instance
  const ob = (target: any).__ob__;
  // Target itself is not reactive data, directly assigned
  if(! ob) { target[key] = val;return val;
  }
  // Perform reactive processing
  defineReactive(ob.value, key, val);
  ob.dep.notify();
  return val;
}
Copy the code

Vue rendering process

  1. Call the compile function to generate the render function string as follows:
  • The parse function parses the template to generate an AST (abstract syntax tree)
  • The optimize function optimizes static nodes (flags don’t need to be updated every time,diff skips static nodes, reducing the comparison process and optimizing patch performance)
  • Generate function render the generated AST abstract syntax tree into a function string
  1. Call the new Watcher function to listen for changes in the data, and when the data changes, the Render function executes to generate the VNode object
  2. The patch method is called to compare the old and new VNode objects, and the DOM diff algorithm is used to add, modify, and delete real DOM elements

The role of keys in vUE

function createKeyToOldIdx(children, beginIdx, endIdx) {
  let i, key;
  const map = {};
  for (i = beginIdx; i <= endIdx; ++i) {
    key = children[i].key;
    if (isDef(key)) map[key] = i;
  }
  return map;
}
Copy the code
  1. Key’s main purpose is to update the virtual DOM more efficiently.

  2. In the process of patch, Vue needs to determine whether two nodes are the same node, at this time, it mainly determines the key and element type of the two nodes. When rendering a set of lists, key is often the unique identifier, so if key is not defined, its value is undefined, it may always think that these are two same nodes. Even if they are not, they can only be updated, which leads to a large number of DOM updates, making the whole patch process inefficient and affecting performance.

  3. In practice, the key must be set when rendering a set of lists, and must be uniquely identified. Array indexes should be avoided as keys, which can lead to hidden bugs. Vue also uses the key attribute when transitioning between the same tag element, so that VUE can distinguish between them. Otherwise, VUE will only replace the internal attribute without triggering the transition effect.

Virtual DOM, diff algorithm

  1. Diff algorithm is the core of virtual DOM technology, and THE patch process is the main part of VUE. By comparing the old and new virtual DOM, the changes are converted into DOM operations
  2. In Vue 1, there is no patch, because each dependency in the interface has a special Watcher to update, so that the larger the project scale will become a performance bottleneck. In Vue 2, in order to reduce the granularity of watcher, there is only one Watcher for each component, but when it needs to update, The patch process needs to be performed to find exactly where the changes have occurred.
  3. Watcher will notify the update and execute its update function. It will execute the render function to obtain the new virtual DOM: newVnode. At this point, it will perform the patch patching process to compare the last render result oldVnode with the new render result newVnode.
  4. The patch process follows the strategy of depth first and same-layer comparison. When two nodes are compared, if they have child nodes, the child nodes are compared first. When comparing two groups of child nodes, it is assumed that the first and last nodes may be the same, and then the search is traversed in a general way after the same node is not found. After the search, process the remaining nodes according to the situation; With the help of key, the same node can be found very accurately, so the whole patch process is very efficient.

The principle of nextTick

JS runtime mechanism

JS execution is single threaded and is based on event loops. The event cycle can be roughly divided into the following steps:

  1. All synchronization tasks are executed on the main thread, forming an execution Context stack.
  2. In addition to the main thread, there is a “task queue”. Whenever an asynchronous task has a result, an event is placed in the “task queue”.
  3. Once all synchronization tasks in the execution stack are completed, the system reads the task queue to see what events are in it. Those corresponding asynchronous tasks then end the wait state, enter the execution stack, and start executing.
  4. The main thread repeats step 3 above.

The execution of the main thread is a tick, and all asynchronous results are scheduled through the “task queue”. Message queues hold individual tasks. Tasks fall into two categories: Macro task and Micro Task. After each Macro task is finished, all micro tasks should be cleared.

The realization principle of VUE’s nextTick method is as follows:

  1. NextTick is a global API provided by Vue. Due to Vue’s asynchronous update strategy, changes to data are not immediately reflected in DOM changes. In this case, if you want to obtain the updated DOM status immediately, you need to use this method. NextTick simply passes in a callback function that performs dom operations.
  2. Vue uses an asynchronous queue to control when DOM updates and nextTick callbacks are executed. As long as it listens for data changes, Vue opens a queue and buffers all data changes that occur in the same event loop. If the same watcher is triggered more than once, it will only be pushed into the queue once. This removal of duplicate data while buffering is important to avoid unnecessary computation and DOM manipulation.
  3. The nextTick method will add a callback to the queue, add the functions we passed in to the Callbacks, perform DOM operations on the callback, and then call them asynchronously with timerFunc, the preferred asynchronous being Promise. This ensures that the function is called after the previous DOM manipulation is complete.
  4. Demotion strategy: Due to its high priority feature, microtask can ensure that the queue of microtasks are completed before an event cycle. Considering compatibility problems, Vue has made a demotion scheme from MicroTask to MacroTask. In the source code of VUe2.5, Macrotask degrades through setImmediate, MessageChannel, and setTimeout.

Component communication

Parent-child component communication, intergenerational component communication, and sibling component communication.

(1)props / $emitApplicable to parent-child component communication

(2)ref$parent / $childrenApplicable to parent-child component communication

(3)EventBus ($emit / $ON)It is suitable for communication between father-child, intergenerational, and sibling components

(4)$attrs/$listenersIt is suitable for intergenerational component communication

(5)provide / injectIt is suitable for intergenerational component communication

(6) Vuex is suitable for father-son, intergenerational and sibling component communication

Vuex status management

  • State: Data structure that defines the State of the application, where the default initial State can be set.
  • Getter: Allows the component to get data from the Store. The mapGetters helper function simply maps the getters in the Store to local computed properties.
  • Mutation: is the only method that changes the state in the store, and must be a synchronization function.
  • Action: Used to commit mutation rather than directly change the state, and can include any asynchronous operation.
  • Module: Allows you to split a single Store into multiple stores and Store them simultaneously in a single state tree.
  1. Vuex is a dedicated vUE state management library. It centrally manages application state in a global manner and ensures predictability of state changes.

  2. Vuex mainly solves the problem of state sharing among multiple components. Although we can achieve state sharing by using various communication modes of components, we often need to maintain state consistency among multiple components, which is prone to problems.

  3. It also complicates the program logic. Vuex extracts the shared state of components and manages it in a global singleton mode, so that any component can obtain and modify state in a consistent manner. Responsive data also ensures a concise one-way flow of data, making our code more structured and maintainable.

  4. Understanding of VUEX: Firstly, the core concept is understood and applied. The global state is put into the state object, which itself is a state tree. Components use the state of store instances to access these states. These states are then modified by a companion mutation method, and only mutation can be used to change the state. The commit method is called in the component to commit mutation. If there are asynchronous operations or complex logic combinations in the application, we need to write actions. After the execution, if there are stateful changes, we still need to submit mutation. These actions are called in the component and distributed using dispatch method. Finally, modularity is used to organize each submodule by modules option. When accessing the state, add the name of the submodule. If the submodule has a namespace, then additional namespace prefixes are required when submitting mutation and dispatching action.

  5. Vuex needs to achieve data responsiveness in the realization of single data flow. After learning the source code, IT is realized by borrowing the data responsiveness feature of VUE. It will make use of VUE to treat state as data for reactive processing, so that when these states change, it can lead to re-rendering of components.

Vue-router routing is implemented

Implementation principles of the Hash mode

Early implementations of front-end routing were based on location.hash. The principle is simple: the value of location.hash is what comes after the # in the URL. The hash routing mode is implemented based on the following features:

  • The hash value in the URL is only a state of the client, that is, when a request is made to the server, the hash part will not be sent.
  • Changes to the hash value add a record to the browser’s access history. So we can switch the hash through the browser’s back and forward buttons;
  • You can use the a tag and set the href attribute. When the user clicks on the tag, the HASH value of the URL will change. Or use JavaScript to assign loaction.hash to change the HASH value of the URL.
  • The hashChange event can be used to listen for changes in the hash value to jump (render) the page

How the history pattern is implemented

HTML5 provides the History API to implement URL changes. The two main apis are history.pushstate () and history.repalcestate (). Both apis allow you to manipulate the browser’s history without refreshing it. The only difference is that the former is to add a history, the latter is to directly replace the current history. The history routing pattern is implemented based on the following features:

  • PushState and repalceState apis to implement URL changes;
  • You can use the PopState event to listen for URL changes to jump (render) the page;
  • History.pushstate () or history.replacEstate () will not trigger the popState event, so we need to trigger the page jump (rendering) manually.

Implementation principle and cache policy of keep-alive

  1. Gets the first child component object wrapped around Keep-Alive and its component name.

  2. Include /exclude (if any) is matched to determine whether to cache. Does not match and returns the component instance directly

  3. The cache Key is generated based on the component ID and tag, and the cache object is looked up to see if the component instance has been cached. If so, fetch the cached value and update the key’s position in this.keys (updating the key’s position is the key to implement the LRU substitution strategy)

  4. Store the component instance and the key in this.cache, then check if the number of cached instances exceeds the Max value. If so, delete the most recent and oldest unused instance (the key with subscript 0) according to the LRU substitution policy.

  5. The last component instance’s keepAlive property is set to true, which is used when rendering and executing the wrapped component’s hook function.

Vue3 changes

1. Changes in monitoring mechanisms

Vue3.0 uses the Observer implementation of Proxy to provide reactivity tracing for full language coverage. This removes many of the limitations of the object.defineProperty-based implementation in Vue 2:

- Can only monitor attributes, not objects - detect the addition and deletion of attributes; - Detect array index and length changes; - Supports Map, Set, WeakMap, and WeakSet.Copy the code

2. Templates (scope slot)

There were no major changes to the template, only the scope slots were changed. 2.x changed the scope slots and the parent component was re-rendered, while 3.0 changed the scope slots to function mode, which only affected the re-rendering of child components and improved rendering performance. As for the render function aspect, vue3.0 will also make a number of changes to make it easier to use the API directly to generate the VDOM.

3. Object component declaration

Components in vue2. X pass in a set of options declaratively, and TypeScript is combined with decorators, which are functional but cumbersome. 3.0 changes the way components are declared to be class-like, making it easy to integrate with TypeScript. In addition, the source code for Vue is written in TypeScript. In fact, when the functionality of the code is complex, it is necessary to have a static type system to do some auxiliary management. Now vue3.0 is fully rewritten in TypeScript, making it easier for exposed apis to incorporate TypeScript. Static type systems are really necessary for the maintenance of complex code.

4. Composition API instead of options API

Avoid up and down repeatedly cross jump

5. Vite packaging

In the development environment, it can be started in seconds, eliminating the packaging process, but the online environment still needs webpack packaging.

6. Other changes

The changes to VUe3.0 are comprehensive, with only three major areas covered above, as well as some other changes:

  • Support for custom renderers, which allows WEEX to be extended by custom renderers instead of forking the source code.
  • Fragment (multiple root nodes) and Protal (rendering components in other parts of the DOM) components are supported for specific scenarios.
  • Based on Treeshaking optimization, more built-in features are provided.

Vue vs. React

  • Whether data is mutable: React is a functional idea. Components are designed as pure components, and state and logic are passed in through parameters. Therefore, React is a one-way data flow, and immutable data is recommended. The idea of VUE is responsive, that is, based on the variable data, Watcher is established for each attribute to monitor. When the attribute changes, the corresponding virtual DOM is updated in a responsive manner. In short, react performance optimization needs to be done manually, while VUE performance optimization is automatic. However, the reactive mechanism of VUE also has problems. When there are too many states, Watcher will also be too many, resulting in lag.
  • React, for example, uses JAVASCRIPT to generate HTML, so JSX is designed. For styled CSS, the community styled Component, JSS, etc. Vue is a combination of HTML, CSS, JS, with their own way of processing, VUE has a single file component, can be HTML, CSS, JS into a file, HTML provides a template engine to process.
  • React is class-based and has very few apis. Vue is declarative, passing in various options, apis, and parameters. React is easier to write with typescript, vue is a bit more complicated.
  • The extension is different: React can be extended through Higher Order Components (HOC), while Vue needs to be extended through mixins.
  • What is built in and what is left to the community: React does very little, leaving a lot of things to the community to do. Vue has a lot of built-in stuff, which is really easier to write. For example, Redux combineReducer corresponds to Vuex modules. For example, resELECT corresponds to the getter of VUex and computed of the VUE component, vuex mutation is directly changed original data, and Redux reducer returns a new state, so Redux combines immutable to optimize performance. Vue does not.

Vuejs optimization strategy

1. Lazy route loading

const router = new VueRouter({
  routes: [{path: '/foo'.component: () = > import('./Foo.vue')}]})Copy the code

2. Keep-alive cache the page

<template>
  <div id="app">
    <keep-alive>
      <router-view/>
    </keep-alive>
  </div>
</template>
Copy the code

3. Use V-show to reuse DOM

<template> <div class="cell"> <! <div v-show="value" class="on"> <Heavy :n="10000"/> </div> <section v-show="! value" class="off"> <Heavy :n="10000"/> </section> </div> </template>Copy the code

4. V-for traversal Avoid using v-if simultaneously

<template>
    <ul>
      <li
        v-for="user in activeUsers"
        :key="user.id">
        {{ user.name }}
      </li>
    </ul>
</template>
<script>
	export default {
        computed: {
          activeUsers: function () {
            return this.users.filter(function (user) {
             return user.isActive
            })
          }
        }
    }
</script>
Copy the code

5. Long list performance optimization

  • If the list is purely a data presentation and nothing changes, there is no need to be reactive

    export default {
      data: () = > ({
        users: []}),async created() {
        const users = await axios.get("/api/users");
        this.users = Object.freeze(users); }};Copy the code
  • If it is a long list of big data, virtual scrolling can be used to render only a few areas of content

    <recycle-scroller
      class="items"
      :items="items"
      :item-size="24"
    >
      <template v-slot="{ item }">
        <FetchItemView
          :item="item"
          @vote="voteItem(item)"
        />
      </template>
    </recycle-scroller>
    Copy the code

6. Destruction of events

When a Vue component is destroyed, all its instructions and event listeners are automatically unbound, but only for the component’s own events.

created() {
  this.timer = setInterval(this.refresh, 2000)},beforeDestroy() {
  clearInterval(this.timer)
}
Copy the code

7. Lazy loading of images

For pages with too many pictures, in order to accelerate the page loading speed, we often need to not load the pictures that do not appear in the visual area of the page, and then load them after scrolling to the visual area.

<img v-lazy="/static/img/1.png">
Copy the code

8. Introduce third-party plug-ins on demand

Third-party component libraries such as Element-UI can be introduced on demand to avoid being too bulky.

import Vue from 'vue';
import { Button, Select } from 'element-ui';

 Vue.use(Button)
 Vue.use(Select)
Copy the code

9. Stateless components are marked as functional components

<template functional>
  <div class="cell">
    <div v-if="props.value" class="on"></div>
    <section v-else class="off"></section>
  </div>
</template>

<script>
export default {
  props: ['value']
}
</script>
Copy the code

10. Sub-component segmentation

<template> <div> <ChildComp/> </div> </template> <script> export default { components: { ChildComp: { methods: {heavy () {/ * time-consuming task * /}}, render (h) {return h (' div ', this heavy ())}}}} < / script >Copy the code

11. Localization of variables

<template>
  <div :style="{ opacity: start / 300 }">
    {{ result }}
  </div>
</template>

<script>
import { heavy } from '@/utils'

export default {
  props: ['start'],
  computed: {
    base () { return 42 },
    result () {
      const base = this.base // 不要频繁引用this.base
      let result = this.start
      for (let i = 0; i < 1000; i++) {
        result += heavy(base)
      }
      return result
    }
  }
}
</script>
Copy the code