写于vue3.0发布前夕的helloworld之三
接上,watcher构造函数:
var Watcher = function Watcher (
vm,
expOrFn,
cb,
options,
isRenderWatcher
) {
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$2; // 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
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
);
}
}
this.value = this.lazy
? undefined
: this.get();
};
watcher构造函数,首先和当前vm组件相互引用,然后初始化了一些下面执行中会用到的属性,然后检查expOrFn是否符合要求,有则用之,无则警告,最后执行:
this.value = this.lazy
? undefined
: this.get();
因为this.lazy为false,进入Wathcer.prototype.get方法:
Watcher.prototype.get = function get () {
pushTarget(this);
var value;
var vm = this.vm;
try {
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
};
这个时候我们发现他调用了pushTarget方法:
function pushTarget (target) {
targetStack.push(target);
Dep.target = target;
}
pushTarget方法很简单,就是将当前watcher实例push进了一个叫做targetStack的栈中,然后将Dep.target设置为当前watcher实例的引用,仔细想想你在哪里看到过这个,Dep.target?
想起来没,就是设置data响应式数据的时候,get里的那个Dep.target,所以有了Dep.target,意味着我们要开始依赖收集了,然后代码继续走回到 Watcher.prototype.get中,调用了this.getter.call(vm, vm),这个方法其实就是我们上边说的updateComponent方法。然后走:
vm._update(vm._render(), hydrating);
这里先会执行vm._render(),hydrating是一个变量表示是否是服务端生成模式,大可不必关心,我们现在要执行的是vm._render:
Vue.prototype._render = function () {
var vm = this;
var ref = vm.$options;
var render = ref.render;
var _parentVnode = ref._parentVnode;
if (_parentVnode) {
vm.$scopedSlots = normalizeScopedSlots(
_parentVnode.data.scopedSlots,
vm.$slots,
vm.$scopedSlots
);
}
// set parent vnode. this allows render functions to have access
// to the data on the placeholder node.
vm.$vnode = _parentVnode;
// render self
var vnode;
try {
// There's no need to maintain a stack becaues all render fns are called
// separately from one another. Nested component's render fns are called
// when parent component is patched.
currentRenderingInstance = vm;
vnode = render.call(vm._renderProxy, vm.$createElement);
} catch (e) {
handleError(e, vm, "render");
// return error render result,
// or previous vnode to prevent render error causing blank component
/* istanbul ignore else */
if (vm.$options.renderError) {
try {
vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e);
} catch (e) {
handleError(e, vm, "renderError");
vnode = vm._vnode;
}
} else {
vnode = vm._vnode;
}
} finally {
currentRenderingInstance = null;
}
// if the returned array contains only a single node, allow it
if (Array.isArray(vnode) && vnode.length === 1) {
vnode = vnode[0];
}
// return empty vnode in case the render function errored out
if (!(vnode instanceof VNode)) {
if (Array.isArray(vnode)) {
warn(
'Multiple root nodes returned from render function. Render function ' +
'should return a single root node.',
vm
);
}
vnode = createEmptyVNode();
}
// set parent
vnode.parent = _parentVnode;
return vnode
};
}
这里他主要做了几件事呢,其实就是将render函数,当前实例,当前实例的相关配置包含data那些东西取出来,因为我们_parentVnode为空,故直接走try里的render,render执行的时候是上下文为我们最开始定义的那个代理的对象,传进去了一个叫做vm.$createElement的方法,这个方法是在initRender的时候创建的,用于创建一个VNode的节点,然后进入render函数,因为render函数这个玩意啊,像我们一般人写的时候基本上不咋写,这里我们看看他长啥样子:
function anonymous() {
with(this){return _c('div',{attrs:{"id":"app"}},[_v(_s(msg))])}
}
好啦,解释一下with干了个啥,相当于将this也就是vm._renderProxy加入到了当前作用域链的头,那么这个时候with里边的语句的变量可以访问到的东西第一个能访问的就是vm._renderProxy里的,然后执行_s(msg)的时候因为是一个读值操作,故而,触发了get方法,由于之前将vm.key,代理到了vm._data.key,所以首先会触发,vm.key的get方法:
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key]
};
然后,在执行this[sourceKey][key]的时候会触发data上响应式数据的get方法,也就是这个时候开始收集依赖,并且见到了我们的Dep.target:
get: function reactiveGetter () {
var value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend();
if (childOb) {
childOb.dep.depend();
if (Array.isArray(value)) {
dependArray(value);
}
}
}
return value
}
前面说过Dep.target就是当前vue实例的watcher,然后执行dep.depend:
function depend () {
if (Dep.target) {
Dep.target.addDep(this);
}
};
这里又调用了watcher的addDep并把当前的dep实例传了过去:
/**
* 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);
}
}
};
判断了几下有没有之后,最终将watcher实例加入到了dep实例的subs数组中,get执行完毕,回到render函数中。
写于vue3.0发布前夕的helloworld之三的更多相关文章
- VUE3.0发布,自己搞个文档网站
9月19日,尤大神发表了VUE3.0版本的演说,强大且震撼,这两天一直在找网站文档,可能还未被百度收录,未找到文档网站.后来在github上面找到了中文代码. 地址为:https://github.c ...
- 预计2019年发布的Vue3.0到底有什么不一样的地方?
摘要: Vue 3.0预览. 原文:预计今年发布的Vue3.0到底有什么不一样的地方? 作者:小肆 微信公众号:技术放肆聊 Fundebug经授权转载,版权归原作者所有. 还有几个月距离 vue2 的 ...
- 把酒言欢话聊天,基于Vue3.0+Tornado6.1+Redis发布订阅(pubsub)模式打造异步非阻塞(aioredis)实时(websocket)通信聊天系统
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_202 "表达欲"是人类成长史上的强大"源动力",恩格斯早就直截了当地指出,处在蒙昧时代即低 ...
- 纯小白入手 vue3.0 CLI - 3.3 - 路由的导航守卫
vue3.0 CLI 真小白一步一步入手全教程系列:https://www.cnblogs.com/ndos/category/1295752.html 尽量把纷繁的知识,肢解重组成为可以堆砌的知识. ...
- 纯小白入手 vue3.0 CLI - 2.1 - 组件 ( component )
vue3.0 CLI 真小白入手全教程系列:https://www.cnblogs.com/ndos/category/1295752.html 我的 github 地址 - vue3.0Study ...
- 快速进阶Vue3.0
在2019.10.5日发布了Vue3.0预览版源码,但是预计最早需要等到 2020 年第一季度才有可能发布 3.0 正式版. 可以直接看 github源码. 新版Vue 3.0计划并已实现的主要架构改 ...
- Vue3实战系列:Vue3.0 + Vant3.0 搭建种子项目
最近在用 Vue3 写一个开源的商城项目,开源后让大家也可以用现成的 Vue3 大型商城项目源码来练练手,目前处于开发阶段,过程中用到了 Vant3.0,于是就整理了这篇文章来讲一下如何使用 Vue3 ...
- Rubinius 2.0 发布,Ruby 虚拟机
Rubinius 2.0 发布了,官方发行说明请看这里. Rubinius是一个运行Ruby程序的虚拟机,其带有Ruby的核心库. Rubinius的设计决定了其调试功能的强大,使得在运行时常规的Ru ...
- Restful.Data v2.0发布,谢谢你们的支持和鼓励
v1.0发布后,承蒙各位博友们的热心关注,也给我不少意见和建议,在此我真诚的感谢 @冰麟轻武 等朋友,你们的支持和鼓励,是这个开源项目最大的推动力. v2.0在除了细枝末节外,在功能上主要做了一下更新 ...
- 开源搜索引擎Iveely 0.7.0发布,不一样,那就让他不一样!
2012年08月05日,Iveely Search Engine 0.1.0发布,今天,怀着对于未来的追求,终于,0.7.0如期和大家见面了,7个版本,历时2年4个月,感谢大家的支持,感谢我不离不弃的 ...
随机推荐
- ASP和jq实现url传递参数乱码的escape编码和unescape解码
<% Function vbsEscape(str) dim i,s,c,a s="" For i=1 to Len(str) c=Mid(s ...
- js获取浏览器宽度和高度值
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>请调整浏览器窗口</t ...
- pytorch 衣服等
衣服 pytorch BP神经网路 猫狗
- kali-国内源-更新系统
1.更换国内源 vim /etc/apt/sources.list deb https://mirrors.aliyun.com/kali kali-rolling main non-free con ...
- [NepCTF2022]signin
signin 题目 from Crypto.Util.number import getStrongPrime,bytes_to_long from gmpy2 import powmod,is_pr ...
- Windows Python2.7环境 安装paramiko模块(转)
http://t.zoukankan.com/staffyoung-p-5587450.html 链接,网上大多数都是同一篇文章 Paramiko是用python语言写的一个模块,遵循SSH2协议,支 ...
- kvm 透传显卡至win10虚拟机
环境 已安装nvidia 显卡 驱动 操作系统:CentOS Linux release 7.9.2009 (Core) 内核版本:Linux 5.4.135-1.el7.elrepo.x86_64 ...
- 写出干净的 JavaScript 5 个小技巧
携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情 降低阅读负担,启发创作心智,轻松学习 JavaScript 技巧,日拱一卒,jym,冲~ 1. 将数字定 ...
- Idea2020.2.3 创建JavaWeb项目(部署Tomcat)方法
1.创建项目不再是Java Enterprise了,而是先New 一个普通Java项目! 2.创建项目后,选择Run->Edit Configuration->左上角加号->Tomc ...
- 前端如何给bearer token传值
Bearer token是一种常见的身份验证机制,通常用于Web API和其他Web服务.在前端中,Bearer token通常是通过HTTP头(HTTP header)发送的,具体来说是通过&quo ...