「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。
环境配置
使用webpack和webpack-dev-server构建
使用webpack的原因:能方便的在浏览器(而不是nodejs环境)中实时调试程序,相比nodejs黑底白字控制台,浏览器控制台更好用。
创建"身份证"文件
npm init
安装webpack,webpack-cli,webpack-dev-server
我装的是最新版本的webpack,webpack-cli,webpack-dev-server,这里其实我给自己挖了个坑(扶额)
书写配置文件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
????我懵了,为什么???然后,我想到了一个情况,那就是,我的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,
}
}
总结:
webpack5的版本需要将静态文件属性名改为static,同时,publicPath虚拟打包路径需要放和filename同级,共同包裹在output对象中。
然后!!!运行!!
完美!打开网址
手写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也要改变,改变尾巴为从当前指针这个字符开始到最后的字符
定义方法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;
}
}
手写将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;
}
如果有一维数组需要循环
那我们除了"{{"和"}}"需要判断,还需要多判断一个"#"和"/",即遍历的起始和结束符号。 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嵌套起来
到目前为止,代码有所推进,但是!当数组嵌套两层及以上的时候,打印出来的东西就不那么直观和美观了。
接下来,就是利用某些结构来美观的嵌套数组。