惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

M
MIT News - Artificial intelligence
罗磊的独立博客
Hugging Face - Blog
Hugging Face - Blog
J
Java Code Geeks
G
Google Developers Blog
美团技术团队
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
腾讯CDC
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
The Blog of Author Tim Ferriss
月光博客
月光博客
B
Blog
WordPress大学
WordPress大学
云风的 BLOG
云风的 BLOG
博客园_首页
人人都是产品经理
人人都是产品经理
aimingoo的专栏
aimingoo的专栏
Y
Y Combinator Blog
Jina AI
Jina AI
S
SegmentFault 最新的问题
H
Help Net Security
博客园 - 聂微东
Microsoft Azure Blog
Microsoft Azure Blog
Google DeepMind News
Google DeepMind News

时间的朋友

Windows 命令行密码重置 Anaconda安装 typescript 注解解读1 Konvajs Shape加载自定义图片 sshpass 使用 why-is-node-running webgl笔记 SharedArrayBuffer is not defined blender 常用快捷键 | 时间的朋友 vue -- v3.4commit提交记录2 vue2 升级vue3报错问题整理 着色器 expressjs 源码 hyper-V arch linux 网络配置 element input数字格式化 three 拼接货架 WebAudio笔记 Windows nginx重启bat脚本 vue -- v3.4commit提交记录 URI malformed vue3 -- Class 对象在组件中使用范例 | 时间的朋友 ruby 安装和升级 element-plus 老版本cascader使用卡死问题 vue3 内置Transition组件 | 时间的朋友 前端memo的实现 | 时间的朋友 linux 优化脚本 typescript 装饰器 | 时间的朋友 microbundle 源码 | 时间的朋友 WSL2问题解决WslRegisterDistribution failed with error: 0x800701bc vue -- vue3利用createVNode函数,建立命令式调用组件 | 时间的朋友
Vue -- vue-class-component源码 | 时间的朋友
2023-09-01 · via 时间的朋友

Published: · LastMod: September 01, 2023 · 1307 words

vue-class-component源码 🔗

入口Component装饰器函数

这里执行了componentFactory这个工厂函数

1
2
3
4
5
6
7
8
function Component (options: ComponentOptions<Vue> | VueClass<Vue>): any {
  if (typeof options === 'function') {
    return componentFactory(options)
  }
  return function (Component: VueClass<Vue>) {
    return componentFactory(Component, options)
  }
}

同时这个函数还有个静态属性,用于注册全局钩子函数

1
2
3
Component.registerHooks = function registerHooks (keys: string[]): void {
  $internalHooks.push(...keys)
}

componentFactory 🔗

    1. 拿到原型

const proto = Component.prototype

这里会拿到class定义的类上面的所有原型属性

    1. 循环处理
1
2
3
  Object.getOwnPropertyNames(proto).forEach(function (key) {
    // ...
  })

Object.getOwnPropertyNames会拿到所有属性key组成的数组,通过forEach进行遍历

  • 2.1 处理钩子函数
1
2
3
4
5
    // hooks
    if ($internalHooks.indexOf(key) > -1) {
      options[key] = proto[key]
      return
    }
  • 2.2 处理特定属性

  • 2.2.1

获取每个属性的描述符

1
    const descriptor = Object.getOwnPropertyDescriptor(proto, key)!

如果当前属性的描述value不是undefined, 说明这个属性有值

这里分别加入到methods中和mixins中,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
if (descriptor.value !== void 0) {
    // methods
    if (typeof descriptor.value === 'function') {
    (options.methods || (options.methods = {}))[key] = descriptor.value
    } else {
    // typescript decorated data
    (options.mixins || (options.mixins = [])).push({
        data (this: Vue) {
        return { [key]: descriptor.value }
        }
    })
    }
} 

如果是undefined, 这里会标记成computed的属性

1
2
3
4
5
6
7
if (descriptor.get || descriptor.set) {
    // computed properties
    (options.computed || (options.computed = {}))[key] = {
    get: descriptor.get,
    set: descriptor.set
    }
}

最后 收集所有类的属性,处理成vue实例的属性

1
2
3
4
5
6
// add data hook to collect class properties as Vue instance's data
;(options.mixins || (options.mixins = [])).push({
data (this: Vue) {
    return collectDataFromConstructor(this, Component)
}
  })

执行所有装饰器函数

1
2
3
4
5
6
  // decorate options
  const decorators = (Component as DecoratedClass).__decorators__
  if (decorators) {
    decorators.forEach(fn => fn(options))
    delete (Component as DecoratedClass).__decorators__
  }

找到根的实例对象,这里可能是Vue对象,也可能是Vue的实例对象 通过forwardStaticMembers去继承属性

1
2
3
4
5
6
const superProto = Object.getPrototypeOf(Component.prototype)
const Super = superProto instanceof Vue
? superProto.constructor as VueClass<Vue>
: Vue
const Extended = Super.extend(options)
forwardStaticMembers(Extended, Component, Super)

forwardStaticMembers 🔗

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

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
    // 剔除configurable为false的
    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.
    // 如果不存在__proto__的情况
    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
      }
    }
    // 往需要继承的对象上写入值
    Object.defineProperty(Extended, key, descriptor)
  })
}

collectDataFromConstructor 🔗

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

export function collectDataFromConstructor (vm: Vue, Component: VueClass<Vue>) {
  // override _init to prevent to init as Vue instance
  // 储存原来的_init方法
  const originalInit = Component.prototype._init
  // 重写_init方法
  Component.prototype._init = function (this: Vue) {
    // proxy to actual vm
    // 获取对象实例上的属性
    const keys = Object.getOwnPropertyNames(vm)
    // 2.2.0 compat (props are no longer exposed as self properties)
    if (vm.$options.props) {
      for (const key in vm.$options.props) {
        if (!vm.hasOwnProperty(key)) {
          // 搜集props中的key,同时也加入到keys中
          keys.push(key)
        }
      }
    }
    // 定义对应key的取值和赋值方式
    keys.forEach(key => {
      Object.defineProperty(this, key, {
        get: () => vm[key],
        set: value => { vm[key] = value },
        configurable: true
      })
    })
  }

  // should be acquired class property values
  // 组件实例化
  const data = new Component()

  // restore original _init to avoid memory leak (#209)
  // 恢复原来的_init方法
  Component.prototype._init = originalInit

  // create plain data object
  // 实例化后的对象
  const plainData = {}
  Object.keys(data).forEach(key => {
    // 并且该值一定是非undefined
    if (data[key] !== undefined) {
      plainData[key] = data[key]
    }
  })

  return plainData
}