Skip to content

Commit f04bd1f

Browse files
committed
补充知识点以及原理图
1 parent 74dc48b commit f04bd1f

File tree

8 files changed

+241
-153
lines changed

8 files changed

+241
-153
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828
* [常用的JS概念](./docs/常用的JS概念.md)
2929
* [函数防抖与节流](./src/debounce/index.ts)
3030
* [React下ECharts的数据驱动探索](./docs/Echats/React下数据驱动.md)
31+
* [Vue知识点相关](./docs/vue笔记)
32+
* [Vue原型的初始化](./docs/vue笔记/01.md)
33+
* [面试可能提问关键的几个函数](./docs/vue笔记/02.md)
34+
* [Vue对模板的解析](./docs/vue笔记/03.md)
3135

3236
## 算法
3337
* [消息队列](./src/RabbitMQ)

docs/vue笔记/01.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
![原理图](../../img/reactive.png)
12
## 构造Vue
23
```javascript
34
function Vue (opts) {

docs/vue笔记/02.md

+155
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,161 @@ export function defineReactive (
119119
})
120120
}
121121
```
122+
```javascript
123+
const queue: Array<Watcher> = []
124+
let has: { [key: number]: ?true } = {}
125+
let waiting = false
126+
let flushing = false
127+
/**
128+
* Push a watcher into the watcher queue.
129+
* Jobs with duplicate IDs will be skipped unless it's
130+
* pushed when the queue is being flushed.
131+
*/
132+
export function queueWatcher (watcher: Watcher) {
133+
const id = watcher.id
134+
if (has[id] == null) {
135+
has[id] = true
136+
if (!flushing) {
137+
queue.push(watcher)
138+
} else {
139+
// if already flushing, splice the watcher based on its id
140+
// if already past its id, it will be run next immediately.
141+
let i = queue.length - 1
142+
while (i > index && queue[i].id > watcher.id) {
143+
i--
144+
}
145+
queue.splice(i + 1, 0, watcher)
146+
}
147+
// queue the flush
148+
if (!waiting) {
149+
waiting = true
150+
nextTick(flushSchedulerQueue)
151+
}
152+
}
153+
}
154+
```
155+
每次添加watcher时检查是否存在,只加入不存在的watcher
156+
157+
```javascript
158+
import { noop } from 'shared/util'
159+
import { handleError } from './error'
160+
import { isIOS, isNative } from './env'
161+
162+
const callbacks = []
163+
let pending = false
164+
165+
function flushCallbacks () {
166+
pending = false
167+
const copies = callbacks.slice(0)
168+
callbacks.length = 0
169+
for (let i = 0; i < copies.length; i++) {
170+
copies[i]()
171+
}
172+
}
173+
174+
// Here we have async deferring wrappers using both microtasks and (macro) tasks.
175+
// In < 2.4 we used microtasks everywhere, but there are some scenarios where
176+
// microtasks have too high a priority and fire in between supposedly
177+
// sequential events (e.g. #4521, #6690) or even between bubbling of the same
178+
// event (#6566). However, using (macro) tasks everywhere also has subtle problems
179+
// when state is changed right before repaint (e.g. #6813, out-in transitions).
180+
// Here we use microtask by default, but expose a way to force (macro) task when
181+
// needed (e.g. in event handlers attached by v-on).
182+
let microTimerFunc
183+
let macroTimerFunc
184+
let useMacroTask = false
185+
186+
// Determine (macro) task defer implementation.
187+
// Technically setImmediate should be the ideal choice, but it's only available
188+
// in IE. The only polyfill that consistently queues the callback after all DOM
189+
// events triggered in the same loop is by using MessageChannel.
190+
/* istanbul ignore if */
191+
if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
192+
macroTimerFunc = () => {
193+
setImmediate(flushCallbacks)
194+
}
195+
} else if (typeof MessageChannel !== 'undefined' && (
196+
isNative(MessageChannel) ||
197+
// PhantomJS
198+
MessageChannel.toString() === '[object MessageChannelConstructor]'
199+
)) {
200+
const channel = new MessageChannel()
201+
const port = channel.port2
202+
channel.port1.onmessage = flushCallbacks
203+
macroTimerFunc = () => {
204+
port.postMessage(1)
205+
}
206+
} else {
207+
/* istanbul ignore next */
208+
macroTimerFunc = () => {
209+
setTimeout(flushCallbacks, 0)
210+
}
211+
}
212+
213+
// Determine microtask defer implementation.
214+
/* istanbul ignore next, $flow-disable-line */
215+
if (typeof Promise !== 'undefined' && isNative(Promise)) {
216+
const p = Promise.resolve()
217+
microTimerFunc = () => {
218+
p.then(flushCallbacks)
219+
// in problematic UIWebViews, Promise.then doesn't completely break, but
220+
// it can get stuck in a weird state where callbacks are pushed into the
221+
// microtask queue but the queue isn't being flushed, until the browser
222+
// needs to do some other work, e.g. handle a timer. Therefore we can
223+
// "force" the microtask queue to be flushed by adding an empty timer.
224+
if (isIOS) setTimeout(noop)
225+
}
226+
} else {
227+
// fallback to macro
228+
microTimerFunc = macroTimerFunc
229+
}
230+
231+
/**
232+
* Wrap a function so that if any code inside triggers state change,
233+
* the changes are queued using a (macro) task instead of a microtask.
234+
*/
235+
export function withMacroTask (fn: Function): Function {
236+
return fn._withTask || (fn._withTask = function () {
237+
useMacroTask = true
238+
const res = fn.apply(null, arguments)
239+
useMacroTask = false
240+
return res
241+
})
242+
}
243+
244+
export function nextTick (cb?: Function, ctx?: Object) {
245+
let _resolve
246+
callbacks.push(() => {
247+
if (cb) {
248+
try {
249+
cb.call(ctx)
250+
} catch (e) {
251+
handleError(e, ctx, 'nextTick')
252+
}
253+
} else if (_resolve) {
254+
_resolve(ctx)
255+
}
256+
})
257+
if (!pending) {
258+
pending = true
259+
if (useMacroTask) {
260+
macroTimerFunc()
261+
} else {
262+
microTimerFunc()
263+
}
264+
}
265+
// $flow-disable-line
266+
if (!cb && typeof Promise !== 'undefined') {
267+
return new Promise(resolve => {
268+
_resolve = resolve
269+
})
270+
}
271+
}
272+
```
273+
IOS下有一个bug,如果当前没有执行的 task 那么不会执行microtask
274+
275+
276+
## 数据的初始化
122277
* 根据 vm.$options.data 选项获取真正想要的数据(注意:此时 vm.$options.data 是函数)
123278
* 校验得到的数据是否是一个纯对象
124279
* 检查数据对象 data 上的键是否与 props 对象上的键冲突

