模版引擎
数组join法(字符串)
es6反引号法(模版字符串换行)
mustache (小胡子)
引入mustache
模版引擎的使用
mustache.render(templatestr,data)
mustache.render
循环简单数组
循环复杂数组
循环单项数组
数组的嵌套
mustache底层核心机制
使用正则实现简单的替换机制(注意点replace替换的时候第二个参数可以为一个函数函数的形参,第一个参数代表查询的第一个正则机制,第二个为找到data中的第二个参数)
但是mustache不能简单使用正则来表示,他中间的话牵扯到的数据类型比较复杂
mustache的原理
什么是tokens
循环状态下的tokens
手写mustache库
安装依赖运行node解析js文件
npm init
npm i -D webpack@4 webpack-dev-server@3 --legacy-peer-deps
新建webpack.congfig.js文件
const path = require('path') // 引用path模块
module.exports = { // 这里是commrnt.js语法
// 使用开发模式打包
mode:"development",
// 入口文件
entry:"./src/index.js",
// 打包后的出口文件
output:{
filename:'build.js',
},
devServer:{
contentBase:path.join(__dirname,"www"),
compress:false,
port:8000,
publicPath:'xuni'
}
}
建立这样的文件格式
index.html文件代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
</head>
<body>
<script src="/xuni/build.js">
</script>
<script>
let temple= `我买了一个{{thing}},好{{good}}`
let data={
}
console.log(window. window.vueMustache);
window.vueMustache.render(temple,data)
</script>
</body>
</html>
我们需要手写算法了!!!!
这个文件是扫描器函数
这个文件是我们需要获取到当前的文件格式
扫描器函数的目的就是为了获取模版,并把它进行拆分
具体的拆分如下
我们把这个模版分为scanUntil和scan2个过程,scanUntil是模拟的是"{{"之前的把它拆分出去,scan这个函数是相当于跳过”{{“,利用scasnUntil这个函数对他进行运用
判断代码逻辑如下
// 扫描器函数
export default class Scanaer{
constructor(temple){
console.log("我是",temple);
//将子列写在自己身上
this.temple=temple
//设计一个指针
this.pio=0;
//尾巴,最开始尾巴是全文
this.tail=temple
}
//相当于是跳过“{{”这个字符没有返回值
scan(tag){
if(this.tail.indexOf(tag)==0){
this.pio+=tag.length;
//尾巴也要进行变化为从当前指针开始到最后的所有字符
this.tail=this.temple.substring(this.pio)
}
}
// 让指针扫描,遇见指定内容返回,并返回结束前的返回值
scanUtil(stopTop){
//记录当前位置指针的当前位置
let pio_backup=this.pio
// 当尾巴不等于0的时候,或者当我的指针小于当前字符串的长度的时候,如果不小于当前的长度的话就会一直往后走,导致死循环
while(this.eos()&&this.tail.indexOf(stopTop)!==0){
this.pio++
//让尾巴也跟着变化,获取到指针位置变化后的尾巴
this.tail=this.temple.substring(this.pio)
}
//返回指针开始的值到现在的值的所有文字
return this.temple.substring(pio_backup,this.pio)
}
//判断我的指针小于当前字符串的长度的时候,如果不小于当前的长度的话就会一直往后走,导致死循环 end 0f string
eos(){
return this.pio<this.temple.length
}
}
在index。js文件中的代码
import Scanaer from "./Scanner";
window.vueMustache={
render(temple,data){
//实例化一个扫描器,构造时提供参数,这个参数就是模版字符串
//这个参数就是给模版字符串工作的
let Scanaerl=new Scanaer(temple)
// let a= Scanaerl.scanUtil("{{")
// console.log(a,);
// console.log(Scanaerl.pio,"测试扫描器函数是否能到指定内容返回");
// Scanaerl.scan("{{")
// console.log(Scanaerl.pio,"测试扫描器函数是否可以跳过指定内容");
while(Scanaerl.pio!=temple.length){
let a= Scanaerl.scanUtil("{{")
console.log(a,"获取到的");
Scanaerl.scan("{{")
let b= Scanaerl.scanUtil("}}")
console.log(b,"获取到的");
Scanaerl.scan("}}")
}
}
}
生成tokens数组
创建这样的目录文件
import Scanaer from "./Scanner";
export default function createTokens(temple) {
let tokens = [];
let scanaer = new Scanaer(temple);
let word;
//让扫描器工作
while (scanaer.eos()) {
console.log("111");
word = scanaer.scanUtil("{{")
//运行到word到最后的时候会出现字符串的情况
if (word !== '') {
tokens.push(["text", word])
}
scanaer.scan("{{")
word = scanaer.scanUtil("}}")
//判断是否是#或者/的判断
if (word !== "") {
if (word[0] == "#") {
tokens.push(["#", word.substring[1]])
} else if (word[0] == "/") {
tokens.push(["/", word.substring[1]])
} else {
tokens.push(["text", word])
}
}
scanaer.scan("}}")
}
return tokens
}
index,js文件
import Scanaer from "./Scanner";
import createTokens from "./createTokens.js"
window.vueMustache={
render(temple,data){
let tokens=createTokens(temple)
console.log(tokens);
}
}
但是以上的方法不能满足于二维数组的使用
token二维数组源码书写
/*
功能是折叠tokens,将#和/之间的tokens整合起来,作为他下标为三的项
*/
export default function nestTokens(tokens) {
//结果数组
var nestedTokens = [];
//栈结构 栈顶的tokens数组中当前操作的小数组
var sections = [];
//收集器,天生指向nestedTokens结果数组,引用类型值,所以指向的同一个数组
//收集器的指向变化.当遇见#的时候,收集器会指向这个token的下标为2的新数组
var collector = nestedTokens;
for (let i = 0; i < tokens.length; i++) {
let token = tokens[i];
switch (token[0]) {
case '#':
//收集器中放入token
collector.push(token);
//入栈
sections.push(token);
//收集器换内容,给token添加下标为2的项,并且让收集器指向它
collector = token[2] = [];
break;
case '/':
//出栈.pop会返回更更弹出的项
sections.pop();
//改变收集器为栈顶那项的下标为2的数组
collector = sections.length > 0 ? sections[sections.length - 1][2] : nestedTokens;
break;
default:
collector.push(token);
}
}
return nestedTokens;
}
接下来就需要把token结合数据生成dom数据
接下来代码展示,大家自己看吧
lookup.js
/*
功能是可以在dataObj对象中,寻找用连续点符号的keyanme属性
识别 a,b,c
*/
export default function lookup(dataObj, keyName) {
//看看keyname中有没有点符号
if (keyName.indexOf('.') != -1 && keyName != '.') {
//如果有点符号,那么拆开
var keys = keyName.split('.');
//设置临时变量,用于周转,一层一层找下去
var temp = dataObj;
for (let i = 0; i < keys.length; i++) {
temp = temp[keys[i]]
}
return temp;
}
//如果这里没有点符号
return dataObj[keyName]
}
parseArray.js
import lookup from './lookup';
import renderTemplate from './renderTemplate';
/*
处理数组,结合renderTemplate实现递归
注意,这个函数收的参数是token!而不是tokens
token是最简单的['#',xxx,[]]
这个函数要递归调用renderTemplate函数
调用次数由data决定
*/
export default function parseArray(token, data) {
//得到 整体数据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
}