深度学习mustache
Posted 嘴巴嘟嘟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深度学习mustache相关的知识,希望对你有一定的参考价值。
来自视频教学课程笔记
什么是模板引擎
就是将模板文件和数据通过模板引擎生成一个html代码。
模板引擎是将数据变为视图最优雅的解决方案
var list = [
name: "张三"
,
name: "王五"
]
var uu= document.getElementById("uu")
for(var i=0;i<list.length;i++)
let liList = document.createElement('li')
liList.className = 'll'
liList.innerText = list[i].name
uu.appendChild(liList)
// 数组
var list = [
name: "张三",
age: 12
,
name: "王五",
age: 23
]
var uu = document.getElementById("uu")
for (let i = 0; i < list.length; i++)
var str = [
' <li>',
' <div class="hd"></div>',
' <div class="bd">',
' <p>姓名'+list[i].name+'</p>',
' <p>年龄'+list[i].age+'</p>',
' </div>',
' </li>'
].join('');
uu.innerHTML += str
// es6
var list = [
name: "张三",
age: 12
,
name: "王五",
age: 23
]
var uu = document.getElementById("uu")
for (let i = 0; i < list.length; i++)
uu.innerHTML += `
<li>
<div class="hd"></div>
<div class="bd">
<p>姓名$list[i].name</p>
<p>年龄$list[i].age</p>
</div>
</li
`
str
开始使用mustache
<ul>
# users
<li><img src=" gravatar "> login </li>
/ users
</ul>
var list = [
name: "张三",
age: 12
,
name: "王五",
age: 23
]
// 循环遍历数组
var templateSter = `
<ul>
#list
<li>
<div class="hd"></div>
<div class="bd">
<p>姓名name</p>
<p>年龄age</p>
</div>
</li>
/list
</ul>
`
var mustacheStr = Mustache.render(templateSter, list)
document.getElementById("uu").innerHTML = mustacheStr
var templateStr2 = `
<h3>things</h3>
`
var obj =
things: '我买东西了',
mood: "开心"
document.getElementById("uu").innerHTML = Mustache.render(templateStr2, obj)
Mustache 核心原理
- 不能用简单正则处理
- mustache 的底层 token 思想
mustache 模板引擎的作用是将字符串模板变为 dom 模板,最后结合数据挂载到 dom 树上,在页面渲染呈现。这个过程中,mustache 引入了一个名为 tokens 的概念.
我们可以通过修改 mustache 源码的方式直接在浏览器控制台打印输出 tokens:
在源码中找到 parseTemplate 函数,然后在该函数的函数体末尾处,
return 的 nestTokens(squashTokens(tokens)) 其实就是 tokens
const myTokens = nestTokens(squashTokens(tokens))
console.log(myTokens)
return myTokens
手写mustache
实现将模板字符串编译为 tokens
1、准备阶段
项目文件结构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="../src/index.js"></script>
<h1>我是index.html</h1>
</body>
</html>
webpack.config.js
const path = require('path');
module.exports =
// 模式开发
mode: 'development',
// 入口
entry: './src/index.js',
// 打包文件
output:
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
,
// 配置webpack-dev-server
devServer:
// 静态个目录
contentBase: path.join(__dirname, 'www'),
// 不压缩
compress: false,
port: 8080,
// 虚拟打包的路径
publicPath: "/xuni/"
;
package.json
"name": "y",
"version": "1.0.0",
"description": "",
"module": "true",
"main": "index.js",
"scripts":
"test": "echo \\"Error: no test specified\\" && exit 1",
"dev": "webpack-dev-server"
,
"author": "",
"license": "ISC",
"devDependencies":
"webpack": "^4.46.0",
"webpack-dev-server": "^3.11.3"
,
"dependencies":
"webpack-cli": "^3.3.12"
实现 Scanner 类
Scanner 类的实例就是一个扫描器,用来扫描构造时作为参数提供的那个模板字符串。
- 属性
pos:指针,用于记录当前扫描到字符串的位置
tail:尾巴,值为当前指针之后的字符串(包括指针当前指向的那个字符)
- 方法
scan:无返回值,让指针跳过传入的结束标识 stopTag
scanUntil:传入一个指定内容 stopTag 作为让指针 pos 结束扫描的标识,并返回扫描内容
/**
* 扫描类
*/
export default class Scanner
constructor(templateStr)
console.log('构造器', templateStr)
this.templateStr = templateStr
this.pos = 0
// 尾巴 一开始就是模本原文
this.tail = templateStr
// 功能弱,就是路过内容,没有返回值
scan(tag)
if (this.tail.indexOf(tag) === 0)
// tag有多长,就让指针后移多少位a
this.pos += tag.length
this.tail = this.templateStr.substring(this.pos)
// 让指针扫描,直到遇见指定内容结束,并且能够给返回结束之前路过的文字
scanUtile(stopTag)
// 开始的时候不是stopTag的时候,到最后的全部字符
const post_backup = this.pos
while (this.tail.indexOf(stopTag) != 0 && this.eos())
// 指针移动
this.pos++;
// 改变尾巴为从当前指针从这个字符开始,直到最后一个字符
this.tail = this.templateStr.substring(this.pos)
// 返回扫描的字符串,不包括this.pos
return this.templateStr.substring(post_backup, this.pos)
// 指针到头
eos()
// 返回一个布尔值
return this.pos >= this.templateStr.length
根据模板字符串生成 tokens
有了 Scanner 类后,就可以着手去根据传入的模板字符串生成一个 tokens 数组了。最终想要生成的 tokens 里的每一条 token 数组的第一项用 name(数据) 或 text(非数据文本) 或 #(循环开始) 或 /(循环结束) 作为标识符。
新建一个 parseTemplateToTokens.js 文件来实现
// parseTemplateToTokens.js
import Scanner from './Scanner.js'
import nestTokens from './nestTokens' // 后面会解释
// 函数 parseTemplateToTokens
export default templateStr =>
const tokens = []
const scanner = new Scanner(templateStr)
let word
while (!scanner.eos())
word = scanner.scanUntil('')
word && tokens.push(['text', word]) // 保证 word 有值再往 tokens 里添加
scanner.scan('')
word = scanner.scanUntil('')
/**
* 判断从 和 之间收集到的 word 的开头是不是特殊字符 # 或 /,
* 如果是则这个 token 的第一个元素相应的为 # 或 /, 否则为 name
*/
word && (word[0] === '#' ? tokens.push(['#', word.substr(1)]) :
word[0] === '/' ? tokens.push(['/', word]) : tokens.push(['name', word]))
scanner.scan('')
return nestTokens(tokens) // 返回折叠后的 tokens, 详见下文
** index.js 引入 parseTemplateToTokens**
// index.js
import parseTemplateToTokens from './parseTemplateToTokens.js'
window.My_TemplateEngine =
render(templateStr, data)
const tokens = parseTemplateToTokens(templateStr)
console.log(tokens)
在index.html中使用
const templateStr = `
<ul>
#arr
<li>
<div>name的基本信息</div>
<div>
<p>name</p>
<p>age</p>
<div>
<p>爱好:</p>
<ol>
#hobbies
<li>.</li>
/hobbies
</ol>
</div>
</div>
</li>
/arr
</ul>
`
实现 tokens 的嵌套
新建 nestTokens.js 文件,定义 nestTokens 函数来做 tokens 的嵌套功能,将传入的 tokens 处理成包含嵌套的 nestTokens 数组返回。
然后在 parseTemplateToTokens.js 引入 nestTokens,在最后 return nestTokens(tokens)。
/**
*
* @param * tokens
*/
export default function nestTokens(tokens)
// 结果数组
var nestTokens = []
// 栈结构 栈顶 (靠近端口,最新进入) 的token数组中当前操作的这个tokens小数组
var sections = []
// 收集器 天生指向nestedTokens结果数组,引用类性质,所以指向的是同一个数组
// 收集器指向会变化 当遇到#时候,收集器会指向token为2的新数组
var collector = nestTokens
for (let i = 0; i < tokens.length; i++)
let token = tokens[i]
switch (token[0])
case '#':
// 收集器放入token
collector.push(token)
// 压栈
sections.push(token)
// 收集器换人
collector = token[2] = []
break
case '/':
// 出栈 pop()返回刚刚弹出的项
sections.pop()
// 改变收集器为栈结构队尾,那项下标为2的数组
collector = sections.length > 0 ? sections[sections.length - 1][2] : nestTokens
break;
default:
collector.push(token)
break
return nestTokens
遍历 tokens 数组,根据每条 token 的第一项的值来做不同的处理,为 text 就直接把 token[1] 加入到最终输出的 dom 字符串,为 name 则根据 token[1] 去 data 里获取数据,结合进来。
当 data 里存在多层嵌套的数据结构,比如 data = test: a: b: 10 ,这时如果某个 token 为 [“name”, “test.a.b”],即代表数据的 token 的第 2 项元素是 test.a.b 这样的有多个点符号的值,那么我么直接通过 data[test.a.b] 是无法拿到正确的值的,因为 js 不认识这种写法。我们需要提前准备一个 lookup 函数,用以正确获取数据。
定义 lookup 函数
/**
* 功能是可以在dataObj对象中,寻找用连续点符号的keyName属性
* 需要师表a.b.c
*/
export default function lookup(dataObj, keyName)
// 首先查看keyName 中有没有点符号 但是不能是本身
if (keyName.indexOf('.') !== -1 && keyName !== '.')
var keys = keyName.splice('.')
// 设置临时变量周转一层一层找下去
let temp = dataObj
for (let i = 0; i < keys.length; i++)
temp = temp[keys[i]]
return temp
return dataObj[keyName]
定义 renderTemplate 函数
接下来就可以开始写个 renderTemplate 函数将 tokens 和 data 作为参数传入,解析为 dom 字符串了。
import lookup from './lookup'
import parseArray from './parseArray'
/**
*
*/
export default function renderTemplate(tokens, data)
for (let i = 0; i < tokens.length; i++)
let token = tokens[i]
let resultStr = ''
//看类型
if (token[0] === 'text')
resultStr = token[i]
else if (token[0] === 'name')
// 防止“a.b.c”
resultStr += lookup(token[1])
else if (token[0] === '#')
resultStr += parseArray(token, data)
return resultStr
当某个 token 的第一项为 “#” 时,要再次递归调用 renderTemplate 函数。这里我们新定义了一个 parseArray 函数来处理。
import lookup from "./lookup"
import renderTemplate from "./renderTemplate"
/**
* 处理数组 结合renderTemplate实现递归
* 这个是函数接收的token
*/
export default function parseArray(token, data)
// 得到整体数据中这个数据的使用部分
var v = lookup(data, token[1])
var resultStr = ''
// 遍历数组v
// 注意遍历数据而不是tokens
for (let i = 0; i < v.length; i++)
resultStr += renderTemplate(token[2],
...v[i],
'.': v[i]
)
return resultStr
以上是关于深度学习mustache的主要内容,如果未能解决你的问题,请参考以下文章