vue源码解析之插件入侵机制
来源:互联网 发布:淘宝店铺点击图片链接 编辑:程序博客网 时间:2024/05/26 07:30
先说一点概念性的东西哈~
插件:聪明的程序员往往希望能更高(tou)效(lan)的完成指定的工作,插件就是按照一定的封装方式,暴露接口。让我们利用这些接口更快捷的实现功能。升职加薪。每个框架都提供了插件的扩展机制。这是框架可扩展性必不可少的一个部分。插件机制越简单。对于框架的生态的发展大有好处。jquery提供了$.fn.extend,angular有对应的依赖注入,module机制。既然vue那么精美,能迅速火起来。插件这部分的可扩展性必须顶级。这里接下来我们看看vue插件的入侵机制。
说到插件。我们最多使用的一个方法。无非就是 Vue.use(MyPlugin, { someOption: true });这么说的话,这个方法应该是所有插件入侵vue的起点。没错。
那么我们来看看这个方法:
Vue.use = function (plugin) { /* istanbul ignore if */ if (plugin.installed) { return //假如插件已经初始化过就不再继续。避免插件重复入侵 } // additional parameters var args = toArray(arguments, 1);//获取插件的配置参数 args.unshift(this); if (typeof plugin.install === 'function') { plugin.install.apply(plugin, args); //调用的是插件的install方法; } else if (typeof plugin === 'function') { plugin.apply(null, args);//若插件本省就是一个函数。则直接调用该函数 } plugin.installed = true; return this };
Vue.use这个方法让我们知道 插件入侵的起点是调用插件自身的install函数。那么不同的插件入侵的机制有些时候很不一样。我们可以知道。这个不一样肯定发生在install函数中。我们来看看官方的install函数中的一些方式:
MyPlugin.install = function (Vue, options) { // 1. 添加全局方法或属性 Vue.myGlobalMethod = function () { // 逻辑... } // 2. 添加全局资源 Vue.directive('my-directive', { bind (el, binding, vnode, oldVnode) { // 逻辑... } ... }) // 3. 注入组件 Vue.mixin({ created: function () { // 逻辑... } ... }) // 4. 添加实例方法 Vue.prototype.$myMethod = function (options) { // 逻辑... }}我们按官网推荐的四种例子。来看看每种方法对应的源码:
1:Vue.myGlobalMethod = function () { // 逻辑... }类似jquery中的jquery.myGlobalMethod或则$.myGlobalMethod简单来说就是给Vue这个全局对象添加一些工具方法。可以供全局快捷调用。我们这里就略过了。
2: // 2. 添加全局资源 Vue.directive('my-directive', { bind (el, binding, vnode, oldVnode) { // 逻辑... } ... })
Vue.directive,Vue.filter,Vue.component等价。当全局使用这些api时。会在vue上把这些指令过滤器组件等放在相应的属性数组里。形如:
Vue.options = { components: { }, directives: {}, filters: {}, _base: Vue}
因为他挂在全局的vue中。在vue初始化。调用init方法时。会执行:
vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor),//策略合并核心函数。可以仔细去看看 options || {}, vm );
vue在创建实例时。会把vue对象上的options的对象中的属性提取出来和传入的options做合并。这里涉及到合并策略。以后会专门讲一下。这里只要知道。vue每个配置相都有自己的合并规则。mergeOptions会根据合并的类目去选择对应的合并规则。这里的component.directive.filter根据合并规则。Vue对象上的全局的这些属性会被放在实例的__proto__上。
同样的。相应的子组件。可以回过头去看一下组件那一章。在render创建子组件的时候。代码如下:
function createComponent ( Ctor, data, context, children, tag) { if (isUndef(Ctor)) { return } var baseCtor = context.$options._base; // plain options object: turn it into a constructor if (isObject(Ctor)) { Ctor = baseCtor.extend(Ctor); } // if at this stage it's not a constructor or an async component factory, // reject. if (typeof Ctor !== 'function') { { warn(("Invalid Component definition: " + (String(Ctor))), context); } return } // async component if (isUndef(Ctor.cid)) { Ctor = resolveAsyncComponent(Ctor, baseCtor, context); if (Ctor === undefined) { // return nothing if this is indeed an async component // wait for the callback to trigger parent update. return } } // resolve constructor options in case global mixins are applied after // component constructor creation resolveConstructorOptions(Ctor);//核心:这里会再次合并一下vue上的全局的一些指令或则组件或则过滤器到组件的构造函数上 data = data || {}; // transform component v-model data into props & events if (isDef(data.model)) { transformModel(Ctor.options, data); } // extract props var propsData = extractPropsFromVNodeData(data, Ctor, tag); // functional component if (isTrue(Ctor.options.functional)) { return createFunctionalComponent(Ctor, propsData, data, context, children) } // extract listeners, since these needs to be treated as // child component listeners instead of DOM listeners var listeners = data.on; // replace with listeners with .native modifier data.on = data.nativeOn; if (isTrue(Ctor.options.abstract)) { // abstract components do not keep anything // other than props & listeners data = {}; } // merge component management hooks onto the placeholder node mergeHooks(data); // return a placeholder vnode var name = Ctor.options.name || tag; var vnode = new VNode( ("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')), data, undefined, undefined, undefined, context, { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children } ); return vnode}
最后在内部组件初始化时。vue会调用:
function initInternalComponent (vm, options) { var opts = vm.$options = Object.create(vm.constructor.options);//这里还是把构造函数的options放在了$options上供后续使用 // doing this because it's faster than dynamic enumeration. opts.parent = options.parent; opts.propsData = options.propsData; opts._parentVnode = options._parentVnode; opts._parentListeners = options._parentListeners; opts._renderChildren = options._renderChildren; opts._componentTag = options._componentTag; opts._parentElm = options._parentElm; opts._refElm = options._refElm; if (options.render) { opts.render = options.render; opts.staticRenderFns = options.staticRenderFns; }}
总的来说。如果是全局的指令过滤器时。vue统一把他放在根构造方法上。根实例初始化时。通过策略合并合并到$options中。而子组件稍微绕了一下。最终也是放在$options的原型上。很连贯啊。这样只要是全局的组件。指令过滤器。每个子组件都可以继承使用。达到了插件的效果。
3、下面来看看mixin方法:
Vue.mixin({ created: function () { // 逻辑... } ... })Vue.mixin = function (mixin) { this.options = mergeOptions(this.options, mixin); };//这里还是不可避免要看看mergeOptions函数:function mergeOptions ( parent, child, vm) { { checkComponents(child); } if (typeof child === 'function') { child = child.options; } normalizeProps(child); normalizeDirectives(child); var extendsFrom = child.extends; if (extendsFrom) { parent = mergeOptions(parent, extendsFrom, vm); } if (child.mixins) { for (var i = 0, l = child.mixins.length; i < l; i++) { parent = mergeOptions(parent, child.mixins[i], vm); } } var options = {}; var key; for (key in parent) { mergeField(key); } for (key in child) { if (!hasOwn(parent, key)) { mergeField(key); } } function mergeField (key) { var strat = strats[key] || defaultStrat; options[key] = strat(parent[key], child[key], vm, key); } return options}
分两种情况:
a:全局注册时。
即vue.mixin时。直接调用合并。直接便利mixin中的项目。分别调用相应合并策略。合并到构造函数的options中。影响后面所有的子组件
b:局部注册时。
if (child.mixins) { for (var i = 0, l = child.mixins.length; i < l; i++) { parent = mergeOptions(parent, child.mixins[i], vm); } }
则会去递归的调用合并策略把该合并的项目合并结束为止;
vue.mixin就相当于是一个传入的额外的配置项目,会让vue重新按照规则合并一次,成功入侵vue。
4、添加实例方法
这个方法就很明显了。在vue的原型上挂载方法。vue的实例自然而然就能继承。子组件在创建的时候。
// 添加实例方法 Vue.prototype.$myMethod = function (options) { // 逻辑... }
还有种就是将原型指向根构造函数Vue的prototype;自然而然就会有Vue的原型上的所有属性和方法。
Sub.prototype = Object.create(Super.prototype); Sub.prototype.constructor = Sub;
以上就是vue比较常用的插件侵入方法.....
好好学习 好好工作 天天向上 lalala~
阅读全文
0 0
- vue源码解析之插件入侵机制
- Vue-lazyload原理详解之源码解析
- vue源码解析之--数据双向绑定
- vue源码解析
- vue mint-ui源码解析之loadmore组件
- RecyclerView源码解析之缓存机制
- Vue之插件使用
- vue插件之vuex
- Vue学习之源码分析--Vue.js事件机制(四)
- Vue源码解析(一)
- Vue源码解析(二)
- Vue源码解析(三)
- Vue源码解析(四)
- Vue源码解析(五)
- 解析 WordPress 插件机制
- maven插件解析机制
- maven插件解析机制
- 动网论坛入侵之插件篇
- OneNET与第三方平台对接(最简单方式)
- IllegalStateException: This Activity already has an action bar supplied by the window decor.
- 数组追加
- 好用的js时间插件 js Date
- 移动端 触摸事件 ontouchstart、ontouchmove、ontouchend、ontouchcancel
- vue源码解析之插件入侵机制
- 注意YAML格式文件空格
- 多线程之间实现同步
- MySQL触发器实现表数据同步
- 用maven管理Spring项目出现org.hibernate.MappingException Unknown entity的问题解决办法
- Android中的Audio播放:控制Audio输出通道切换
- javascript面向对象——语言特征
- 计算机视觉 常见的数据集
- python defaultdict