赞
踩
目录
数据data又怎们和tokens结合变为变为理想的dom然后上树的?
parseTemplateToTokens(将HTML变为tokens)
renderTemplate`(将tokens和data数据结合,返回处理好的dom然后上树)
dom
然后上树的?www
文件夹下的index为入口文件,全局封装对象Fel_TemplateEngine
(自定义)
内部暴露一个render函数作为入口,供页面调用接收两个参数,模板(templateStr
)和数据(data)
Scanner类,作为扫描器将模板编译为tokens(无嵌套格式,一个数组走到末尾)
nextTokens
类,编译好的tokens做处理,当做
建立parseTemplateToTokens
类,
parseTemplateToTokens
(将HTML变为tokens)理解:parseTemplateToTokens
将Scanner扫描器和nextTokens
(零星的tokens嵌套起来)
scanner在的作用是扫描模板字符串,遇到"{{" 、"}}"、"#"、"/"、"^"等符号进行拆分,并整合到一个数组中,但是结果是一个常规的数组,并未分层,和嵌套数组所应该有的嵌套结构不符,所以就有了nextTokens
()方法,将这个无嵌套的数组转为原本模板对应的嵌套结构
- import Scanner from './Scanner.js'
- import nextTokens from './nextTokens.js'
- /**
- * 将模板字符串变为数组
- */
- export default function parseTemplateToTokens(templateStr){
- var tokens = [];
- // 创建扫描器
- var scanner = new Scanner(templateStr);
- var words;
- //让扫描器工作
- while(!scanner.eos()){
- // 收集开始标记
- words = scanner.scanUtil("{{");
- // 保存
- if(words!=''){
- tokens.push(['text',words]);
- }
- // 过双大括号
- scanner.scan("{{")
- // 收集开始标记
- words = scanner.scanUtil("}}");
- // 保存
- if(words!=''){
- // 这时的words就是{{}}中间的东西,判断首字符
- if(words[0] == "#"){
- // 存起来,从下标为1的项开始存,因为下标为0的像是#
- tokens.push(["#",words.substring(1)]);
- }else if(words[0] == '/'){
- // 存起来,从下标为1的项开始存,因为下标为0的项是/
- tokens.push(["/",words.substring(1)]);
- }else{
- // 存起来
- tokens.push(['name',words]);
- }
- }
- // 过双大括号
- scanner.scan("}}")
- }
- // 返回折叠收集的tokens
- console.log(tokens);
- return nextTokens(tokens);
- }
单独建立Scanner.js
文件,作为扫描器类
内部三个函数
constructor构造函数接收一个模板(templateStr
)
scan扫描方法
scanUtil
方法
大致流程
以此模板为例
我买了一个{{thing}},好{{mod}}阿
左侧起始位置一个指针,指针往右移动从"我"到"个"的这个过程为
scanUtil ,然后以scan扫描"{{",指针做过"thing"的过程为
scanUtil
....直至末尾
相关知识点
1、定义和用法 substring() 方法用于提取字符串中介于两个指定下标之间的字符。 substring() 方法返回的子串包括 开始 处的字符,但不包括 结束 处的字符。 2、语法 string.substring(from, to) 3、参数 from 必需。一个非负的整数,规定要提取的子串的第一个字符在 string Object 中的位置。 to 可选。一个非负的整数,比要提取的子串的最后一个字符在 string Object 中的位置多 1。 如果省略该参数,那么返回的子串会一直到字符串的结尾。
1、定义和用法 indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。 如果没有找到匹配的字符串则返回 -1。 注意: indexOf() 方法区分大小写。 提示: 同样你可以查看类似方法 lastIndexOf() 。 2、语法 string.indexOf(searchvalue,start) 3、参数 searchvalue 必需。规定需检索的字符串值。 start 可选的整数参数。规定在字符串中开始检索的位置。它的合法取值是 0 到 string Object.length - 1。如省略该参数,则将从字符串的首字符开始检索。 4、返回值 Number类型 查找指定字符串第一次出现的位置,如果没找到匹配的字符串则返回 -1。
Scanner.js
代码
- /**
- * 扫描类
- */
- 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有多长,比如{{长度是2,就让指针后移多少位
- this.pos += tag.length;
- // 尾巴也改变.改变尾巴位当前指针这个字符开始,到最后的全部字符
- this.tail = this.templateStr.substring(this.pos)
- }
- }
- // 让指针进行扫描,直到指定内容结束,并且能够返回结束之前路过的文字
- scanUtil(stopTag){
- // 记录一下执行本方法的时候的pos的值
- const pos_backup = this.pos;
- // 当尾巴的开头不是stopTag的时候,说明还没有扫描到stopTag
- // 写&&很必要,因为防止找不到,那么寻找到最后也要停下来
- while( !this.eos() && this.tail.indexOf(stopTag) !=0 ){
- this.pos++
- // 改变尾巴为从当前指针这个字符开始,到最后的全部字符
- this.tail = this.templateStr.substring(this.pos);
- }
- return this.templateStr.substring(pos_backup,this.pos);
- }
- // 指针是否到头,返回布尔值
- eos(){
- return this.pos >= this.templateStr.length;
- }
- }
nextTokens
(处理tokens为嵌套结构)利用栈的先入后出的思想,结合对应的储存从而实现结构的更改,比较精妙的还有收集器的创建,一直指向最终要返回的数组,类似于一种引用,看似一直操作的是这个收集器,其实也相当于对要返回的数组进行添加,通过收集器指向的改变控制返回数组的结构。
- /**
- * 函数的功能是折叠tokens,将#和/之间的tokens能够整合起来,最为他的下标为3的项
- */
- export default function nextTokens(tokens){
- // 结果数组
- var nextedTokens = [];
- // 栈结构,存放小的tokens,栈顶(靠近端口的,最新进入的)的tokens数组中当前的操作的这个tokens小数组
- var sections = [];
- // 收集器,天生指向nextedTokens结果数组,引用类型值,所以指向的是同一个数组
- // 收集器的指向会变化,当遇到#的时候,收集器会指向这个下标为2的新数组
- var collector = nextedTokens;
- 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的项,并且让收集器指向他
- token[2]=[];
- collector=token[2];
- break;
- case '/':
- // 出栈,pop()会返回刚刚弹出的项
- sections.pop();
- // 改变收集器为栈结构队尾(队尾为栈顶) 那项的下标为2的数组
- collector = sections.length>0?sections[sections.length-1][2]:nextedTokens;
- break;
- default:
- collector.push(token);
- }
- }
- return nextedTokens;
- }
`(将tokens和data
数据结合,返回处理好的dom
然后上树)内部通过对tokens的循环,判断当前token的第一个值如果text则直接加在需要返回的数据后面,如果是name则说明要添加data数据,不过在添加之前需要用lookup函数处理,防止识别错误造成渲染失败,遇到#号则说明需要循环数组遍历添加数据,结合parseArray
函数来回嵌套形成递归调用从而将处理数组及其嵌套的数据渲染
- import lookup from "./lookup.js";
- import parseArray from "./parseArray.js";
- /**
- * 函数的功能是让tokens数组变为dom字符串
- */
- export default function renderTemplate(tokens,data){
- console.log(tokens,data);
- // 结果字符串
- var resultStr = '';
- // 遍历tokens
- for(let i=0;i<tokens.length;i++){
- let token = tokens[i];
-
- // 看类型
- if(token[0] == 'text'){
- resultStr +=token[1];
- }else if(token[0] == 'name'){
- // 如果是name类型,直接使用他的值,使用lookup
- // 因为防止出现'a.b.c'的形式造成的无法识别问题
- resultStr += lookup(data, token[1]);
- }else if(token[0] == '#'){
- console.log(token);
- resultStr += parseArray(token,data)
- }
- }
- return resultStr;
- }
keyName
属性)- /**
- * 功能是可以在dataObj对象中,寻找用连续点符号的keyName属性
- * 比如dataObj是
- * {
- * a:{
- * b:{
- * c:100
- * }
- * }
- * }
- * 那么lookup(dataObj,'a.b.c')结果是abc
- */
- export default function lookup(dataObj, 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
(循环数组渲染dom
)- import lookup from "./lookup.js"
- import renderTemplate from "./renderTemplate.js"
- /**
- * 处理数组,结合renderTemplate实现递归
- * 注意函数接收的参数是token不是tokens
- *
- * 这个函数要递归调用renderTemplate函数,调用次数由data界定
- */
- export default function parseArray(token,data){
- // 得到整体数组中这个数据要使用的部分
- console.log(token,data);
- var v= lookup(data,token[1]);
- console.log(v);
- // 结果字符串
- var resultStr = '';
- // 遍历v数组,v可能为数组也可能为布尔值,此处布尔值忽略
- // 这个循环可能是这个包最那思考的一个循环
- // 他是遍历数据而不是遍历tokens
- for(let i=0; i< v.length; i++) {
- resultStr += renderTemplate(token[2],{
- // 现在这个数据小对象是v[i]的展开,就是v[i]本身
- ...v[i],
- '.':v[i]
- })
- }
- console.log(resultStr);
- return resultStr
- }
mustache
中的模板引擎的功能主要是将HTML模板代码和需要渲染的数据相结合,然后是返回模板和数据结合后的DOM,模板引擎在工作中可以分为两大块,一个是将事先准备好的HTML解析为tokens数组的形式,方便后面和数据之间的结合(parseTemplateToTokens
),另一个是就是将data和tokens结合返回真实的dom了(renderTemplate
)。
在parseTemplateToTokens
中它又可以分为两小块,一个是扫描器(scanner.js,Scanner类)一个是nextTokens
方法,将初步得到的tokens处理为嵌套的,和原DOM结构嵌套而是一样的形式
在renderTemplate
中也有又两小块,一个是lookup判断处理防止渲染出错,另一个是parseArray
方法循环数组渲染dom
,利用栈的思想将tokens读取完毕即结束,返回最终dom
尽管是跟着视频把代码敲了出来但是还需要进一步的研究,其中的一些奥妙还是需要继续理解一番,对一些函数的处理理解的并不是很透彻,总结不是很到位,包括这个记录还是又不少问题,比如自身的理解太少、比如内容相对有点拖泥带水感觉,再接再励了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。