赞
踩
Vue 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。
Vue.js 实现了数据的双向绑定,即当数据发生变化时,视图会自动更新,反之亦然。这使得开发者可以更轻松地管理和更新数据,同时保持视图与数据的同步。
Vue.js 将页面拆分为多个独立的组件,每个组件负责自己的视图和逻辑。这种组件化的开发方式使得代码更加模块化、可维护性更高,也有利于团队协作。
Vue.js 使用虚拟 DOM 技术,将页面的 DOM 结构表示为 JavaScript 对象,通过比较新旧虚拟 DOM 树的差异,最小化 DOM 操作,从而提高页面的性能和效率。
Vue.js 提供了丰富的指令、用于在模板中添加特定的功能或行为。指令使得开发者可以更便捷地操作 DOM 元素,实现动态数据绑定、条件渲染等功能。
Vue.js 提供了灵活的插件系统,允许开发者根据项目需求扩展 Vue 的功能
什么不是响应式数据?
数据和视图(dom属性值)相互独立、互相并不影响、即数据发生变化视图并不发生变化、视图发送变化数据并不发生变化、想要实现双向绑定、得在一方的值发生变化时去修改另一方的值。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>测试响应式</title>
- </head>
- <body>
- <input type="text">
- <button>value++</button>
- <script>
- // 设置 value 初始值
- let value = 1
- // 读取输入框 dom 元素
- const ipt = document.querySelector('input')
- // 按钮 dom 元素
- const btn = document.querySelector('button')
-
- // 设置 ipt 的 value 值为 value
- ipt.value = value
-
- // 为输入框绑定输入事件
- ipt.addEventListener('input', (event) => {
- console.log('iptValue: ', event.target.value);
- console.log('value ', value);;
- // 重新为 value 赋值
- // value = event.target.value
- })
-
- // 为按钮绑定点击事件
- btn.addEventListener('click', (event) => {
- value++
- // 重新为 ipt.value 赋值
- // ipt.value = ipt.value
- console.log('iptValue: ',ipt.value)
- console.log('value ', value);
- })
- </script>
- </body>
-
- </html>
什么是响应式数据?
数据发生变化、视图绑定该数据会自动更新、反之亦然。详细说明、例如页面上的表单元素通过v-model: value 绑定 data 方法里返回的对象属性值 value、当 value 值发送变化时视图会自动更新、在页面上修改表单元素时(修改value值)、data 方法里返回的对象属性值 value 也会同步变化。
为什么数据发送变化视图也会更新呢、底层源码是如何实现的?
如上图所示、这就是响应式对象发送变化时视图发送变化的机制。让我们一步一步的来剖析。
-
- new Vue({
- render: h => h(App)
- }).$mount('#app')
上面代码大家都很熟悉、简单来讲就是通过new Vue({render: h => h(App)}) 创建一个 Vue 实例、render: h => h(App) 就是一个指令,告诉 Vue 使用 createElement 函数来创建 App 组件的虚拟 DOM 对象,
然后将实例通过其内置的 $mount 方法挂载到 id 为 app 根节点上。
从 new 操作符、咱们可以看出 Vue 其实就是一个构造函数、没啥特别的、传入的参数就是一个对象、源码中我们叫做 options(选项)。
让我们来看看构造函数做了什么?
- import { initMixin } from './init'
- import { stateMixin } from './state'
- import { renderMixin } from './render'
- import { eventsMixin } from './events'
- import { lifecycleMixin } from './lifecycle'
- import { warn } from '../util/index'
- import type { GlobalAPI } from 'types/global-api'
-
- function Vue(options) {
- if (__DEV__ && !(this instanceof Vue)) {
- warn('Vue is a constructor and should be called with the `new` keyword')
- }
- this._init(options)
- }
-
- //@ts-expect-error Vue has function type
- initMixin(Vue)
- //@ts-expect-error Vue has function type
- stateMixin(Vue)
- //@ts-expect-error Vue has function type
- eventsMixin(Vue)
- //@ts-expect-error Vue has function type
- lifecycleMixin(Vue)
- //@ts-expect-error Vue has function type
- renderMixin(Vue)
-
- export default Vue as unknown as GlobalAPI
很明显、核心在于调用了方法 this._init(options) 这里开始进行 Vue 实例的初始化工作
options 就是传入的虚拟 dom 对象。
那么 _init() 方法是从哪里来的呢? _init() 方法内部干了什么?
核心关注 initMixin(Vue)
_init()这个方法就是 initMixin(Vue) 在 vue 实例的原型上挂载的。
- export function initMixin(Vue: typeof Component) {
- Vue.prototype._init = function (options?: Record<string, any>) {
- const vm: Component = this
- // a uid
- vm._uid = uid++
-
- let startTag, endTag
- /* istanbul ignore if */
- if (__DEV__ && config.performance && mark) {
- startTag = `vue-perf-start:${vm._uid}`
- endTag = `vue-perf-end:${vm._uid}`
- mark(startTag)
- }
-
- // a flag to mark this as a Vue instance without having to do instanceof
- // check
- vm._isVue = true
- // avoid instances from being observed
- vm.__v_skip = true
- // effect scope
- vm._scope = new EffectScope(true /* detached */)
- // #13134 edge case where a child component is manually created during the
- // render of a parent component
- vm._scope.parent = undefined
- vm._scope._vm = true
- // merge options
- if (options && options._isComponent) {
- // optimize internal component instantiation
- // since dynamic options merging is pretty slow, and none of the
- // internal component options needs special treatment.
- initInternalComponent(vm, options as any)
- } else {
- vm.$options = mergeOptions(
- resolveConstructorOptions(vm.constructor as any),
- options || {},
- vm
- )
- }
- /* istanbul ignore else */
- if (__DEV__) {
- initProxy(vm)
- } else {
- vm._renderProxy = vm
- }
- // expose real self
- vm._self = vm
- initLifecycle(vm)
- initEvents(vm)
- initRender(vm)
- callHook(vm, 'beforeCreate', undefined, false /* setContext */)
- initInjections(vm) // resolve injections before data/props
- initState(vm)
- initProvide(vm) // resolve provide after data/props
- callHook(vm, 'created')
-
- /* istanbul ignore if */
- if (__DEV__ && config.performance && mark) {
- vm._name = formatComponentName(vm, false)
- mark(endTag)
- measure(`vue ${vm._name} init`, startTag, endTag)
- }
-
- if (vm.$options.el) {
- vm.$mount(vm.$options.el)
- }
- }
- }
让我们来关注几个核心点
vm.$options
)初始化。如果是内部组件:通过 initInternalComponent 函数 优化初始化
如果是非内部组件:通过 mergeOptions 函数 合并传入的 options 对象和 当前 Vue 实例成为最终的 $options 对象
- if (options && options._isComponent) {
- // optimize internal component instantiation
- // since dynamic options merging is pretty slow, and none of the
- // internal component options needs special treatment.
- initInternalComponent(vm, options as any)
- } else {
- vm.$options = mergeOptions(
- resolveConstructorOptions(vm.constructor as any),
- options || {},
- vm
- )
- }
- // 生命周期初始化
- initLifecycle(vm)
- // 事件初始化
- initEvents(vm)
- // 渲染初始化
- initRender(vm)
initState(vm)
- export function initState(vm: Component) {
- const opts = vm.$options
- if (opts.props) initProps(vm, opts.props)
-
- // Composition API
- initSetup(vm)
-
- if (opts.methods) initMethods(vm, opts.methods)
- if (opts.data) {
- initData(vm)
- } else {
- const ob = observe((vm._data = {}))
- ob && ob.vmCount++
- }
- if (opts.computed) initComputed(vm, opts.computed)
- if (opts.watch && opts.watch !== nativeWatch) {
- initWatch(vm, opts.watch)
- }
- }
可以看到依次初始化了
props > setup > methods > data > computed > watch
想要弄清楚 Vue2 的响应式原理重点得关注初始化 data 上
- if (opts.data) {
- initData(vm)
- } else {
- const ob = observe((vm._data = {}))
- ob && ob.vmCount++
- }
如果组件定义了 data
,则直接调用 initData(vm)
来初始化;如果没有定义 data
,则创建一个空对象,并将其转换为响应式对象。
- function initData(vm: Component) {
- let data: any = vm.$options.data;
- // 获取组件配置中的 data 选项
- data = vm._data = isFunction(data) ? getData(data, vm) : data || {};
- // 如果 data 是一个函数,则通过 getData 函数获取其返回值,否则直接使用 data 或者默认为空对象 {}
-
- if (!isPlainObject(data)) {
- // 如果 data 不是一个纯对象,给出开发者警告(开发环境下)
- data = {};
- __DEV__ &&
- warn(
- 'data functions should return an object:\n' +
- 'https://v2.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;
- // 遍历 data 的键,将每个键设置为 vm 实例的代理属性(如果不是保留键)
-
- while (i--) {
- const key = keys[i];
- if (__DEV__) {
- // 在开发环境下,检查是否有同名方法已经定义为数据属性
- if (methods && hasOwn(methods, key)) {
- warn(`Method "${key}" has already been defined as a data property.`, vm);
- }
- }
- if (props && hasOwn(props, key)) {
- // 如果 data 的属性已经被声明为 prop,给出警告
- __DEV__ &&
- warn(
- `The data property "${key}" is already declared as a prop. ` +
- `Use prop default value instead.`,
- vm
- );
- } else if (!isReserved(key)) {
- // 如果属性不是保留属性,则将其代理到 vm 实例上
- proxy(vm, `_data`, key);
- }
- }
-
- // observe data
- // 观察数据,确保数据变化时可以通知相关的依赖
- const ob = observe(data);
- if (ob) {
- // 如果成功创建了观察对象,则增加其引用计数
- ob.vmCount++;
- }
可以看到无论如何都会执行 observe(data) 、就是其让data数据变成响应式数据的。
- /**
- * 尝试为一个值创建观察者实例,
- * 如果成功观察,则返回新的观察者实例,
- * 如果值已经有观察者实例,则返回现有的观察者。
- *
- * @param value 需要观察的值。
- * @param shallow 是否执行浅层观察。
- * @param ssrMockReactivity 是否在服务端渲染时模拟响应性。
- * @returns 如果成功观察到,则返回 Observer 实例,否则返回 void。
- */
- export function observe(
- value: any,
- shallow?: boolean,
- ssrMockReactivity?: boolean
- ): Observer | void {
- // 检查值是否已经有 __ob__ 属性,并且该属性是 Observer 的实例
- if (value && hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
- return value.__ob__; // 返回现有的观察者实例
- }
-
- // 创建新的观察者实例的条件判断
- if (
- shouldObserve && // 全局标志,确定是否进行观察
- (ssrMockReactivity || !isServerRendering()) && // 确保在非服务端渲染时或者模拟响应性时启用响应性
- (isArray(value) || isPlainObject(value)) && // 值必须是数组或普通对象
- Object.isExtensible(value) && // 值必须是可扩展的(即未被密封)
- !value.__v_skip /* ReactiveFlags.SKIP */ && // 值不能被标记为跳过观察
- !isRef(value) && // 值不能是 ref 对象
- !(value instanceof VNode) // 值不能是 Vue 虚拟节点
- ) {
- // 创建一个新的 Observer 实例来观察该值
- return new Observer(value, shallow, ssrMockReactivity);
- }
-
- // 如果不满足观察条件,则返回 void
- }
初始化传进来的 vm._data 满足上述条件、所以会执行
return new Observer(value, shallow, ssrMockReactivity);
核心: 是将传入的 data
对象(或数组)转换为响应式对象(或响应式数组)
- /**
- * Observer 类被附加到每个被观察的对象上。
- * 一旦附加,Observer 将目标对象的属性键转换为 getter 和 setter,
- * 用于收集依赖和分发更新。
- */
- export class Observer {
- dep: Dep; // 依赖管理对象
- vmCount: number; // 使用该对象作为根 $data 的 vm 数量
-
- constructor(public value: any, public shallow = false, public mock = false) {
- // 初始化 Observer 实例
- this.dep = mock ? mockDep : new Dep(); // 创建依赖管理对象 Dep
- this.vmCount = 0; // 记录有多少个 vm 实例使用该对象作为根 $data
-
- // 在值 value 上定义 __ob__ 属性,指向当前 Observer 实例
- def(value, '__ob__', this);
-
- // 如果值是数组
- if (isArray(value)) {
- if (!mock) {
- if (hasProto) {
- /* eslint-disable no-proto */
- // 如果支持原型链修改,则将数组的原型指向 arrayMethods
- ;(value as any).__proto__ = arrayMethods;
- /* eslint-enable no-proto */
- } else {
- // 否则,逐个定义数组的方法
- for (let i = 0, l = arrayKeys.length; i < l; i++) {
- const key = arrayKeys[i];
- def(value, key, arrayMethods[key]);
- }
- }
- }
-
- // 如果不是浅层观察,则递归观察数组中的每一项
- if (!shallow) {
- this.observeArray(value);
- }
- } else { // 如果值是普通对象
- // 遍历对象的所有属性,转换为 getter/setter
- const keys = Object.keys(value);
- for (let i = 0; i < keys.length; i++) {
- const key = keys[i];
- defineReactive(value, key, NO_INITIAL_VALUE, undefined, shallow, mock);
- }
- }
- }
-
- /**
- * 观察数组中的每一项。
- */
- observeArray(value: any[]) {
- for (let i = 0, l = value.length; i < l; i++) {
- observe(value[i], false, this.mock);
- }
- }
- }
def(value, key, arrayMethods[key]):将数组数据变成响应式数据核心方法。
defineReactive(value, key, NO_INITIAL_VALUE, undefined, shallow, mock):将对象数据转化成响应式数据核心方法。
将一个对象的属性变成响应式,即当属性被访问或修改时能触发相应的依赖收集和通知更新操作。核心方法就是 Object.defineProperty()
- /**
- * 在对象上定义一个响应式属性。
- * @param obj 要定义属性的对象。
- * @param key 要定义的属性的名称。
- * @param val 可选,属性的初始值。
- * @param customSetter 可选,自定义的 setter 函数。
- * @param shallow 可选,是否进行浅层观察。
- * @param mock 可选,是否模拟对象。
- * @param observeEvenIfShallow 默认为 false,即使是浅层观察也进行观察。
- */
- export function defineReactive(
- obj: object,
- key: string,
- val?: any,
- customSetter?: Function | null,
- shallow?: boolean,
- mock?: boolean,
- observeEvenIfShallow = false
- ) {
- const dep = new Dep(); // 创建一个依赖对象
-
- const property = Object.getOwnPropertyDescriptor(obj, key);
- if (property && property.configurable === false) {
- return; // 如果属性已存在且不可配置,直接返回
- }
-
- // 处理预定义的 getter 和 setter
- const getter = property && property.get;
- const setter = property && property.set;
- if ((!getter || setter) && (val === NO_INITIAL_VALUE || arguments.length === 2)) {
- val = obj[key]; // 获取属性的初始值
- }
-
- // 观察子对象,决定是否进行深层观察
- let childOb = shallow ? val && val.__ob__ : observe(val, false, mock);
-
- // 定义属性的 getter 和 setter
- Object.defineProperty(obj, key, {
- enumerable: true,
- configurable: true,
- get: function reactiveGetter() {
- const value = getter ? getter.call(obj) : val; // 获取属性值
- if (Dep.target) {
- if (__DEV__) {
- dep.depend({
- target: obj,
- type: TrackOpTypes.GET,
- key
- }); // 进行依赖收集
- } else {
- dep.depend();
- }
- if (childOb) {
- childOb.dep.depend(); // 子对象依赖收集
- if (isArray(value)) {
- dependArray(value); // 数组依赖收集
- }
- }
- }
- return isRef(value) && !shallow ? value.value : value; // 如果是 ref 类型且不是浅层观察,返回其值
- },
- set: function reactiveSetter(newVal) {
- const value = getter ? getter.call(obj) : val; // 获取属性当前值
- if (!hasChanged(value, newVal)) {
- return; // 新旧值相同则直接返回
- }
- if (__DEV__ && customSetter) {
- customSetter(); // 在开发环境下调用自定义的 setter 函数
- }
- if (setter) {
- setter.call(obj, newVal); // 使用属性的原始 setter 函数
- } else if (getter) {
- // 如果属性是 accessor 类型但没有 setter
- return;
- } else if (!shallow && isRef(value) && !isRef(newVal)) {
- value.value = newVal; // 处理 ref 类型属性的赋值
- return;
- } else {
- val = newVal; // 更新属性值
- }
- childOb = shallow ? newVal && newVal.__ob__ : observe(newVal, false, mock); // 更新子对象的观察状态
- if (__DEV__) {
- dep.notify({
- type: TriggerOpTypes.SET,
- target: obj,
- key,
- newValue: newVal,
- oldValue: value
- }); // 发送属性变更通知
- } else {
- dep.notify();
- }
- }
- });
-
- return dep; // 返回依赖对象
- }
Obeject.defineProperty 是一个用于定义或修改对象属性的方法。它允许你精确地控制属性的特性,包括可写性、可枚举性、可配置性以及访问器方法(getter 和 setter)。
Object.defineProperty(obj, 'age',descriptor);
obj
: 要在其上定义属性的对象。prop
: 要定义或修改的属性的名称或 Symbol。descriptor
: 描述符对象,定义了要定义或修改的属性的特性。value: 设置属性的值(仅适用于数据属性)。默认为 undefined
。不能与 get
或 set
同时使用。
writable: 值是否可写。默认为 false
。如果为 true
,则属性的值可以被赋值运算符改变。
enumerable: 属性是否可以被枚举。默认为 false
。如果为 true
,则属性可以在 for...in
循环和 Object.keys
方法中被枚举。
configurable: 属性是否可以被删除或修改特性。默认为 false
。如果为 true
,则可以使用 Object.defineProperty
修改该属性的特性,或者使用 delete
删除该属性。
get: 属性的 getter 函数,当访问该属性时调用。不能与 value
或 writable
同时使用。
set: 属性的 setter 函数,当属性被赋值时调用。不能与 value
或 writable
同时使用。
value
和 get
或 set
。configurable: false
),则不能再修改其特性,也不能删除该属性。- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>测试代码执行顺序</title>
- </head>
- <body>
- <script>
- // 定义数据属性
- let objOne = {};
- Object.defineProperty(objOne, 'myProperty', {
- value: 42,
- writable: true,
- enumerable: true,
- configurable: true
- });
- console.log(objOne.myProperty); // 输出: 42
- objOne.myProperty = 50;
- console.log(objOne.myProperty); // 输出: 50
-
- // 定义访问器属性
- let objTwo = {
- _myProperty: 0
- };
- Object.defineProperty(objTwo, 'myProperty', {
- get: function() {
- console.log('获取值');
- return this._myProperty;
- },
- set: function(value) {
- if(value === this._myProperty) {
- return
- }
- console.log('设置值');
- this._myProperty = value;
- },
- enumerable: true,
- configurable: true
- });
- // 设置属性值 myProperty 就是在调用 set 方法
- objTwo.myProperty = 10;
- // 访问属性值 myProperty 就是在调用 get 方法
- console.log(objTwo.myProperty); // 输出: 10
- </script>
- </body>
- </html>
- javascript
- Object.defineProperty(obj, key, {
- enumerable: true, // 可枚举
- configurable: true, // 可配置
- get: function reactiveGetter() {
- const value = getter ? getter.call(obj) : val; // 获取属性值
- if (Dep.target) { // 如果存在正在依赖此属性的 Watcher
- if (__DEV__) {
- // 在开发模式下进行依赖收集
- dep.depend({
- target: obj,
- type: TrackOpTypes.GET,
- key
- });
- } else {
- dep.depend(); // 正常情况下进行依赖收集
- }
- if (childOb) {
- childOb.dep.depend(); // 如果存在子响应式对象,也进行依赖收集
- if (isArray(value)) {
- dependArray(value); // 如果值是数组,还需依赖收集数组的元素
- }
- }
- }
- // 如果值是 Ref 对象且不是浅层响应式,则返回其实际值;否则返回原始值
- return isRef(value) && !shallow ? value.value : value;
- },
- set: function reactiveSetter(newVal) {
- const value = getter ? getter.call(obj) : val; // 获取当前属性值
- if (!hasChanged(value, newVal)) {
- return; // 如果新旧值相同,则不进行更新
- }
- if (__DEV__ && customSetter) {
- customSetter(); // 在开发模式下,如果定义了自定义 setter,则调用它
- }
- if (setter) {
- setter.call(obj, newVal); // 如果定义了 setter 函数,则调用它设置新值
- } else if (getter) {
- // 如果只定义了 getter 函数但未定义 setter,不执行任何操作(适用于只读属性)
- return;
- } else if (!shallow && isRef(value) && !isRef(newVal)) {
- value.value = newVal; // 如果属性值是 Ref 对象且不是浅层响应式,则设置其值
- return;
- } else {
- val = newVal; // 否则直接更新属性的值
- }
- // 更新子响应式对象的依赖关系
- childOb = shallow ? newVal && newVal.__ob__ : observe(newVal, false, mock);
- if (__DEV__) {
- // 在开发模式下,通知依赖此属性的 Watcher 进行更新
- dep.notify({
- type: TriggerOpTypes.SET,
- target: obj,
- key,
- newValue: newVal,
- oldValue: value
- });
- } else {
- dep.notify(); // 正常情况下通知依赖进行更新
- }
- }
- });
dep核心就是调用Watcher的两个方法get()和update()
- // 定义一个 Dep 类,用于管理依赖和通知更新(演示非源码)
-
- export default class Dep {
- constructor() {
- this.subs = []; // subs 数组用来存储 Watcher 对象
- }
-
- // 添加 Watcher 对象到 subs 数组
- addSub(sub) {
- this.subs.push(sub);
- }
-
- // 从 subs 数组移除指定的 Watcher 对象
- removeSub(sub) {
- remove(this.subs, sub); // 使用 remove 函数移除
- }
-
- // 当前 Dep 对象收集依赖
- depend() {
- if (Dep.target) {
- Dep.target.addDep(this); // 将当前 Dep 对象添加到 Watcher 的依赖列表中
- }
- }
-
- // 通知所有依赖于该 Dep 对象的 Watcher 执行更新操作
- notify() {
- // 遍历 subs 数组,调用每个 Watcher 的 update 方法
- const subs = this.subs.slice(); // 使用 slice() 创建副本,避免在遍历过程中被修改
- for (let i = 0, l = subs.length; i < l; i++) {
- subs[i].update(); // 调用 Watcher 的 update 方法
- }
- }
- }
-
- Dep.target = null; // 静态属性,用来存储当前正在执行的 Watcher 对象
- const targetStack = []; // Watcher 栈,用于处理嵌套依赖
-
- // 将指定的 Watcher 对象推入 Watcher 栈中
- export function pushTarget(target) {
- if (Dep.target) targetStack.push(Dep.target);
- Dep.target = target; // 将当前 Watcher 对象赋值给 Dep.target
- }
-
- // 从 Watcher 栈中弹出最后一个 Watcher 对象
- export function popTarget() {
- Dep.target = targetStack.pop(); // 恢复 Watcher 栈的上一个 Watcher 对象
- }
- export default class Watcher {
- constructor(vm, expOrFn, cb) {
- this.vm = vm; // Vue 实例
- this.getter = expOrFn; // 数据获取函数
- this.cb = cb; // 回调函数,数据变化时触发
- this.value = this.get(); // 初始化时执行 getter 函数,获取当前数据的值
- }
-
- // 更新 Watcher 的回调函数
- update() {
- const oldValue = this.value;
- this.value = this.get(); // 重新获取数据的值
- this.cb.call(this.vm, this.value, oldValue); // 执行回调函数,通知数据变化
- }
-
- // 评估 getter 函数,建立依赖关系
- get() {
- pushTarget(this); // 将当前 Watcher 对象推入 Watcher 栈中
- const vm = this.vm;
- let value;
- try {
- value = this.getter.call(vm, vm); // 执行 getter 函数,获取当前数据的值
- } catch (e) {
- throw e;
- } finally {
- popTarget(); // 从 Watcher 栈中弹出最后一个 Watcher 对象
- }
- return value;
- }
- }
dep.depend()
实现的,其中 dep
是依赖对象(Dep
实例)。dep.notify()
来通知所有依赖于该属性的 Watcher 进行更新。模板中绑定data数据发送变化时为什么视图会同步更新呢?
这时候这张图就更高理解了。
响应式对象(响应式原理):
data
选项时,Vue 会遍历这个对象的属性,并使用 Object.defineProperty
或 Proxy
将每个属性转换为 getter 和 setter、引用data数据实际上是访问数据对象属性的 get 方法、修改数据实际上是在调用数据对象属性的set方法。依赖追踪与 Watcher:
虚拟 DOM 及更新优化(虚拟dom和diff算法):
异步更新队列:
渲染 Watcher:
render
函数渲染成虚拟 DOM,并在数据变化时重新渲染组件。- callHook(vm, 'beforeCreate', undefined, false /* setContext */)
- initInjections(vm) // resolve injections before data/props
- initState(vm)
- initProvide(vm) // resolve provide after data/props
- callHook(vm, 'created')
这里也很好理解了生命周期hook 中的 beforeCreate 和 created 的调用时机。
beforeCreate 在状态初始化前、这时状态数据的肯定是不能用的
created 在状态初始化完成后调用。
3、总结响应式原理
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。