手写Vue (1) 编译文本

1. 当我们初始化数据后 就可以进行将数据进行挂载 

 首先先判断 配置是否有 el  如果有 el  再进行挂载

 if (vm.$options.el) {
    vm.$mount();
  }

2.  $mount  这个方法中 我们首先要获取到   获取当前挂载的节点

let vm = this;
  let el = vm.$options.el
  el = vm.$el = query(el)  // 获取当前挂载的节点

3.  获取当前挂载的节点 有两种情况 

el: '#app',ru
el: document.getElementById('app;);

所以我们要判断el 是否是一个字符串 如果是字符串 的话  通过  document.querySelector 获取元素 否则直接返回

function query(el) {
  if (typeof el === 'string') {
    return document.querySelector(el);
  }
  return el
}

4. 添加   渲染watchder

observe/watch.js   传入一系列参数 

let id = 0;
class Watcher {
  // watch 有唯一标识
  constructor(vm, exprOrfn, cb = () => { }, opts = {}) {
    // exprOrfn 表达式  cb 回调
    this.vm = vm;
    this.exprOrfn = exprOrfn;
    if (typeof exprOrfn == 'function') {
      // getter 就是  Watcher 传入的第2个函数
      this.getter = exprOrfn;
    }
    this.cb = cb;
    this.opts = opts;
    this.id = id++;
    this.get()
  }
  get() {
  }
}
export default Watcher;

5. 在index.js  引入  watch.js 

import Watcher from './observe/watch'
Vue.prototype.$mount = function () {
  let vm = this;
  let el = vm.$options.el
  el = vm.$el = query(el)  // 获取当前挂载的节点
  let updateComponent = () => {
    vm.$update()
    // console.log('数据更新')
  }
  // 渲染watchder
  new Watcher(vm, updateComponent)
}

6.  updateComponent  是一个函数 里面有一个   $update 方法 

在这个方法中主要做的操作 是 创建一个 文档碎片 循环的将 el 上挂载的节点依次 遍历 复制到文档碎片里面 

Vue.prototype.$update = function () {
  let vm = this;
  let el = vm.$el
  console.log(el)// 拿到节点
  // 需要匹配{{}}
  let node = document.createDocumentFragment();
  let firstChild;
  while (firstChild = el.firstChild) {
    node.appendChild(firstChild)
  }
  // console.log(node)  // 获取到文档碎片
  el.appendChild(node)
}
let node = document.createDocumentFragment();  创建文档碎片
循环添加到文档碎片里面
while (firstChild = el.firstChild) {
    node.appendChild(firstChild)
 }

7. 测试

8. 获取到 node 节点后 我们需要进行 编译 

compiler(node, vm)

9.编译 的时候要判断 node  节点的类型 

function compiler(node, vm) {
  let childNodes = node.childNodes;
  ([...childNodes]).forEach(child => {
    // 1  元素  3  文本
    if (child.nodeType == 1) {
      // console.log('2020')
      compiler(child, vm)  //编译孩子节点
    } else if (child.nodeType == 3) {
      // console.log('111')
      util.compilerText(child, vm)
    }
  });
}

如果是元素 编译 里面 的孩子节点 

compiler(child, vm)  //编译孩子节点

如果是文本  编写一个 方法 

util.compilerText(child, vm)

10.  compilerText 编译文本的方法 

  compilerText(node, vm) {  //编译文本
    // 匹配正则
    node.textContent = node.textContent.replace(defaultRe, function (...args) {
      console.log(args, 'test')
      return util.getValue(vm, args[1])
    })
  }
defaultRe 代表 正则
  const defaultRe = /{{((?:.| ? )+?)}}/
 
测试正则:
 "{{xxx}}".match(/{{((?:.|
?
)+?)}}/);
 (2)["{{xxx}}", "xxx", index: 0, input: "{{xxx}}", groups: undefined]
const util = {

通过正则我们 可以拿到 {{ }} 里面的东西  再通过   util.getValue 获取值

 util.getValue 方法

  getValue(vm, expr) {
    console.log(vm, expr)
    let keys = expr.split('.');
    return keys.reduce((arr, current) => {
      arr = arr[current]  // vm.school.name
      return arr
    }, vm)
  },
keys.reduce 是为了防止  vm.school.name 这种表述情况 
reduce 可以将上传的结果 作为传入参数 

const util = {
  getValue(vm, expr) {
    console.log(vm, expr)
    let keys = expr.split('.');
    return keys.reduce((arr, current) => {
      arr = arr[current]  // vm.school.name
      return arr
    }, vm)
  },
  compilerText(node, vm) {  //编译文本
    // 匹配正则
    node.textContent = node.textContent.replace(defaultRe, function (...args) {
      console.log(args, 'test')
      return util.getValue(vm, args[1])
    })
  }
}

码云 地址 :

https://gitee.com/guangzhou110/vue_principle



 
原文地址:https://www.cnblogs.com/guangzhou11/p/12670072.html