从0到1手写vue源码01(模版引擎)

模版引擎

数组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
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值