Vue2源码
为什么data是函数,而components是对象?
data函数如果没有返回值会报错吗?
答:会
vue源码位置:src\core\instance\state.js
1 |
|
Vue源码位置:packages\weex-template-compiler\build.js
1 |
|
data函数
执行之后将返回值重新赋值给data
,如果data
不是个object
,name
就会报出一个警告
为什么props定义的数据不能和data同名?
vue源码路径:vue\src\core\instance\state.js
1 |
|
可以看到先获取data
的key
,然后再去调用hasOwn
方法去判断methods
和props
里面是否有重复的key
,有的话就会发出警告。最后都通过了之后就会调用proxy(vm, '_data', key)
,用封装的proxy
方法对vm实例
做了一层代理,使得我们可以直接通过this
直接调用key
vue2代理的实现
1 |
|
使用的是Object.defineProperty
来实现的
Vue的初始化
Vue源码位置:src\core\instance\index.js
1 |
|
最先执行的是initMixin()
,我们看看里面有啥。
Vue源码位置:src\core\instance\init.js
1 |
|
initState()
是从state.js
文件里面导入的,我们再去看看里面有什么。
src\core\instance\state.js
1 |
|
可见在beforeCreate
与created
之间初始化了inject
,props
,methods
,data
,computed
,watch
基于Vue2的proxy,自己实现一个代理
1 |
|
Vue的实例为什么不能挂载在body或根节点上?挂载了会报错吗?
答:挂载节点会被虚拟dom生成的Dom替换,会报错
Vue源码位置:src\platforms\web\entry-runtime-with-compiler.js
1 |
|
Vue.set方法是如何实现的
在 Vue2中由于Object.defineProperty
的局限性,无法原生监听对象新增属性、删除属性或者数组索引操作,所以提供了 Vue.set
方法作为补偿 api来解决这个问题。
Vue.set的核心作用是:
- 为响应式对象新增属性,并让该属性也具备响应式能力(即被 getter、setter 拦截)
- 触发该对象的依赖更新(即通知所有依赖该属性的 watcher 执行更新)
实现的核心逻辑
- 参数校验与边界处理
- 接收三个参数:target(目标对象/数组)、key(新增属性名/索引)、value(新增属性值)
- 如果 target 是 vue 实例或者 $data(根数据对象),直接报错。vue 不允许给实例添加根级响应式属性
- 如果 target 不是响应式对象(未被 Observer 处理过),直接赋值target[key] = value,然后返回。非响应式数据无需劫持。
- 处理数组的情况
如果 target 是数组且 key 是有效的索引:- 计算实际索引(处理负数索引,如
key=-1
对应target.length-1
) - 使用 splice 方法在指定索引位置新增元素
- 计算实际索引(处理负数索引,如
核心操作: 调用数组的 splice 方法替换元素(target.splice(key,1,val))。因为 Vue 已经重写了数组的 splice 方法,使其执行的时候会触发依赖更新(dep.notify()),同时新插入的 val 会被转为响应式。
- 处理对象的情况
如果 target 是对象:- 如果 key 已存在与 target,那么旧直接赋值
target[key] = value
,会触发 setter,自动更新依赖。 - 如果 key 是新属性:
- 获取 target 对象的 Observer 实例(Vue 内部用于管理响应式的对象,每个响应式数据都有一个 Observer 实例)
- 调用 Observer 实例的 defineReactive 方法,为新属性key 添加 getter/setter,使其具备响应式属性。
- 触发 target 对象的依赖更新,通过 Observer 实例的 dep.notify() 方法通知所有依赖该对象的 watcher 执行更新。
- 如果 key 已存在与 target,那么旧直接赋值
computed 和 watch 的区别
computed
从现有响应式数据中动态派生新值。
- 显式依赖响应式数据(data、props),依赖关系是“多对一”,可从多个数据派生出一个新值。
- 只有当依赖的数据发生变化时,才会重新计算,若依赖未变,直接返回缓存的结果。
- 触发时机是惰性的,只有当计算结果被模板或者其他响应式数据使用时才会触发计算。
watch
- 监听响应式数据变化并执行副作用。
- 可以监听单个或多个响应式数据(data、props、computed),依赖关系是“一对多”或者“多对多”(一个数据变化可以触发多个操作)
- 触发时机是主动的,当监听的响应式数据发生变化时,会立即执行回调函数。
- 当监听的数据变化时,都会执行回调函数,不会缓存执行结果。
- 配置灵活,支持
deep: true
深度监听,以及immediate: true
立即执行回调,handler(回调函数)
等选项。
new Vue()过程中做了些什么
new Vue(options)的过程是实例初始化的核心流程,本质是通过一系列初始化步骤将传入的配置(options)转化为一个可响应、可渲染的 Vue 实例。
整个过程可分为选项合并、初始化核心模块、模板编译与挂载三大阶段。
选项合并
Vue 实例接收的 options(如 data、methods、components 等)并非直接使用,而是需要与全局配置(Vue.config)、组件继承的配置(若为组件实例)进行合并,最终生成一个统一的配置对象。
初始化核心模块
- 初始化生命周期
- 初始化事件系统($on、$emit、$off等)
- 初始化注入(inject),从父组件的 provide 中获取对应的值
- 初始化状态,处理 data、props、computed、watch 等核心数据
- 初始化 props
- 初始化 methods
- 初始化 data
- 初始化 computed
- 初始化 watch
- 初始化 provide
模板编译与挂载
- 模板编译
将模板转换为 render 渲染函数- 解析:将模板字符串解析为 AST(抽象语法数)
- 优化:优化 AST,标记 AST 中的静态节点,避免每次更新时重新渲染
- 生成:将 AST 转换为 render 函数
- 挂载
通过 render 函数生成虚拟 DOM,再将虚拟 DOM 渲染为真实 DOM 并插入到 el 对应的容器中
Vue.observable的了解
在 Vue 2 中,Vue.observable 是一个全局 API,定义在 Vue 构造函数上,源码基于响应式系统的核心逻辑实现。
核心作用
将一个普通对象转换为响应式对象,使其具备 “数据变化时触发依赖更新” 的能力。转换后的对象可以被组件引用,当对象属性变化时,引用它的组件会自动重新渲染。
实现原理
与组件内 data 的响应式处理逻辑一致:通过 Object.defineProperty 对对象的属性递归添加 getter/setter,实现依赖收集(getter 时)和更新触发(setter 时),本质是创建了一个 Observer 实例来管理该对象的响应式。
示例
1 |
|