Step by step implementation of a simple version of vue (2) component class analysis

1. Constructor

  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. Explanation and examples of attribute methods

1、data

Is the writing method the same as vue2? Both functions return objects.

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

2、methods

Method object, written in the same way as vue2

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

3、watch

Monitor reference types and basic types, but the writing methods are the same. Different from vue2, there is no monitoring of a single value inside the object.

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

4、created, mounted, destroyed

These three life cycles are consistent with the usage of vue2. The internal core is as follows. I did not refer to anything on this point and wrote it based on my own ideas~

3. Core source code explanation

Data responsiveness is the core of mvvm architecture.vue2 uses Object.defineProperty and vue uses Proxy. Then I will combine the two and use the reference type. Use Proxy to monitor, and I use Object.defineProperty to monitor basic types

The source code is as follows. They all traverse the data in data. When it is found that it isbasic type, use Object.defineProperty to convert it into a responsive type. , and the updateWatcher method is to passively trigger the dom containing this data in the page structure, and then let it update ( This part of the vue source code clearly explains that all doms with the same data are put into the collection In the collector, when this data needs to be updated, each DOM in the collector is notified to update dependencies), and if the watch object monitors this data, it will actively trigger the watch in the Method: 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
          }
        })
      }
    }
  }

 When it is found to be a reference type, use Proxy to convert the reference type. Of course, as for what the LveProxy class is like, I will post this next Let’s write again.The core is actually Proxy and 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
  }

So the general logic of the component class is figured out. Data, watch, etc. are passed in through the constructor, and then mounted to the dom. The logic layer and the view layer are related. The data in the logic layer is mainly in the data. In the dom All data in data will be added to the corresponding collector (dependency collection). When data changes, the dependencies in the collector will be updated uniformly (dependency update). At this point, the core of the MVVM framework will come out. Of course, as for how to convert Proxy and template, I will explain it in the next blog~

Guess you like

Origin blog.csdn.net/qq_39404437/article/details/134015707