vue源码之mustache模板引擎 02

328 阅读4分钟

「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。

环境配置

使用webpack和webpack-dev-server构建

使用webpack的原因:能方便的在浏览器(而不是nodejs环境)中实时调试程序,相比nodejs黑底白字控制台,浏览器控制台更好用。

创建"身份证"文件

npm init

image.png

安装webpack,webpack-cli,webpack-dev-server

我装的是最新版本的webpack,webpack-cli,webpack-dev-server,这里其实我给自己挖了个坑(扶额)

image.png

image.png

书写配置文件webpack.config.js

我照着教程非常自信的写了配置文件,如下:

const path = require("path");
module.exports = {
    mode: "development",
    entry: "./src/index.js",
    output: {
        filename: "bundle.js",
    },
    // 配置Dev-server
    devServer: {
        contentBash: path.join(__dirname, "www"),
        compress: false,
        port: 8080,
        publicPath: "/xuni/",
    }
}

在说我遇到的问题之前,先说一下每句配置是什么意思:
mode:表示开发模式;
entry:表示入口文件;
filename:虚拟打包文件;
contentBash:表示静态文件根目录;
compress:表示不压缩;
port:表示端口号;
publicPath:表示虚拟打包路径

书写入口文件 index.js

自定义即可。

书写静态文件 index.html

这里注意,一定要引入你的js文件,但!不是index.js,而是你自己定义的出口(虚拟打包)文件。
原因:这里用虚拟打包的文件可以实现修改入口文件index.js保存的同时,浏览器自动更新。

<h1>我是index.html</h1>
<script src="/xuni/bundle.js"></script>

运行

运行之前,记得在package.json中改一下调试命名

"scripts": {
    "dev": "webpack-dev-server"
  }

改完之后,自信运行:

npm run dev

image.png

????我懵了,为什么???然后,我想到了一个情况,那就是,我的webpack版本太新了!!!而教程里面老师安装的webpack是4的,所以这个是不是很有可能造成配置版本的更新?我乖乖去查了错误之后,发现果然!webpack5的配置和webpack4有了不同!
下面是webpack5的配置文件:

const path = require("path");

module.exports = {
    mode: "development",
    entry: "./src/index.js",
    output: {
        publicPath: "/xuni/",
        filename: "bundle.js",
    },
    // 配置Dev-server
    devServer: {
        static: path.join(__dirname, "www"),
        compress: false,
        port: 8080,

    }
}

image.png 总结:
webpack5的版本需要将静态文件属性名改为static,同时,publicPath虚拟打包路径需要放和filename同级,共同包裹在output对象中。
然后!!!运行!!

image.png

完美!打开网址

image.png

手写scanner扫描类

作用:将模板字符串通过"{{"和"}}"字符作为分割符分成多部分。(将变量分离出来)

首先,一定要引入我们需要的模板字符串和数据。

index.html:

<script src="/xuni/bundle.js"></script>
<script>
    var templateStr = "今天星期{{day}},天气{{weath}}";
    var data = {
        day: "天",
        weath: "暴雪",
    }
    templateEngine.render(templateStr, data);
</script>

在index.js中引入我们的scanner类

index.js:

import Scanner from "./scanner"

window.templateEngine = {
    render(templateStr, data) {
        var scanner = new Scanner(templateStr);
    },
}

这里是实例化一个扫描器,构造时提供只需提供一个模板字符串的参数,说明扫描器只针对模板字符串工作(只是拆分字符串)。

在scanner.js中书写类

scanner.js:

定义类scanner:

// 扫描类 Scanner
export default class Scanner {
    constructor(templateStr) {
        this.templateStr = templateStr;
        // 指针 
        this.pos = 0;
        // 尾巴 尾巴一开始是模板字符串 
        this.tail = templateStr;
    }
}

定义方法scannerUtil():

作用:让指针进行扫描,直到遇到指定内容就结束,并且返回结束之前扫描过的所有文字

