Understanding of the MVVM pattern

MVVM has three components, Model, View, and ViewModel.

  • A View is the structure, layout, and appearance that the user sees on the screen, also known as UI.
  • The ViewModel is a binder that communicates with the View and Model layers.
  • Model is data and logic.

Views can’t talk to models directly; they can only talk to The ViewModel. The interaction between the Model and ViewModel is bidirectional. The ViewModel connects the View layer to the Model layer through bidirectional data binding, so changes to the View data are synchronized to the Model. Changes to Model data are immediately reflected in the View.

An aside:

You may not know that Vue is not exactly MVVM mode

The strict MVVM requirement that the View not communicate directly with the Model was violated by Vue providing the $refs attribute in the component to allow the Model to manipulate the View directly.

Vue rendering process

The process is mainly divided into three parts:

  1. Template compilation.parseParse templates to generate abstract syntax trees (AST);optimizeMark static nodes, which will be skipped in subsequent page updates;generateThe AST intorenderThe function,renderFunctions are used to buildVNode.
  2. Building a VNode (Virtual DOM), used by the build processcreateElementbuildVNode.createElementIt’s also customrenderThe first argument received when the function is called.
  3. VNode to real DOM.patchThe function takes care ofVNodeTo convert to the real DOM, the core method iscreateElmTo recursively create a real DOM tree and eventually render it on the page.

Vue life cycle

  1. beforeCreate:Called before the instance is created and cannot be accessed on the instance because the instance has not yet been createddata,computed,methodAnd so on.
  2. created:Called after the instance has been created, when the data has been observed and can be retrieved and changed, but not yet interacted with the DOM, which can be used if you want to access itvm.$nextTick. Data can be changed at this point without triggeringupdated.
  3. beforeMount:Called before mounting, when the template has been compiled and generatedrenderFunction, ready to start rendering. Changes can also be made to the data at this point without triggeringupdated.
  4. mounted:After the mount is complete, the real DOM is mounted and can be accessed to the DOM node$refsProperty to manipulate the DOM.
  5. BeforeUpdate: Called before updates, which is triggered before reactive data is updated and the virtual DOM is re-rendered. Changes to data at this stage do not cause re-rendering.
  6. Updated: Called after the update is complete, the component DOM is updated. Care should be taken to avoid changing data during this period, which can lead to an endless loop.
  7. BeforeDestroy: Called before the instance is destroyed, when the instance can still be used, typically this cycle is used to clear timers and cancel event listening.
  8. Destroyed: Called after the instance is destroyed, when the instance is no longer accessible. The current instance is removed from the parent, the observation is unloaded, all event listeners are removed, and all child instances are destroyed.

Vue parent and child component lifecycle execution order

Rendering process

  1. The parent componentbeforeCreate
  2. The parent componentcreated
  3. The parent componentbeforeMount
  4. Child componentsbeforeCreate
  5. Child componentscreated
  6. Child componentsbeforeMount
  7. Child componentsmounted
  8. The parent componentmounted

The child component is mounted before the parent because the tree of nodes is created recursively, and the child component is created first before the entire parent node is created.

The update process

  1. The parent componentbeforeUpdate
  2. Child componentsbeforeUpdate
  3. Child componentsupdated
  4. The parent componentupdated

Destruction of the process

  1. The parent componentbeforeDestroy
  2. Child componentsbeforeDestroy
  3. Child componentsdestroyed
  4. The parent componentdestroyed

Why is data required to be a function

When a component is defined, data must be declared as a function that returns an initial data object, because the component may be used to create multiple instances. If data were still a pure object, all instances would share references to the same data object! By providing the data function, each time a new instance is created, we can call the data function to return a new copy of the original data.

Vue constructs instances using the same constructor, and data directly using objects results in shared references to instances, i.e. states between components affect each other. Shared references usually occur when components are reused. Using a function to return an object, since it is a different reference, naturally avoids this problem.

Component communication mode

  1. props/emit
  2. $children/$parent
  3. ref
  4. $attrs/$listeners
  5. provide/inject
  6. eventBus
  7. vuex

Name five instructions for Vue

  1. v-if
  2. v-for
  3. v-show
  4. v-html
  5. v-model

Difference between V-if and V-show

V-if causes elements or components within the conditional block to be destroyed and rebuilt appropriately.

V-show elements or components are rendered from the start, with a simple CSS switch.

By contrast, v-if has a higher switching cost, so v-show is used for frequent switching and V-if is used for infrequent switching.

V – model under the said

