美文网首页
2020-06-03 (Vue-class-Component

2020-06-03 (Vue-class-Component

作者: 法西斯qwq | 来源:发表于2020-06-09 11:18 被阅读0次

    概述

      相对于typeScript的class写法,是对当前类的封装(个人理解)
    

    首先看2.0 js的写法

     const App=Vue.extent({
         data(){return{'hello':'hello'}},
         computed: {
         world() {
            return this.hello + 'world';
           },
       },
    
       mounted(){
        this.sayHello();
       }
     methods:{
            sayHello() {
                        console.log(this.hello);
                    },
                 
            }
      })
    

    3.0 ts写法

        import Component from 'vue-class-component';
        
        @Component({
            name: 'App'
        })
        class App extends Vue {
            hello = 'world';
        
            get world() {
                return this.hello + 'world';
            }
        
            mounted() {
                this.sayHello();
            }
        
            sayHello() {
                console.log(this.hello);
            }
        }
    

    疑问点

     1:@Component()  是?(@Component就是一个修饰器, 用来修改类的行为,更多关于装饰器信息请参阅阮老师Es6) 
    
     2:hello = 'world';  这是啥写法
     js语法中使用的是constructor去给属性赋值,在chrome上像vue-class-component中定义class是会报错的,但vue-class-component中却又这么做了.
     然后我们看看class通过webpack + babel-loader解析后会变成什么样子 
    
     转换前
        class App{
        hello='world';
        sathello(){
         console.log(this.hello);
       } 
      }
     // 转换后  挂在原型上
        function App () {
            this.hello = 'world'
        }
        
        App.prototype.sayHello = function () {
            console.log(this.hello);
        }
    
    
     3:App类没有constructor 构造函数
     4:导出的类没有new 直接使用?
    

    重点分析(index.js所做的东西)

    // Component实际上是既作为工厂函数,又作为装饰器函数
    function Component (options: ComponentOptions<Vue> | VueClass<Vue>): any {
      if (typeof options === 'function') {
        // 区别一下。这里的命名虽然是工厂,其实它才是真正封装装饰器逻辑的函数
        return componentFactory(options)
      }
      return function (Component: VueClass<Vue>) {
        return componentFactory(Component, options)
      }
    }
    

    componentFactory此方法的封装所要做的东西

    import Vue, { ComponentOptions } from 'vue'
    import { copyReflectionMetadata, reflectionIsSupported } from './reflect'
    import { VueClass, DecoratedClass } from './declarations'
    import { collectDataFromConstructor } from './data'
    import { hasProto, isPrimitive, warn } from './util'
    
    export const $internalHooks = [
      'data',
      'beforeCreate',
      'created',
      'beforeMount',
      'mounted',
      'beforeDestroy',
      'destroyed',
      'beforeUpdate',
      'updated',
      'activated',
      'deactivated',
      'render',
      'errorCaptured', // 2.5
      'serverPrefetch' // 2.6
    ]
    
    export function componentFactory (
      Component: VueClass<Vue>,
      options: ComponentOptions<Vue> = {}
    ): VueClass<Vue> {
      // 为component的name赋值
      options.name = options.name || (Component as any)._componentTag || (Component as any).name
      // prototype props.
      // 获取原型
      const proto = Component.prototype
      // 遍历原型
      Object.getOwnPropertyNames(proto).forEach(function (key) {
    // 如果是constructor, 则不处理
    if (key === 'constructor') {
      return
    }
    
    // hooks
    // 如果原型属性(方法)名是vue生命周期钩子名,则直接作为钩子函数挂载在options最外层
    if ($internalHooks.indexOf(key) > -1) {
      options[key] = proto[key]
      return
    }
    // getOwnPropertyDescriptor 返回描述对象
    const descriptor = Object.getOwnPropertyDescriptor(proto, key)!
    // void 0 === undefined
    if (descriptor.value !== void 0) {
      // methods
      // 如果是方法名就挂载到methods上
      if (typeof descriptor.value === 'function') {
        (options.methods || (options.methods = {}))[key] = descriptor.value
      } else {
        // typescript decorated data
        // 把成员变量作为mixin放到options上,一个变量一个mixin,而不是直接统计好放到data或者同一个mixin中
        // 因为data我们已经作为了保留字段,可以在类中声明成员方法data()和options中声明data同样的方法声明变量
        (options.mixins || (options.mixins = [])).push({
          data (this: Vue) {
            return { [key]: descriptor.value }
          }
        })
      }
    } else if (descriptor.get || descriptor.set) {
      // computed properties
      // 转换成计算属性的getter和setter
      (options.computed || (options.computed = {}))[key] = {
        get: descriptor.get,
        set: descriptor.set
      }
    }
      })
    
      // add data hook to collect class properties as Vue instance's data
      // 这里再次添加了一个mixin,会把这个类实例化,然后把对象中的值放到mixin中
      // 只有在这里我们声明的class的constructor被调用了
      ;(options.mixins || (options.mixins = [])).push({
        data (this: Vue) {
          return collectDataFromConstructor(this, Component)
        }
      })
    
      // decorate options
      // 如果这个类还有其他的装饰器,也逐个调用. vue-class-component只提供了类装饰器
      // props、components、watch等特殊参数只能写在Component(options)的options参数里
      // 因此我们使用vue-property-decorator库的属性装饰器
      // 通过下面这个循环应用属性装饰器就可以合并options(ps: 不明白可以看看createDecorator这个函数)
      const decorators = (Component as DecoratedClass).__decorators__
      if (decorators) {
        decorators.forEach(fn => fn(options))
        delete (Component as DecoratedClass).__decorators__
      }
    
      // find super
      // 找到这个类的父类,如果父类已经是继承于Vue的,就直接调用它的extend方法,否则调用Vue.extend
      const superProto = Object.getPrototypeOf(Component.prototype)
      const Super = superProto instanceof Vue
        ? superProto.constructor as VueClass<Vue>
        : Vue
      // 最后生成我们要的Vue组件
      const Extended = Super.extend(options)
    
      // 处理静态成员
      forwardStaticMembers(Extended, Component, Super)
    
      // 如果我们支持反射,那么也把对应的反射收集的内容绑定到Extended上
      if (reflectionIsSupported) {
        copyReflectionMetadata(Extended, Component)
      }
    
      return Extended
    }
    
    const reservedPropertyNames = [
      // Unique id
      'cid',
    
      // Super Vue constructor
      'super',
    
      // Component options that will be used by the component
      'options',
      'superOptions',
      'extendOptions',
      'sealedOptions',
    
      // Private assets
      'component',
      'directive',
      'filter'
    ]
    
    const shouldIgnore = {
      prototype: true,
      arguments: true,
      callee: true,
      caller: true
    }
    
    function forwardStaticMembers (
      Extended: typeof Vue,
      Original: typeof Vue,
      Super: typeof Vue
    ): void {
      // We have to use getOwnPropertyNames since Babel registers methods as non-enumerable
      Object.getOwnPropertyNames(Original).forEach(key => {
    // Skip the properties that should not be overwritten
    if (shouldIgnore[key]) {
      return
    }
    
    // Some browsers does not allow reconfigure built-in properties
    const extendedDescriptor = Object.getOwnPropertyDescriptor(Extended, key)
    if (extendedDescriptor && !extendedDescriptor.configurable) {
      return
    }
    
    const descriptor = Object.getOwnPropertyDescriptor(Original, key)!
    
    // If the user agent does not support `__proto__` or its family (IE <= 10),
    // the sub class properties may be inherited properties from the super class in TypeScript.
    // We need to exclude such properties to prevent to overwrite
    // the component options object which stored on the extended constructor (See #192).
    // If the value is a referenced value (object or function),
    // we can check equality of them and exclude it if they have the same reference.
    // If it is a primitive value, it will be forwarded for safety.
    if (!hasProto) {
      // Only `cid` is explicitly exluded from property forwarding
      // because we cannot detect whether it is a inherited property or not
      // on the no `__proto__` environment even though the property is reserved.
      if (key === 'cid') {
        return
      }
    
      const superDescriptor = Object.getOwnPropertyDescriptor(Super, key)
    
      if (
        !isPrimitive(descriptor.value) &&
        superDescriptor &&
        superDescriptor.value === descriptor.value
      ) {
        return
      }
    }
    
    // Warn if the users manually declare reserved properties
    if (
      process.env.NODE_ENV !== 'production' &&
      reservedPropertyNames.indexOf(key) >= 0
    ) {
      warn(
        `Static property name '${key}' declared on class '${Original.name}' ` +
        'conflicts with reserved property name of Vue internal. ' +
        'It may cause unexpected behavior of the component. Consider renaming the property.'
      )
    }
    
    Object.defineProperty(Extended, key, descriptor)
     })
     }
    

    相关文章

      网友评论

          本文标题:2020-06-03 (Vue-class-Component

          本文链接:https://www.haomeiwen.com/subject/lwfizhtx.html