简单实现迷你Vue框架

发布时间:2019-06-30 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了简单实现迷你Vue框架脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

本教程说明将采用es6语法来编写

创建MiniVue.js文件

//创建一个MVVM类
class MVVM {
  // 构造器
  constructor(option) {
    // 缓存重要属性
    this.$vm = this;
    this.$el = option.el;
    this.$data = option.data;
  }
}

MVVM类的作用: 解析视图模板,把对应的数据,渲染到视图

首先得判断视图是否存在,在视图存在的时候,创建模板编译器,来解析视图

class MVVM {
  // 构造器
  constructor(option) {
    // 缓存重要属性
    this.$vm = this;
    this.$el = option.el;
    this.$data = option.data;
    // 判断视图是否存在
    if (this.$el) {
      // 创建模板编译器, 来解析视图
      this.$compiler = new TemplateCompiler(this.$el, this.$vm)
    }
  }
}

下面我们来创建文件TemplateCompiler.js, 输入以下内容

// 创建一个模板编译工具
class TemplateCompiler {
  // el 视图
  // vm 全局vm对象
  constructor(el, vm) {
    // 缓存重要属性
    this.el = document.querySelector(el);
    this.vm = vm;
  }
} 

当缓存好重要的属性后,就要解析模板了

步骤分三步

  1. 把模板内容放进内存(内存片段)
  2. 解析模板
  3. 把内存的结果,放回到模板

    class TemplateCompiler {

    // el 视图
    // vm 全局vm对象
    constructor(el, vm) {
      // 缓存重要属性
      this.el = document.querySelector(el);
      this.vm = vm;
      
      // 1. 把模板内容放进内存(内存片段)
      let fragment = this.node2fragment(this.el);
      // 2. 解析模板
      this.COMpile(fragment);
      // 3. 把内存的结果,放回到模板
      this.el.apPEndChild(fragment);
    }

    }

上面定义node2fragment()方法和compile()方法下面我们来实现

分析TemplateCompiler 类有两大类方法:工具方法、核心方法


    class TemplateCompiler {
      // el 视图
      // vm 全局vm对象
      constructor(el, vm) {
        // 缓存重要属性
        this.el = document.querySelector(el);
        this.vm = vm;
    
        // 1. 把模板内容放进内存(内存片段)
        let fragment = this.node2fragment(this.el)
        // 2. 解析模板
        this.compile(fragment);
        // 3. 把内存的结果,放回到模板
        this.el.appendChild(fragment);
       }
      }

  // 工具方法
  isElementNode(node) {
    // 1. 元素节点  2. 属性节点  3. 文本节点
    return node.nodeType === 1;
  }
  isTextNode(node) {
    return node.nodeType === 3;
  }

  // 核心方法
  node2fragment(node) {
    // 1. 创建内存片段
    let fragment = document.createDocumentFragment();
    // 2. 把模板内容放进内存
    let child;
    while (child = node.FirstChild) {
      fragment.appendChild(child);
    }
    // 3. 返回
    return fragment;
  }
  compile(node) {
  }
}

关于createDocumentFragment的描述:

DocumentFragments 是DOM节点。它们不是主DOM树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。在DOM树中,文档片段被其所有的子元素所代替。

因为文档片段存在于内存中,并不在DOM树中,所以将子元素插入到文档片段时不会引起页面回流(对元素位置和几何上的计算)。因此,使用文档片段通常会带来更好的性能。

分析解析模板compile()方法实现方式

首先获取每一个子节点,然后遍历每一个节点,再判断节点类型
下面childNode是类数组没有数组方法

使用扩展运算符( sPRead )[...childNode]使其转化为数组便于操作

compile(parent) {
    // 1. 获取子节点
    let childNode = parent.childNodes;
    // 2. 遍历每一个节点
    [...childNode].foreach(node => {
      // 3. 判断节点类型
      if (this.isElementNode(node)) {
        // 解析元素节点的指令
        this.compileElement(node);
      }
    })
  }

下面来实现compileElement()方法 当前调用传过来的是元素节点

接下来就要获取元素的所有属性,然后遍历所有属性,再判断属性是否是指令

v-text是vue的指令,像ng-xxx指令是不进行收集的

判断为指令时就要收集结果

compileElement(node) {
    // 1. 获取当前节点的所有属性
    let attrs = node.attributes;
    // 2. 遍历当前元素的所有属性
    [...attrs].forEach(attr => {
      let attrName = attr.name
      // 3. 判断属性是否是指令
      if (this.isDirective(attrName)) {
        // 4. 收集
        let type = attrName.substr(2); // v-text
        // 指令的值就是表达式
        let expr = attr.value;
        // 解析指令
        CompilerUtils.text(node, this.vm, expr);
      }
    })
  }