V-model is the syntax sugar for the attribute value and input events. The internal default is value as a value, and the attribute is updated using $emit to trigger the input event, thereby enabling bidirectional binding. Custom bidirectional binding can be implemented by defining the component’s Model option Settings prop and Event.

Principle of bidirectional binding

Two-way binding is where view changes are reflected in the data, and data changes are reflected in the view, and v-Model is a good example of this. In fact, the main test is still responsive principle, responsive principle includes three main members, Observer is responsible for monitoring data changes, Dep is responsible for dependency collection, Watcher is responsible for data or view update, we often say that the collection of dependencies is to collect Watcher.

The main working process of the responsive principle is as follows:

  1. ObserveruseObject.definePropertyHijack the data and set it upsetget.
  2. Each data will have its owndep. Data value triggergetFunction, calldep.dependCollect dependencies; Data update triggersetFunction, calldep.notifynoticeWatcherThe update.
  3. WatcherUpdated notifications are received, they are added to an asynchronous queue, de-processed, and the view is updated once all synchronization operations are complete.

What does the key in Vue do

The key is the unique identifier of each VNode. By using the key, the corresponding VNode can be found faster when the DIFF algorithm is executed, improving the DIFF speed.

Key can ensure that the state between elements is independent, and the state will not be reused after update, avoiding some unexpected results. For example, in the case of input box reuse, the contents of the input box are not refreshed even if the elements are updated, because Vue uses a “reuse in place” policy by default, minimizing creation and destruction.

The official example

The difference between computed and Watch

  1. computedRely ondataChange by change,computedHas a return value;watchobservationdataExecute the corresponding function.
  2. computedWith caching, repeated values do not perform evaluation functions.
  3. computedDependency collection is triggered when the page is rendered,watchDependency collection is triggered before the page renders.
  4. computedThe update requires the help of render Watcher,computedUpdate is just settingdirty, requires page rendering to triggergetTo evaluate

How is computed in Vue cached

The “calculated property Watcher” will have a dirty property, and after the initialization is complete, it will cache the evaluation results and set dirty to false. As long as the dependent properties are not updated, the dirty is always false and the repeated values are not evaluated. Instead, the cache is returned directly. Instead, dependent property updates set the dirty of the calculated property Watcher to true, triggering the evaluation function to update the calculated property again when the page renders the calculated property.

Object.defineProperty(target, key, {
    get() {
        const watcher = this._computedWatchers && this._computedWatchers[key]
        // Calculate the property cache
        if (watcher.dirty) {  
            // Evaluate attributes
            watcher.evaluate()  
        }
        return watcher.value
    }
})
Copy the code

Why can’t a child component change props directly? What is one-way data flow?

All prop forms a one-way downlink binding between their parent prop: updates to the parent prop flow down to the child, but not the other way around. This prevents accidental changes in the state of the parent component from the child, which can make the data flow of your application difficult to understand.

Child components cannot change props because of the principle of one-way data flow.

One-way data flow only allows data to be passed from parent to child, and data can only be updated by the parent. When data is passed to multiple subcomponents, and subcomponents can update data within themselves, it is subjectively difficult to know which subcomponent updated the data, resulting in unclear data flow, which makes application debugging more difficult.

However, a scenario does exist where a child component updates the parent component’s data, and there are three ways to use it:

  1. Child componentsemit, the parent component accepts custom events. This method is ultimately modified by the parent, and the child only acts as a notification.
  2. Subcomponent custom bidirectional binding, set the component’smodelOption to add a custom bidirectional binding for the component.
  3. .syncProperty modifier, which is the syntactic sugar of the first method, is added to the pass property and can be called within a child componentThis.$emit('update: attribute name ', value)Update properties.

How does Vue detect array updates

Vue internally overwrites the array prototype chain. When an array changes, dep.notify is called to notify Watcher of the update, in addition to executing the native array methods. There are 7 ways to trigger an array update:

  • push
  • pop
  • shift
  • unshift
  • splice
  • sort
  • reverse

Do you know keep-alive? How is it done?

Keep-alive is a built-in component of Vue and is an abstract component that is not rendered as an element on the page. It is used for component caching. When the component is switched, the VNode of the component will be cached. When the component is reactivated, the component VNode of the cache will be taken out for rendering, so as to achieve caching.

The two common attributes include and exclude support string, regular, and array formats to allow components to cache conditionally. There is also the Max attribute, which sets the maximum number of caches.

The two lifecycle Activated and deactivated are triggered when a component is activated and deactivated.

The keep-alive cache mechanism uses LRU(Least Recently Used) algorithm,

Let’s talk about how nextTick works

