vue源码-模板解析

313 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情

前言

学习框架,仅仅会使用是不够的,想要更熟悉框架,需要对源码有一定的了解。本篇文章通过vue源码解析,实现vue的模板解析。

介绍

我们先来看看vue的模板语法

Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统

<div id="app">
  {{ message }}
</div>
var app = new Vue({
  el: '#app',
  data: {
    message: 'HelloWorld!'
  }
})

通过模板字符串的形式,将data中的数据,渲染到DOM节点中

接下来,我们就来实现一下模板解析!

实现

首先将vue的结构搭建好,通过引入vue.js的形式实现,模板字符串的解析

<!DOCTYPE html>

<head>
  <title></title>
</head>

<body>
  <div id="app">
    {{message}}
  </div>

  <script src="vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        message: 'helloWorld'
      }
    })
  </script>
</body>

</html>

image-20220603140736326

接下来,我们通过自己写vue.js实现模板字符串解析

  • 首先,我们需要了解几点:

    • this.$data:是vue中的 data

    • this.$el:是DOM结构中的根节点

​ 让我们打印看看

image-20220603141336676

image-20220603141344516

  • 分析vue结构

    • vue是一个构造函数,并且接收一个参数,参数为一个对象
    • 我们可以通过对象来获取对象内部的值
  • 实现vue.js

    • 定义一个Vue类

    • 构造函数中接收一个对象options,可以通过options获取对象

      vue.js

      class Vue {
        constructor(options) {
          console.log(options)
        }
      }
      

      image-20220603142145711

    • 获取elel和data。通过对象.el节点,获取到el节点;通过对象.data获取到data;

      vue.js

      class Vue {
        constructor(options) {
          this.$el = document.querySelector(options.el)
          this.$data = options.data
          console.log(this.$el, this.$data);
        }
      }
      

      image-20220603142609396

    • 实现模板解析。

      • 定义一个模板解析方法Parser
      • 在构造函数中,调用Parser,并传入this.$el
      • 在Parser方法中,通过传入的父节点,可以找到所有的子节点

      vue.js

      class Vue {
        constructor(options) {
          this.$el = document.querySelector(options.el)
          this.$data = options.data
          this.Parser(this.$el)
        }
        Parser(node) {
          console.log(node.childNodes)
        }
      }
      

      当前子节点只有一个,我们多加几个查看效果

        <div id="app">
          {{message}}
          <h1>{{message}</h1>
          <h2>{{message}</h2>
          <div>{{message}}</div>
        </div>
      

      image-20220603143250498

      ​ text代表文本,h1、h2、div代表标签,我们只需要将文本替换即可

      • 通过循环,获取所有节点

        node.childNodes.forEach(item => console.log(item))
        

        image-20220603143638072

      • 通过nodeType,查看节点对应的数字类型

        node.childNodes.forEach(item => console.log(item.nodeType))
        

        image-20220603143750709

        可以看出,文本节点对应的是3,标签对应的是1

      • 根据对应的数字类型,处理对应数据

        • 元素节点。通过递归,调用Parser继续解析

        • 文本节点。通过正则匹配,替换{{}}括号内的数据,并去除空格

                // 文本
                if (item.nodeType == 1) {
                  this.Parser(item)
                }
                // 标签
                if (item.nodeType == 3) {
                  let reg = /\{\{(.*?)\}\}/g
                  let text = item.textContent
                  item.textContent = text.replace(reg, value => this.$data[value.trim()])
                }
          

完整代码

index.html

<!DOCTYPE html>

<head>
  <title></title>
</head>

<body>
  <div id="app">
    {{message}}
    <h1>{{name}}</h1>
    <h2>{{age}}</h2>
  </div>

  <script src="vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        message: 'helloWorld',
        name: '张三',
        age: '20'
      },
      mounted() {
        console.log(this.$data, this.$el)
      }
    })
  </script>
</body>

</html>

vue.js

class Vue {
  constructor(options) {
    this.$el = document.querySelector(options.el)
    this.$data = options.data
    this.Parser(this.$el)
  }
  // 解析模板函数
  Parser(node) {
    node.childNodes.forEach(item => {
      // 文本
      if (item.nodeType == 1) {
        this.Parser(item)
      }
      // 标签
      if (item.nodeType == 3) {
        let reg = /\{\{(.*?)\}\}/g
        let text = item.textContent
        item.textContent = text.replace(reg, (match,value) => this.$data[value.trim()])
      }
    })
  }
}

image-20220603223902837

补充

正则规则

image-20220603222859942

总结

通过修改DOM节点,然后进行判断是否为标签,再通过正则替换{{}}里面的内容,从而实现模板字符串替换。