一个简易的双向绑定demo

2022-04-04  本文已影响0人  苍老师的眼泪
<!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">
    <div>
      一个叫<strong>{{ name }}</strong>的人,他今年
      <strong>{{ age }}</strong>岁,
      家住在<strong>{{ home.location }}</strong>
      他有一个朋友也是 <strong> {{ age }}</strong> 岁
    </div>
  </div>

  <script>
    let data = {
      name: "Edison",
      age: 26,
      home: {
        location: 'Guangdong'
      }
    }
    let el = "#app"

    let vm = {
    
    }


    function reactive(target, obj) {

      for (let key of Object.keys(obj)) {

        if (typeof obj[key] == 'object') {
          vm[key] = {}
          reactive(vm[key], obj[key])
          return
        }

        // 记录依赖的watcher
        let dep = []

        Object.defineProperty(target, key, {
          get: function () {

            if (global_watcher) {
              dep.push(global_watcher)
            }

            return obj[key]
          },
          set: function (val) {

            // console.log('哈哈, ' + key + '的值为:' + val)
            
            obj[key] = val

            dep.forEach(element => {

              element.update(val)
            });
          }
        })
      }

    }

    let global_watcher = null
  
    reactive(vm, data)

    class Watcher {
      constructor(node) {
        this.node = node
      }
      update(new_value) {
        
        this.node.textContent = new_value
      }
    }

    compile(el)

    function compile(root) {
      let root_node = document.querySelector(root)

      compile_node(root_node)

    }

    function compile_node(node) {
      if (node.nodeType == 1) {
        // 元素节点

        node.childNodes.forEach(child => {
          compile_node(child)
        })

      } else if (node.nodeType == 3) {
        // 文本节点

        let reg_exp = /\{\{(.*)\}\}/

        if (reg_exp.test(node.textContent)) {
          let exp = RegExp.$1.trim()

          // 怎么才能放到 exp 属性对应的dep里面呢?
          // 通过触发getter和setter来访问相应的dep
          
          global_watcher = new Watcher(node.parentNode)
          // 触发一下相应的getter, 以便在get中将watcher加入刀相应的dep
          // 顺便改一下相应html节点的值
          node.textContent = get_value(vm, exp)

          global_watcher = null

        }

      } else 
        throw '暂时考虑别的类型的节点'
      

    }

  
    function get_value(vm, exp) {

      let value = vm

      exp.split('.').forEach(e => {
        value = value[e]
      })

      return value
    }

    

  </script>

</body>

</html>
上一篇下一篇

猜你喜欢

热点阅读