头图

Original address Nuggets

Three consecutive oh more good articles github

Hello everyone, I am Lin Yiyi. This is an interview question about the principle of vue. If you can fully understand it, I believe it will be very helpful to everyone.

Interview questions

1. As the old saying goes, what are the advantages and disadvantages of the understanding of MPA/SPA?

MPA multi-page application.

  • Composition: There are multiple pages html composition,
  • Jump method: the page jump is from one page to another page
  • Refresh mode: full page refresh
  • Page data jump: rely on URL/cookie/localStorage
  • The redirected resource will be reloaded
  • Advantages: It is more friendly to SEO, and the development difficulty is lower.

SPA Single page application

  • Page composition: wrapped by a shell page, composed of multiple page (component) fragments
  • Jump mode: Jump in the shell page, show or hide the fragment page (component)
  • Refresh mode: partial refresh of page fragments
  • Page data jump: it is easier to pass values between components
  • after the jump will not be reloaded
  • Disadvantages: It is not very friendly to SEO search and needs to be configured separately, and the development is more difficult and requires a special development framework
The iframe is actually MPA , but it can achieve SPA , but it has many problems.

2. As the old saying goes, why do we need these MVC/MVVM patterns? Talk about the difference between your MVC and MVVM modes,

Purpose: To learn from the back-end ideas, responsibilities division and layering
  • Vue, React is not really MVVM or MVC. Both cores only deal with the view layer view .

MVC mode

MVC.jpg

For one-way data, the user needs to re-request the database for each step of the operation to modify the rendering of the view layer, forming a one-way closed loop. For example, jQuery+underscore+backbone .
  • M: model data storage layer
  • V: view : View layer page
  • C: controller : Controller js logic layer.
controller control layer processes the data of the data layer model layer and displays it in the view layer view layer. Similarly, the view layer view layer can receive user instructions through the control layer controller and act on the data layer model . Therefore MVC is that the view layer cannot directly interact with the data layer.

MVVM mode

Hidden controller control layer, direct manipulation View view layer and Model data layer.

MVVM.jpg

  • M: model data model
  • V: view template
  • VM: view-model view data template (the layer processed by vue, the definedProperty in vue is the logic of processing the VM layer)
Two-way data binding: model data model layer through data binding Data Bindings directly affect the view layer View , while the view layer view by listening Dom Listener can also change the data model layer model .
  • Data binding and DOM event monitoring are the main things viewModel layer Vue That is to say: as long as the data model layer Model is mounted to the ViewModel layer Vue , two-way data binding can be realized.
  • Plus vuex/redux can be used as vue and react model data layer.
var vm = new Vue()
vm is the view-model data model layer, and data: is the data view-model
  • To sum up the difference between the two: MVC's view layer and data layer interaction needs to pass through the control layer controller is a one-way link. MVVM hides the control layer controller , so that the view layer and the data layer can directly interact, which is a two-way connection.

3. Talk about the understanding of reactive data in Vue

Small tip: Responsive data refers to data that has changed, and the view can be updated is responsive data
  • vue implements a definedReactive method, internal methods borrowed Object.definedProperty() to each attribute added get/set properties.
  • definedReactive can only monitor the outermost objects, and the inner objects need to recursively hijack data.
  • The array is rewritten 7 push pop shift unshift reverse sort splice to intercept the data of the array, because these methods will change the original array
  • Expansion:
// src\core\observer\index.js
export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  // 准备给属性添加一个 dep 来依赖收集 Watcher 用于更新视图。
  const dep = new Dep()
  // some code

  // observe() 用来观察值的类型,如果是属性也是对象就递归,为每个属性都加上`get/set`
  let childOb = !shallow && observe(val)
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
        // 这里取数据时依赖收集
        const value = getter ? getter.call(obj) : val
        if (Dep.target) {
            dep.depend()
            // childOb 是对对像进行收集依赖
            if (childOb) {
                childOb.dep.depend()

                //这里对数组和内部的数组进行递归收集依赖,这里数组的 key 和 value 都有dep。
                if (Array.isArray(value)) {
                    dependArray(value)
                }
            }
        }
        return value
    },
    set: function reactiveSetter (newVal) {
      // 属性发生改变,这里会通知 watcher 更新视图
    }
  })
}
What is the Dep (class) above? Answer: What is Watcher and Watcher used to collect and render? Answer: watcher is a class used to update the view

