关注公众号阅读更多文章:
hcysunyang / vue-design Goto Github PK
View Code? Open in Web Editor NEW📖 master分支:《渲染器》
Home Page: http://hcysun.me/vue-design/zh/
📖 master分支:《渲染器》
Home Page: http://hcysun.me/vue-design/zh/
在8vue-reactive-dep-watch.md
文章中有如下一段:
callbacks = [
flushSchedulerQueue, // queue = [renderWatcher]
() => {
this.name = 'hcy'
this.$nextTick(() => { console.log('第二个 $nextTick') })
}
]
接下来主线程处于空闲状态(调用栈清空),开始执行
microtask
队列中的任务,即执行 flushCallbacks 函数,flushCallbacks
函数会按照顺序执行callbacks
数组中的函数,首先会执行flushSchedulerQueue
函数,这个函数会遍历queue
中的所有观察者并重新求值,完成重新渲染,在完成渲染之后,本次更新队列已经清空,queue
会被重置为空数组,一切状态还原。接着会执行如下函数:
() => {
this.name = 'hcy'
this.$nextTick(() => { console.log('第二个 $nextTick') })
}
这里提到在microtask
中的任务flushCallbacks在执行callbacks
数组,此时数组中有上面的两个函数,而这个函数会遍历
queue中的所有观察者并重新求值,完成重新渲染,在完成渲染之后
这句话提到重新渲染,这里的重新渲染是指的浏览器的渲染吗?我在翻查关于eventloop
的文章时,写的都是浏览器的渲染发生在两个(macro)task
之间,且是否执行该次渲染根据浏览器的render
策略决定,具体可查看深入探究 eventloop 与浏览器渲染的时序问题。
所以,我觉的这里的重新渲染应该是指的DOM更新吧,而这里是等microtask
都执行完后,也就是后面的嵌套的$nextTick
(应为它也会添加到microtask队列中)也执行完之后,得到最终的DOM,然后会触发浏览器重新渲染,得到渲染后的页面。
以上疑问如果有问题,还请大佬给指正一下,3Q。
`
...
vue-parsing-2 小节的 注释节点的元素描述对象内容这段内容的错别字还是存在
没人情况 comments 选项的值为 false,即不保留注释,假如将其设置为 true,则当计息期遇到注释节点时会保留该注释节点,此时 parseHTML 函数的 comment 钩子函数会被调用
function normalizeInject (options: Object, vm: ?Component) {
const inject = options.inject
if (!inject) return
const normalized = options.inject = {}
if (Array.isArray(inject)) {
...
} else if (isPlainObject(inject)) {
for (const key in inject) {
const val = inject[key]
normalized[key] = isPlainObject(val)
? extend({ from: key }, val)
: { from: val }
}
} else if (process.env.NODE_ENV !== 'production') {
...
}
}
inject: {
data1,
d2: 'data2',
data3: { someProperty: 'someValue' }
}
inject: {
'data1': { from: 'data1' },
'd2': { from: 'data2' },
'data3': { from: 'data3', someProperty: 'someValue' }
}
请问data3的someProperty是如何被赋值过去的?
当调用栈空闲后每次事件循环只会从(macro)task 中读取一个任务并执行,而在同一次事件循环内会将 microtask 队列中所有的任务全部执行完毕,且要先于 (macro)task。
执行完一次(macro)task任务之后,会执行所有microtask。microtask优先于第二次(macro)task任务执行是否更合理。
膜拜楼主, 提个小小的建议, 当介绍的函数调用栈比较深的时候, 可以把 Chrome DevTools 里的调用栈截图贴出来, 感觉这样会更通俗易懂
跟vue 文档的文字类型 字体颜色 字号的保持统一..
保证用户切换的一致性.
官方文档对于extends的解释:允许声明扩展另一个组件(可以是一个简单的选项对象或构造函数),而无需使用 Vue.extend。这主要是为了便于扩展单文件组件。这和 mixins 类似,区别在于,组件自身的选项会比要扩展的源组件具有更高的优先级。
不理解 “这和 mixins 类似,区别在于,组件自身的选项会比要扩展的源组件具有更高的优先级。” 这句话。都是通过mergeOptions,mixins和extends为什么会有不一样的优先级。
既然flushSchedulerQueue和$nextTick中的函数都是通过nextTick添加到callbacks中的,那是如何保证先执行flushSchedulerQueue的呢?比如我先执行this.$nextTick(() => console.log(1)),再执行this.a = 1,此时按理说() => console.log(1)比flushSchedulerQueue先push到callbacks中啊。请大神赐教。
文中提到:“也就是说 childOb.dep.depend() 这句话的执行说明除了要将依赖收集到属性 a 自己的“筐”里之外,还要将同样的依赖收集到 data.a.ob.dep 这里”筐“里,为什么要将同样的依赖分别收集到这两个不同的”筐“里呢?其实答案就在于这两个”筐“里收集的依赖的触发时机是不同的,即作用不同”。
即使两个“筐”收集的依赖的触发时机不同,但是把它们俩“合二为一”,会有什么问题呢?比如:
export function defineReactive (...) {
const dep = new Dep();
// 省略
// 将闭包中的dep作为参数传给observe,并赋值给childOb的dep属性
let childOb = !shallow && observe(val, dep)
}
这样一来,依旧可以在set函数中触发闭包的dep,也可以在$set中主动触发childOb.dep。
Is there an English language version of this?
这个会运用 defaultStrat 策略 如果没有子元素 则会返回父元素 不谈会直接返回子元素。。并不是求和。。
看完了现有已经写好的章节,写的很详细很好,十分感谢作者!期待剩余部分内容的补充
//41行
if (process.env.NODE_ENV !== 'production') {
strats.el = strats.propsData = function (parent, child, vm, key) {
if (!vm) {
warn(
`option "${key}" can only be used during instance ` +
'creation with the `new` keyword.'
)
}
return defaultStrat(parent, child)
}
}
该段代码中的defaultStrat是在下文中声明的,const 不是不会提升变量么?为什么defaultStrat可以被执行?是因为打包后会被处理么?
//240行
/**
* Default strategy.
*/
const defaultStrat = function (parentVal: any, childVal: any): any {
return childVal === undefined
? parentVal
: childVal
}
ps:感谢作者的指导
代码生成部分的解析会更新吗?Vue3.0出来之后会持续更新该项目吗?
在methods里面改变两个属性值this.a='更新一';this.b='更新二';如果使用同步队列更新,为什么会渲染两次?
我的理解:在同一个tick里执行this.a='更新一';this.b='更新二';执行了两次run()和patch()。这些都是同一个tick里面的宏任务,是发生在渲染之前的,应该只会渲染一次。
求大佬解答,非常感谢
在看的过程中会有些疑问,不知道有没有合适的地方交流,比如群或者评论功能?
RT
选项 el、propsData 的合并策略 的第三段代码
// 子组件 var ChildComponent = { ...
这样写并不会报错只有 Vue.extend({el}) 或 Vue.component("child", {el}) 这样写才会报错
Hi,
I'm really looking forward, if it would be possible to have this awesome project translated to Eng!
You did a great job. Awesome project!
Will manage somehow with google.translate for now ;) Thanks a lot!
Regards,
D.
在 Observer 构造函数 中写道:
export class Observer {
value: any;
dep: Dep;
vmCount: number; // number of vms that has this object as root $data
constructor (value: any) {
// 省略...
}
walk (obj: Object) {
// 省略...
}
observeArray (items: Array<any>) {
// 省略...
}
}
可以清晰的看到 Observer 类的实例对象将拥有三个实例属性,分别是 value、dep 和 vmCount 以及两个实例方法 walk 和 observeArray。Observer 类的构造函数接收一个参数,即数据对象。下面我们就从 constructor 方法开始,研究实例化一个 Observer 类时都做了哪些事情。
在class
中声明的应该是原型方法吧?所以应该是 Observer 类的实例对象拥有 walk
和 observeArray
两个原型方法吧?
这算是理解上的偏差么?
一个是属于 arr 属性自身的 dep 对象,另一个是 childOb.dep 对象,其中 childOb 就是 ob1
这里其中childOb
应该是ob2
在 原文 中有以下描述:
当调用栈空闲后每次事件循环只会从 (macro)task 中读取一个任务并执行,而在同一次事件循环内会将 microtask 队列中所有的任务全部执行完毕,且要先于 (macro)task。
我的疑问在于为什么在当前 event loop
中 mictotask queue
中的任务为什么会先于 (marco)task
执行?
根据 Tasks, microtasks, queues and schedules 中的介绍 Microtasks
中的第二段最后一句话:
microtasks always happen before the next task.
根据 W3C 和 WHATWG living standard 所描述的 event loops processing model
中,每次 event loops
在 task queue
不为空的情况下始终以一个 (marco)task
作为 event loop
开始,之后才是 microtask
。
个人仍然觉得 @lz-lee 在 #130 所言的当前 event loop
中的 microtask
先于 下一次 (macro)task任务执行更加合理。
并且全局执行上下文 script
作为包含初始代码执行环境的执行上下文,也并不影响 (marco)task
在当次 event loop
先于 microtask
执行的规则。那么当前 event loop
中的 microtask
先于 下一次 macrotask
更加合理?
还是我理解有误?想请教一下你们是如何理解在当前 event loop
中 microtask
先于 marcotask
执行的? 能否提供一下相关的示例规范依据呢?@HcySunYang @lz-lee
关于开始标签的闭合部分:
const startTagClose = /^\s*(/?)>/
在chrome上,如果是一个 > 不是一个一元签标的闭合方式。在捕获组中的分显示空字符串,而不是undefined
期待中.... 😊
因为beforeCreate钩子是在initState之前执行的,所以如果我在beforeCreate钩子中改变了vm.$options.data的话,这个initData中的类型判断就是有必要的了
const app = new Vue({
el: '#app',
data: {
name: 'a'
},
beforeCreate () {
this.$options.data = {
name: 'b'
}
}
})
链接:揭开数据响应系统的面纱
位置:那么经过 def 函数处理之后,data 对象应该变成如下这个样子
下边这一段代码;
const data = {
a: 1,
// __ob__ 是不可枚举的属性
__ob__: {
value: data, // value 属性指向 data 数据对象本身,这是一个循环引用
dep: dep实例对象, // new Dep()
vmCount: 0
}
}
在Observer
的constructor
中调用的def
的第三个参数是this
,即生成的Observer对象,所以上边的value
属性值应是observer(Observer对象),即
const data = {
a: 1,
// __ob__ 是不可枚举的属性
__ob__: {
value: observer, // 生成的Observer对象,它的value属性才是data
dep: dep实例对象, // new Dep()
vmCount: 0
}
}
自己试了下也的确是这样的。
console.log(data.__ob__.value === data); // true
在5vue-merge.md文件中:
1)这个时候则直接返回 mergeDataOrFn 的函数执行结果,但是会多透传一个参数 vm:
疑问:多透传? “透” 字是多余的吗?
2)那么 mergeHook 函数时怎样合并生命周期选项的呢?我们看看 mergeHook 函数的代码,如下:
疑问:“函数时” ? 还是 “函数是”
3)整个函数体由三组三目运算符组成,有一点值得大家学习的就是这里写三目运算符的方式,是不是感觉非常的清晰易读?那么这段代码的分析我们同样使用与上面代码相同的格式来写:
疑问:“非常的清晰易读” 应该为 “非常地清晰易读”
clone后 本地跑项目 跑不起 怎么解决
关于mergeHook这个函数我有一个问题,您这里有一句
如果有 parentVal 那么其一定是数组,如果没有 parentVal 那么 strats[hooks] 函数根本不会执行。
我想问的是为什么没有parentVal ,strats[hooks] 函数就不会执行?在Vue 选项的合并的这段代码中
`const options = {}
let key
for (key in parent) {
mergeField(key)
}
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key)
}
}
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
return options`
第一个for中parent中没有那个key,但在child中不是也执行了mergeField了吗?那为什么说strats[hooks]根本不会执行呢?
Location: 6vue-init-start.md
1.hasHandler 这个变量就定义在当前文件,如下:
const hasHandler = {
has (target, key) {
// has 变量是真实经过 in 运算符得来的结果
const has = key in target
~-----------------------------------------------------
hasHandler 和 has 均为常量.
~-----------------------------------------------------
2.上面的代码由于 render 函数时我们手动书写的,所以 render 函数并不会被包裹在 with 语句块内,
~-----------------------------------------------------
“函数时” or “函数是” ?
~-----------------------------------------------------
3、最后我们注意到 callHook 函数的最后有这段一段代码:
~-----------------------------------------------------
“最后有这段一段代码:” or “最后有这样一段代码:” ?
~-----------------------------------------------------
作者加油更新啊
还有其他如computed,有变化
在<实例对象代理访问数据 data>一节中写道: ”props优先级 > data优先级 > methods优先级“
实际优先级是否应该是 ”props优先级 > methods优先级 > data优先级 “
因为initMethod执行在前,只是Vue的warn message 写的看起来像data的优先级比较高
@ALL I love you.
export function makeMap (
str: string,
expectsLowerCase?: boolean
): (key: string) => true | void {
const map = Object.create(null)
const list: Array = str.split(',')
for (let i = 0; i < list.length; i++) {
map[list[i]] = true
}
return expectsLowerCase
? val => map[val.toLowerCase()]
: val => map[val]
}
测试一下
export const isVowel = makeMap('a,e,i,o,u', true)
isVowel('e')
疑问在这???
//文中说这里返回 true,实际上返回的是一个 function. val => map[val.toLowerCase()] 或者 val => map[val]
初始化之 initEvents这篇源码分析里的
createComponentInstanceForVnode
函数已经不一样了。告知一下作者
export function createComponentInstanceForVnode (
vnode: any, // we know it's MountedComponentVNode but flow doesn't
parent: any, // activeInstance in lifecycle state
parentElm?: ?Node,
refElm?: ?Node
): Component {
const vnodeComponentOptions = vnode.componentOptions
const options: InternalComponentOptions = {
_isComponent: true,
parent,
propsData: vnodeComponentOptions.propsData,
_componentTag: vnodeComponentOptions.tag,
_parentVnode: vnode,
_parentListeners: vnodeComponentOptions.listeners,
_renderChildren: vnodeComponentOptions.children,
_parentElm: parentElm || null,
_refElm: refElm || null
}
// check inline-template render functions
const inlineTemplate = vnode.data.inlineTemplate
if (isDef(inlineTemplate)) {
options.render = inlineTemplate.render
options.staticRenderFns = inlineTemplate.staticRenderFns
}
return new vnodeComponentOptions.Ctor(options)
}
vue.js现在的代码:
export function createComponentInstanceForVnode (
vnode: any, // we know it's MountedComponentVNode but flow doesn't
parent: any, // activeInstance in lifecycle state
): Component {
const options: InternalComponentOptions = {
_isComponent: true,
_parentVnode: vnode,
parent
}
// check inline-template render functions
const inlineTemplate = vnode.data.inlineTemplate
if (isDef(inlineTemplate)) {
options.render = inlineTemplate.render
options.staticRenderFns = inlineTemplate.staticRenderFns
}
return new vnode.componentOptions.Ctor(options)
}
在 3vue-example.md文档中,
..............................................................................................................................
const vm: Component = this
// a uid
vm._uid = uid++
首先声明了变量 vm,其值为 this 也就是当前这个 Vue 实例啦,.........
..............................................................................................................................
vm 应该常量吧?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.