1. What is MVC MVP MVVM

MVC

  • Model: represents the data Model, whose main task is to manipulate data;
  • View: represents the UI View. The main task is to convert the data model into a UI View for presentation.
  • Controller: a Controller responsible for service logic.

The View sends instructions to the Controller. After the Controller completes the business logic, the Model is required to change the state. The Model sends new data to the View and the user gets feedback.

MVP

  1. Communication between the parts is two-way;
  2. A View and a Model are not connected. They are both delivered by Presenter.
  3. A view is very thin and does not deploy any business logic. It is called a Passive view, meaning there is no initiative, whereas a Presenter is very thick and all the logic is deployed there.

MVVM

  • Model stands for data Model, whose main task is to manipulate data;
  • View stands for UI View, the main task is to convert the data model into A UI View to display;
  • ViewModel is an object that synchronizes View and Model, and connects Model and View.

In MVVM architecture, there is no direct connection between View and Model, but interaction through ViewModel. The interaction between Model and ViewModel is bidirectional, so the change of View data will be synchronized to Model, and the change of Model data will be immediately reflected on View. ViewModel connects View layer and Model layer through two-way data binding, and the synchronization between View and Model is completely automatic, so developers only need to pay attention to business logic, do not need to manually manipulate DOM, do not need to pay attention to the data state synchronization problem, Complex data state maintenance is managed by MVVM.

2. Introduce VUE

VUE is an incremental framework that can be used either as a third-party library or to build complex single-page projects.

  • Learn HTML, CSS, and JS. Learn ES6, JSX, and functional programming first.
  • Flexible: can be used as a third-party library or as an architectural project;
  • Efficient: virtual DOM;
  • Easy to use: instructions, templates, data binding. In contrast to React, two-way data binding can be changed automatically. React requires manual setState;
  • Component: Compared to React, vUE component is easier for beginners to receive HTML, CSS, and JS. React is implemented using JS.

3. Why not use objects for data in components

When component reuse, all component instances will share data. If data is an object, it will cause a component to modify data, which will affect all other components. Therefore, it is necessary to change data into a function, and call the function once every time it is used to obtain a new object. When we use new Vue(), we can set data as an object or as a function. Because new Vue() generates a root component that is not reused, there is no sharing of data.

VueComponent.prototype.$options = {
    data: {name: 'stoney'}
}

let vc1 = new VueComponent();
vc1.$options.data = 'eric';
let vc2 = new VueComponent();
console.log(vc2.$options.data);
Copy the code

When the same component is reused multiple times, multiple instances are created using the same constructor. If data is an object, then all components share the same object. In order to ensure the data independence of the component, each component must return a new object as the state of the component through the data function.

Principle: the core/global – API/extend. Js.

