什么是模板解析?
如下面一段代码,{{name}}是如何工作的呢?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
{{name}}
</div>
</body>
</html>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.9/vue.min.js"></script>
<script>
const vm = new Vue({
el:'#app',
data:{
name: 'jack'
}
})
</script>
思路很简单:
1、获取#app元素下的所有节点,放入文档碎片处理
2、用正则表达式将文本节点的{{name}}匹配出来
3、用vm.data里面的name替换掉大括号信息
4、将替换完成的文档碎片塞回去
function Vue(options){
this.$options = options
this._data = this.$options.data
this.$compile = new Compile(options.el,this) //模板解析
}
function Compile(el,vm){
this.$vm = vm
this.$el = document.querySelector(el)
console.log(this.$el)
if(this.$el){
this.$fragment = this.node2Fragment(this.$el) //将节点转到fragment处理
this.init() //开始处理
this.$el.appendChild(this.$fragment) //塞回原位
}
}
Compile.prototype = {
//将#app里面的节点都转到文档碎片中
node2Fragment(el){
var fragment = document.createDocumentFragment()
var child = null
while(child = el.firstChild) {
fragment.appendChild(child)
}
return fragment
},
//处理碎片中的信息
init(){
this.compileElement(this.$fragment)
},
//正则匹配
compileElement(el){
var childNodes = el.childNodes;
[].slice.call(childNodes).forEach(node=>{
var text = node.textContent // 获取文本信息
var reg = /\{\{(.*)\}\}/
if(node.nodeType === 3 && reg.test(text)) {
this.compileText(node,RegExp.$1)
}
})
},
//用vm.data信息,替换大括号的name
compileText(node,exp){
node.textContent = this.$vm._data[exp]
}
}
嵌套对象如何处理?
这个例子比较简单,那如果是嵌套的对象呢?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
{{a.b.c}}
</div>
</body>
</html>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.9/vue.min.js"></script>
<script>
const vm = new Vue({
el:'#app',
data:{
a: {
b:{
c:'mary'
}
}
}
})
</script>
其实也很容易处理:
function Vue(options){
this.$options = options
this._data = this.$options.data
this.$compile = new Compile(options.el,this) //模板解析
}
function Compile(el,vm){
this.$vm = vm
this.$el = document.querySelector(el)
console.log(this.$el)
if(this.$el){
this.$fragment = this.node2Fragment(this.$el) //将节点转到fragment处理
this.init() //开始处理
this.$el.appendChild(this.$fragment) //塞回原位
}
}
Compile.prototype = {
//将#app里面的节点都转到文档碎片中
node2Fragment(el){
var fragment = document.createDocumentFragment()
var child = null
while(child = el.firstChild) {
fragment.appendChild(child)
}
return fragment
},
//处理碎片中的信息
init(){
this.compileElement(this.$fragment)
},
//正则匹配
compileElement(el){
var childNodes = el.childNodes;
[].slice.call(childNodes).forEach(node=>{
var text = node.textContent // 获取文本信息
var reg = /\{\{(.*)\}\}/
if(node.nodeType === 3 && reg.test(text)) {
this.compileText(node,RegExp.$1)
}
})
},
//用vm.data信息,替换大括号的name
compileText(node,exp){
node.textContent = this.getVMVal(exp)
},
//处理层级问题
getVMVal(exp){ // a.b.c
var val = this.$vm._data
var arr = exp.split('.') //["a", "b", "c"]
console.log(exp)
arr.forEach(k=>{
//debugger
val = val[k] // 层级递进
})
return val
}
}
子节点如何处理?
再延伸一下,如果是有子节点的呢?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<p>名字:<b>{{a.b.c}}</b></p>
</div>
</body>
</html>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.9/vue.min.js"></script>
<script>
const vm = new Vue({
el:'#app',
data:{
a: {
b:{
c:'tom'
}
}
}
})
</script>
我们可以用递归来处理:
function Vue(options){
this.$options = options
this._data = this.$options.data
this.$compile = new Compile(options.el,this) //模板解析
}
function Compile(el,vm){
this.$vm = vm
this.$el = document.querySelector(el)
console.log(this.$el)
if(this.$el){
this.$fragment = this.node2Fragment(this.$el) //将节点转到fragment处理
this.init() //开始处理
this.$el.appendChild(this.$fragment) //塞回原位
}
}
Compile.prototype = {
//将#app里面的节点都转到文档碎片中
node2Fragment(el){
var fragment = document.createDocumentFragment()
var child = null
while(child = el.firstChild) {
fragment.appendChild(child)
}
return fragment
},
//处理碎片中的信息
init(){
this.compileElement(this.$fragment)
},
//正则匹配
compileElement(el){
var childNodes = el.childNodes;
[].slice.call(childNodes).forEach(node=>{
var text = node.textContent // 获取文本信息
var reg = /\{\{(.*)\}\}/
if(node.nodeType === 3 && reg.test(text)) {
this.compileText(node,RegExp.$1)
} else if(node.childNodes && node.childNodes.length) {
//递归
this.compileElement(node)
}
})
},
//用vm.data信息,替换大括号的name
compileText(node,exp){
node.textContent = this.getVMVal(exp)
},
//处理层级问题
getVMVal(exp){ // a.b.c
var val = this.$vm._data
var arr = exp.split('.') //["a", "b", "c"]
console.log(exp)
arr.forEach(k=>{
//debugger
val = val[k] // 层级递进
})
return val
}
}
网友评论