资讯专栏INFORMATION COLUMN

Vue源码解析(一) data属性映射及methods函数引用的重定义

itvincent / 2561人阅读

摘要:使用框架进行开发时,我们在的和中定义属性和方法,在调用时直接使用或的形式,而不是用或的方式。项目的地址,后续会持续更新,分析的源码,争取实现一个精简版的。中的所有方法都直接在实例重新定义了引用。

使用Vue框架进行开发时,我们在option的data和methods中定义属性和方法,在调用时直接使用 vm.attr 或 vm.func()的形式,而不是用vm.data.attr或vm.methods.func()的方式。

项目的git地址: https://github.com/xubaodian/... ,后续会持续更新,分析Vue的源码,争取实现一个精简版的Vue。
Vue解析系列文章如下:

Vue源码解析(二)Vue的双向绑定讲解及实现

我们传入Vue的options对象一般为以下这种形式,

{
  data: {
    name: "xxx"
  },
  mounted() {
    //调用方法,没有使用this.methods.getInfo();
    this.getInfo();
  },
  methods: {
    getInfo() {
      //获取属性,没有使用this.data.name
      this.name = "xxxx2314";
      //操作等等....
    }
  },
  computed: {
    getName() {
      return this.name;
    }
  },
  watch: {
    "name"(val, oldVal) {
      //这是操作
    }
  }
}

在vue实例中,我们无论data还是method,都直接调用,这是因为一下vue初始化时做了下面两点操作:

1、给data中的属性做了代理,所有访问和设置vm[key]时,最终操作的是vm._data[key],而Vue在初始化时,会vm._data其实是options中data的引用。

2、methods中的所有方法都直接在vue实例重新定义了引用。

看下我的实现代码,是对Vue源码的精简,如下:

//vue构造函数
class Vue {
  constructor(options) {
    //$options存储构造选项
    this.$options = options || {};
    //data保存构造设置中的data,暂时忽略data为函数的情况
    let data = options.data;
    this._data = data;
    //初始化
    this._init();
  }

  _init() {
    //映射key
    mapKeys(this);
    //在vue实例上重新定义方法的引用
    initMethods(this, this.$options.methods)
  }
}


//重新定义方法的引用,注意修改调用函数时的上下文环境,这里用bind,当然也可以用apply和call
function initMethods (vm, methods) {
  for (const key in methods) {
    vm[key] = typeof methods[key] !== "function" ? noop : methods[key].bind(vm);
  }
}

//重新定义data的get和set
function mapKeys(vm) {
  let data = vm._data;
  if (null !== data && typeof data === "object") {
    const keys = Object.keys(data);
    let i = keys.length;
    while (i-- >= 0) {
      //所有属性的操作就重新定向到了_data上
      proxy(vm, `_data`, keys[i]);
    }
  }
}