Sub.options = mergeOptions( Super.options, extendOptions ) function mergeOptions() { function mergeField (key) { const strat = strats[key] || defaultStrat options[key] = strat(parent[key], child[key], vm, key) } strats.data = function ( parentVal: any, childVal: any, vm? : Component ): ? Function { if (! If (childVal &&typeof childVal! == 'function') { process.env.NODE_ENV ! == 'production' && warn( 'The "data" option should be a function ' + 'that returns a per-instance value in component ' +  'definitions.', vm ) return parentVal } return mergeDataOrFn(parentVal, childVal) } return mergeDataOrFn(parentVal, childVal, vm) }Copy the code

4. The difference between V-if and V-show in Vue

  • V-if does not render the DOM element of the node where the current instruction resides if the condition is not true
  • V-show simply toggles the display or hide of the current DOM
  • V-if is good for initialization, v-show for update

Principle:

The v-if template is compiled as follows:

const VueTemplateCompiler = require('vue-template-compiler')
let r1 = VueTemplateCompiler.compile(`<div v-if="true"><span v-for="i in 3">hello</span></div>`)
console.log(r1)

render:
   'with(this){return (true)?_c(\'div\',_l((3),function(i){return _c(\'span\',[_v("hello")])}),0):_e()}',
Copy the code

V-show compiled from the template is as follows:

let r2 = VueTemplateCompiler.compile(`<div v-show="true"></div>`) console.log(r2) /** * with(this){ * return _c(\'div\',  * {directives:[{ * name:"show", * rawName:"v-show", * value:(true), * expression: "true" * * *}})}] * / / / v - show the operating style, Defined in the platforms/web/runtime/directives/show. Js export default {bind (el: any, {value} : VNodeDirective, vnode: VNodeWithData) { vnode = locateNode(vnode) const transition = vnode.data && vnode.data.transition const originalDisplay = el.__vOriginalDisplay = el.style.display === 'none' ? '' : el.style.display if (value && transition) { vnode.data.show = true enter(vnode, () => { el.style.display = originalDisplay }) } else { el.style.display = value ? originalDisplay : 'none' } },Copy the code

5. Why can’t V-for and V-if be used together

const r3 = VueTemplateCompiler.compile(`<div v-if="false" v-for="i in 3">hello</div>`) console.log(r3) /** * with(this){  * return _l((3),function(i){ * return (false)?_c(\'div\',[_v("hello")]):_e() * }) * } * errors: [ 'Cannot use v-for on stateful component root element because it renders multiple elements.' ], */Copy the code

The v-for command has a higher priority than the v-if command. If the v-if command is used continuously, each element is added, resulting in a waste of performance.

6. Which lifecycle ajax requests are placed in

  • When created, the DOM in the view is not rendered, so if you directly manipulate the DOM node, you cannot find the relevant element.
  • In Mounted, you can manipulate the DOM node directly because the DOM has already been rendered.

In mounted mode, the ajax lifecycle is executed synchronously. Ajax is executed asynchronously. Mounted method is not supported in server rendering, so it is added to created.

7. When to use beforeDestroy

  • The $on method is used in the current page and needs to be unbound before the component is destroyed
  • Clear self-defined timers
  • Unbind events. Scroll Mousemove…

8. What problems can V-HTML cause in Vue

  • XSS attacks may result
  • V-html replaces the child elements inside the tag

The principle of

let r4 = VueTemplateCompiler.compile(`<div v-html=" '<span>hrllo</span>' "></div>`)
console.log(r4)

/**
 * with(this){return _c(\'div\',{domProps:{"innerHTML":_s( \'<span>hrllo</span>\' )}})}
 */

Copy the code

9. Vue parent component lifecycle call order

Loading the rendering process

Parent beforeCreated -> Parent created -> Parent beforeMount -> child beforeCreated -> child created -> child mounted -> Parent moubted

Child component update process

Parent beforeUpdate -> Child beforeUpdate -> Child updated -> Parent updated

Parent component update process

Parent beforeUpdate -> Parent updated

Destruction of the process

Parent beforeDestroy -> Child beforeDestroy -> Child destroyed -> Parent destroyed

conclusion

  • Components are called in the order of the parent before the child, rendering must be completed in the order of the child after the parent;
  • The destruction operation of the component is parent before child, and the destruction is child before parent;

10. Vue component related

What is componentization and what are its benefits

Any page can be abstracted as a tree of components. Large to a page, small to a button can be a component, a page is composed of many components nested splicing, this is componentization;

Benefits of componentization

  • Reusability is strong
  • The division of development
  • Easy to manage code
  • The coupling is low

How does VUE create components

Global components

Vue.component(component name, component details object);

Local components

new Vue({ ... Components: {component name: component detail object}})Copy the code

Single file component

The vue file

<template>
<script>
<style>
Copy the code

How do Vue components communicate

  • Parent -> child via props, child -> parent,on, on,on, emit;
  • How to get parent component instances,parent, parent,parent, children;
  • Provide child components in the parent component to consume Provider, inject;
  • Ref calls a component property or method by getting an instance.
  • Event Bus enables cross-component communication;
  • slot
  • Vuex state management to achieve communication;
  • attrs: Contains attribute bindings (except class and style) that are not recognized (and retrieved) as prop in the parent scope. When a component does not declare any prop, All parent-scoped bindings (except class and style) are included here, and internal components can be passed in via v-bind=”$attrs” – useful when creating high-level components;
  • provider inject; Provide and Inject are mainly used in the development of high-level plug-ins/component libraries and are not recommended for common application code. Variables are provided by providers in the parent component and injected by inject in the child component. No matter how deep the child component is, as long as the Inject method is called, the data in the provider can be injected; Rather than being limited to fetching data only from the prop property of the current parent component, child components can be called as long as the parent component lives;
  • $emit9’input’, val); this.$emit9’input’, val);