scanUtil(stopFlag) {
        // 记录执行方法开始时的pos值
        var begin = this.pos;
        // 如果一开始的匹配结果不是stopFlag,就一直继续扫描 
        while (this.pos < this.templetaStr.length && this.tail.indexOf(stopFlag) !== 0) {
            this.pos++;
            this.tail = this.templateStr.substring(this.pos);
        }

        // 返回之前的所有内容 substring(begin,end)
        var result = this.templateStr.substring(begin, this.pos);
        return result;
    }

定义方法scan():

作用:功能仅仅是略过指定内容

 scan(flag) {
        if (this.tail.indexOf(flag) == 0) {
            this.pos += flag.length;
            this.tail = this.templateStr.substring(this.pos);
        }
    }

这里要注意:一定要记住tail也要改变,改变尾巴为从当前指针这个字符开始到最后的字符 image.png

定义方法eos()判断是否需要结束扫描:

全称end of string 判断是否需要结束

eos() {
        return this.pos >= this.templateStr.length;
    }

在index.js中引用方法:

while (!scanner.eos()) {
            var word = scanner.scanUtil("{{");
            console.log(word);
            scanner.scan("{{");

            var word1 = scanner.scanUtil("}}");
            console.log(word1);
            scanner.scan("}}");
        }

完整代码

index.js:

import Scanner from "./scanner"

window.templateEngine = {
    render(templateStr, data) {
        var scanner = new Scanner(templateStr);

        while (!scanner.eos()) {
            var word = scanner.scanUtil("{{");
            console.log(word);
            scanner.scan("{{");

            var word1 = scanner.scanUtil("}}");
            console.log(word1);
            scanner.scan("}}");
        }

    },
}

scanner.js:

export default class Scanner {
    constructor(templateStr) {
        this.templateStr = templateStr;
        this.pos = 0;
        this.tail = templateStr;
    }

    scan(flag) {
        if (this.tail.indexOf(flag) == 0) {
            this.pos += flag.length;
            this.tail = this.templateStr.substring(this.pos);
        }
    }

    scanUtil(stopFlag) {
        var begin = this.pos;
        while (!this.eos() && this.tail.indexOf(stopFlag) !== 0) {
            this.pos++;
            this.tail = this.templateStr.substring(this.pos);
        }

        var result = this.templateStr.substring(begin, this.pos);
        return result;
    }

   
    eos() {
        return this.pos >= this.templateStr.length;
    }
}

image.png

手写将HTML变为tokens

其实思路很简单,就是将之前分好的字符串转换成数组集合,也就是在一个空数组里面添加子数组的步骤而已。

如果没有需要循环的情况

templateToTokens.js:

import Scanner from "./scanner"

export default function templateToTokens(templateStr) {
    var tokens = [];
    var scanner = new Scanner(templateStr);
    while (!scanner.eos()) {
        // 收集开始标记出现之前的字符
        var word = scanner.scanUtil("{{");
        // 存储到数组
        if (word !== "") {
            tokens.push(["text", word]);
        }
        // 跳过双括号
        scanner.scan("{{");
        // 收集结束标记出现之前的字符
        var word1 = scanner.scanUtil("}}");
        tokens.push(["text", word1]);
        scanner.scan("}}");
    }
    return tokens;
}

image.png

如果有一维数组需要循环

那我们除了"{{"和"}}"需要判断,还需要多判断一个"#"和"/",即遍历的起始和结束符号。 templateToTokens.js:

var word1 = scanner.scanUtil("}}");
        if (word1 !== "") {
            // 这里的Word已经是{{}}里面的,所以只需要判断一下word[0]
            if (word1[0] == "#") {
                tokens.push(["#", word1.substring(1)]);
            } else if (word1[0] == "/") {
                tokens.push(["/", word1.substring(1)]);
            } else {
                tokens.push(["name", word1]);
            }
        }
        scanner.scan("}}");

index.js:

import templateToTokens from "./templateToTokens"

window.templateEngine = {
    render(templateStr, data) {
        var tokens = new templateToTokens(templateStr);
        console.log(tokens);
    },
}

手写将tokens嵌套起来

到目前为止,代码有所推进,但是!当数组嵌套两层及以上的时候,打印出来的东西就不那么直观和美观了。 image.png

接下来,就是利用某些结构来美观的嵌套数组。

上一节: Vue源码之mustache模板引擎 01