Skip to content

Commit d20cfd7

Browse files
committedJul 21, 2023
生成ast语法树
1 parent cf99ca4 commit d20cfd7

File tree

7 files changed

+220
-9
lines changed

7 files changed

+220
-9
lines changed
 

‎README.md

+2
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@
33
## 总结
44

55
- object.defineProperty 缺点 只能对对象的某个属性进行劫持
6+
- 重写数组 7 种方法
7+
- vue 初次渲染 -> 先初始化数据 -> 将模板进行编译 -> 变成 render() -> 生成虚拟节点 -> 变成真实 DOM -> 放到页面

‎package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"dev": "rollup -cw"
99
},
1010
"keywords": [],
11-
"author": "",
11+
"author": "coder-czy",
1212
"license": "ISC",
1313
"devDependencies": {
1414
"@babel/core": "^7.20.12",

‎src/compile/index.js

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
2+
// ast语法树 vnode
3+
//<div id="app">hello {{name}}</div>
4+
5+
/**
6+
* {
7+
* tag:'div',
8+
* attrs:[{id:'app'}],
9+
* children:[
10+
* {tag:null,text:"hello"}
11+
* ]
12+
* }
13+
*
14+
*/
15+
// 标签名称
16+
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`
17+
//<span:xx>
18+
const qnameCapture = `((?:${ncname}\\:)?${ncname})`
19+
// 标签开头正则,捕获的内容是标签名
20+
const startTagOpen = new RegExp(`^<${qnameCapture}`)
21+
// 匹配标签结尾的</div>
22+
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)
23+
//<div id="app"></div> 属性
24+
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
25+
// 匹配标签结束的 >
26+
const startTagClose = /^\s*(\/?)>/
27+
var defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g
28+
29+
// 开始标签
30+
function start (tag, attrs) {
31+
console.log(tag, attrs)
32+
}
33+
// 获取文本
34+
function charts (text) {
35+
console.log(text)
36+
}
37+
function end (tag) {
38+
console.log(tag)
39+
}
40+
41+
// 遍历
42+
function parseHTML (html) {
43+
//<div id="app">hello {{name}}</div>
44+
while (html) {
45+
// 判断标签<>
46+
let textEnd = html.indexOf('<')
47+
if (textEnd === 0) {
48+
// 开始标签
49+
const startTagMatch = parseStartTag(html)
50+
if (startTagMatch) {
51+
52+
start(startTagMatch.tagName, startTagMatch.attrs)
53+
continue
54+
}
55+
56+
// 结束标签
57+
let endTagMatch = html.match(endTag)
58+
if (endTagMatch) {
59+
advance(endTagMatch[0].length)
60+
end(endTagMatch[1])
61+
continue
62+
}
63+
}
64+
// 文本
65+
let text
66+
if (textEnd > 0) {
67+
text = html.substring(textEnd)
68+
// console.log(text)
69+
}
70+
if (text) {
71+
advance(text.length)
72+
charts(text)
73+
}
74+
}
75+
function parseStartTag () {
76+
// debugger
77+
const start = html.match(startTagOpen)
78+
if (start) {
79+
80+
// console.log(start)
81+
//创建ast语法树
82+
let match = {
83+
tagName: start[1],
84+
attrs: []
85+
}
86+
// 刪除开始标签
87+
advance(start[0].length)
88+
//属性
89+
//注意多个遍历
90+
//注意 >
91+
let attr
92+
let end
93+
while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {
94+
// console.log(attr)
95+
match.attrs.push({ name: attr[1], value: attr[3] || attr[4] || attr[5] })
96+
advance(attr[0].length)
97+
98+
}
99+
if (end) {
100+
// console.log(end)
101+
advance(end[0].length)
102+
return match
103+
}
104+
}
105+
106+
}
107+
function advance (n) {
108+
html = html.substring(n)
109+
// console.log(html)
110+
}
111+
}
112+
113+
export function compileToFunction (el) {
114+
let ast = parseHTML(el)
115+
}

‎src/init.js

+43-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,52 @@
11
import { initState } from "./state"
2+
import { compileToFunction } from "./compile/index"
23

34
export function initMixin (Vue) {//给vue添加init方法
45
Vue.prototype._init = function (options) {//用于初始化操作
56
const vm = this
67
vm.$options = options
78

89
initState(vm)
10+
11+
// 渲染模板 el
12+
if (vm.$options.el) {
13+
vm.$mount(vm.$options.el)
14+
}
15+
16+
917
}
10-
}
18+
19+
Vue.prototype.$mount = function (el) {
20+
// el template render
21+
let vm = this
22+
// 获取元素
23+
el = document.querySelector(el)
24+
let options = vm.$options
25+
if (!options.render) {
26+
let template = options.template
27+
if (!template && el) {
28+
// 获取HTML
29+
el = el.outerHTML
30+
//<div id="app">hello {{name}}</div>
31+
32+
// 变成ast语法树
33+
let ast = compileToFunction(el)
34+
}
35+
}
36+
37+
}
38+
}
39+
40+
// ast语法树 vnode
41+
//<div id="app">hello {{name}}</div>
42+
43+
/**
44+
* {
45+
* tag:'div',
46+
* attrs:[{id:'app'}],
47+
* children:[
48+
* {tag:null,text:"hello"}
49+
* ]
50+
* }
51+
*
52+
*/

‎src/observe/arr.js

+25-3
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,43 @@ let oldArrayProtoMethods = Array.prototype
66
// 2、继承
77
export let ArrayMethods = Object.create(oldArrayProtoMethods)
88

9-
// 劫持
9+
// 劫持方法 7种
1010
let methods = [
1111
'push',
1212
'pop',
1313
'unshift',
1414
'shift',
15-
'splice'
15+
'splice',
16+
'sort',
17+
'reverse'
1618
]
1719

1820
methods.forEach(item => {
21+
// debugger
1922
ArrayMethods[item] = function (...args) {
2023
console.log('劫持数组')
2124
let result = oldArrayProtoMethods[item].apply(this, args)
22-
return result
2325

26+
// 数组添加数据的情况
27+
let inserted
28+
switch (item) {
29+
case 'push':
30+
case 'unshift':
31+
inserted = args
32+
break
33+
case 'splice':
34+
inserted = args.splice(2)
35+
break
36+
default:
37+
break
38+
}
39+
40+
let ob = this.__ob__
41+
if (inserted) {
42+
ob.observeArray(inserted)
43+
}
44+
45+
return result
2446
}
2547

2648
})

‎src/observe/index.js

+18-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { ArrayMethods } from './arr'
2-
3-
export function observer (data) {
2+
export function observer (data,) {
43
// 判断
54
if (typeof data != 'object' || data == null) {
65
return data
@@ -12,12 +11,22 @@ export function observer (data) {
1211

1312
class Observer {
1413
constructor(data) {
14+
// 给data定义一个属性 __ob__,用于数组新增对象数据的处理
15+
Object.defineProperty(data, '__ob__', {
16+
enumerable: false,
17+
value: this
18+
})
19+
1520
//判断数组
1621
if (Array.isArray(data)) {
1722
data.__proto__ = ArrayMethods
23+
24+
// 数组对象
25+
this.observeArray(data) //处理数组对象
1826
}
1927
this.walk(data)
2028
}
29+
2130
walk (data) {
2231
let keys = Object.keys(data)
2332
for (let i = 0; i < keys.length; i++) {
@@ -27,6 +36,12 @@ class Observer {
2736
defineReactive(data, key, value)
2837
}
2938
}
39+
40+
observeArray (data) {
41+
for (let i = 0; i < data.length; i++) {
42+
observer(data[i])
43+
}
44+
}
3045
}
3146

3247
// 对象中的属性进行劫持
@@ -47,7 +62,7 @@ function defineReactive (data, key, value) {
4762
})
4863
}
4964

50-
// 数组劫持 [1,1] [{a:1}]
65+
// 数组劫持 两种数据格式[1,1] [{a:1}]
5166
// 函数劫持 重写数组方法
5267
// 'push',
5368
// 'pop',

‎src/state.js

+16-1
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,25 @@ export function initState (vm) {
1010

1111
function initData (vm) {
1212
let data = vm.$options.data
13-
data = vm._data = typeof data === 'function' ? data.call(vm) : data //修改this指向
13+
data = vm._data = typeof data === 'function' ? data.call(vm) : data //call修改this指向
1414
// debugger
15+
// 将data的所有属性代理到实例上去
16+
for (const key in data) {
17+
proxy(vm, '_data', key)
18+
}
1519

1620
// 对data数据进行劫持
1721
observer(data)
1822

23+
}
24+
25+
function proxy (vm, source, key) {
26+
Object.defineProperty(vm, key, {
27+
get: () => {
28+
return vm[source][key]
29+
},
30+
set: (newValue) => {
31+
vm[source][key] = newValue
32+
}
33+
})
1934
}

0 commit comments

Comments
 (0)