11. How to separate the same logic in Vue

Vue.mixin usage, where functions are mixed with some common logic for each component lifecycle;

12. Explain the principle of reactive data

  • Core point: Object.defineProperty
  • By default, when Vue initializes data, it will redefine all attributes of data using Object.defineProperty. When the page gets the corresponding attribute, it will collect dependencies (collect the watcher of the current component). If the attribute changes, it will notify related dependencies to update the operation.

Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { const value = getter ? getter.call(obj) : Val if (dep.target) {dep.depend() // Collect dependencies if (childOb) {childob.dep.depend () if (array.isarray (value)) { dependArray(value) } } } return value }, set: function reactiveSetter (newVal) { const value = getter ? getter.call(obj) : val /* eslint-disable no-self-compare */ if (newVal === value || (newVal ! == newVal && value ! == value)) { return } /* eslint-enable no-self-compare */ if (process.env.NODE_ENV ! == 'production' && customSetter) { customSetter() } // #7981: for accessor properties without setter if (getter && ! setter) return if (setter) { setter.call(obj, newVal) } else { val = newVal } childOb = ! Shallow && observe(newVal) dep.notify() // Notify dependencies to update}})Copy the code

13. How are array changes detected in Vue

  • Using function hijacking method, overwrite the array method
  • Vue rewrites the prototype chain of the array in Data, pointing to the array prototype method defined by itself, so that when the array API is called, it can notify the dependency update. If the array contains reference types, the reference types in the array will be monitored again.

const arrayProto = Array.prototype export const arrayMethods = Object.create(arrayProto) const methodsToPatch = [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'] /** * Intercept mutating methods and emit events */ methodsToPatch. ForEach (function (method) Cache original 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 change ob.dep.notify() Manually notifying view updates return result})}) this.observeArray(value) // Perform depth monitoringCopy the code

14. Why does Vue use asynchronous rendering

Vue is component-level update. If asynchronous update is not adopted, the current component will be re-rendered each time the data is updated. For the sake of performance, VUE will update the view asynchronously after this round of data update.

update () { /* istanbul ignore else */ if (this.lazy) { this.dirty = true } else if (this.sync) { this.run() } else { }} export function queueWatcher(queueWatcher: queueWatcher(queueWatcher: queueWatcher) Watcher) { const id = watcher.id if (has[id] == null) { has[id] = true if (! flushing) { queue.push(watcher) } else { // if already flushing, splice the watcher based on its id // if already past its id, it will be run next immediately. let i = queue.length - 1 while (i > index && queue[i].id > watcher.id) { i-- } queue.splice(i + 1, 0, watcher) } // queue the flush if (! waiting) { waiting = true if (process.env.NODE_ENV ! == 'production' && ! Async) {flushSchedulerQueue() return nextTick(flushSchedulerQueue)}}Copy the code

15. Implementation principle of nextTick

The nextTick method mainly uses macro tasks and micro tasks to define an asynchronous method. Calling nextTick for many times will store the method into a queue, and the current queue will be cleared through this asynchronous method, so the nextTick method is an asynchronous method.

export function nextTick (cb? : Function, ctx? : Object) { let _resolve callbacks.push(() => { if (cb) { try { cb.call(ctx) } catch (e) { handleError(e, ctx, 'nextTick') } } else if (_resolve) { _resolve(ctx) } }) if (! pending) { pending = true timerFunc() } // $flow-disable-line if (! cb && typeof Promise ! == 'undefined') { return new Promise(resolve => { _resolve = resolve }) } } let timerFunc // The nextTick behavior leverages the microtask queue, which can be accessed // via either native Promise.then or MutationObserver. // MutationObserver has wider support, However it is seriously bugged in // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers stops working after triggering a few times... so, if native // Promise is available, we will use it: /* istanbul ignore next, $flow-disable-line */ if (typeof Promise ! == 'undefined' && isNative(Promise)) { const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) // In problematic UIWebViews, Promise.then doesn't completely break, but // it can get stuck in a weird state where callbacks are pushed into the // microtask queue but the queue isn't being flushed, until the browser // needs to do some other work, e.g. handle a timer. Therefore we can // "force" the microtask queue to be flushed by adding an empty timer. if (isIOS) setTimeout(noop) } isUsingMicroTask = true } else if (! isIE && typeof MutationObserver ! == 'undefined' && ( isNative(MutationObserver) || // PhantomJS and iOS 7.x MutationObserver.toString() === '[object MutationObserverConstructor]' )) { // Use MutationObserver where native Promise is not available, // e.g. PhantomJS, iOS7, Android 4.4 // (#6466 MutationObserver is unreliable in IE11) let counter = 1 const observer = new MutationObserver(flushCallbacks) const textNode = document.createTextNode(String(counter)) observer.observe(textNode, { characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } isUsingMicroTask = true } else  if (typeof setImmediate ! == 'undefined' && isNative(setImmediate)) { // Fallback to setImmediate. // Techinically it leverages the (macro) task queue, // but it is still a better choice than setTimeout. timerFunc = () => { setImmediate(flushCallbacks) } } else { // Fallback to setTimeout. timerFunc = () => { setTimeout(flushCallbacks, 0) } }Copy the code

16. Difference between Method, computed, and Watch

  • Method: The page data is re-executed every time it is re-rendered, which is a high performance drain, except when caching is not desired.
  • Computed: Computes attributes, depending on other attributes, and computed values are cached and returned only when computed values change.
  • Watch: A callback is executed when a change in value is heard, and some logical operations can be performed in the callback.
  • Conclusion: Methods are generally not used unless caching is not desired. Generally, computed can be used when values need to be obtained dynamically by relying on other attributes, and watch can be used when asynchronous or expensive operations need to be performed to monitor changes in values

17. Vue lifecycle correlation

What is the life cycle

The life cycle of a VUE is the process from initialization to destruction of a VUE.

What is a hook function

In the process of life cycle, we have a lot of special period, I hope that in the special period of vue do something, so the hooks, hooks is the author in the design of the vue, the vue from initialization to destroy to the special period of the time we define some function. If it is defined, it will be executed. If it is not defined, it will not be executed.

What are the life cycles of vUE

  • BeforeCreate created before
  • Created after creation
  • BeforeMount mount ago
  • After mounted mount
  • BeforeUpdate Before data update
  • Updated After data is updated
  • BeforeDestroy destroyed before
  • Destroyed after the destruction of

What difference does any of this make

  • BeforeCreate is called just after the instance is initialized and before the data Observer is called.
  • When created, properties and methods are mounted to the instance; At this step, the instance is configured with: Data Observer, property and method operations, watch/event event callbacks, no $EL;
  • BeforeMount Before mounting, the node range corresponding to el or mount is found but data is not replaced. The associated render function is called for the first time;
  • After mounted, all vue variables are replaced with the corresponding data values in data.
  • BeforeUpdate is called before data is updated and occurs before the virtual DOM is re-rendered and patched.
  • Updated This hook is called after the virtual DOM is re-rendered and patched due to data changes.
  • BeforeDestroy called before instance destruction, at this step, the instance is still fully available.
  • After destroyed. When called, everything indicated by the Vue instance is unbound, all event listeners are removed, and all subinstances are destroyed. This hook is not called during server-side rendering.

When do we use it in the project

  • Created: The instance is already created because it is the first trigger to request some data resources.
  • Mounted: The INSTANCE is mounted. DOM operations can be performed on the instance.
  • BeforeUpdate: Changes state further in this hook, which does not trigger additional rerendering.
  • Updated: DOM-dependent operations can be performed, however in most cases you should avoid changing the state in the meantime as this can lead to an infinite loop. This hook is not called during server-side rendering;
  • Destroyed: Performs optimization operations, clears timers, and unbinds events.

18. The role of the keep-alive component

The keep-alive component is mainly used to save the state of the component during component switching to prevent multiple rendering. The keep-alive component can be used to wrap the component to be saved. For a keep-alive component, it has two unique lifecycle hook functions, activated and deactived. Components wrapped with keep-alive are not destroyed when switched, but are cached in memory and deactived hook functions are executed;

  • Include – a string or regular expression. Only components whose names match are cached.
  • Exclude – a string or regular expression. Any component whose name matches will not be cached.
  • Max = number, maximum number of component instances can be cached;

19, Vue loader

If we develop a VUE using scaffolding, or a VUE project using a Webpack configuration, we will definitely touch componentized development; Components are defined in three forms: global components, local components, and file components. The file component is the component form of.vue, one file at a time; One problem is that.vue files cannot be parsed by the browser; Vue loader is used to parse. Vue files into HTML, CSS, and JS files that browsers can understand. Of course, there are many loaders in Webpack that do the same thing;

When the style tag has scoped, its CSS applies only to the elements in the current component.

The < style > global style / * * / < / style > < style scoped > local style / * * / < / style >Copy the code

The two are interchangeable. With scoped, the style of the parent component does not infiltrate into the child component.

20. Why use asynchronous components

If the component has many functions, the result of packaging will be larger, we can use the asynchronous way to load the component, mainly relying on the syntax import(), can realize the file split load.

components: { AddCustomerSchedulr: (resolve) => import (".. /component/AddCustomerSchedulr") }Copy the code

21. What is a scope slot

slot

  • When a component virtual node is created, the virtual node of the child of the component is saved. When the component is initialized, the child is classified by slot {a:[vNode], b[vnode]}.
  • The component will be rendered with a node with the corresponding slot attribute for replacement. (The slot’s scope is the parent component)
let ele = VueTemplateCompiler.compile(`
  <my-component>
    <div slot="header">node</div>
    <div>react</div>
    <div slot="footer">vue</div>
  </my-component>
`)

console.log(ele)
/**
 * with(this){
 *  return _c(\'my-component\',
 *    [_c(\'div\',{
 *      attrs:{
 *        "slot":"header"
 *      },
 *      slot:"header"
 *    },[_v("node")]
 * ),_v(" "),_c(\'div\',[_v("react")]),_v(" "),_c(\'div\',{
 *    attrs:{
 *      "slot":"footer"
 *    },
 *    slot:"footer"
 *    },[_v("vue")])])
 * }
 */
Copy the code
let ele = VueTemplateCompiler.compile(`
  <div>
    <slot name="header"></slot>
    <slot name="footer"></slot>/slot>
  </div>
`)
console.log(ele)

/**
 * with(this){return _c(\'div\',[_t("header"),_v(" "),_t("footer"),_v("/slot>\\n  ")],2)}
 */
Copy the code

Scope slot

  • A scoped slot is not a child node of a component when it is parsed. This function is called when a child component is rendered.
let ele = VueTemplateCompiler.compile(`
  <app>
    <div slot-scope="msg" slot="footer">{{msg.a}}</div>
  </app>
`)
console.log(ele)

/**
 * with(this){
 *  return _c(\'app\',{
 *    scopedSlots:_u([{
 *      key:"footer",
 *      fn:function(msg){
 *        return _c(\'div\',{},[_v(_s(msg.a))])
 *      }
*      }])
    })
  }
 */
Copy the code
let ele = VueTemplateCompiler.compile(`
  <div>
    <slot name="footer" a="1" b="2"></slot>
  </div>
`)

console.log(ele)
/**
 * with(this){
 *  return _c(\'div\',[_t("footer",null,{
 *    "a":"1",
 *    "b":"2"
 *  })],2)
 * }
 */
Copy the code

22. Time complexity of diff algorithm

The two-tree complete diff algorithm is a time complexity O(n^3),Vue is optimized to transform the O(n^3) responsibility problem into O(n) complexity problem (only comparing the same level, not considering the cross-level problem). In the front end, dom elements are rarely moved across levels. So the Virtual DOM only compares elements at the same level.

23. Briefly describe the principle of diff algorithm in VUE

  1. Sibling comparison first, then child node comparison
  2. First determine if one parent has a son and the other does not
  3. The comparison is that they all have sons
  4. Recursively compare child nodes (two-pointer)

24. Performance optimizations common in Vue

1. Coding optimization

  1. Don’t put all data in data, data adds getters and setters, and collects the corresponding watcher;
  2. Vue uses an event broker to bind events to each element in V-FOR;
  3. Spa page adopts keep-alive cache component;
  4. Splitting components (increased reusability, increased code maintainability, reduced unnecessary rendering)
  5. V-if: When the value is false, the internal command will not be executed and has the blocking function. In most cases, v-if is used instead of V-show.
  6. Key: ensure uniqueness (default VUE uses in-place reuse strategy);
  7. Object. freeze Freezes data;
  8. Rational use of routing lazy loading, asynchronous components;
  9. Try to use the Runtime runtime version;
  10. Data persistence issues (stabilization, interception)

2. Vue loading performance optimization

  • Import third-party modules on demand (babel-plugin-component);
  • Scroll to visual area for dynamic loading;
  • Lazy loading of images;

3. User experience optimization

  • App-skeleton screen;
  • App – shell app shell

4, SEO optimization

  • Prerender-spa-plugin for pre-rendering
  • Server rendering SSR

5. Package optimization

  • Load the third party module by CDN;
  • Multithreaded packaging;

Action and mutation are different

  • Mutation is a synchronous update of data (an internal test is performed to determine whether the data is updated asynchronously).
  • The action is an asynchronous operation that retrieves the data and calls mutation to submit the final data

25. Characteristics of computed tomography in VUE

Computed by default is also a Watcher, but it has a cache that updates data only when dependent attributes change;

function initComputed (vm: Component, computed: Object) { // $flow-disable-line const watchers = vm._computedWatchers = Object.create(null) // computed properties are just getters during SSR const isSSR = isServerRendering() for (const key in computed) { const userDef = computed[key] const getter = typeof userDef === 'function' ? userDef : userDef.get if (process.env.NODE_ENV ! == 'production' && getter == null) { warn( `Getter is missing for computed property "${key}".`, vm ) } if (! isSSR) { // create internal watcher for the computed property. watchers[key] = new Watcher( vm, getter || noop, noop, computedWatcherOptions ) } // component-defined computed properties are already defined on the // component prototype. We only need to define computed properties defined // at instantiation here. if (! (key in vm)) { defineComputed(vm, key, userDef) } else if (process.env.NODE_ENV ! == 'production') { if (key in vm.$data) { warn(`The computed property "${key}" is already defined in data.`, vm) } else if (vm.$options.props && key in vm.$options.props) { warn(`The computed property "${key}" is already defined as a prop.`, Vm)}}}} function createComputedGetter (key) {return function computedGetter () {const watcher = This._computedWatchers && this._computedWatchers[key] if (watcher) {if (watcher watcher.evaluate() } if (Dep.target) { watcher.depend() } return watcher.value } } } /** * Subscriber interface. * Will Be called when a dependency changes. */ / update () {/* Istanbul ignore else */ if (this.lazy) {// count attributes, This.dirty = true} else if (this.sync) {// synchronize watcher this.run()} else {queueWatcher(this) // queueWatcher(this)} }Copy the code

26. How is deep: true implemented in Watche

When the user sets the deep attribute in watch to true, if the value currently monitored is an array type, each item in the object will be evaluated. At this time, the current Watcher will be stored in the corresponding attribute dependency, so that when the object in the array changes, data update will be notified.

/** * Evaluate the getter, And re-collect dependencies. */ get() {pushTarget(this) let value const vm = this.vm try {value = this.getter.call(vm, vm) } catch (e) { if (this.user) { handleError(e, vm, `getter for watcher "${this.expression}"`) } else { throw e } } finally { // "touch" every property so they are all Traverse (value) {// traverse(value) {// traverse(value) {// traverse(value) {// traverse(value) PopTarget () this.cleanupdeps ()} return value}Copy the code

27. Use vnodes to describe a DOM structure

A virtual node is an object that represents a real DOM element

function _c(tag, data, ... children) { let key = data.key delete data.key children = children.map(child => { if (typeof child === 'object') { return child } else { return vnode(undefined, undefined, undefined, undefined, child) } }) return vnode(tag, data, key, Children)} function vnode(tag, data, key, children, text) {return {tag, // } let r = _c('div', {id: 'container' }, _c('p', {}, 'hello'), 'zf') console.log(r) /** * { tag: 'div', data: { id: 'container' }, key: undefined, children: [ { tag: 'p', data: {}, key: undefined, children: [Array], text: undefined }, { tag: undefined, data: undefined, key: undefined, children: undefined, text: 'zf' } ], text: undefined } */Copy the code

28. What are the improvements of VUe3.0

  • TS is used to write
  • Support composition API
  • Reactive data principle changed to proxy
  • Vdom comparison algorithm update, update only the vDOM bound to dynamic data parts

29. Principles of VUE template rendering

{{MSG}} => Hello regular expression, match {{XXX}} format, read XXX data and replace; Data: {MSG: ‘hello’} converts template to render function

export const createCompiler = createCompilerCreator(function baseCompile ( template: string, options: CompilerOptions): CompiledResult {const ast = parse(template.trim(), options) //1, convert the template to the EST tree if (options.optimize! > > > optimize(ast, options) // 2 > > const code = generate(ast, options) // render: code.render, staticRenderFns: code.staticRenderFns } })Copy the code

30, vue – the router to participate

The simplest of these is url passing. There are two types of URL passing, params and Query.

  • Params value transmission refers to dynamic routing value transmission.
{path: '/user/:id'} // Define a route parameter <router-link to="/user/123"></router-link> // Pass a value this.$route.params.id // ValueCopy the code
  • Query to pass a value? The following concatenation parameter is passed
<router-link to="/user? Id =123"></router-link> // This.$route.query.id // ValueCopy the code

31. The principle of routing and how to achieve it

  • Route definition: display different page or component functions by clicking different buttons;
  • Principle: according to different path address, display different pages, components;
  • Implementation:
  1. hash #a #b
  2. history /c /d
  • How do you monitor these two changes
  1. hash: hashchange
  2. history: popstate

32. Principle of event binding in VUE

Vue event binding is divided into two types, one is the native event binding, and the other is the component event binding;

  1. The native DOM event binding is implemented by addEventListener;
  2. The component’s binding takes the $on method

Principle: Compilation of events

let r1 = VueTemplateCompiler.compile('<div @click="fn()"></div>')
let r2 = VueTemplateCompiler.compile('<my-component @click.native="fn" @click="fn1"></my-component>')
console.log(r1.render)
console.log(r2.render)

with(this){return _c('div',{on:{"click":function($event){return fn()}}})}
with(this){return _c('my-component',{on:{"click":fn1},nativeOn:{"click":function($event){return fn($event)}}})}
Copy the code

1. Native DOM binding

  • Vue calls createElm when it creates the real DOM. By default, invokeCreateHooks are called.
  • The process iterates through the relative properties of the current platform, including updateDOMListeners and adds.
function updateDOMListeners (oldVnode: VNodeWithData, vnode: VNodeWithData) { if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)) { return } const on = vnode.data.on || {} const oldOn = oldVnode.data.on || {} target = vnode.elm normalizeEvents(on) updateListeners(on, oldOn, add, remove, createOnceHandler, vnode.context) target = undefined } function add ( name: string, handler: Function, capture: boolean, passive: boolean ) { // async edge case #6566: inner click event triggers patch, event handler // attached to outer element during patch, and triggered again. This // happens because browsers fire microtask ticks between event propagation. // the solution is  simple: we save the timestamp when a handler is attached, // and the handler would only fire if the event passed to it was fired // AFTER it was attached. if (useMicrotaskFix) { const attachedTimestamp = currentFlushTimestamp const original = handler handler = original._wrapper = function (e) { if  ( // no bubbling, should always fire. // this is just a safety net in case event.timeStamp is unreliable in // certain weird environments... e.target === e.currentTarget || // event is fired after handler attachment e.timeStamp >= attachedTimestamp || // #9462 bail for iOS 9 bug: event.timeStamp is 0 after history.pushState e.timeStamp === 0 || // #9448 bail if event is fired in another document in  a multi-page // electron/nw.js app, since event.timeStamp will be using a different // starting reference e.target.ownerDocument ! == document) {return original.apply(this, arguments)}}} target.addeventListener () supportsPassive ? { capture, passive } : capture ) }Copy the code

Binding events in VUE are directly bound to real DOM elements

2. Binding events in the component

export function updateComponentListeners ( vm: Component, listeners: Object, oldListeners: ? Object ) { target = vm updateListeners(listeners, oldListeners || {}, add, remove, createOnceHandler, vm) target = undefined } function add (event, fn) { target.$on(event, fn) }Copy the code

Component binding events are implemented through vUE’s custom $on= method

33. Implementation principle of V-Model and how to customize V-Model

V-model can be thought of as the syntactic sugar of the value+input method;

<el-checkbox :value="" @input=""></el-checkbox>
<el-checkbox v-model="check"></el-checkbox>
Copy the code

You can redefine the meaning of the V-Model yourself

Vue.component('el-checkbox', {
    template: `<input type="checkbox" :checked="check" @change="$emit('change', $event.target.checked)">`,
    model: {
        prop: 'check',
        event: 'change'
    },
    props: {
        check: Boolean
    }
})
        
Copy the code

Converts the component’s V-model default to value+input

let r1 = VueTemplateCompiler.compile(`<el-checkbox v-model="check"></el-checkbox>`)
console.log(r1)

/**
 * with(this){
 *  return _c(\'el-checkbox\',{
 *    model:{
 *      value:(check),
 *      callback:function ($$v) {
 *        check=$$v
 *      },
 *      expression:"check"
 *    }
 *  })
 * }
 */
Copy the code

Source:

function transformModel (options, data: any) { const prop = (options.model && options.model.prop) || 'value' const event = (options.model && options.model.event) || 'input' ; (data.attrs || (data.attrs = {}))[prop] = data.model.value const on = data.on || (data.on = {}) const existing = on[event] const callback = data.model.callback if (isDef(existing)) { if ( Array.isArray(existing) ? existing.indexOf(callback) === -1 : existing ! == callback ) { on[event] = [callback].concat(existing) } } else { on[event] = callback } }Copy the code

The native V-Model generates different events and attributes depending on the tag

let r1 = VueTemplateCompiler.compile('<input v-model="value" />')
console.log(r1)

/**
 * with(this){
 *  return _c(\'input\',{
 *    directives:[{
  *    name:"model",
  *    rawName:"v-model",
  *    value:(value),
  *    expression:"value"
 *    }],
 *    domProps:{
 *      "value":(value)
 *    },
 *    on:{
 *      "input":function($event){
 *        if($event.target.composing)return;
 *        value=$event.target.value
 *      }
 *    }
 *  })
 * }
 */

Copy the code

Compile time: different labels parsing out is different, the content of the platform/web/compiler directive/mode. Js

if (el.component) { genComponentModel(el, value, modifiers) // component v-model doesn't need extra runtime return false } else if (tag === 'select') { genSelect(el, value, modifiers) } else if (tag === 'input' && type === 'checkbox') { genCheckboxModel(el, value, modifiers) } else if (tag === 'input' && type === 'radio') { genRadioModel(el, value, modifiers) } else if (tag === 'input' || tag === 'textarea') { genDefaultModel(el, value, modifiers) } else if (! config.isReservedTag(tag)) { genComponentModel(el, value, modifiers) // component v-model doesn't need extra runtime return false }Copy the code

Runtime: Handles some input method issues with elements:

inserted (el, binding, vnode, oldVnode) { if (vnode.tag === 'select') { // #6903 if (oldVnode.elm && ! oldVnode.elm._vOptions) { mergeVNodeHook(vnode, 'postpatch', () => { directive.componentUpdated(el, binding, vnode) }) } else { setSelected(el, binding, vnode.context) } el._vOptions = [].map.call(el.options, getValue) } else if (vnode.tag === 'textarea' || isTextInputType(el.type)) { el._vModifiers = binding.modifiers if (! binding.modifiers.lazy) { el.addEventListener('compositionstart', onCompositionStart) el.addEventListener('compositionend', OnCompositionEnd) // Safari < 10.2&uiwebView doesn't fire compositionEnd when // switching focus before confirming composition choice // this also fixes the issue where some browsers e.g. iOS Chrome // fires "change" instead of "input"  on autocomplete. el.addEventListener('change', onCompositionEnd) /* istanbul ignore if */ if (isIE9) { el.vmodel = true } } } },Copy the code