Ice Time


  • Home

  • Archives

数据结构笔记

Posted on 2018-08-11

shuffle

Fisher-Yates

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var source_array = [1,2,3,4,5]

function shuffle(source_array) {
var target_array = []
while(source_array.length) {
var random_index = Math.floor(Math.random() * source_array.length)
target_array.push(source_array[random_index])
source_array.splice(random_index, 1)
console.log(random_index, source_array)
}
return target_array
}

var result = shuffle(source_array)

console.log(result)

两个有序数组合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var arr1 = [2, 7, 15, 23]
var arr2 = [8, 9, 10, 11]

function sort_2_ordered_array(arr1, arr2) {
var result = [];
var index1 = 0, index2 = 0, len1 = arr1.length, len2 = arr2.length
while (index1 < len1 && index2 < len2) {
if (arr1[index1] < arr2[index2]) {
result.push(arr1[index1])
index1 ++
} else {
result.push(arr2[index2])
index2 ++
}
}

while (index1 < len1) {
result.push(arr1[index1])
index1 ++
}

while (index2 < len2) {
result.push(arr2[index2])
index2 ++
}

return result
}

var result = sort_2_ordered_array(arr1, arr2)

console.log(result)

获取一个数组中第二大的值

求解一颗二叉树的最大权重路径

数组去重

VueLifeCycle源码学习笔记

Posted on 2018-08-10

lifecycle

mountComponent

new Watcher(vm, updateComponent)

文件路径:~/github/vuejs/vue/src/core/instance/lifecycle.js

initLifecycle


文件路径:~/github/vuejs/vue/src/core/instance/lifecycle.js

updateDOMListeners

createElem
-> invokeCreateHooks
-> updateDOMListeners

Web Performance 笔记

Posted on 2018-08-10

performance

html

CSS

阻塞渲染

JS

因为JS将会中断CSSOM构建,所以同样也是阻塞渲染

JavaScript

Posted on 2018-08-10

作用域

引擎,编译器,作用域

this

关键字,提供了一种更优雅的方式来隐式“传递”一个对象的引用

闭包

函数在非定义的词法作用域被调用

原型

类型转换

webpack 笔记

Posted on 2018-08-10

概念

loader

plugin

chunk

module

webpack@3.x.x

周期

webpack@4.x.x

周期

Vue源码学习笔记--Observer

Posted on 2018-05-13

Vue Observer

流程分析

observer

流程一,初始化

这个操作是在initData中Observer实例初始化时通过defineReactive函数实现,同时每个属性对应一个dep实例。同时Watcher被实例化,每个组件都有相应的Watcher实例。

1
2
3
4
5
6
7
8
9
10
// 在初始化属性时,初始化观察者,并且遍历属性,使其成为响应属性
initState
-> initData
-> observe(data, true)
-> new Observer(data)
-> walk
-> defineReactive(obj, keys[i])
-> new Dep () // 一个属性对应一个Dep实例
-> new Watcher (vm, updateComponent, noop) // 运行updateComponent进行求值,updateComponent返回值始终为undefined,由依赖触发其更新
-> watcher.get() // 执行updateComponent函数,此时第一次运行render

流程二,收集依赖

当render函数运行时,getter将被触发,此时watcher收集依赖。【将wathcer添加到dep的subscribes中】

1
2
3
4
5
// 渲染函数运行时,触发各个属性的getter
reactiveGetter[ defineReactive ]
-> dep.depend()
-> watcher.addDep() // 收集render中用到的各个属性对应的dep,保存到watcher.deps组数中
-> dep.addSubs() // 此时watcher被收集为订阅者

流程三,触发更新

属性被赋值时,值变更触发依赖更新:

1
2
3
4
5
6
7
// 属性发生变更时,触发对应属性的setter
reactiveSetter[ defineReactive ]
-> dep.notify() // 发布消息
-> subs[i].update()
-> queueWatcher
-> flushSchedulerQueue
-> watcher.run()

代码实现

Observer [观察者]


源码注释👹

Observer class that are attached to each observed object. Once attached, the observer converts target object's property keys into getter/setters that collect dependencies and dispatches updates.

解读

观察者,会为目标对象上的各个属性添加getter/setters, 用于收集依赖,触发更新。

属性

1
2
3
value: any;
dep: Dep;
vmCount: number; // 某个JS对象被作为data的vm个数

方法

  • constructor
  • walk
  • observeArray

流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
observe(data, true)
-> new Observer(data)
-> walk to defineReactive


defineReactive
-> reactiveGetter
-> dep.depend()
-> Dep.target.addDep(this)

-> reactiveSetter
-> dep.notify()
-> subs[i].update()
-> queueWatcher(this)
-> nextTick
-> watcher.run()
-> this.getAndInvoke(this.cb) // 运行expOrFn,收集依赖,执行$watch的回调

Watcher [侦听器]


源码中的注释👹

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.

侦听器,解析一个表达式,并且收集过程中的依赖,当表达式的值发生变化时,触发回调函数。

