手机版

深入了解vue-class-component源代码阅读

时间:2021-08-27 来源:互联网 编辑:宝哥软件园 浏览:

Vue-class-component是Vue作者犹大开发的一个库,支持使用class方法开发vue单文件组件。然而,在使用的过程中,我发现了几个奇怪的地方。

首先,让我们看一个简单的使用示例:

//App . vuescriptionimport Vue from ' Vue ' import Component from ' Vue-class-Component ' @ Component({ prop s : { prop message : String } })导出默认类App扩展Vue { //初始数据msg=123 //为初始数据helloMsg='Hello ',this . prop message//life hook mounted(){ this . greet()}//computed get computed msg(){ return ' computed ' this . msg }//方法greet(){ alert(' Hello 3: '/app . vue ' new vue({ El : ' # app },路由器,商店,组件3360 {app},模板:' app/。

1.app类没有构造函数;2.导出的类是直接使用的,而不是新的。3.msg=123,这是什么语法?

首先针对前两个问题,需要说明的是类不一定要有构造函数,也不一定要用new才能使用。熟悉原理的朋友应该知道,类只是ES6的一个语法糖果,说白了就是一个Function。但是,这两点无疑是语法糖的重要价值,但是在这里却毫无用处,这就让人疑惑,甚至认为,既然用类不合适,为什么不直接用Function呢?

然而,第三点是一个恰当的语法错误。为此我还特意打开Chrome控制台试了一下,确实报错了。实验结果如下:

那这到底是怎么回事?出于程序员的好奇心,我探索了vue-class-component的源代码。让我们来看看。相信看完之后可以回答以上的疑惑。

第一步,在阅读源代码之前,一定要对装饰器知识有一定的了解。有几种装饰者。类装饰器主要用于vue类组件。本文仅简单介绍类装饰器。更多信息请参考阮的文章:ECMAScript 6入门。

类修饰器,顾名思义就是用来修饰类的,说白了就是用来修改类的。它有两种用途。如下所示:

//用法1 function Decorator(target){//process target return target } @ Decorator class class test(){ }//用法2 function decorator factory(options){ return function Decorator(target){//@ todo使用options一起处理target//然后返回return target } } @ Decorator fctory(options)class test(){ }在两种用法中,我们分别称Decorator为decorator function和DecoratorFactory为Decorator factory。

类装饰函数只接受类构造函数本身,如果需要传入额外的参数,就需要装饰工厂函数。

我们以装饰工厂函数为例来说明它的执行流程:

1.JS引擎会先执行工厂函数,然后保存它返回的装饰器函数;2.然后解析该类并将其转换为构造函数;3.使用上述构造函数作为参数,执行第一步中获得的装饰函数。4.如果装饰函数有返回值,那么类变量(比如示例中的ClassTest变量)将指向返回值,否则,类变量仍将指向构造函数。根据JS引用变量的特性,即使它仍然指向原始的构造函数,这个构造函数也可能在装饰器中被修改过。

直接使用装饰器功能的情况与上面类似,除了装饰工厂。

了解了基础知识,我们开始第二步,分析vue-class-component的执行流程。这里根据decorator的实现过程,分三个部分进行说明。第一,工厂功能做了什么;二、解析后的类是什么;第三,装饰函数做了什么?

工厂功能做了什么?

//vue-class-component使用TS语法。//Component实际上是一个函数组件(options : componentoptionvue | vueclassvue): any { if(options的类型==' function '){//有区别。这里的名字叫factory,但实际上真正封装了decorator逻辑的是函数:return Component factory(Options)} return function(Component : vueclasvue){ return Component factory(Component,Options) }}从源代码可以看出,Component function只判断参数,说明它既可以作为factory function,也可以作为decorator function。实际装饰器的逻辑封装在组件工厂函数中。这里要注意区分命名,这个工厂不是另一个工厂。

解析后的类是什么样的

在文章的开头,我们有一个问题。在没有构造函数的类中直接赋值属性不符合JS语法。而且,我们已经在Chrome上试过了,会报错。但是当我们使用组件-类-组件时,我们实际上做到了,并且没有任何问题。发生了什么事?事实上,Chrome等主流浏览器对ES6和更高级别的ES7、ES8的支持并不完全,很多功能也不被支持,这也是我们通常使用babel将高级ES语法转换为ES5的原因。而我们前面提到的困惑,正是因为这个原因。Chrome不支持,不代表babel不支持。

然而,即便如此,我们还有一个新的疑问。我之前没见过这个语法,那么巴别塔改造后的类会是什么呢?毕竟,转换结果将作为参数传递给组件装饰器进行处理。要了解Component的处理过程,需要先了解这个参数。所以我在Component函数中添加了一个console.log(),得到了打印出来的结果,只是用webpack babel-loader编译的,结果很难读取。我简单翻译了一下,并将其与类源代码进行了比较,如下所示:

//转换前的class user { name=' yl ' age=10 get compute method(){ cn sole . log(1)} method(){ console . log(2)}//转换后的函数User(){ this . name=' yl ' this . age=10 }//计算出的属性定义了User。原型。定义属性(这个,“计算值”,{get () {console.log (1)返回这个。name}})用户。prototype . method=function(){ console . log(2)}由此,我们还可以猜测从. vue文件导出的类将被解析成什么。

