vue 双向绑定的实现

本文介绍了一种基于Vue.js框架实现MVVM模式的方法。通过自定义Vue类并实现核心功能,如数据观察(observer)、指令解析(compile)、监听器(watch)等,展示了如何让数据变化自动更新视图的过程。此实现包括了双向数据绑定的基本功能。

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

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>mvvm</title>
  <link rel="stylesheet" href="">
</head>
<body>
  <div id="app">
    <input type="text" v-model="msg"> {{msg}}
  </div>
  <script>
 /* new Vue() --> observer() --> defineReactive() --> new Dep()
  --> nodeToFragment() --> compile() --> new watch() --> Dep.target --> watch.update()*/
    // vue mvvm实现
    class Vue {
      constructor(opts) {
        this.data = opts.data;
        this.el = opts.el;
        let data = this.data;
        let me = this;
        let el = document.querySelector(this.el);
        observer(data,this);
        let dom = nodeToFragment(el,this)
        el.appendChild(dom);
      }
    }
    // 将el内元素编译成fragment dom 片段
    function nodeToFragment(node,vm) {
      let frag = document.createDocumentFragment();
      let child;
      while(child = node.firstChild){
        compile(child,vm);
        frag.append(child);
      }
      return frag;
    }
    // 解析指令
    function compile(node,vm){
      let reg = /\{\{(.*)\}\}/;
      if(node.nodeType == 1){ // 元素
        let attrs = node.attributes;
        let name = '';
        for(let i = 0,len = attrs.length; i < len; i++){
          if(attrs[i].nodeName == 'v-model'){
            name = attrs[i].nodeValue; 
            node.addEventListener('input', e => vm[name] = e.target.value);
            node.value = vm[name];
            node.removeAttribute('v-model');
          }
        }
        new watch(vm,node,name,'input') //添加监听器
      }
      if(node.nodeType==3){ //元素或属性中的文本内容
        if(reg.test(node.nodeValue)){
          name = RegExp.$1.trim();
          console.log(name)
          nodeType = 'text';
          new watch(vm,node,name,'text');
        }
      }
    }
    //监听器
    class watch{
      constructor(vm,node,name,nodeType){
        console.log(this)
        Dep.target = this; //将监听的放在Dep队列里
        this.name = name;
        this.node = node;
        this.vm = vm;
        this.nodeType = nodeType;
        this.update(); //第一次update input ,第二次update text
        Dep.target = null;
      }
      update(){
        this.get();
        if(this.nodeType == 'text'){
          this.node.nodeValue = this.value;
        }
        if(this.nodeType == 'input'){
          this.node.value = this.value;
        }
      }
      get(){
        this.value = this.vm[this.name];
      }
    }
    //observer
    function observer(data,vm){
      Object.keys(data).forEach(key => defineReactive(vm,key,data[key]))
    }
    function defineReactive(vm,key,val){
      let dep = new Dep();
      Object.defineProperty(vm,key,{
        get(){
          if(Dep.target){
            dep.addSub(Dep.target);
          }
          return val;
        },
        set(newValue){
          if(newValue == val) return;
          val = newValue;
          dep.notify();
        }
      })
    }
    class Dep{
      constructor(){
        this.subs = [];
      }
      addSub(sub){
        this.subs.push(sub)
      }
      notify(){
        this.subs.forEach(sub => sub.update())
      }
    }
  </script>
  <script>
    var vm = new Vue({
      el:'#app',
      data:{
        'msg':'hello word'
      }
    })
  </script>
</body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值