//使用defineProperty重新定义get和set
function proxy (target, sourceKey, key) {
  sharedPropertyDefinition.get = function proxyGetter () {
    return this[sourceKey][key]
  }
  sharedPropertyDefinition.set = function proxySetter (val) {
    this[sourceKey][key] = val
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

const sharedPropertyDefinition = {
  enumerable: true,
  configurable: true,
  get: noop,
  set: noop
}

//空函数,占位用
function noop () {}


//使用
let options = {
    data: {
        name: "xxx",
        age: 18
    },
    methods: {
        sayName() {
            console.log(this.name);
        }
    }
}


let vm = new Vue(options);

vm.sayName();//控制台打印了xxx,可以把代码直接复制出去试一下

上面代码就完成了属性的重新映射和方法的引用重新定义。

看下vue中源码,,如下,我做了注释,应该比较好懂:

简单说明一下,源码中使用了flow作为js代码的静态检查工具,原理和typescript类似,所以代码看起来会有些不同,不影响整体阅读

//初始化,参数是vue实例
function initData (vm: Component) {
  //获取options中的
  let data = vm.$options.data
  //设置vm._data,判断data是obj还是函数
  data = vm._data = typeof data === "function"
    ? getData(data, vm)
    : data || {}
  if (!isPlainObject(data)) {
    data = {}
    process.env.NODE_ENV !== "production" && warn(
      "data functions should return an object:
" +
      "https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function",
      vm
    )
  }
  // proxy data on instance
  const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  while (i--) {
    const key = keys[i]
    //这是在开发环境打印的一些提示不用关心
    if (process.env.NODE_ENV !== "production") {
      if (methods && hasOwn(methods, key)) {
        warn(
          `Method "${key}" has already been defined as a data property.`,
          vm
        )
      }
    }
    if (props && hasOwn(props, key)) {
      process.env.NODE_ENV !== "production" && warn(
        `The data property "${key}" is already declared as a prop. ` +
        `Use prop default value instead.`,
        vm
      )
    } else if (!isReserved(key)) {
      //代理访问,这就是为何操作vm[key]被定位到vm._data[key]的原因
      proxy(vm, `_data`, key)
    }
  }


  const sharedPropertyDefinition = {
  enumerable: true,
  configurable: true,
  get: noop,
  set: noop
}

//代理函数
export function proxy (target: Object, sourceKey: string, key: string) {
  sharedPropertyDefinition.get = function proxyGetter () {
    return this[sourceKey][key]
  }
  sharedPropertyDefinition.set = function proxySetter (val) {
    this[sourceKey][key] = val
  }
  //利用defineProperty设置对象的get和set,操作属性时,target[key]会映射到target[sourceKey][key]
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

//方法映射
function initMethods (vm: Component, methods: Object) {
  const props = vm.$options.props
  for (const key in methods) {
    //这些都是开发环境的提示信息,可以忽略
    if (process.env.NODE_ENV !== "production") {
      if (typeof methods[key] !== "function") {
        warn(
          `Method "${key}" has type "${typeof methods[key]}" in the component definition. ` +
          `Did you reference the function correctly?`,
          vm
        )
      }
      if (props && hasOwn(props, key)) {
        warn(
          `Method "${key}" has already been defined as a prop.`,
          vm
        )
      }
      if ((key in vm) && isReserved(key)) {
        warn(
          `Method "${key}" conflicts with an existing Vue instance method. ` +
          `Avoid defining component methods that start with _ or $.`
        )
      }
    }
    //关键在这,重新定义了引用
    vm[key] = typeof methods[key] !== "function" ? noop : bind(methods[key], vm)
  }
}

有疑问可以给我留言,或发邮件至472784995@qq.com,欢迎大家来讨论

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/100595.html

相关文章

  • Vue源码解析(二)Vue的双向绑定讲解实现

    摘要:上篇文章,我们讲解了的属性映射和方法的重定义,链接地址如下源码解析一属性映射和函数引用的重定义这篇文章给大家带来的是的双向绑定讲解。这就是的双向绑定。使用定时器定时检查的值,发生变化就通知订阅者。这个方法不好,定时器不能实时反应变化。 文章中的代码时阶段,可以下载源码测试一下。git项目地址:https://github.com/xubaodian/...项目使用webpack构建,下...

    ckllj 评论0 收藏0
  • vue源码阅读之数据渲染过程

    摘要:图在中应用三数据渲染过程数据绑定实现逻辑本节正式分析从到数据渲染到页面的过程,在中定义了一个的构造函数。一、概述 vue已是目前国内前端web端三分天下之一,也是工作中主要技术栈之一。在日常使用中知其然也好奇着所以然,因此尝试阅读vue源码并进行总结。本文旨在梳理初始化页面时data中的数据是如何渲染到页面上的。本文将带着这个疑问一点点追究vue的思路。总体来说vue模版渲染大致流程如图1所...

    AlphaGooo 评论0 收藏0
  • VUE

    摘要:注意指令前面需要加,对指令传递数据赋值使用例如约定速成加上,表示自定义指令不要使用驼峰式命名。需要通过方法实现自定义指令注册完成。 vue Vue.js 构建数据驱动的web界面库。集中实现MVVM 的 VM层。容易与其他库或项目整合 通过尽可能简单的API实现相应的数据绑定和组合的视图组件核心:相应的数据绑定系统, 数据与DOM保持同步数据驱动的视图,普通的HTML模板中使用特殊的语...

    bergwhite 评论0 收藏0
  • Vue源码解析:双向绑定原理

    摘要:无论是还是都提倡单向数据流管理状态,那我们今天要谈的双向绑定是否和单向数据流理念有所违背我觉得不是,从上篇文章语法树转函数了解到,双向绑定,实质是的单向绑定和事件侦听的语法糖。源码解析今天涉及到的代码全在文件夹下。 通过对 Vue2.0 源码阅读,想写一写自己的理解,能力有限故从尤大佬2016.4.11第一次提交开始读,准备陆续写: 模版字符串转AST语法树 AST语法树转rend...

    oliverhuang 评论0 收藏0
  • Vue源码解析:双向绑定原理

    摘要:无论是还是都提倡单向数据流管理状态,那我们今天要谈的双向绑定是否和单向数据流理念有所违背我觉得不是,从上篇文章语法树转函数了解到,双向绑定,实质是的单向绑定和事件侦听的语法糖。源码解析今天涉及到的代码全在文件夹下。 通过对 Vue2.0 源码阅读,想写一写自己的理解,能力有限故从尤大佬2016.4.11第一次提交开始读,准备陆续写: 模版字符串转AST语法树 AST语法树转rend...

    Kross 评论0 收藏0

发表评论

0条评论

itvincent

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<