手把手实现简易版vue(二)组件类解析

1、构造器

  constructor(propsArgus = {}) {
    const {
      data = () => {},
      methods = {},
      watch = {},
      computed = {}, // 待实现
      props = {}, // 待实现
      created = () => {}, // created钩子函数
      mounted = () => {}, // mounted钩子函数
      destroyed = () => {} // destroyed钩子函数
    } = propsArgus
    this.$watch = watch // watch方法集合
    this.$watchers = {} // 收集器集合
    this.$methods = methods // 执行方法集合
    this.$data = data() || {} // 数据定义集合
    this.$computed = computed // 计算属性集合 待开发
    this.$props = props // 传递属性集合
    this.$created = created // created生命周期注入
    this.$mounted = mounted // mounted生命周期注入
    this.$destroyed = destroyed // destroyed生命周期注入
    bindProperties(this, this.$data)
    this.observe(this)
    bindProperties(this, this.$methods)
    this.$created() // created钩子执行 在dom挂载之前执行
  }

2、属性方法讲解及示例

1、data

写法是不是跟vue2一样啊,都是函数返回对象形式

      data: () => {
            return {
                article: {
                    title: '前标题',
                    text: {
                        word: '文字'
                    }
                },
                author: '前林大大哟'
            };
        }

2、methods

方法对象,跟vue2写法一致

        methods: {
            onButtonClick() {
                this.article.title = "标题"
                this.author = '林大大哟'
            },
            itemClick(item, type) {
                console.log(item, type);
            }
        }

3、watch

监听引用类型和基本类型,但写法都是一致的,和vue2不同的是并没有对对象内部单个值进行监听

    watch: {
            author(value) {
                console.log("watch 监听author:", value);
            },
            article(value){
                console.log("watch 监听 article.title:", value);
            }
        }

4、created, mounted, destroyed

这三个生命周期都和vue2用法一致,内部核心如下,这一点我倒是没有参考任何东西,根据自己想法写的~

3、核心源码讲解

数据响应式,是mvvm架构核心,vue2用的是Object.defineProperty,vue用的是Proxy,那我将这两者结合起来用,引用类型我用Proxy监测,基本类型我用Object.defineProperty监测

源码如下,都是遍历data中的数据,当发现是基本类型时候,用Object.defineProperty转换成响应式类型,而updateWatcher方法,就是被动触发页面结构中包含这数据的dom,然后让他进行更新(这部分vue源码中清除解释了,将同一数据的dom都放进收集器中,当此数据需要更新时,则通知收集器中的各个dom进行依赖更新),而watch对象中有监听这数据的话,就主动的去触发watch中的方法:data.$watch[key].call(data, val)。

  /**
   * 转换观测模式
   * @param {*} data 当前作用指针
   * @returns 
   */
  observe (data) {
    if (!data || getDataType(data) !== 'object') { // 按顺序写一般都不会进入这一步 用于初始化
      return
    }
    const arrayMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'reverse', 'sort']
    const keys = Object.keys(this.$data)
    for (let key of keys) {
      let value = data[key]
      if (getDataType(value) === 'object' ||
          getDataType(value) === 'array' ||
          getDataType(value) === 'null'
        ) {
        value = getDataType(value) === 'null' ? {} : value // Proxy不能传入不是对象的类型,所以在这里转一下
        data[key] = this.deepProxy(key, value)
      } else {
        Object.defineProperty(data, key, {
          configurable: false,
          enumerable: true,
          set(val) {
            value = val
            updateWatcher(this.$watchers[key], val) // 数据改变 watcher触发修改对应界面上的值
            if (data.$watch[key]) {
              data.$watch[key].call(data, val) // 主动触发watch对象的方法
            } 
          },
          get() {
            return value
          }
        })
      }
    }
  }

 当发现是引用类型时候,就用Proxy转换此引用类型,当然至于LveProxy类是啥样,这个我下篇再来写,核心其实就是Proxy和Reflect

  /**
   * 深度遍历并转换为自定义Proxy代理
   * @param {*} obj 
   * @returns
   */
  deepProxy(key, obj) {
    if (getDataType(obj) === 'array') { // 数组深度遍历
      obj.forEach((v, i) => {
        if (getDataType(obj) === 'object') {
          obj[i] = this.deepProxy(v)
        }
      })
    } else  { // 对象深度遍历
      if (Object.keys(obj).length === 0) return
      Object.keys(obj).forEach(v => {
        if (getDataType(obj[v]) === 'object') {
          obj[v] = this.deepProxy(key, obj[v])
        }
      });
    }
    const proxy = new LveProxy(this, obj).init(key) // proxy代理
    return proxy
  }

所以组件类的大体逻辑出来了,通过构造器将data,watch等这些传进去,然后mount到dom后,逻辑层和视图层就有了关联,逻辑层中数据以data中的为主,在dom中有关于data中的数据,都会被加入对应的收集器中(依赖收集),当data有数据更改,会将收集器中的依赖进行统一更新(依赖更新),至此MVVM框架的核心也就出来了,当然至于Proxy和模板如何转换的,那我将在下一篇博客中讲解~

猜你喜欢

转载自blog.csdn.net/qq_39404437/article/details/134015707
今日推荐