vue.js源码分析之第三次提交(a5e27b1)***

这次提交我们能看到vue.js的雏形开始慢慢凸显,开始有了directive,filter和scope的概念

项目结构

指令文件directives.js

定义了sd-text,sd-show,sd-class,sd-on四个指令

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
33
34
35
36
37
38
39
40
module.exports = {
text: function (el, value) {
el.textContent = value || ''
},
show: function (el, value) {
el.style.display = value ? '' : 'none'
},
class: function (el, value, classname) {
el.classList[value ? 'add' : 'remove'](classname)
},
on: {
update: function (el, handler, event, directive) {
if (!directive.handlers) {
directive.handlers = {}
}
var handlers = directive.handlers
if (handlers[event]) {
el.removeEventListener(event, handlers[event])
}
if (handler) {
handler = handler.bind(el)
el.addEventListener(event, handler)
handlers[event] = handler
}
},
unbind: function (el, event, directive) {
if (directive.handlers) {
el.removeEventListener(event, directive.handlers[event])
}
},
customFilter: function (handler, selectors) {
return function (e) {
var match = selectors.every(function (selector) {
return e.target.webkitMatchesSelector(selector)
})
if (match) handler.apply(this, arguments)
}
}
}
}

核心文件main.js

整个文件的核心思想

  • 利用selector(sd前缀加上directives里面的定义的方法名,例如:sd-text)来获取带有这类属性的所有dom节点
  • 在parseDirective中,将dom属性解析成directive,并根据key,存储在seed中的bindings里
  • 在bindDirective中,将seed中的scope与directive进行绑定(根据directive的key判断如果已经绑定了,就不在绑定了),改变scope的属性的值,自动触发对应的所有directive的更新,从而更新视图
  • 用构造函数中传进来的opts.scope来更新scope,从而达到数据更新视图的目的

源码如下

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
var prefix = 'sd',
Filters = require('./filters'),
Directives = require('./directives'),
selector = Object.keys(Directives).map(function (d) {
return '[' + prefix + '-' + d + ']'
}).join()
//构造函数
function Seed (opts) {
var self = this,
root = this.el = document.getElementById(opts.id),
els = root.querySelectorAll(selector),
bindings = {} // internal real data
self.scope = {} // external interface
// 将dom节点解析成directive
;[].forEach.call(els, processNode)
processNode(root)
// 初始化变量,改变视图
for (var key in bindings) {
self.scope[key] = opts.scope[key]
}
function processNode (el) {
cloneAttributes(el.attributes).forEach(function (attr) {
//解析directive
var directive = parseDirective(attr)
if (directive) {
将self.scope与解析出来的directive进行绑定
bindDirective(self, el, bindings, directive)
}
})
}
}
// clone attributes so they don't change
function cloneAttributes (attributes) {
return [].map.call(attributes, function (attr) {
return {
name: attr.name,
value: attr.value
}
})
}
function bindDirective (seed, el, bindings, directive) {
el.removeAttribute(directive.attr.name)
var key = directive.key,
binding = bindings[key]
if (!binding) {
bindings[key] = binding = {
value: undefined,
directives: []
}
}
directive.el = el
binding.directives.push(directive)
// 钩子函数,目前没什么用
if (directive.bind) {
directive.bind(el, binding.value)
}
//防止重复绑定
if (!seed.scope.hasOwnProperty(key)) {
bindAccessors(seed, key, binding)
}
}
function bindAccessors (seed, key, binding) {
Object.defineProperty(seed.scope, key, {
get: function () {
return binding.value
},
//数据改变触发改变视图
set: function (value) {
binding.value = value
binding.directives.forEach(function (directive) {
if (value && directive.filters) {
value = applyFilters(value, directive)
}
directive.update(
directive.el,
value,
directive.argument,
directive,
seed
)
})
}
})
}
function parseDirective (attr) {
if (attr.name.indexOf(prefix) === -1) return
// parse directive name and argument
var noprefix = attr.name.slice(prefix.length + 1),
argIndex = noprefix.indexOf('-'),
dirname = argIndex === -1
? noprefix
: noprefix.slice(0, argIndex),
def = Directives[dirname],
arg = argIndex === -1
? null
: noprefix.slice(argIndex + 1)
// parse scope variable key and pipe filters
var exp = attr.value,
pipeIndex = exp.indexOf('|'),
key = pipeIndex === -1
? exp.trim()
: exp.slice(0, pipeIndex).trim(),
filters = pipeIndex === -1
? null
: exp.slice(pipeIndex + 1).split('|').map(function (filter) {
return filter.trim()
})
return def
? {
attr: attr,
key: key,
filters: filters,
definition: def,
argument: arg,
update: typeof def === 'function'
? def
: def.update
}
: null
}
function applyFilters (value, directive) {
if (directive.definition.customFilter) {
return directive.definition.customFilter(value, directive.filters)
} else {
directive.filters.forEach(function (filter) {
if (Filters[filter]) {
value = Filters[filter](value)
}
})
return value
}
}
module.exports = {
create: function (opts) {
return new Seed(opts)
},
filters: Filters,
directives: Directives
}

运行测试

1
grunt

测试页面dev.html

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
<!DOCTYPE html>
<html>
<head>
<title>title</title>
<meta charset="utf-8">
<script src="dist/seed.js"></script>
</head>
<body>
<div id="test" sd-on-click="changeMessage | .button">
<p sd-text="msg | capitalize"></p>
<p sd-show="something">YOYOYO</p>
<p class="button" sd-text="msg"></p>
<p sd-class-red="error" sd-text="hello"></p>
</div>
<script>
var Seed = require('seed')
var app = Seed.create({
id: 'test',
// template
scope: {
msg: 'hello',
hello: 'WHWHWHW',
changeMessage: function () {
app.scope.msg = 'hola'
}
}
})
</script>
</body>
</html>

用浏览器打开dev.html,页面如下

Hello

Hello

WHWHWHW
佘伟春 wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!

热评文章