Vue原理: data属性为什么能直接通过this.访问?

您所在的位置:网站首页 一加怎么访问data Vue原理: data属性为什么能直接通过this.访问?

Vue原理: data属性为什么能直接通过this.访问?

2024-01-07 01:50| 来源: 网络整理| 查看: 265

Hello, 这里是Link🤩, 从今天开始, 我打算写一份Vue源码分析的系列文章, 但是请放心, 绝对不会是单纯枯燥的源码, 我们从一些面试题, 场景出发, 我们每篇文章只去关注Vue实现的某些功能, 以及解决一些面试常问的问题. 之后再去理解具体的实现. 让你不必直接面对枯燥的源码.💪💪

20211217173440.jpg

后续, 我们会讲到Vue是如何用一个 watcher 去完成组件实例化, 计算属性, 监听属性等核心功能的, 并且你会理解到, Vue设计的很多精妙之处, 比如最为人熟知的响应式系统等, 一定会收获颇丰哦😋

Vue 实例化的流程图

这是我自己写的一份关于Vue的流程图笔记, 能够非常直观方便的了解整个实例化做的事情. 并且这张图已经放在我的Vue源码项目vue-core-analyse, 并且这个项目的源码有我个人读源码时的备注, 相信对你也会有一定的帮助😝, 欢迎star✨

这个图涵盖了整个Vue实例数据与组件实例化的流程 image.png

这个流程图是vscode插件Draw.io Integration提供的, 你需要下载本插件才能启用

image.png

场景

为什么可以直接通过 this.message 的形式去访问到data函数中的值?

var vm = new Vue({ el: '#el', data () { return { message: "Hello Vue" } }, mounted() { const message = this.message } })

在我们看来, 似乎mouted 和 data 是两个不同的作用域, 我们应该先访问共同的作用域, 然后再去访问这个data对象才合理🤔, 就像这样

mounted() { const message = this.data.message } 原理

实际上这个处理也很好理解, 我们一起从入口来看一下 Vue 对 data 属性的处理

_init函数是在Vue这整个项目初始化的时候通过initMixin(Vue)挂载上去的, 当我们 new Vue({})的时候会执行下面这个函数, 这个options, 就是上面那个demo中我们传给 Vue 构造函数的大对象, 里面有data, mounted等配置

// src/core/instance/index.js function Vue (options) { if (process.env.NODE_ENV !== 'production' && // 要求 new 执行 !(this instanceof Vue) ) { warn('Vue is a constructor and should be called with the `new` keyword') } this._init(options) // 👈 } 在 Vue 中的最后一行执行了一个 this._init(options) 函数就是Vue在被实例化的时候触发一系列行为 这里我对源码进行的删减, 我们只关注data部分的初始化. 这样的一个_init 函数 做了一下几件事情

定义一个vm等于当前实例

初步挂载$options并合并配置选项

这里我们先简单的理解为 vm.$options = options

执行initState(vm)初始化全部状态

步骤3是我们最需要关注的. 在这个函数中负责初始化配置项中的 props methods data等属性

// src/core/intance/init.js Vue.prototype._init = function (options?: Object) { const vm: Component = this // a flag to avoid this being observed vm._isVue = true // merge options vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ) // expose real self vm._self = vm // 初始化状态 initState(vm) } 这个函数这里也就到了我们的终极目标initData(vm)在这里负责初始我们的数据(data) // src/core/intance/state.js // 初始化状态 export function initState (vm: Component) { const opts = vm.$options // 基于我们options中存在的属性进行初始化 if (opts.data) { // 初始化数据 initData(vm) } } initData函数首先会判断我们的data是否为一个函数, 是的话执行他并返回一个对象. 如果不是则直接使用. while循环首先对我们的data与pros和method做一层重名校验, 这里的逻辑十分简单. 我们无需关注 将这个对象赋值一份给 data 和 vm._data 然后调用 proxy(vm, _data, key) // src\core\instance\state.js function initData (vm: Component) { let data = vm.$options.data data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {} 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] /** * 对data中的key与 props 和 method 做对比, 要求不能够 key 重复 */ if (process.env.NODE_ENV !== 'production') { //.. } if (props && hasOwn(props, key)) { //... } else if (!isReserved(key)) { // 代理 proxy(vm, `_data`, key) // 👈 } } }

重点在于proxy(vm, _data, key)

proxy函数首先为sharedPropertyDefinition对象定义了一个get和set方法 通过defineProperty去对vm对象做拦截操作

也就是说 当我们通过 vm 也就是我们常用的 this.message 的形式 去访问数据时候, 会通过defineProperty函数做的拦截操作将 this._data.message 转发给我们(赋值同理)

// \src\core\instance\state.js 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 } /** * 为什么 在其他属性里, 可以直接通过 this.message 就能拿到 data 中的值? * 答案就在这里, vue 在 初始化 data 的时候会通过这个代理函数 * 将 data 中的 key 值直接放到 vm 实例上进行监控,然后基于上面的对象进行监控 * 访问 this.message 相当于访问了 this._data.message */ Object.defineProperty(target, key, sharedPropertyDefinition) } 流程图展示

image.png

new Vue({ el: '#app', data:{ msg: 'parent-Vue' }, mounted () { console.log(this.msg); console.log(this._data.msg); // 当然并不推荐通过这种方式访问数据 } })

image.png

感谢😘

如果觉得文章内容对你有帮助:

❤️欢迎关注点赞哦! 我会尽最大努力产出高质量的文章 个人公众号: 前端Link 联系作者: linkcyd 😁 往期:

面试中常考的6大排序算法原理, 看完即懂!

关于正则表达式, 你要了解的都在这里了

React入门指南: 6张脑图带你入门React

面试官: 来, 手写一个promise

原型与原型链: 如何自己实现 call, bind, new?



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3