一步一步的实现Vue(一)

391 阅读2分钟

前言

“ 这次想要自己学着实现简单版的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

欢迎关注我的公众号:“进击的前端巨人”