4. How does Vue detect array changes?

  • definedProperty() for data interception for each item in the array, but instead rewrites the array push pop shift unshift reverse sort splice .
  • Manually call notify, notify render watcher, execute update
  • If there are object types ( objects and arrays) in the array, data will be intercepted.
  • Therefore, by modifying the array subscript and array length, data interception will not occur, and there will be no responsive changes. For example, arr[0] = 1, arr.length = 2 will not be responsive
  • Expansion:
// src\core\observer\array.js
const methodsToPatch = ['push','pop','shift','unshift','splice','sort','reverse']
methodsToPatch.forEach(function (method) {
  const original = arrayProto[method]
  def(arrayMethods, method, function mutator (...args) {
    const result = original.apply(this, args)
    const ob = this.__ob__
    let inserted
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    // 新增的类型再次观察
    if (inserted) ob.observeArray(inserted)
    // 手动调用 notify 派发更新
    ob.dep.notify()
    return result
  })
})

5. How does Vue rely on collection? (What is the relationship between dep and Watcher)

dep.jpg

tip: Dep is a class responsible for collecting Watcher , and Watcher is a class that encapsulates the rendering view logic and is used to distribute updates. It should be noted that Watcher cannot directly update the view. It also needs to be combined with Vnode to pass the diff algorithm in patch() to generate the real DOM.
  • Each attribute has its own dep attribute to store the dependent Watcher . When the attribute changes, it will notify Watcher to update.
  • When the user obtains ( getter ) data, Vue adds the dep attribute to each attribute to (collect as Dependency) to collect Watcher . When the user setting sets the attribute value, dep.notify() notifies the Watcher collected by re-render. defineReactive() above for details
  • Dep dependent collection class It has a many-to-many two-way storage relationship Watcher class
  • Each attribute can have multiple Watcher classes, because the attributes may be used in different components.
  • At the same time, a Watcher class can also correspond to multiple attributes.

6. Template compilation in Vue

<img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bc2f78cda8514103b4d0a29acb1a4c9a~tplv-k3u1fbpfcp-watermark.image" width="80%" height="360px"/>

Template compilation in Vue: In fact, it is to convert template into render function. To put it is to compile the real 160aca788891f4 DOM (template) into a virtual dom(Vnode)
  • The first step is to convert the template template string into ast syntax tree (parser parser), where a large number of regulars are used to match tag names, attributes, texts, etc.
  • The second step is to static of the AST, which is mainly used to optimize the rendering of the virtual DOM (optimize optimizer). Here, all the child nodes will be traversed and statically marked
  • The third step is to use the ast syntax tree to regenerate the render function code string code. (CodeGen code generator)
Why do you need to statically mark the node? If it is a static node (no data binding, no change before and after the node), then the subsequent diff algorithm is not needed for comparison.

7. The realization principle of life cycle hook

  • The lifecycle hook in vue is just a callback function, and the corresponding hook is called to execute during the process of creating component instantiation.
  • Use Vue.mixin({}) mixed hooks or multiple functions defined in the life cycle, vue will call mergeHook() to merge the hooks and put them in the queue for execution in sequence
  • Expand
// src\core\util\options.js
function mergeHook (
  parentVal: ?Array<Function>,
  childVal: ?Function | ?Array<Function>
): ?Array<Function> {
  const res = childVal
    ? parentVal
      ? parentVal.concat(childVal) // 合并
      : Array.isArray(childVal)
        ? childVal
        : [childVal]
    : parentVal
  return res
    ? dedupeHooks(res)
    : res
}

8. What are the commonplace vue life cycles, and where do I send requests?

  • beforeCreate : Initialize the vue instance at the beginning, it is called before the observer , and the attributes such as data/methods
  • created : The initialization of the vue instance is over, and all the attributes have been created.
  • beforeMount : This hook is triggered before vue mounts the data to the page, and the render function is triggered at this time.
  • mounted : el is vm.$el , the data initialized by vue has been mounted on the page, and the real DOM can be accessed here. Generally, data is requested here.
  • beforeUpdate : Called when the data is updated, that is, before the virtual dom is re-rendered.
  • updated : Data changes cause the virtual dom to be re-rendered.
  • beforeDestroy : This hook is called before the instance is destroyed, and the instance is still there. vm.$destroy triggers two methods.
  • destroyed : Called after the Vue instance is destroyed. All event listeners will be touched.
