Vue.extend(options) : Base Vue constructor
The parameter is an object that contains component options.
The data option is a special case, and in vue.extend () it must be a function to keep the reference data intact.
<div id='app'> <span>MyApp</span> </div> <script> var Profile = Vue.extend({ template: '< p > goal: Print the data in data {{p1}},{{p2}}</p>', Data :function(){return {p1:' printf ', p2:' printf '}}}).$mount('# printf '); </script>Copy the code
Why extend?
In general, all of our pages are managed with router and components are registered locally with import. There are also some shortcomings.
For example: suppose we need to dynamically render a component from an interface? How to implement a window.alert() -like alert component that requires js to be used?
How to construct a custom popover using extend
1. Write popover components
// It is recommended to copy and paste <template> <div V-if ="show" ref="modal" class=" Ek-modal_wrap "> <div class=" Ek-modal_content "> <div class="modal-title-wrap"> <div class="modal-title">{{ title }}</div> <slot name="description"></slot> </div> <div class="modal-button"> <a v-if="confirmVisible" class="contral-btn confirm-btn" href="javascript:;" @click="confirm">{{ confirmText }}</a> <a v-if="cancleVisible" class="contral-btn cancle-btn" href="javascript:;" @click="cancle">{{ cancleText }}</a> </div> </div> </div> </template> <script> export default { data() { return { show: True, title: '', // title confirmText: 'confirm ', // confirmText confirmVisible: true, // whether to display the confirm button onConfirm: $emit('confirm')}, cancleText: 'cancel ', cancleVisible: true, onCancle: () = > {/ / cancel executive function enclosing $emit (' cancle ')}}}, the methods: { confirm() { this.onConfirm() this.close() }, cancle() { this.onCancle() this.close() }, Close () {this.show= false if (this.$refs.modal) {this.$refs.modal. Remove () // Close to remove the current element}}}} </script> <style lang="scss" scoped> .ek-modal_wrap { position: fixed; top: 0; left: 0; z-index: 999; width: 100%; height: 100%; font-size: 28px; Background: RGBA (0, 0, 0, 0.7); .ek-modal-content { position: fixed; top: 50%; left: 50%; Min - width: 7.2 rem; overflow-x: hidden; overflow-y: hidden; text-align: center; background-color: white; Border - the top - left - the radius: 0.266667 rem. Border - top - right - the radius: 0.266667 rem. Border - bottom - right - the radius: 0.266667 rem. Border - bottom - left - the radius: 0.266667 rem. transform: translate(-50%, -50%); .modal-title-wrap { display: flex; flex-direction: column; flex-grow: 1; justify-content: center; min-height: 55px; padding: 0 20px; color: #333; } .modal-title { display: flex; flex-direction: column; flex-grow: 1; justify-content: center; min-height: 100px; margin-top: 30px; margin-bottom: 30px; font-weight: 600px; line-height: 50px; color: #333; } .modal-button { display: flex; line-height: 1; color: #333; border-top-color: #e7e7e7; border-top-style: solid; border-top-width: 1px; & > a { color: #333; } .contral-btn { flex-basis: 0%; flex-grow: 1; flex-shrink: 1; font-weight: 600px; line-height: 3; text-align: center; &.cancle-btn { border-left-color: #e7e7e7; border-left-style: solid; border-left-width: 1px; } } } } } </style>Copy the code
Step 2: Bind methods to the prototype of vUE.
import Vue from 'vue'; import dialog from './components/Dialog.vue'; function showDialog(options) { const Dialog = Vue.extend(dialog); // Create an instance and mount const app = new Dialog().$mount(document.createElement('div')); For (let key in options) {app[key] = options[key]; } / / insert elements in the document in the body. The body. The appendChild (app) $el); } Vue.prototype.$showDialog = showDialog; // Put the method on the prototype.Copy the code
Step 3: Call the method from the VUE page
. / / mounted() {console.log(this.$showDialog); This.$showDialog({title: 'test popup ', confirmText:' Want popup to call correctly ', cancelVisible: false,}); },... Welcome to try itCopy the code
Vue. Extend principle:
What Vue. Extend returns an extended instance constructor, that is, a Vue instance constructor with partial options preset but not instantiated, which can be interpreted as creating a subclass that inherits some functionality from Vue.
Create a subclass of Vue. Let cid = 1; / / for special tag function extend (extendOptions) {extendOptions = extendOptions | | {} / / this - > vue const Super = this const SuperId = super. cid // Use the parent id as the cache key // as long as the parent ID is the same, Each call will return the same subclass const cachedCtors = extendOptions. _Ctor | | (extendOptions. _Ctor = {}) / / if there is a cache, can return directly. For performance reasons, Add a cache policy if (cachedCtors[SuperId]) {return cachedCtors[SuperId]} // Validates the naming of name // /^[a-za-z][\w-]*/ const name = extendOptions.name || Super.options.name if (process.env.NODE_ENV ! == 'production' && name) { validateComponentName(name) } const Sub = function VueComponent (options) { // vue._init This function will have below parse enclosing _init (options)} / / inherit the parent class constructor Sub. The prototype = Object. The create (Super) prototype) Sub. Prototype. The constructor = Sub sub. cid = cid++ // mergeOptions sub. options = mergeOptions(super.options, extendOptions ) Sub['super'] = Super // For props and computed properties, we define the proxy getters on // the Vue instances at extension time, On the extended prototype. This // avoids object. defineProperty calls for each instance created Mount props under the _props property of the prototype object if (sub.options. props) {initProps(Sub)} // Initialize computed // Mount initialized computed under the prototype object if (Sub.options.computed) {initComputed(Sub)} // allow further extension/mixin/plugin usage // Copy the static method of the parent class // the ability of subclasses to inherit Vue Sub.extend = Super.extend Sub.mixin = Super.mixin Sub.use = Super.use // create asset registers, so extended classes // can have their private assets too. ASSET_TYPES.forEach(function (type) { Sub[type] = Super[type] }) // enable recursive self-lookup if (name) { Sub.options.components[name] = Sub } // keep a reference to the super options at extension time. // later at instantiation we can check if Super's options have // been updated. Sub.superOptions = Super.options Sub.extendOptions = extendOptions Sub.sealedOptions = extend({}, Sub.options) // cache constructor cachedCtors[SuperId] = Sub return Sub }Copy the code
1. For performance, a cache policy was added to the vue. extend method. Repeated calls to vue.extend should return the same result.
2. As long as the return result is fixed, the result can be cached, and when the extend method is called again, it simply needs to be retrieved from the cache.
3. Cache subclasses in cachedCtors using the id of the parent class as the cache key.
4. Check name. If name is found unqualified, a warning will be issued in the development environment.
5. Finally, create a subclass and return it. There is no inheritance logic for this step.
Create vue subclass _init() function resolution
A vue _init function appears in the above code, and its parsing is attached. A simplified version
let uid = 0; export function initMixin(Vue: Class<Component>) { Vue.prototype._init = function(options? : Object) { const vm: Component = this; vm._uid = uid++; // a flag to avoid this being observed // Use _isVue to indicate that the current instance is a Vue instance, This is done for the follow-up observed VM._isVue = true; Options if (options && options._isComponent) {// _isComponent identifies the current internal Component // internal Component Options Initialize initInternalComponent(VM, options); } else {/ / the internal Component options initialization vm. $options = mergeOptions (resolveConstructorOptions (vm) constructor), options || {}, vm ); } // Render this to vm._renderProxy if (process.env.node_env! == 'production') { initProxy(vm); } else { vm._renderProxy = vm; } // expose real self vm._self = vm; initLifecycle(vm); // Initialize the lifecycle initEvents(VM); // Initialize the event initRender(vm); // Initialize the render function callHook(vm, 'beforeCreate'); // Call the beforeCreate hook function initInjections(vm); // resolve injection before data/props // initialize the VM state initState(VM); initProvide(vm); // resolve provide after data/props callHook(vm, 'created'); Created hook function if (vm.$options.el) {// Mount instance vm.$options.el (vm.$options.el); }}Copy the code