A deferred callback is performed after the next DOM update. NextTick mainly uses macro and micro tasks. According to the execution environment, try to adopt:

  • Promise
  • MutationObserver
  • setImmediate
  • setTimeout

NextTick is mainly used for asynchronous updates to internal Watcher, but externally we can use vue. nextTick and vm.$nextTick. The updated DOM is available in nextTick.

Compare Vue3 with Vue2. X

  1. useProxyInstead ofObject.defineProperty
  2. newComposition API
  3. Templates allow multiple root nodes

Why Vue3 uses Proxy instead of Object.definedProperty

Object.definedproperty can only detect attribute fetching and setting, not additions and deletions. When the data is initialized, Vue simply observes all the data recursively because it does not know which data will be used, resulting in an unnecessary performance drain.

Proxy hijacks the entire object and detects the addition and deletion of object attributes. Proxy does not listen for deep internal object changes, so Vue 3.0 deals with recursive responses in getters, and only internal objects that are actually accessed become responsive, rather than mindless recursion, greatly improving performance.

How is routing lazy loading implemented

Lazy loading of routes is a means of performance optimization. You can use import() to import routing components when writing code. Lazy loading of routes will be separated into a JS file when packaging. After the project goes online, lazy JS files will not be loaded at the first time. Instead, script labels will be dynamically created to load this JS file when the corresponding route is accessed.

{
  path:'users'.name:'users'.component:() = > import(/*webpackChunkName: "users"*/ '@/views/users'),}Copy the code

Vue routing hook function

Global hooks

  • beforeEach

Called before route entry

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) = > {
  // ...
})
Copy the code
  • BeforeResolve (New in 2.5.0)

Called after guard and asynchronous components have been resolved within all components

router.beforeResolve((to, from, next) = > {
 // ...
})
Copy the code
  • afterEach

Routes are invoked after confirmation

router.afterEach((to, from) = > {
  // ...
})
Copy the code

Route exclusive hooks

  • beforeEnter

Called before the route enters, beforeEnter is executed after beforeEach

const router = new VueRouter({
  routes: [{path: '/foo'.component: Foo,
      beforeEnter: (to, from, next) = > {
        // ...}}}])Copy the code

Component hooks

  • beforeRouteEnter

Called before route validation, component instance has not been created, cannot get component instance this

beforeRouteEnter (to, from, next) {
    // ...
    // Instances can be accessed via callbacks
    next(vm= > {
        // VM is the component instance})},Copy the code
  • BeforeRouteUpdate (2.2 New)

Called when the route changes and can access the component instance

beforeRouteUpdate (to, from, next) {
    // ...
},
Copy the code
  • beforeRouteLeave

Called when leaving the corresponding route of the component to access the component instance this

beforeRouteLeave (to, from, next) {
    // ...
}
Copy the code

Vue – the principle of the router

Vue-router works by updating the view without re-requesting the page. Vue-router has three modes: hash, history, and Abstract.

Hash pattern

Hash mode Uses hashchange to listen for changes in the hash value of the address bar and load the corresponding page. Every time the hash value changes, history is still left in the browser. You can use the forward and back buttons of the browser to return to the previous page.

The history mode

The History mode is based on the History Api and uses popState to listen for changes in the address bar. Use pushState and replaceState to modify the URL without loading the page. However, when a page is refreshed, a request is still sent from the back end, which requires the cooperation of the back end to redirect resources back to the front end for processing by the front end routing.

abstract

No records related to the browser address are involved. Maintains a history stack that emulates the browser through arrays.

How is VUEX invoked across modules

Cross-module invocation is when the current namespace module calls a global module or another namespace module. Set the third parameter to {root: true} when calling dispatch and commit.

modules: {
  foo: {
    namespaced: true.actions: {
      someAction ({ dispatch, commit, getters, rootGetters }) {
        // Call your own action
        dispatch('someOtherAction') // -> 'foo/someOtherAction'
        // Call the global action
        dispatch('someOtherAction'.null, { root: true }) // -> 'someOtherAction'
        // Invoke action for other modules
        dispatch('user/someOtherAction'.null, { root: true }) // -> 'user/someOtherAction'}, someOtherAction (ctx, payload) { ... }}}}Copy the code

How does VUex persist

The vuEX stored state will be lost after the page is refreshed. Persistence ensures that the state will still exist after the page is refreshed.

  1. Using local storage, setstateAt the same time set upstorage, and then initialize after refreshingvuex
  2. Vuex – persistedstate plug-in

Recommended reading

  • Gold nine Silver ten, preliminary intermediate front-end interview review summary “JavaScript chapter”
  • Summary of “Browser, HTTP, Front-end security”