Request data depends on specific business requirements to determine where to send ajax

9. Usage scenarios and principles of Vue.mixin(())

  • Use scenario: Used to extract a common business logic to achieve reuse.
  • Realization principle: calling mergeOptions() method adopts strategy mode to merge different attributes. If there is a conflict between the mixed data and the data of the component, the component itself is used.
  • Vue.mixin({}) defects, 1. May cause naming conflicts between the mixed attribute name and the component attribute name; 2. The source of data dependence
  • Expand
export function mergeOptions (
  parent: Object,
  child: Object,
  vm?: Component
): Object {
  // some code
  if (!child._base) {
    if (child.extends) {
      parent = mergeOptions(parent, child.extends, vm)
    }
    if (child.mixins) {
      for (let i = 0, l = child.mixins.length; i < l; i++) {
        parent = mergeOptions(parent, child.mixins[i], vm)
      }
    }
  }

 // 递归遍历合并组件和混入的属性
  const options = {}
  let key
  for (key in parent) {
    mergeField(key)
  }
  for (key in child) {
    if (!hasOwn(parent, key)) {
      mergeField(key)
    }
  }
  function mergeField (key) {
    const strat = strats[key] || defaultStrat
    options[key] = strat(parent[key], child[key], vm, key)
  }
  return options
}

10. Why must the data in the vue component be a function?

  • This is related to the mechanism of js itself data function is different, which can ensure that the data between different components does not pollute each other.
  • Vue.mixin() if mixed data attributes, data must also be a function. Because Vue.mixin() can also be used in many places.
  • In the example, data can be an object or a function, because we generally only initialize one Vue instance (singleton) for a page

11. The realization principle and scenario of vm.$nextTick(cb) in vue

  • Scenario: called after the end of the dom update cycle to obtain the updated dom data
  • Implementation principle: vm.$nextTick(cb) is an asynchronous method for compatibility with a lot of downgrade processing, followed by promise.then,MutationObserver,setImmediate,setTimeout . After modifying the data view is not immediately updated, but after set method of notification notify Watcher updates will need to update the Watcher put into an asynchronous queue, nexTick callback function to put Watcher behind until the main thread synchronization code Execute the lodging and then clear the queue in turn, so vm.nextTick(callback) is executed after the update of dom
Watcher in the above column in vue asynchronous batch update. A little thought: Why not just use setTimeout instead? Because setTimeout is a macro task, the performance of multiple macro tasks will be poor. About the event loop, you can look at JS event loop

12. The commonplace talk about the difference between watch and computed

  • computed is implemented internally based on Object.definedProperty()
  • computed cache function, and the dependent value will not be recalculated if it does not change.
  • watch is the monitoring value change, when the value changes, the corresponding callback function will be executed.
  • computed and watch are implemented based on the Watcher class.
computed caching feature relies on a variable dirty , represents the default value is not dirty is true , the value is false , when the value again dirty or false directly or return to previous values.
// src\core\instance\state.js computed 取值函数
function createComputedGetter (key) {
  return function computedGetter () {
    const watcher = this._computedWatchers && this._computedWatchers[key]
    if (watcher) {
      if (watcher.dirty) {  // 判断值是不是脏 dirty
        watcher.evaluate()
      }
      if (Dep.target) {
        watcher.depend()
      }
      return watcher.value
    }
  }
}


// src\core\instance\state.js watch 实现
  Vue.prototype.$watch = function (
    expOrFn: string | Function,
    cb: any,
    options?: Object
  ): Function {
    const vm: Component = this
    if (isPlainObject(cb)) {
      return createWatcher(vm, expOrFn, cb, options)
    }
    options = options || {}
    options.user = true
    // 实例化 watcher
    const watcher = new Watcher(vm, expOrFn, cb, options)
    if (options.immediate) {
      const info = `callback for immediate watcher "${watcher.expression}"`
      pushTarget()
      invokeWithErrorHandling(cb, vm, [watcher.value], vm, info)
      popTarget()
    }
    return function unwatchFn () {
      watcher.teardown()
    }
  }

reference

Vue template compilation principle

The principle and use of

the end

Thank you everyone for reading here. If you think the writing is okay, welcome Sanlian, I am Lin Yiyi, see you next time.

林一一
171 声望7 粉丝