docs/vue笔记/03.md

+37-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,40 @@
99
8. 构建 AST 并建立父子级关系是在 start 钩子函数中完成的,每当遇到非一元标签,会把它存到 currentParent 变量中,当解析该标签的子节点时通过访问 currentParent 变量获取父级元素。
1010
9. 如果一个元素使用了 v-else-if 或 v-else 指令,则该元素不会作为子节点,而是会被添加到相符的使用了 v-if 指令的元素描述对象的 ifConditions 数组中。
1111
10. 如果一个元素使用了 slot-scope 特性,则该元素也不会作为子节点,它会被添加到父级元素描述对象的 scopedSlots 属性中。
12-
11. 对于没有使用条件指令或 slot-scope 特性的元素,会正常建立父子级关系。
12+
11. 对于没有使用条件指令或 slot-scope 特性的元素,会正常建立父子级关系。
13+
14+
## v-for指令
15+
1. 如果 v-for 指令的值为 'obj in list',则 alias 的值为字符串 'obj'
16+
2. 如果 v-for 指令的值为 '(obj, index) in list',则 alias 的值为字符串 'obj, index'
17+
3. 如果 v-for 指令的值为 '(obj, key, index) in list',则 alias 的值为字符串 'obj, key, index'
18+
4. 如果 alias 字符串的值为 'obj',则匹配结果 iteratorMatch 常量的值为 null
19+
5. 如果 alias 字符串的值为 'obj, index',则匹配结果 iteratorMatch 常量的值是一个包含两个元素的数组:[', index', 'index']
20+
6. 如果 alias 字符串的值为 'obj, key, index',则匹配结果 iteratorMatch 常量的值是一个包含三个元素的数组:[', key, index', 'key', 'index']
21+
22+
## v-if指令
23+
1. 如果标签使用了 v-if 指令,则该标签的元素描述对象的 el.if 属性存储着 v-if 指令的属性值
24+
2. 如果标签使用了 v-else 指令,则该标签的元素描述对象的 el.else 属性值为 true
25+
3. 如果标签使用了 v-else-if 指令,则该标签的元素描述对象的 el.elseif 属性存储着 v-else-if 指令的属性值
26+
4. 如果标签使用了 v-if 指令,则该标签的元素描述对象的 ifConditions 数组中包含“自己”
27+
5. 如果标签使用了 v-else 或 v-else-if 指令,则该标签的元素描述对象会被添加到与之相符的带有 v-if 指令的元素描述对象的 ifConditions 数组中。
28+
29+
## 判断管道符
30+
1. 不在 '' "" `` // 中
31+
2. 当前字符所对应的 ASCII 码必须是 0x7C,即当前字符必须是管道符。
32+
3. 该字符的后一个字符不能是管道符。
33+
4. 该字符的前一个字符不能是管道符。
34+
5. 该字符不能处于花括号、方括号、圆括号之内
35+
36+
## 判断ref属性
37+
1. 该标签的元素描述对象会被添加 el.ref 属性,该属性为解析后生成的表达式字符串,与 el.key 类似。
38+
2. 该标签的元素描述对象会被添加 el.refInFor 属性,它是一个布尔值,用来标识当前元素的 ref 属性是否在 v-for 指令之内使用。
39+
40+
## 解析 slot
41+
1. 对于 <slot> 标签,会为其元素描述对象添加 el.slotName 属性,属性值为该标签 name 属性的值,并且 name 属性可以是绑定的。
42+
2. 对于 <template> 标签,会优先获取并使用该标签 scope 属性的值,如果获取不到则会获取 slot-scope 属性的值,并将获取到的值赋值给元素描述对象的 el.slotScope 属性,注意 scope 属性和 slot-scope 属性不能是绑定的。
43+
3. 对于其他标签,会尝试获取 slot-scope 属性的值,并将获取到的值赋值给元素描述对象的 el.slotScope 属性。
44+
4. 对于非 <slot> 标签,会尝试获取该标签的 slot 属性,并将获取到的值赋值给元素描述对象的 el.slotTarget 属性。如果一个标签使用了 slot 属性但却没有给定相应的值,则该标签元素描述对象的 el.slotTarget 属性值为字符串 '"default"'
45+
46+
## v-bind指令
47+
1. 任何绑定的属性,最终要么会被添加到元素描述对象的 el.attrs 数组中,要么就被添加到元素描述对象的 el.props 数组中。
48+
2. 对于使用了 .sync 修饰符的绑定属性,还会在元素描述对象的 el.events 对象中添加名字为 'update:${驼峰化的属性名}' 的事件

docs/vue笔记/03补充.md

-35
This file was deleted.

img/reactive.png

178 KB
Loading

main.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
function observe (obj) {
2+
if (obj.__ob__) {
3+
return obj
4+
} else {
5+
return new Proxy(obj, {
6+
get (target, key) {
7+
if (typeof target[key] === 'object' && !target.__ob__) {
8+
target[key] = observe(target[key])
9+
target.__ob__ = true
10+
}
11+
console.log(`get : ${target[key]}`)
12+
return target[key]
13+
},
14+
set (target, key, val) {
15+
console.log(`set ${key} to ${val}`)
16+
target[key] = val
17+
}
18+
})
19+
}
20+
}
21+
22+
const obj = { a: 1 }
23+
24+
const p = observe(obj)
25+
p.a
26+
p.a = 3

0 commit comments

Comments
 (0)