手写模板引擎

模板引擎

阅读vue和mustache源码后,自己实现一个简易版的模板引擎

实现思路

1、获取页面模板字符串

let template = document.getElementById(id).innerHTML

2、解析成树结构tokens数组,通过栈管理
手写模板引擎_第1张图片
3、 将数据替换上去然后渲染到页面上

// 渲染
function resuleStr(Tokens, data) {
	let resuleStr = ''
	Tokens.forEach((item, index) => {
		if (item[0] == 'text') {
			resuleStr += item[1]
		} else if (item[0] == 'name') {
			resuleStr += lookup(data, item[1])
		} else if (item[0] == '#') {
			resuleStr += arrRender(item[2], data[item[1]])
		}
	})
	return resuleStr
}
// 循环数组处理
function arrRender(template, arrData) {
	let str = ''
	arrData.forEach((item) => {
		str += resuleStr(template, item)
	})
	return str
}

代码

<!DO
CTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>title>
	head>
	<script src="./templateEngine.js">script>
	<body>
		<div id="container">
			<div>
				 <ul>
					{{#students}}
						<li class="myli">
							学生{{name}}的爱好是
							<ol>
								{{#hobbies}}
									<li>{{.}}li>
								{{/hobbies}}
							ol>
							<h1>{{a.b.c}}h1>
						li>
					{{/students}}
				ul>
			div>
		div>
		<script>
			// 数据
			let data = {
				students: [{
						'name': '小明',
						'hobbies': ['编程', '游泳'],
						a: {
							b: {
								c: '多层级测试1'
							}
						}
					},
					{
						'name': '小红',
						'hobbies': ['看书', '弹琴', '画画'],
						a: {
							b: {
								c: '多层级测试2'
							}
						}
					},
					{
						'name': '小强',
						'hobbies': ['锻炼'],
						a: {
							b: {
								c: '多层级测试3'
							}
						}
					}
				]
			};
			render('container', data)
		script>
	body>
html>

templateEngine文件

class scanner {
	constructor(template) {
		this.index = 0 //下标
		this.tail = template // 尾部
		this.template = template
	}
	sacnText(tag) {
		var startIndex = this.index; // 记录开始下标
		while (this.tail.indexOf(tag) !== 0 && this.tail) {
			this.index++
			this.tail = this.template.substr(this.index)
		}
		var endText = this.template.substring(startIndex, this.index)
		return endText
	}
	sacnTag(tag) {
		this.index += tag.length
		this.tail = this.tail.substring(tag.length)
		return this.tail.substr(0, tag.length)
	}
}
// 获取tokens
function getTokens(template) {
	var token = new scanner(template)
	var tokenArr = []
	var word
	while (token.index < template.length) {
		word = token.sacnText("{{");
		if (word) {
			switch (word[0]) {
				case '#':
					tokenArr.push(["#", word.substr(1)])
					break;
				case '/':
					tokenArr.push(["/", word.substr(1)])
					break;
				default:
					// 智能判断是普通文字的空格,还是标签中的空格
					// 标签中的空格不能去掉,比如
不能去掉class前面的空格 let isInJJH = false; // 空白字符串 var _words = ''; for (let i = 0; i < word.length; i++) { // 判断是否在标签里 if (word[i] == '<') { isInJJH = true; } else if (word[i] == '>') { isInJJH = false; } // 如果这项不是空格,拼接上 if (!/\s/.test(word[i])) { _words += word[i]; } else { // 如果这项是空格,只有当它在标签内的时候,才拼接上 if (isInJJH) { _words += ' '; } } } tokenArr.push(["text", _words]) break; } } token.sacnTag("{{") word = token.sacnText("}}"); if (word) { switch (word[0]) { case '#': tokenArr.push(["#", word.substr(1)]) break; case '/': tokenArr.push(["/", word.substr(1)]) break; default: tokenArr.push(["name", word]) break; } } token.sacnTag("}}") } return handleTokens(tokenArr) } // 处理循环tokens function handleTokens(tokens) { var resultArr = []; var loopArr = [] var temArr = resultArr; tokens.forEach((item, index) => { switch (item[0]) { case '#': temArr.push(item) loopArr.push(item) temArr = item[2] = [] break; case "/": loopArr.pop(); if (loopArr.length > 0) { temArr = loopArr[loopArr.length - 1][2] } else { temArr = resultArr } break; default: temArr.push(item) break } }) return resultArr } // 渲染 function resuleStr(Tokens, data) { let resuleStr = '' Tokens.forEach((item, index) => { if (item[0] == 'text') { resuleStr += item[1] } else if (item[0] == 'name') { resuleStr += lookup(data, item[1]) } else if (item[0] == '#') { resuleStr += arrRender(item[2], data[item[1]]) } }) return resuleStr } // 循环数组处理 function arrRender(template, arrData) { let str = '' arrData.forEach((item) => { str += resuleStr(template, item) }) return str } // 解析a.b.c这种 function lookup(data, key) { if (key.indexOf('.') !== -1 && key !== '.') { return eval('data.' + key) } else if (key === '.') { return data } else { return data[key] } } function render(id,data){ debugger let template = document.getElementById(id).innerHTML let Tokens = getTokens(template) let innerHTMLStr = resuleStr(Tokens, data) document.getElementById(id).innerHTML = innerHTMLStr }

效果如图

你可能感兴趣的:(源码解析实现,js,vue,源码解析,模板引擎)