vue bidirectional data binding principle

vue responsive principle

Data driven

Data responsive: according to the model, it is just a common JavaScript object. When we modify the data, the view will be updated, avoiding frequent DOM operations and improving development efficiency. This is different from Jquery, which operates DOM frequently

Bidirectional binding:

Data changes, view changes, view changes, data changes( Bidirectional binding contains the content of data response)

We can use`v-model` Create a two-way data binding on a form element

Data driven is one of Vue's most unique features

 In the development process, we only need to pay attention to the data itself, not how the data is rendered to the view. Mainstream`MVVM`Frameworks have implemented data responsive and two-way binding, so data can be bound to`DOM`Come on.

Responsive core principles

vue2 principle

Official documents: https://cn.vuejs.org/v2/guide/reactivity.html

In vue2 The implementation of response in X is through object Defineproperty. Note that this property cannot be shimmed, so Vue does not support IE8 and earlier browsers.

Data changes are reflected in the guide map

Declarative change view:

Render the value of the name attribute in data as text into the p tag marked with v-text. In vue, we call this marked declarative rendering instruction

<!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">
    <p v-text='name'></p>
    <p v-text='age'></p>
  </div>
  <script>

    let data = { name: 'Li Bai', age: 12 }

    Object.keys(data).forEach(key => {
      define(data, key, data[key])

    });
    function define(data, key, value) {

      Object.defineProperty(data, key, {
        get() {
          return value
        },
        set(newValue) {
          value = newValue
          compile()
        }
      })
    }

    function compile() {
      let app = document.querySelector('#app')
      let nodes = app.childNodes
      // console.log(nodes);
      nodes.forEach(item => {
        if (item.nodeType === 1) {
          let arr = item.attributes
          console.log(arr);
          Array.from(arr).forEach(node => {
            let name = node.nodeName
            let value = node.nodeValue
            if (name === 'v-text') {
              item.innerHTML = data[value]
            }
          })
        }
      })
    }
    compile()
  </script>
</body>

</html>

Command change view:

Through the original operation of dom, the latest value of name can be displayed inside the p element every time

<!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">
    <p></p>
  </div>
  <script>
    let p = document.querySelector("#app p")
    let data = { name: 'Li Bai', age: 12 }
    // console.log(Object.keys(data));

    Object.keys(data).forEach(key => {
      console.log(data);
      console.log(key);
      define(data, key, data[key])

    });
    function define(data, key, value) {
      // console.log(data, key, value);
      Object.defineProperty(data, key, {
        get() {
          return value
        },
        set(newValue) {
          value = newValue
          p.innerHTML = value
        }
      })
    }
    p.innerHTML = data.name
  </script>
</body>

</html>

View changes reflect data

<!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">
    <input type="text" v-model="name">
  </div>
  <script>
    let data = {
      name: 'Li Bai',
      age: 18
    }
    Object.keys(data).forEach(key => {
      defineReactiveProperty(data, key, data[key])
    })
    function defineReactiveProperty(data, key, value) {
      console.log();
      Object.defineProperty(data, key, {
        get() {
          return value
        },
        set(newValue) {
          if (newValue === value) {
            return
          }
          value = newValue
          // When using declarative rendering, it is necessary to facilitate all nodes to see which node has our custom binding attributes
          compile()
        }
      })
    }
    function compile() {
      let app = document.querySelector('#app')
      // Get all child elements under the app
      const nodes = app.childNodes
      nodes.forEach(node => {
        if (node.nodeType === 1) {
          const attrs = node.attributes
          Array.from(attrs).forEach(attr => {
            const nodeName = attr.nodeName
            const nodeValue = attr.nodeValue
            if (nodeName === 'v-model') {
              // When the data in the data changes, change the value attribute of the text box to update the value of the text box
              node.value = data[nodeValue]
              // When the content of the text box changes, the value of the attribute in data is updated
              node.addEventListener('input', e => {
                data[nodeValue] = e.target.value
              })
            }
          })
        }
      })
    }
    compile()
  </script>

</html>

Vue3 responsive principle

Vue3's responsive principle is accomplished through Proxy.

Proxy listens directly to objects rather than attributes, so there is no need to use loops when converting multiple attributes into getters / setters.

Proxy is newly added in ES6 course and is not supported by IE

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Proxy</title>
  </head>
  <body>
    <div id="app">hello</div>
    <script>
      //Simulate the data option in Vue
      let data = {
        msg: "hello",
        count: 0,
      };
      //Simulate Vue instances
      //Create a proxy object vm for data so that you can use vm msg to get the value of msg attribute in data, and the get method will be executed at this time
      let vm = new Proxy(data, {
        // Is executed when a member of the vm is accessed
        //target represents the proxy object (data object here), and key represents the attribute in the proxy object
        get(target, key) {
          console.log("get key:", key, target[key]);
          return target[key];
        },
        //When setting the members of vm
        set(target, key, newValue) {
          console.log("set key:", key, newValue);
          if (target[key] === newValue) {
            return;
          }
          target[key] = newValue;
          document.querySelector("#app").textContent = target[key];
        },
      });
      //test
      vm.msg = "aaaa";
      console.log(vm.msg);
    </script>
  </body>
</html>

Bidirectional data binding

Bidirectional data binding includes two parts: data change updates the view, and view change updates the data.

Realize bidirectional data binding

The basic idea is that we can add an input event to the text box (the first text box) and trigger the event after inputting data, At the same time, the data entered by the user in the text box is assigned to the attributes in data (the view changes and the data is updated. When the data changes, the set method in observer.js will be executed to update the view, that is, the responsive mechanism is triggered).

//Processing v-model
  modelUpdater(node, value, key) {
    //v-model is the attribute of the text box. Assigning a value to the text box requires the value attribute
    node.value = value;
    new Watcher(this.vm, key, (newValue) => {
      node.value = newValue;
    });
    //Realize bidirectional binding
    node.addEventListener("input", () => {
      this.vm[key] = node.value;
    });
  }

An input event is added to the current text box node. This event will be triggered when content is entered in the text box. At the same time, the value entered by the user in the text box node is re assigned to the corresponding attribute in data.

Enter a value in the text box, and the corresponding difference expression and the content in v-text will change. At the same time, output VM. In the console The value of MSG will find that the data has also changed.

After assigning a value to the attribute in data, it will execute observer The set method in JS updates the view, which triggers a responsive mechanism.

Keywords: Vue

Added by kevdoug on Thu, 03 Feb 2022 03:56:39 +0200