属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
vm: Component,
expression: string,
cb: Function,
id: number,
deep: boolean, // 是否监听一个对象内的属性变更
user: boolean, // options default false
computed: boolean, // 监听的是否为计算属性
sync: boolean, // options default false
dirty: boolean,
active: boolean,
dep: Dep,
deps: Array<Dep>, // 用于记录依赖
newDeps: Array<Dep>, // 记录reactiveGetter每次执行获取的依赖,和deps对比,移除不再需要的订阅器
depIds: SimpleSet,
newDepIds: SimpleSet,
before: ?Function,
getter: Function,
value: any

方法

  • get
  • cleanupDeps
  • update
  • run
  • getAndInvoke
  • evaluate
  • depend
  • teardown

流程

1
2
3
4
5
6
7
8
9
10
new Watcher(vm, expOrFn, cb, opitons)
-> this.value = this.get()
-> pushTarget(this)
-> value = this.getter.call(vm, vm) // 运行expOrFn,将触发reactiveGetter收集依赖
-> popTarget()
-> this.cleanupDeps()

-> nextTick
-> watcher.run()
-> this.getAndInvoke(this.cb) // 运行expOrFn,将触发reactiveGetter收集依赖 ,执行$watch的回调

Dep [依赖]


源码注释👹

A dep is an observable that can have multiple directives subscribing to it. Sub array to save which properties depend on it

解读

用于记录订阅者(Sub数组),并为每个vue实例的侦听器添加属性依赖

属性

1
2
3
static target: ?Watcher,
id: number,
subs: Array<Watcher>

方法

  • addSub
  • removeSub
  • depend
  • notify

流程

1
new Dep()

scheduler [调度器]

流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
watcher.update()
- has <Map> 多次set触发的更新去重
- circular <Map>
- index <Number> 当前正在运行中的侦听器队列索引
- waiting <Boolean> 不同侦听器进行更新操作时,在上个侦听器完成更新之前,等待
- flushing <Boolean> 是否正在运行队列中的任务
- queueWatcher(this)
-> waiting = true
-> nextTick(flushSchedulerQueue)
-> flushing = true
-> queue.sort((a, b) => a.id - b.id)
-> has[id] = null
-> watcher.run()
-> resetSchedulerState()
-> waiting = flushing = false

关于queueWatcher

源码注释👹

Push a watcher into the watcher queue. Jobs with duplicate IDs will be skipped unless it's pushed when the queue is being flushed.

解读

将监听器放入队列尾部。重复的任务将会被跳过。

关于flushSchedulerQueue

源码注释👹

Flush both queues and run the watchers.

解读

清空队列并且运行监听器的回调函数

检测变化的注意事项

  1. 不能检测到对象属性的添加或删除。

VuePatch源码学习笔记

Posted on 2018-05-13

Vue Patch

VNode 了解

createPatchFunction


文件路径:~/github/vuejs/vue/src/core/vdom/patch.js

笔记:

这个文件中会创建一个patch的函数,传入nodeOps以及modules

关于nodeOps

关于modules

其中,modules中包含了baseModules以及platformModules;baseModules中包含了ref及directives的钩子函数,分别为create, update, destory,这些函数会在vnode对应的周期"create", "activate", "update", "remove", "destroy"中被调用。

Patch 流程


patch函数在vue进行update时被调用

文件路径:~/github/vuejs/vue/src/core/vdom/patch.js

条件一:isUndef(oldVnode)

源码注释👹
empty mount (likely as component), create new root element

解读:当未创建vnode时,创建一个新的vnode作为根节点

条件二:!isRealElement && sameVnode

源码注释👹
patch existing root node

解读:如果不是一个真实的dom节点,则对根节点开始进行patchVnode的流程

流程
patchVnode

条件二:isRealElement || sameVnode

解读:不是相同的节点,则创建一个新的dom节点,并把老的节点移除(如果是真实元素的话,会根据这个节点再创建vnode)

流程
create new Elm
➡️
update parent placeholder node element, recursively
➡️
destroy old node

PatchVnode 流程

如果两个vnode引用是相同的,会直接退出

data.hook.prepatch ➡️

directive.cbs.update
data.hook.update ➡️

情况1, vnode.text -> setTextContent
!vnode.text

情况2, oldCh && ch -> updateChildren

情况3, ch -> addVnodes

情况4, oldCh -> removeVnodes

updateChildren 流程

情况1,sameVnode(oldStartVnode, newStartVnode),不需要移动
情况2,sameVnode(oldEndVnode, newEndVnode),不需要移动
情况3,sameVnode(oldStartVnode, newEndVnode),当前节点右移
情况4,sameVnode(oldEndVnode, newStartVnode),当前节点右移
情况5-1,有key的情况 sameVnode(vnodeToMove, newStartVnode),当前节点左移
情况5-2,没有key的情况 创建一个新的节点

当结束上述比对时

如果oldVnode先遍历完,此时newStarIndex到newEndIndex是新增的节点,则addVnodes
如果newVnode先遍历完,此时oldStarIndex到oldEndIndex是需要删除的节点,则removeVnodes

createElm

情况1,如果vnode.elm已经存在,说明该vnode在先前的render中,已经使用,而在这里作为一个新的节点来使用,需要克隆一个新的vnode,不影响之前的节点

如果是组件,则创建对应组件,并退出

ice grapefruit

step on javascript

7 posts
6 tags
© 2019 ice grapefruit
Powered by Hexo
|
Theme — NexT.Muse v5.1.4