vue源码学习记录
对vue的源码一直只看过原理的解析以及部分代码,没有真正的手写实现,感觉理解不够深入,因此手写了部分重要原理,参考尚硅谷vue2源码解析视频课程。
1、mustache模板解析
let str = `<div>
<ol>
{
{#students}}
<li>
学生{
{name}}的爱好是
<ol>{
{#hobbies}}
<li>{
{.}}</li>
{
{/hobbies}}
</ol>
<li>
{
{/students}}
</ol>
</div>`
let data = {
students:[
{
name:'zz1',hobbies:['zz1']},
{
name:'zz2',hobbies:['zz2','zzzz2']},
{
name:'zz3',hobbies:['zz3','zzzz3']},
]
}
目的是将str字符串解析成对应的html,使之能正常展示。
首先将str解析成token数组,以变量为分隔,上述字符串将被解析成13个数组如下图所示
生成的内容是没被折叠的,可以知道,3-11的数据应该在students的内部,因此,对生成的tokens进行处理,处理成有包含关系的tokens,如下图所示
接下来就是将生成的tokens解析成我们熟悉的html,包括变量的读取以及循环等指令的处理,具体实现如下
//index.js
import parseTemplateToToken from './parseTemplateToToken'
import renderTemplate from './renderTemplate'
window.TemplateEngine = {
render(templateStr,data){
const tokens = parseTemplateToToken(templateStr)
console.log(tokens)
const template = renderTemplate(tokens,data)
console.log(template)
return template
}
}
//parseTemplateToToken.js
import Scanner from './scanner'
import foldTokens from './foldTokens'
export default function parseTemplateToToken(templateStr) {
const scanner = new Scanner(templateStr)
let tokens = []
var word
while (!scanner.eos()) {
word = scanner.scanUtil('{
{')
if (word) {
tokens.push(['text',word])
}
scanner.scan('{
{')
word = scanner.scanUtil('}}')
if (word) {
if (word[0] === '#') {
tokens.push(['#',word.substring(1)])
} else if (word[0] === '/') {
tokens.push(['/',word.substring(1)])
} else {
tokens.push(['name',word])
}
}
scanner.scan('}}')
}
console.log(JSON.stringify(tokens))
return foldTokens(tokens)
}
//Scanner类
class Scanner {
constructor(templateStr){
this.templateStr = templateStr
this.tail = templateStr;
this.pos = 0
}
//跳过需要查找的字符
scan(tag){
if(this.tail.indexOf(tag)===0){
this.pos += tag.length
this.tail = this.templateStr.substring(this.pos)
}
}
//找到需要查找的字符位置
scanUtil(tag){
const pos_backUp = this.pos
while(this.tail.indexOf(tag)!==0&&!this.eos()){
this.pos++
this.tail = this.templateStr.substring(this.pos)
}
return this.templateStr.substring(pos_backUp,this.pos)
}
eos(){
return this.pos>this.templateStr.length-1
}
}
export default Scanner
//foldTokens.js,将解析出的tokens折叠
export default function foldTokens(tokens) {
let result = []
let collector = result
let sections = []
for (let i = 0; i < tokens.length; i++) {
let token = tokens[i]
switch (token[0]) {
case '#':
collector.push(token)
sections.push(token)
collector = token[2] = []
break;
case '/':
sections.pop()
collector = sections.length ? sections[sections.length - 1][2] : result
break;
default:
collector.push(token)
}
}
return result
}
//renderTemplate.js,将折叠好的token生成html
import lookup from './lookup'
export default function renderTemplate(tokens,data) {
let result = ''
for(let i=0;i<tokens.length;i++){
if(tokens[i][0] === 'text'){
result +=tokens[i][1]
}else if(tokens[i][0] === 'name'){
result +=lookup(data,tokens[i][1])
}else{
result +=parseArray(data,tokens[i])
}
}
return result
}
function parseArray(data,tokens){
let value = l