装饰函数做了什么

此时,我们已经知道传递给装饰函数的参数是什么。这个参数应该是一个构造函数,它的主体会给类实例的属性赋值,它的原型会携带各种属性和方法。我们知道,如果不使用vue类组件,那么. vue文件应该导出以下对象:

导出默认{name: 'test ',data(){ return }.}},computed : { com 1()}.},com 2(){ 0.}},方法3360 {.},//各种钩子函数}显然是装饰函数,那么,内部是怎么做的呢?让我们看看源代码。(源代码作者添加了详细的注释,但是比较长,可以跳过下面的总结。)

//这个函数是封装装饰器逻辑的函数,接受两个参数://第一个是修饰类的构造函数;其次,开发人员引入的mixins对象,function component factory(component : vueclassvue,options 3360 component toptionvue={ }): vueclassvue {//}首先给options.name赋值,确保最终生成的对象具有name属性。选项。name=options。名称| |(任何组件)。_组件标记| |(任何组件)。name//获取构造函数原型。原型挂在方法const proto=组件上。原型//遍历原型对象。getown property name(proto)。foreach(function(key){//如果是构造函数,就不处理。//这也是vue单文件组件类不需要构造函数的直接原因,因为如果(key==' constructor '){ return }//如果prototype属性(方法)名称是vue生命周期钩子名称,那么直接将其作为钩子函数挂载在最外层的options if ($ internalhooks)上。index of(key)-1){ options[key]=proto[key]return }//首先获取原型属性的描述符。//如前所述,计算出的属性实际上是挂载在原型上的,所以需要判断描述符const descriptor=object . getownpertysdescriptor(proto,key)!if (descriptor.value!==void 0) {//如果属性值是函数,则认为是方法。If(描述符的类型。value===' function') {(选项。方法| |(选项。methods={})) [key]=descriptor。value } else {//如果不是,则视为普通数据属性。//但是这个在原型上,所以更像mixin,所以挂在mixin下面。(options . mixins | |(options . mixins=[]))。push({ data(this : Vue){ return {[key]: descriptor . value } })} } else if(descriptor . get | | descriptor . set){//if值未定义(ps: void 0===undefined)。//并且描述符有get或set方法,则认为它是一个计算属性。如果你不明白,请参考我把类转换成构造函数的例子。//可能和普通的计算属性不同,因为普通的计算属性只是用来取值的,但是这里有setter。//但是如果不用setter,和非类开发一样,但是有了这一步,有些场景会有特效。(选项。计算| |(选项。computed={ }))[key]={ get : descriptor。get,set:descriptor。set } })//收集由构造函数实例化的对象的属性作为数据。并放入mixins(选项。mixins | |(选项。mixins=[]))。push ({data (this:vue) {//)实例化Component构造函数,并收集自己的(非原型)属性进行导出,内部兼容不同的vue版本。//如果有兴趣,可以自己看看源代码。不复杂,这里就不重复了。return CollectDataFromConstructor(this,component)} })//处理属性装饰器,vue-class-component只提供类装饰器。//道具、组件等特殊参数只能写入Component(选项)的选项参数中。//属性装饰器可以通过这个接口进行扩展,属性装饰器像vue-property-decorator库const decorators=(组件作为装饰类)。_ _装饰者_ _ if(装饰者){装饰者。foreach (fn=fn (options) ) delete(组件作为修饰类)。_ _ decorator _ _ }//获取Vue对象const super pro to=object . get prototype of(component . prototype)const super=super pro to Vue的实例?Superpro to。构造函数as vueclassvue : vue//通过vue.extend const extended=super生成一个vue实例。extend(options)//之前只处理过Component构造函数原型及其实例化对象的属性和方法。//构造函数本身的静态属性没有经过处理,所以这里的处理和前面的处理类似,不重复。forwardstationmembers(Extended,Component,Super)//反射相关的处理,这是一个新特性,我不太了解,但不影响理解,可以跳过。//如果对此有所了解,请补充。

if(reflectionsupported){ copy reflectionmetadata(Extended,component)}//finally return这个vue实例对象return Extended}有一个很长的源代码,这里总结一下。这里有四件主要的事情要做:

首先,将传入的构造器原型上的属性放入数据中,根据是生命周期钩子还是计算属性,将方法放在相应的位置。

其次,实例化构造函数,将构造函数实例化的对象的属性放入数据中。实例化的对象本身(不在原型上)没有方法。即使属性值是函数类型,也应该将其视为数据。

第三,构造函数的静态属性和方法的处理方式与原型相同。

第四,提供了属性修饰器的扩展功能。组件只装饰类。如果想进一步处理类中的属性,可以从这里开始。例如,vue-property-decorator库提供的那些装饰器依赖于这个扩展函数。

说到这里,我想大家都解除了之前的疑惑,同时对vue-class-component的实现原理有了大概的了解。由于我的技能有限,这篇文章可能有肤浅和错误的地方。如果你发现什么,请随时给我建议。谢谢大家!

以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。

版权声明:深入了解vue-class-component源代码阅读是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。