日期: 2022-09-20 15:33:25 浏览数:3

上往建站提供服务器空间服务商,百度快照排名,网站托管,百度推广运营,致力于设计外包服务与源代码定制开发,360推广,搜狗推广,增加网站的能见度及访问量提升网络营销的效果,主营:网站公司,百度推广公司电话,官网搭建服务,网站服务企业排名,服务器空间,英文域名等业务,专业团队服务,效果好。
尉氏微信公众号开发【尉氏网络推广】尉氏建站、尉氏网站维护、尉氏网页制作、尉氏微信小程序代运营公司
尉氏县位于豫东平原,属河南省开封市。东邻通许县、扶沟县,南与鄢陵县、长葛县接壤,西与新郑市交界,北与祥符区、中牟县相连。南北长40.77公里,东西宽43.76公里,总面积约1307.7平方公里,总人口81万人(2000年),中共尉氏县委、尉氏县人民政府驻地:两湖街道。 [1]
尉氏古称“尉州”,是河南省经济管理扩权县,电价趸售优惠县,拥有省政府批准的全省八大特色基地之一的河南省中原纺织工业基地,省认定的尉氏高效农业示范园区。 [2]
2018年,全县完成地区生产总值388亿元,增长8%;一般公共预算收入首次突破20亿元、达到20.6亿元,增长16.8%;税收收入15.96亿元,增长29.9%;城镇和农村居民人均可支配收入预计达到26951元、13911元,分别增长8.3%和8.9%。 [3]
本文将 vue 中与数据侦测相关的源码摘了出来,配合上文(侦测数据的变化 - [基本实现]) 一起来分析一下 vue 是如何实现数据侦测的。
Tip: 以下代码出自 vue.esm.js,版本为 v2.5.20。无关代码有一些删减。中文注释都是笔者添加。
/**
* Define a property.
* 定义属性的方法
*/
function def (obj, key, val, enumerable) { Object.defineProperty(obj, key, { value: val, enumerable: !!enumerable, writable: true, configurable: true
});
}
/**
* Parse simple path.
* 解析简单路径。比如 vm.$watch('a.b.c', function(){})
*/
var bailRE = /[^w.$]/; function parsePath (path) { if (bailRE.test(path)) { return
} var segments = path.split('.'); return function (obj) { // 例如 a.b.c
for (var i = 0; i < segments.length; i++) { if (!obj) { return } // 最后读取到 c
obj = obj[segments[i]];
} return obj
}
} /**
* A dep is an observable that can have multiple
* directives subscribing to it.
* 依赖。对我们的依赖列表 dependList 进行了封装,这里提取出来了一个类,用于存储依赖(Watcher)。
*/
var Dep = function Dep () { this.id = uid++; // subs 也就是我们的依赖列表 dependList
this.subs = [];
};
Dep.prototype.addSub = function addSub (sub) { this.subs.push(sub);
};
Dep.prototype.removeSub = function removeSub (sub) {
remove(this.subs, sub);
}; // 收集依赖
Dep.prototype.depend = function depend () { // Dep.target 也就是我们的全局变量(globalData),指向 Watcher。
if (Dep.target) { // 收集依赖 Watcher
Dep.target.addDep(this);
}
}; // 通知依赖
Dep.prototype.notify = function notify () { // stabilize the subscriber list first
var subs = this.subs.slice(); for (var i = 0, l = subs.length; i < l; i++) {
subs[i].update();
}
}; // the current target watcher being evaluated.
// this is globally unique because there could be only one
// watcher being evaluated at any time.
// 类似我们的全局变量(globalData ),用于存储 Watcher
Dep.target = null; var targetStack = []; function pushTarget (target) {
targetStack.push(target);
Dep.target = target;
} function popTarget () {
targetStack.pop();
Dep.target = targetStack[targetStack.length - 1];
} /*
* not type checking this file because flow doesn't play well with
* dynamically accessing methods on Array prototype
*/
// 接下来是侦测数组的变化
// 也就是通过拦截器来实现数组的侦测
var arrayProto = Array.prototype; // arrayMethods就是拦截器
var arrayMethods = Object.create(arrayProto); // 能改变数组的7个方法
var methodsToPatch = [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'
]; /**
* Intercept mutating methods and emit events
* 给拦截器(arrayMethods)定义以上7个方法
*/
methodsToPatch.forEach(function (method) { // cache original method
// 数组的原始方法
var original = arrayProto[method];
def(arrayMethods, method, function mutator () { var args = [], len = arguments.length; while ( len-- ) args[ len ] = arguments[ len ]; // 调用拦截器中的方法,拦截器接着会去调用数组中对应的方法
var result = original.apply(this, args); // 数据变成响应式后,数据上就会挂载 __ob__(Observer 的实例) 属性,里面有数据的依赖
var ob = this.__ob__; // 只有 push、unshift、splice 这三个方法能增加数据,而增加的数据也需要转为响应式
var inserted; switch (method) { case 'push': case 'unshift':
inserted = args; break
case 'splice':
inserted = args.slice(2); break
} // 数组增加的数据也需要转为响应式
if (inserted) { ob.observeArray(inserted); } // notify change
// 通知依赖
ob.dep.notify(); return result
});
}); /**
* Observer class that is attached to each observed
* object. Once attached, the observer converts the target
* object's property keys into getter/setters that
* collect dependencies and dispatch updates.
* 1. 将数据转为响应式的主入口。
* 2. 在我们的实现中是通过 defineReactive() 将数据转为响应式,没有递归侦测所有的 key。比如
* data = {a: 1, b: {c:1}},我们只侦测了数据的第一层(data.a、data.b),孩子节点如果是对象,
* 也需要侦测 data.b.c。
* 3. 递归侦测调用顺序:Observer -> walk -> defineReactive$$1 -> observe -> Observer
* 4. 将对象和数组分别处理。
*/
var Observer = function Observer (value) {
this.value = value; // 定义依赖,用于存储于数据有关的依赖
// 比如数据 let data = {a: [11,22]},某处使用了 data.a。当执行 data.a.push(33) 时,
// data.a 就应该通知其依赖
this.dep = new Dep(); this.vmCount = 0; // 将 this 挂载到数据的 __ob__ 属性上。Array 的拦截器就可以通过数据取得 Observer 的 dep,从而通知依赖
def(value, '__ob__', this); if (Array.isArray(value)) { // 如果有原型,就通过更改原型的方式将拦截器挂载到数组上,否则就将拦截器中的方法依次拷贝到数组上
if (hasProto) {
protoAugment(value, arrayMethods);
} else {
copyAugment(value, arrayMethods, arrayKeys);
} // 数组中的每一项也需要转为响应式
this.observeArray(value);
} else { // 依次遍历对象中每个 key,将其转为响应式
this.walk(value);
}
}; /**
* Walk through all properties and convert them into
* getter/setters. This method should only be called when
* value type is Object.
*/
Observer.prototype.walk = function walk (obj) { var keys = Object.keys(obj); for (var i = 0; i < keys.length; i++) { // 通过 Object.defineProperty() 侦测对象
defineReactive$$1(obj, keys[i]);
}
}; /**
* Observe a list of Array items.
*/
Observer.prototype.observeArray = function observeArray (items) { for (var i = 0, l = items.length; i < l; i++) {
observe(items[i]);
}
}; /**
* Augment a target Object or Array by intercepting
* the prototype chain using __proto__
* 通过更改原型来挂载拦截器,实现数组的侦测。
*/
function protoAugment (target, src) { // 作用与 setPrototype 相同
target.__proto__ = src;
} // 将拦截器中的方法拷贝到数组中,实现数组的侦测。
function copyAugment (target, src, keys) { for (var i = 0, l = keys.length; i < l; i++) { var key = keys[i];
def(target, key, src[key]);
}
} /**
* Attempt to create an observer instance for a value,
* returns the new observer if successfully observed,
* or the existing observer if the value already has one.
* 观察数据。如果数据不是对象,直接返回;如果已经是响应式,则返回 Observer 的实例;否则将值转为响应式
*/
function observe (value, asRootData) { if (!isObject(value) || value instanceof VNode) { return
} var ob; if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__;
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && // 不能是 vue 实例
!value._isVue
) {
ob = new Observer(value);
} if (asRootData && ob) {
ob.vmCount++;
} return ob
} /**
* Define a reactive property on an Object.
* 侦测数据变化。功能与我们的 defineReactive() 方法类似。
*/
function defineReactive$$1 (
obj,
key,
val,
customSetter,
shallow ) { // 每个 key 都有一个 Dep 用于存储依赖
// dep 就是我们的依赖列表
var dep = new Dep(); var property = Object.getOwnPropertyDescriptor(obj, key); if (property && property.configurable === false) { return
} // cater for pre-defined getter/setters
var getter = property && property.get; var setter = property && property.set; if ((!getter || setter) && arguments.length === 2) {
val = obj[key];
} // 值如果是对象,也需要转为响应式
var childOb = !shallow && observe(val); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { var value = getter ? getter.call(obj) : val; // 如果有值
if (Dep.target) { // 收集依赖
dep.depend(); // 如果值(childOb)是对象,childOb也需要收集依赖
if (childOb) { // 可能主要针对数组?
childOb.dep.depend(); // 数组中数据,如果需要也得收集依赖,因为里面的数据若发生变化,应该通知外界
if (Array.isArray(value)) {
dependArray(value);
}
}
} return value
}, set: function reactiveSetter (newVal) { var value = getter ? getter.call(obj) : val; /* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) { return
} /* eslint-enable no-self-compare */
if (customSetter) {
customSetter();
} // #7981: for accessor properties without setter
if (getter && !setter) { return } if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
} // 新的值也需要转为响应式
childOb = !shallow && observe(newVal); // 通知依赖
dep.notify();
}
});
} /**
* A watcher parses an expression, collects dependencies,
* and fires callback when the expression value changes.
* This is used for both the $watch() api and directives.
* Watcher 相对比较复杂,稍微分析一下
*/
var Watcher = function Watcher (
vm,
expOrFn,
cb,
options,
isRenderWatcher ) { // vue 实例
this.vm = vm; if (isRenderWatcher) {
vm._watcher = this;
}
vm._watchers.push(this); // options
if (options) { this.deep = !!options.deep; this.user = !!options.user; this.lazy = !!options.lazy; this.sync = !!options.sync; this.before = options.before;
} else { this.deep = this.user = this.lazy = this.sync = false;
} this.cb = cb; this.id = ++uid$1; // uid for batching
this.active = true; this.dirty = this.lazy; // for lazy watchers
this.deps = []; this.newDeps = []; this.depIds = new _Set(); this.newDepIds = new _Set(); this.expression = expOrFn.toString(); // parse expression for getter
// expOrFn 可以是函数,也可以是表达式,例如 a.b.c,统一为 this.getter
if (typeof expOrFn === 'function') { this.getter = expOrFn;
} else { this.getter = parsePath(expOrFn); if (!this.getter) { this.getter = noop;
warn( "Failed watching path: "" + expOrFn + "" " + 'Watcher only accepts simple dot-delimited paths. ' + 'For full control, use a function instead.',
vm
);
}
} // 通过 get() 方法读取数据
this.value = this.lazy
? undefined
: this.get();
}; /**
* Evaluate the getter, and re-collect dependencies.
*
*/
Watcher.prototype.get = function get () { // 会将自己赋值给 Dep.target
pushTarget(this); var value; var vm = this.vm; try { // 调用 Watcher 构造函数中分装的 getter() 方法
// 触发数据的 getter,从而收集依赖(Watcher)
value = this.getter.call(vm, vm);
} catch (e) { if (this.user) {
handleError(e, vm, ("getter for watcher "" + (this.expression) + """));
} else { throw e
}
} finally { // "touch" every property so they are all tracked as
// dependencies for deep watching
if (this.deep) {
traverse(value);
}
popTarget(); this.cleanupDeps();
} return value
}; /**
* Add a dependency to this directive.
*/
Watcher.prototype.addDep = function addDep (dep) { var id = dep.id; if (!this.newDepIds.has(id)) { this.newDepIds.add(id); this.newDeps.push(dep); if (!this.depIds.has(id)) {
dep.addSub(this);
}
}
}; /**
* Subscriber interface.
* Will be called when a dependency changes.
*/
Watcher.prototype.update = function update () { /* istanbul ignore else */
if (this.lazy) { this.dirty = true;
} else if (this.sync) { this.run();
} else {
queueWatcher(this);
}
};
尉氏微信公众号开发【尉氏网络推广】尉氏建站、尉氏网站维护、尉氏网页制作、尉氏微信小程序代运营公司
上往建站提供搭建网站,域名注册,官网备案服务,网店详情页设计,企业网店,专业网络店铺管理运营全托管公司咨询电话,服务器空间,微信公众号托管,网页美工排版,致力于域名申请,竞价托管,软文推广,全网营销,提供标准级专业技术保障,了却后顾之忧,主营:虚拟主机,网站推广,百度竞价托管,网站建设,上网建站推广服务,网络公司有哪些等业务,专业团队服务,效果好。
服务热线:400-111-6878 手机微信同号:18118153152(各城市商务人员可上门服务)