【无标题】

文章详细阐述了Vue框架中简单指令如v-text、v-html、v-model的编译过程,以及双向绑定的实现机制,通过Observer类进行数据响应化,Watcher类处理数据变化的监听和更新,Compile类解析DOM中的指令。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

vue简单指令编译和双向绑定的原理

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <h2>{{person.name}} -- {{person.age}}</h2>
    <h3>{{person.fav}}</h3>
    <ul>
      <li>1</li>
      <li>2</li>
      <li>3</li>
    </ul>
    <h3>{{msg}}</h3>
    <div v-text="msg"></div>
    <div v-html = 'htmlStr'></div>
    <input type="text" v-model = 'msg'>
    <button v-on:click="handleClick">onclick</button>
    <button @click="handleClick">@click</button>
  </div>

  <!-- <script src="./vue.js"></script> -->
  <script src="./MVue.js"></script>
  <script src="./Observer.js"></script>

  <script>
    let vm = new MVue({
      el: "#app",
      data: {
        person: {
          name: '小马哥',
          age: '18',
          fav: '书法'
        },
        msg: '学习vue的简单实现原理',
        htmlStr: '<h1>787887</h1>'
      },
      methods: {
        handleClick () {
          this.person.name='1212'
        }
      }
    })
  </script>
</body>
</html>

MVue.js

compileUtil = {
  getVal (expr, vm) {
    return expr.split('.').reduce((data, currentVal) => {
      return data[currentVal]
    }, vm.$data)

  },
  setVal (expr, vm, inputVal) {
    return expr.split('.').reduce((data, currentVal) => {
      return data[currentVal] = inputVal
    }, vm.$data)
  },
  getContentVal (expr, vm) {
    return expr.replace(/\{\{(.+?)\}\}/g, (...args)=> {
      return this.getVal(args[1], vm)
    })
  },
  text (node, expr, vm) {
    let value
    if (expr.indexOf('{{') !== -1) {
      value = expr.replace(/\{\{(.+?)\}\}/g, (...args) => {
        new Watcher(vm, args[1], (newVal) => {
          this.updater.textUpdate(node, this.getContentVal(expr, vm))
        })
        return this.getVal(args[1], vm)
      })
    } else {
      value = this.getVal(expr, vm)
    }
    this.updater.textUpdate(node, value)
  },
  html (node, expr, vm) {
    const value = this.getVal(expr, vm)
    new Watcher(vm, expr, (newVal) => {
      this.updater.htmlUpdater(node, newVal)
    })
    
    this.updater.htmlUpdater(node, value)
  },
  model (node, expr, vm) {
    const value = this.getVal(expr, vm)
    new Watcher(vm, expr, (newVal) => {
      this.updater.modelUpdater(node, newVal)
    })
    node.addEventListener('input', (e)=> {
      this.setVal(expr, vm, e.target.value)
    })
    this.updater.modelUpdater(node, value)
  },
  on (node, expr, vm, eventName) {
    let fn = vm.$options.methods && vm.$options.methods[expr]
    node.addEventListener(eventName, fn.bind(vm), false)
  },
  updater: {
    modelUpdater (node, value) {
      node.value = value

    },
    htmlUpdater (node, value) {
      node.innerHTML = value
    },
    textUpdate (node, value) {
      node.textContent = value
    }
  }
}
class Compile{
  constructor(el,vm){
    this.el = this.isElementNode(el) ? el : document.querySelector(el)
    this.vm = vm
    // 1.获取文档碎片对象 放入内存中减少页面的回流和重绘
    const fragment = this.node2Fragment(this.el)
    // 2.编辑模板
    this.compile(fragment)
    // 3.追加子元素到根元素
    this.el.appendChild(fragment)
  }
  compile (fragment) {
    const childNodes = fragment.childNodes;
    [...childNodes].forEach((child) =>{
      if (this.isElementNode(child)) {
        this.compileElement(child)
      } else {
        this.compileText(child)
      }
      if (child.childNodes && child.childNodes.length) {
        this.compile(child)
      }
    })
  }
  compileElement (node) {
    const attributes = node.attributes;
    [...attributes].forEach(attr => {
      const {name, value} = attr
      if (this.isDirective(name)) {
        const [, directive] = name.split('-')
        const [dirName, eventName] = directive.split(':')
        compileUtil[dirName](node, value, this.vm, eventName)
        node.removeAttribute('v-' + directive)
      } else if (this.isEventName(name)) {
        let [,eventName] = name.split('@')
        compileUtil['on'](node, value, this.vm, eventName)
      }
    })
  }
  compileText (node) {
    const content = node.textContent;
    if (/\{\{(.+?)\}\}/.test(content)) {
      compileUtil['text'](node, content, this.vm)
    }
  }
  isEventName (arrtName) {
    return arrtName.startsWith('@')
  }
  isDirective (arrtName) {
    return arrtName.startsWith('v-')
  }
  node2Fragment(el) {
    // 创建文档碎片
    const f = document.createDocumentFragment()
    let firstChild
    while (firstChild = el.firstChild) {
      f.appendChild(firstChild)
    }
    return f
  }
  isElementNode (node) {
    return node.nodeType === 1
  }
}
class MVue{
  constructor(options) {
    this.$el = options.el
    this.$data = options.data
    this.$options = options
    if (this.$el) {
      // 1.实现一个数据观察者
      new Observer(this.$data)
      // 2.实现一个指令解析器
      new Compile(this.$el, this)
      // 代理
      this.proxyDara(this.$data)
    }
  }
  proxyDara (data) {
    for (const key in data) {
      Object.defineProperty(this, key, {
        get () {
          return data[key]
        },
        set (newValue) {
          data[key] = newValue
        }
      })
    }
  }

}

Observer.js

class Watcher{
  constructor (vm, expr, cb) {
    this.vm = vm
    this.expr = expr
    this.cb = cb
    this.oldVal = this.getOldVal()
  }
  getOldVal () {
    Dep.target = this
    const oldVal = compileUtil.getVal(this.expr, this.vm)
    Dep.target = null
    return oldVal
  }
  update () {
    const newVal = compileUtil.getVal(this.expr, this.vm)
    if (newVal !== this.oldVal) {
      this.cb(newVal)
    }
  }
}

class Dep{
  constructor() {
    this.subs = []
  }
  addSub (watcher) {
    this.subs.push(watcher)
  }
  notify () {
    this.subs.forEach(w => {
      w.update()
    })
  }
}

class Observer{
  constructor(data) {
    this.observer(data)
  }
  observer (data) {
    if (data && typeof data === 'object') {
      Object.keys(data).forEach(key => {
        this.defineReactive(data, key, data[key])
      })
    }
  }
  defineReactive (obj, key, value) {
    this.observer(value)
    const dep = new Dep()
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: false,
      get () {
        Dep.target && dep.addSub(Dep.target)
        return value
      },
      set: (newVal)=> {
        this.observer(newVal)
        if (newVal !== value) {
          value = newVal
        }
        dep.notify()
      }
    })
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值