@H_88_360@实现CompilerUtils类
CompilerUtils = {
  // 解析text指令
  text(node, vm, expr) {
    // 1. 找到更新方法
    let updaterFn = this.updater['textUpdater'];
    // 执行方法
    updaterFn && updaterFn(node, vm.$data[expr]);
  },
  // 更新规则对象
  updater: {
    // 文本更新方法
    textUpdater(node, value) {
      node.textContent = value;
    }
  }
}

在TemplateCompiler类工具方法下添加方法isDirective()判断属性是否是指令

isDirective(attrName) {
    // 判断属性是否是指令
    return attrName.indexOf('v-') >= 0;
  }

实现(v-text)完整代码

// 创建一个模板编译工具
class TemplateCompiler {
  // el 视图
  // vm 全局vm对象
  constructor(el, vm) {
    // 缓存重要属性
    this.el = document.querySelector(el);
    this.vm = vm;
    // 1. 把模板内容放进内存(内存片段)
    let fragment = this.node2fragment(this.el)
    // 2. 解析模板
    this.compile(fragment);
    // 3. 把内存的结果,放回到模板
    this.el.appendChild(fragment);
  }
  // 工具方法
  isElementNode(node) {
    // 1. 元素节点  2. 属性节点  3. 文本节点
    return node.nodeType === 1;
  }
  isTextNode(node) {
    return node.nodeType === 3;
  }
  isDirective(attrName) {
    // 判断属性是否是指令
    return attrName.indexOf('v-') >= 0;
  }
  // 核心方法
  node2fragment(node) {
    // 1. 创建内存片段
    let fragment = document.createDocumentFragment();
    // 2. 把模板内容放进内存
    let child;
    while (child = node.firstChild) {
      fragment.appendChild(child);
    }
    // 3. 返回
    return fragment;
  }
  compile(parent) {
    // 1. 获取子节点
    let childNode = parent.childNodes;
    // 2. 遍历每一个节点
    [...childNode].forEach(node => {
      // 3. 判断节点类型
      if (this.isElementNode(node)) {
        // 元素节点 (解析指令)
        this.compileElement(node);
      }
    })
  }
  // 解析元素节点的指令
  compileElement(node) {
    // 1. 获取当前节点的所有属性
    let attrs = node.attributes;
    // 2. 遍历当前元素的所有属性
    [...attrs].forEach(attr => {
      let attrName = attr.name
      // 3. 判断属性是否是指令
      if (this.isDirective(attrName)) {
        // 4. 收集
        let type = attrName.substr(2); // v-text
        // 指令的值就是表达式
        let expr = attr.value;
        CompilerUtils.text(node, this.vm, expr);
      }
    })
  }
  // 解析表达式
  compileText() {
  }
}
CompilerUtils = {
  // 解析text指令
  text(node, vm, expr) {
    // 1. 找到更新方法
    let updaterFn = this.updater['textUpdater'];
    // 执行方法
    updaterFn && updaterFn(node, vm.$data[expr]);
  },
  // 更新规则对象
  updater: {
    // 文本更新方法
    textUpdater(node, value) {
      node.textContent = value;
    }
  }
}

下面来实现(v-model)

修改如下代码

compileElement(node) {
  // 1. 获取当前节点的所有属性
  let attrs = node.attributes;
  // 2. 遍历当前元素的所有属性
  [...attrs].forEach(attr => {
    let attrName = attr.name
    // 3. 判断属性是否是指令
    if (this.isDirective(attrName)) {
      // 4. 收集
      let type = attrName.substr(2); // v-text
      // 指令的值就是表达式
      let expr = attr.value;
      //-----------------------修改代码start---------------------
      // CompilerUtils.text(node, this.vm, expr);
      CompilerUtils[type](node, this.vm, expr);
      //-----------------------修改代码end---------------------
    }
  })
}

在CompilerUtils类添加实现

CompilerUtils = {
  ...
  //----------------------新增方法---------------------
  // 解析model指令
  model(node, vm, expr) {
    // 1. 找到更新方法
    let updaterFn = this.updater['modelUpdater'];
    // 执行方法
    updaterFn && updaterFn(node, vm.$data[expr]);
  },
  // 更新规则对象
  updater: {
    ...
    //----------------------新增方法---------------------
    // 输入框更新方法
    modelUpdater(node, value) {
      node.value = value
    }
  }
}

实现(v-htML)就由读者自行添加对应的方法,形式和(v-model)差不多

