The end of February ~ more continuous a month hee Hee (although is the shortest month of a year, although a lot of water… Next should follow the fate of the update, there is time to vUE source part will slowly add a bit deeper. Start sending out resumes
Vue constructor
When using Vue, the call is made using the new operator, which indicates that Vue is a constructor.
Vue constructor prototype
// Import five methods from five files (excluding WARN)
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '.. /util/index'
// Define the Vue constructor
// Remind to use the new operator to call vue
function Vue (options) {
if(process.env.NODE_ENV ! = ='production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')}this._init(options)
}
// Pass Vue as a parameter to the five imported methods
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
/ / export Vue
export default Vue
Copy the code
As shown in the code above, five methods are imported from./init.js,./state.js,./render.js,./events.js, and./lifecycle. InitMixin, stateMixin, renderMixin, eventsMixin, and lifecycleMixin, then define the Vue constructor, which uses the safe mode to remind you to call Vue using the new operator, The Vue constructor is then passed as an argument to each of the five imported methods, and finally the Vue is exported.
Here’s what these methods do:
- InitMixin method
Prototype defines two read-only properties: $data and $props and three methods: $set, $delete, and $watch.
- EventsMixin method
Four methods have been added to Vue.prototype: $ON, $once, $off, and $emit.
- LifecycleMixin method
Four methods were added to vue. prototype: _update, $forceUpdate, and $destroy.
- RenderMixin method
Finally, after renderMixin, vue. prototype was added with the following methods:
// installRenderHelpers function
Vue.prototype._o = markOnce
Vue.prototype._n = toNumber
Vue.prototype._s = toString
Vue.prototype._l = renderList
Vue.prototype._t = renderSlot
Vue.prototype._q = looseEqual
Vue.prototype._i = looseIndexOf
Vue.prototype._m = renderStatic
Vue.prototype._f = resolveFilter
Vue.prototype._k = checkKeyCodes
Vue.prototype._b = bindObjectProps
Vue.prototype._v = createTextVNode
Vue.prototype._e = createEmptyVNode
Vue.prototype._u = resolveScopedSlots
Vue.prototype._g = bindObjectListeners
Vue.prototype.$nextTick = function (fn: Function) {}
Vue.prototype._render = function () :VNode {}
Copy the code
As you can see from the above, each *Mixin method simply wraps vue. prototype and mounts some properties and methods on it.
Static properties and methods of Vue constructors (global API)
// Import Vue from Vue's birth file
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'
// Pass the Vue constructor as an argument to the initGlobalAPI method, which comes from the./global-api/index.js file
initGlobalAPI(Vue)
Prototype adds the $isServer attribute that proxies the isServerRendering method from the core/util/env.js file
Object.defineProperty(Vue.prototype, '$isServer', {
get: isServerRendering
})
// Add the $ssrContext attribute to vue. prototype
Object.defineProperty(Vue.prototype, '$ssrContext', {
get () {
/* istanbul ignore next */
return this.$vnode && this.$vnode.ssrContext
}
})
// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
value: FunctionalRenderContext
})
Vue. Version Stores the version number of the current Vue
Vue.version = '__VERSION__'
/ / export Vue
export default Vue
Copy the code
The initGlobalAPI method is called and two read-only properties are added to vue. prototype: $isServer and $ssrContext define the FunctionalRenderContext static property on the constructor for use in SSR. Finally, a static attribute version is added to the Vue constructor to store the version value of the current Vue.
Here’s what the initGlobalAPI method does: Add the global API to the Vue constructor.
-
Add the config read-only attribute to the Vue constructor
-
The util attribute is added to Vue. The util object has four attributes: WARN, extend, mergeOptions, and defineReactive.
(Vue.util and the four methods under util are not considered part of the public API, so avoid relying on them, but you can still use them at your own risk.)
-
Added four attributes to Vue: set, DELETE, nextTick, and Options (empty objects created by Object.create(null))
-
Next modify vue.options
Vue.options = { components: Object.create(null), directives: Object.create(null), filters: Object.create(null), _base: Vue } Copy the code
-
Blend the builtInComponents properties into Vue.options.components. Finally, vue.options looks like this:
Vue.options = { components: { KeepAlive }, directives: Object.create(null), filters: Object.create(null), _base: Vue } Copy the code
-
The last part of the initGlobalAPI method calls four init* methods with Vue as arguments
initUse(Vue) // Add the vue. use method to install the plug-in initMixin(Vue) // Add mixin global API on Vue initExtend(Vue) // Add Vue. Cid static attribute and Vue. Extend static method to Vue initAssetRegisters(Vue) Vue.component, vuue. Directive and vuue. Filter are static methods used to register components, directives, and filters respectively. Copy the code
conclusion
In this core/index.js file, it first imports the core Vue, the Vue in the core/instance/index.js file, or the Vue after the prototype has been wrapped (adding properties and methods). We then add static methods and attributes to the Vue using initGlobalAPI methods. In addition, in this file, we also modify the prototype to add two attributes: $isServer and $ssrContext, and finally add Vue. Version and export Vue.
Vue platform packaging
Platforms/web/runtime/index. The role of js file is platform of Vue packing:
- Set the platform vue. config.
- There are two directives mixed on vue. options, which are model and show.
- Vue. Options blends two components, Transition and TransitionGroup.
- Two methods have been added to vue.prototype:
__patch__
和$mount
.
Add compiler
The entry-Runtime-with-Compiler. js file contains not only the runtime version of the Vue constructor, but also the compiler.
/ /... Other import statements
// Import the runtime Vue
import Vue from './runtime/index'
/ /... Other import statements
// Import compileToFunctions from the./ Compiler /index.js file
import { compileToFunctions } from './compiler/index'
// Get the innerHTML of the element based on the ID
const idToTemplate = cached(id= > {
const el = query(id)
return el && el.innerHTML
})
// Cache Vue. Prototype.$mount method with mount variable
const mount = Vue.prototype.$mount
// Override vue.prototype.$mount
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
) :Component {
/ /... Function body elision
}
/** * Gets the element's outerHTML */
function getOuterHTML (el: Element) :string {
if (el.outerHTML) {
return el.outerHTML
} else {
const container = document.createElement('div')
container.appendChild(el.cloneNode(true))
return container.innerHTML
}
}
// Add a global API 'vue.compile' to Vue whose value is the imported compileToFunctions above
Vue.compile = compileToFunctions
/ / export Vue
export default Vue
Copy the code
When this file runs, it has two effects on Vue.
- Rewrote the vue.prototype.$mount method
- Added the vue.compile global API
Vue life cycle
-
When is the hook function called
-
beforeCreate
The data Observer is called after the instance initializes new Vue() and before reactive processing
-
created
The instance is called after it has been created and configured with data Observer, property and method operations, and Watch/Event event callbacks. Data is available, but not $EL.
-
beforeMount
Called before the mount begins: The associated render function is called for the first time.
-
mounted
El is called after being replaced by the newly created vm.$el and mounted to the instance. Page rendered
-
beforeUpdate
Called when data is updated, 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 which point the instance is still fully available.
-
destroyed
Called after the Vue instance is 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.
-
-
What can be done in the life hook
-
created
The instance has been created because it is the first to trigger and can make some requests for data resources.
-
mounted
The instance is mounted and ready for some DOM manipulation.
-
beforeUpdate
You can further change the state in this hook without triggering additional rerendering.
-
updated
You can perform DOM-dependent operations and try to avoid changing the state in the meantime, as this could lead to an infinite update loop. This hook is not called during server-side rendering.
-
beforeDestory
Some optimizations can be performed, such as emptying timers and unbinding native bindings of events. If you use the $on method on the current instance, you need to unbind the component before it is destroyed.
-
thinking
In which lifecycle do ajax requests go?
When created, the DOM in the view is not rendered, so the DOM node is directly manipulated and relevant elements cannot be found. In Mounted, the DOM has been rendered and DOM nodes can be directly manipulated. In most cases, it is placed in Mounted to ensure logical uniformity. Because the lifecycle is executed synchronously, ajax is executed asynchronously. Since server-side rendering does not have DOM and does not support Mounted methods, it is created in the case of server-side rendering.
Vue parent component lifecycle call order
Loading the rendering process
Parent beforeCreate ==> Parent created ==> parent beforeMount ==> child beforeCreat ==> child created ==> child beforeMount ==> child Mounted ==> Parent Mounted
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 ==
understand
Components are called in the order of parent after child, rendering is completed in the order of child after parent
The destruction of a component is parent before child, and the destruction is completed in the order of child after parent
Schematic diagram
Talk about Vue event mechanics, handwritten$on
.$off
.$emit
.$once
The Vue event mechanism is essentially a publish-subscribe implementation.
class Vue {
constructor() {
// Event channel dispatch center
this._events = Object.create(null);
}
$on(event, fn) {
if (Array.isArray(event)) {
event.map(item= > {
this.$on(item, fn);
});
} else{(this._events[event] || (this._events[event] = [])).push(fn);
}
return this;
}
$once(event, fn) {
function on() {
this.$off(event, on);
fn.apply(this.arguments);
}
on.fn = fn;
this.$on(event, on);
return this;
}
$off(event, fn) {
if (!arguments.length) {
this._events = Object.create(null);
return this;
}
if (Array.isArray(event)) {
event.map(item= > {
this.$off(item, fn);
});
return this;
}
const cbs = this._events[event];
if(! cbs) {return this;
}
if(! fn) {this._events[event] = null;
return this;
}
let cb;
let i = cbs.length;
while (i--) {
cb = cbs[i];
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1);
break; }}return this;
}
$emit(event) {
let cbs = this._events[event];
if (cbs) {
const args = [].slice.call(arguments.1);
cbs.map(item= > {
args ? item.apply(this, args) : item.call(this);
});
}
return this; }}Copy the code
Why does the Vue component Data have to be a function?
Q: In the new Vue() instance,data can be an object directly. Why must data be a function in the Vue component?
Because components can be reused, if data is an object and the object is of a reference type, the data property values in the child components will pollute each other. If it is a function, each instance can maintain a separate copy of the returned object.
Instances of new Vue() are not reused, so data can be an object.
Implementation principle of keep-alive
is an abstract component that does not render DOM elements on its own and does not appear in the parent component chain. When dynamic components are wrapped, inactive component instances are cached rather than destroyed. Saves the render state of the component.
The source code
export default {
/ / component name
name: "keep-alive".// The key to determining whether the current component is rendered as a real DOM
abstract: true.// Abstract component properties, which are ignored when a component instance sets up a parent-child relationship, during initLifecycle
props: {
include: patternTypes, // The cached component
exclude: patternTypes, // The component is not cached
max: [String.Number] // Specify the cache size
},
created() {
this.cache = Object.create(null); // The cached virtual DOM
this.keys = []; // The cached VNode key
},
destroyed() {
for (const key in this.cache) {
// Delete all caches
pruneCacheEntry(this.cache, key, this.keys); }},mounted() {
// Listen for cached/uncached components
this.$watch("include".val= > {
pruneCache(this.name= > matches(val, name));
});
this.$watch("exclude".val= > {
pruneCache(this.name= >! matches(val, name)); }); },render() {
// Get the vNode of the first child
const slot = this.$slots.default;
const vnode: VNode = getFirstComponentChild(slot);
constcomponentOptions: ? VNodeComponentOptions = vnode && vnode.componentOptions;if (componentOptions) {
// Name is not in inlcude or is returned directly to vnode from exlude
// check pattern
constname: ? string = getComponentName(componentOptions);const { include, exclude } = this;
if (
// not included(include && (! name || ! matches(include, name))) ||// excluded
(exclude && name && matches(exclude, name))
) {
return vnode;
}
const { cache, keys } = this;
// Get the component's name field first, otherwise the component's tag
constkey: ? string = vnode.key ==null
? // same constructor may get registered as different local components
// so cid alone is not enough (#3269)
componentOptions.Ctor.cid +
(componentOptions.tag ? ` : :${componentOptions.tag}` : "")
: vnode.key;
// Hit the cache, take the vNode component instance directly from the cache, and rearrange the key order to the last one
if (cache[key]) {
vnode.componentInstance = cache[key].componentInstance;
// make current key freshest
remove(keys, key);
keys.push(key);
}
// Set vNode to cache if the cache is not hit
else {
cache[key] = vnode;
keys.push(key);
// prune oldest entry
// If Max is configured and the cache length exceeds this. Max, remove the first object from the cache
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode); }}// keepAlive flag bit
vnode.data.keepAlive = true;
}
return vnode || (slot && slot[0]); }};// src/core/components/keep-alive.js
// Removing cached VNodes also corresponds to the destory hook function that executes the component instance.
function pruneCacheEntry (
cache: VNodeCache,
key: string,
keys: Array<string>, current? : VNode) {
const cached = cache[key]
if(cached && (! current || cached.tag ! == current.tag)) { cached.componentInstance.$destroy()// Execute the component's destory hook function
}
cache[key] = null
remove(keys, key)
}
Copy the code
Realize the principle of
- Gets the first child component object wrapped around Keep-Alive and its component name.
- Include /exclude (if any) is matched to determine whether to cache. Does not match and returns the component instance directly.
- 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)
- 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.
- Finally, the keepAlive property of the component instance is set to true, which is used when rendering and executing the wrapped component’s hook function to keep the component instance from entering the $mount process. All previous hook functions (beforeCreate, Created, and Mounted) are no longer executed.
LRU cache elimination algorithm
LRU (Least Recently Used) algorithm eliminates data according to the historical access records of data. Its core idea is that “if data has been accessed recently, it has a higher chance of being accessed in the future”. Therefore, when the amount of data to be stored exceeds the maximum value that can be stored, the data that has not been accessed for the longest time will be removed and new data will be added.The keep-alive implementation uses the LRU policy to push the most recently accessed component to the end of this.keys. This.keys [0] is the component that has not been accessed for the longest.
How do components communicate with each other
-
Parent-child communication
-
The parent passes data to the child through props
-
The child reports to the parent via events ($emit) V-on :event
-
Can also communicate via parent/child chain ($parent / $children)
-
Ref can also access component instances;
-
Provide/inject API;
-
$attrs
/$listeners
-
-
Brother communication
- Bus;
- Vuex
-
Across the communication level
- Bus;
- Vuex;
- Provide/Inject API,
$attrs
/$listeners
Vuex implements a one-way data flow and stores data globally in a state. When a component wants to change the data in the state, it must do so through Mutation, which also provides a subscriber mode for external plug-in to call to obtain the update of state data. Methods need to be defined in the Action for all asynchronous operations (common for back-end interface calls to retrieve data) or for batch synchronization tasks (which take a long time). However, the methods in the Action cannot change state, or the data in the state needs to be modified by Mutation. Finally, update the render to the view based on changes in the data in state.
The reference reading: segmentfault.com/a/119000001…
What is slot? What does it do? How does it work?
When a component tag is used, the content in the component tag is automatically filled (replacing the slot position in the component template) as an outlet for distributing content. Whether and how a label element is displayed is determined by the parent component.
Slots allow users to extend components to better reuse and customize them.
Slots are divided into three categories: default slots, named slots, and scoped slots.
Implementation principle: $slot. Default: vm.$slot.default: vm.$slot. XXX: vm.$slot. In this case, data can be passed to the slot, or if there is data, the slot can be called a scoped slot.
What’s the difference between template and JSX?
Template and JSX are both representations of the render function, except that:
JSX has more flexibility and advantages in complex components than Template, which is a bit of a clunker. Template, however, is much simpler, more intuitive, and more maintainable because of its code structure, which is more consistent with the separation of view and logic.
What is SSR? What are the benefits?
When the client requests the server, the server obtains the relevant data from the database, and renders the Vue components into HTML inside the server, and returns the data and HTML to the client. This process of converting data and components into HTML on the server is called the server side rendering SSR.
After the client gets the HTML and data rendered by the server, the client does not need to request the data again because the data is already there, but only needs to synchronize the data to the component or Vuex. In addition to data, HTML structure is already available. When rendering components, the client only needs to map THE HTML DOM node to the Virtual DOM instead of recreating the DOM node. This process of synchronizing data with HTML is also called client activation.
Benefits of using SSR:
-
Is conducive to SEO
In fact, it is beneficial for crawlers to climb your page, because some page crawlers do not support JavaScript execution, and the non-SSR page captured by crawlers that do not support JavaScript execution will be an empty HTML page, and with SSR, these crawlers can obtain complete HTML structure data. Then included in the search engine.
-
The white screen time is shorter
In contrast to client-side rendering, server-side rendering already gets an HTML text with data after the browser requests the URL. The browser simply parses the HTML and builds the DOM tree directly. The client rendering needs to get an empty HTML page first, at which time the page has entered a white screen. After that, it needs to load and execute JavaScript, request the back-end server to obtain data, and render the page with JavaScript before the final page can be seen. Especially in complex applications, JavaScript scripts need to be loaded. The more complex the application is, the more and larger the JavaScript scripts need to be loaded. As a result, the first screen of the application takes a long time to load, which reduces the experience.
Implement a read-only property using JS
We can follow the implementation in VUE.
$data and $props are two very important properties in vUE, and the following code sets them to read-only.
// The $data attribute actually represents the _data instance attribute
const dataDef = {}
dataDef.get = function () { return this._data }
// $props represents the instance attribute _props.
const propsDef = {}
propsDef.get = function () { return this._props }
// In production, set $data and $props. If set is triggered, a warning is raised indicating that these two properties are read-only
if(process.env.NODE_ENV ! = ='production') {
dataDef.set = function (newData: Object) {
warn(
'Avoid replacing instance root $data. ' +
'Use nested data properties instead.'.this
)
}
propsDef.set = function () {
warn(`$props is readonly.`.this)}}Copy the code