前言
“ 这次想要自己学着实现简单版的vue,从而对vue有更加深入的理解。”
注意,这不是教程!而是我自己动手实践的记录,肯定有很多不完善的地方,若发现错误,欢迎指出错误~
推荐一个好用的vscode插件,可以本地快速启动服务器,并运行html文件,且拥有热加载功能:"live server"。
01. 看看真正的Vue
首先,我们都知道,当使用 new Vue 的时候,就可以将 html 中的模板{{}}用 data 里面的数据替换,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../node_modules/vue/dist/vue.js"></script>
</head>
<body>
<article id="root">
<section>{{name}}</section>
<section>{{message}}</section>
</article>
<script>
const app = new Vue({
el: '#root',
data: {
name: 'romeo',
message: 'wants to be rich',
}
})
</script>
</body>
</html>
此时,vue
会找到el
这个节点,(如果有template
属性,则会将此字符串模板编译为render
函数),然后生成抽象语法树(AST),然后用data
替换对应的数据,创建虚拟DOM,生成真正的DOM,并最终挂载到文档中。
既然我们要一步一步来,那么先只看最基本的:拿到节点,进行编译,将节点与数据结合,并替换节点。
02. 数据与模板结合
这里我们分为四个步骤来实现。
1. 获取节点
const rootElement = document.querySelector('#root');
const generateRoot = rootElement.cloneNode(true); // 避免对节点本身直接修改
2. 声明数据
const data = {
name: 'romeo',
message: 'wants to be rich',
}
3. 将数据与模板结合
这里的模板就是克隆的节点。需要深度遍历这颗树,方法有递归或者用栈保存遍历的节点,这里采用递归实现。
function compiler(template, data) {
const childNodes = template.childNodes;
for (const childNode of childNodes) {
const type = childNode.nodeType;
switch(type) {
case 1: // 元素节点,递归
compiler(childNode, data);
break;
case 3: // 文本节点, 判断是否有mustache{{}}
let txt = childNode.nodeValue;
txt = txt.replace(regMustache, function(_, g) {
// 此函数的第0个参数为匹配到的内容,例如 {{name}}
// 第n个参数为第n组内容,例如 name
return data[g.trim()];
})
childNode.nodeValue = txt;
break;
default: break;
}
}
}
compiler(generateRoot, data); // 调用函数
4. 替换节点
rootElement.parentElement.replaceChild(generateRoot, rootElement);
03、效果
加载script前:
加载script后:详细的代码请看我的 github
欢迎关注我的公众号:“进击的前端巨人”