下面来实现解析表达式 ,在compile添加以下代码

重要的怎么用验证表达式

compile(parent) {
    // 1. 获取子节点
    let childNode = parent.childNodes;
    // 2. 遍历每一个节点
    [...childNode].forEach(node => {
      // 3. 判断节点类型
      if (this.isElementNode(node)) {
        // 元素节点 (解析指令)
        this.compileElement(node);
      //-----------------新增代码--------------------
        // 文本节点
      } else if (this.isTextNode(node)) {
        // 表达式解析
        // 定义表达式正则验证规则
        let textReg = /{{(.+)}}/;
        let expr = node.textContent;
        // 按照规则验证内容
        if (textReg.test(expr)) {
          // 获取分组内容
          expr = RegExp.$1;
          // 调用方法编译
          this.compileText(node, expr);
        }
      }
    })
  }

下面来实现文本解析器,通过分析(v-text)和表达式解析差不多

// 解析表达式
  compileText(node, expr) {
    CompilerUtils.text(node, this.vm, expr);
  }

完整实现代码

// 创建一个模板编译工具
class TemplateCompiler {
  // el 视图
  // vm 全局vm对象
  constructor(el, vm) {
    // 缓存重要属性
    this.el = document.querySelector(el);
    this.vm = vm;
    // 1. 把模板内容放进内存(内存片段)
    let fragment = this.node2fragment(this.el)
    // 2. 解析模板
    this.compile(fragment);
    // 3. 把内存的结果,放回到模板
    this.el.appendChild(fragment);
  }
  // 工具方法
  isElementNode(node) {
    // 1. 元素节点  2. 属性节点  3. 文本节点
    return node.nodeType === 1;
  }
  isTextNode(node) {
    return node.nodeType === 3;
  }
  isDirective(attrName) {
    // 判断属性是否是指令
    return attrName.indexOf('v-') >= 0;
  }
  // 核心方法
  node2fragment(node) {
    // 1. 创建内存片段
    let fragment = document.createDocumentFragment();
    // 2. 把模板内容放进内存
    let child;
    while (child = node.firstChild) {
      fragment.appendChild(child);
    }
    // 3. 返回
    return fragment;
  }
  compile(parent) {
    // 1. 获取子节点
    let childNode = parent.childNodes;
    // 2. 遍历每一个节点
    [...childNode].forEach(node => {
      // 3. 判断节点类型
      if (this.isElementNode(node)) {
        // 元素节点 (解析指令)
        this.compileElement(node);
      } else if (this.isTextNode(node)) {
        // 表达式解析
        // 定义表达式正则验证规则
        let textReg = /{{(.+)}}/;
        let expr = node.textContent;
        // 按照规则验证内容
        if (textReg.test(expr)) {
          expr = RegExp.$1;
          // 调用方法编译
          this.compileText(node, expr);
        }
      }
    })
  }
  // 解析元素节点的指令
  compileElement(node) {
    // 1. 获取当前节点的所有属性
    let attrs = node.attributes;
    // 2. 遍历当前元素的所有属性
    [...attrs].forEach(attr => {
      let attrName = attr.name;
      // 3. 判断属性是否是指令
      if (this.isDirective(attrName)) {
        // 4. 收集
        let type = attrName.substr(2); // v-text
        // 指令的值就是表达式
        let expr = attr.value;
        // CompilerUtils.text(node, this.vm, expr);
        CompilerUtils[type](node, this.vm, expr);
      }
    })
  }
  // 解析表达式
  compileText(node, expr) {
    CompilerUtils.text(node, this.vm, expr);
  }
}
CompilerUtils = {
  // 解析text指令
  text(node, vm, expr) {
    // 1. 找到更新方法
    let updaterFn = this.updater['textUpdater'];
    // 执行方法
    updaterFn && updaterFn(node, vm.$data[expr]);
  },
  // 解析model指令
  model(node, vm, expr) {
    // 1. 找到更新方法
    let updaterFn = this.updater['modelUpdater'];
    // 执行方法
    updaterFn && updaterFn(node, vm.$data[expr]);
  },
  // 更新规则对象
  updater: {
    // 文本更新方法
    textUpdater(node, value) {
      node.textContent = value;
    },
    // 输入框更新方法
    modelUpdater(node, value) {
      node.value = value;
    }
  }
}

数据双向绑定(完结篇)

后续内容更精彩

简单实现迷你Vue框架

脚本宝典总结

以上是脚本宝典为你收集整理的简单实现迷你Vue框架全部内容,希望文章能够帮你解决简单实现迷你Vue框架所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。