The responseful principle of vue.js relies on Object.defineProperty. Vue listens for changes in data by setting setter/getter methods for Object properties, and relies on getter methods for collection. Each setter method is an observer. Notify the subscriber to update the view when the data changes.

What does Vue do with Data?

index.html:

<body>
  <div id="app"></div>
  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
</body>
Copy the code

main.js:

const Vue = window.Vue

const myData = {
  n:0
}

new Vue({
  data: myData,
  template: ` 
      
{{n}}
`
}).$mount('#app') setTimeout(() = > { myData.n += 10 }, 0) console.log(myData) Copy the code

Console:

// {__ob__: we}
// n: (...)
// __ob__: we {value: {... }, dep: ce, vmCount: 1}
// get n: f ()
// set n: f (t)
// __proto__: Object
Copy the code

As we can see, myData is tampered with when passed to Vue.

ES6 getter/setter

Here is an example of using the new ES6 syntax:

let obj = {
  firstName: 'Eden'.lastName: 'Sheng'.name() {
    return this.firstName + this.lastName
  }
}

console.log(obj.name()) 

//EdenSheng
Copy the code

The console can print EdenSheng

Using computed properties

  1. If we use the get method:
let obj = {
  firstName: 'Eden'.lastName: 'Sheng'.get name() {
    return this.firstName + this.lastName
  }
}

console.log(obj.name)

//EdenSheng
Copy the code

You can use the obj.name attribute directly by using get. You can also print EdenSheng.

This is called a getter, and the key word is get, and it gets a value. Is defined as a function, but is used without parentheses.

  1. Set the name with set:
let obj = {
  firstName: 'Eden'.lastName: 'Sheng'.get name() {
    return this.firstName + this.lastName
  },
  set name(e) {
    this.firstName = e[0]
    this.lastName = e.slice(1)
  }
}

obj.name = 'xy'

console.log(obj.name, `${obj.firstName}.${obj.lastName}`) 

//xy x,y
Copy the code

Obj.name = ‘xy’ is equivalent to firing the set function. Setters are used to rewrite data.

Look at this object

So let’s see what obj is.

.console.log(obj)

// {firstName: "x", lastName: "y"}
// name: (...)
// firstName: "x"
// lastName: "y"
// get name: f name()
// set name: f name(e)
// __proto__: Object
Copy the code

conclusion

Vue wrapped and we wrote obj, n, and name are not real attributes; The browser displays (…) , indicates that n and name can be read and written, but they do not exist. Get and name are used to simulate read and write operations on them.

How do I operate on declared objects?

How do we define a new attribute for an already declared object?

useObject.defineProperty( )Methods.

The object.defineProperty () method directly defines a new property on an Object, or modifies an existing property of an Object, and returns the Object.

grammar

Object.defineProperty(obj, prop, descriptor)

parameter

  • Obj: Object on which attributes are to be defined.

  • Prop: The name of the property to define or modify.

  • Descriptor: Attribute descriptor to be defined or modified.

The return value

The object passed to a function.

example

Define a new property n for the declared object data.

let data = {}

Object.defineProperty(data, 'n', {
  value: 0
})

console.log(data)

// Print {n: 0}
Copy the code

The problem

When we have the requirement that n cannot be less than 0. But n doesn’t really exist. How do we determine that?

Sneak in a variable in data to store n.

let data = {}

data._n = 0

Object.defineProperty(data, 'n', {
  get() {
    return this._n
  },
  set(value) {
    if (value < 0) return
    this._n = value
  }
})

console.log(data.n)
/ / 0

data.n = -1
console.log(data.n)
/ / 0

data.n = 1
console.log(data.n)
/ / 1
Copy the code

Here’s the problem: If you access and tamper with data._n directly from the outside, your judgment logic part won’t notice and it will cause chaos.

How to solve this problem? Use proxies and listeners.

Proxy/monitor

Using the agent

Proxies are also a design pattern that uses proxies to fulfill the above requirements. Construct a proxy function.

function proxy({ data }) {
  const obj = {}
  Object.defineProperty(obj, 'n', {
    get() {
      return data.n
    },
    set(value) {
      if (value < 0) return
      data.n = value
    }
  })
  return obj
}

let dataProxy = proxy({ data: { n: 0}})console.log(dataProxy.n)
/ / 0

dataProxy.n = -1
console.log(dataProxy.n)
/ / 0

dataProxy.n = 1
console.log(dataProxy.n)
/ / 1
Copy the code

The function first declares an empty object, obj. When you read and write to dataProxy, the actual return value is obj, which is actually the return value of Data.

Thus, between them, a layer of proxy is implemented: OBJ. We only have one object exposed to the user or outside: obj.

Used to monitor

If you declare a reference at initialization, you can bypass the proxy and read and write directly.

let myData = {n: 0}
let dataProxy = proxy({data: myData})
...
console.log(dataProxy.n)
/ / 0

myData.n = -1
console.log(dataProxy.n)
// -1
Copy the code

Add listener Add listener for an object:

function proxy({ data }) {

  // Store data.n in value
  let value = data.n
  // If n already exists, it will be overwritten automatically
  delete data.n
  Object.defineProperty(data, 'n', {
    get() {
      return value
    },
    set(newValue) {
      if (newValue < 0) return
      value = newValue
    }
  })

  /* add listener */

  /* Proxies the new "data" */

  const obj = {}
  Object.defineProperty(obj, 'n', {
    get() {
      return data.n
    },
    set(value) {
      if (value < 0) return
      data.n = value
    }
  })
  return obj
}

let myData = {n: 0}
let dataProxy = proxy({data: myData})

console.log(dataProxy.n)
/ / 0

myData.n = -1
console.log(dataProxy.n)
/ / 0

myData.n = 1
console.log(dataProxy.n)
/ / 1
Copy the code

Note: we are only modifying the object {n: 0}, the listener is the process of modifying the object, the proxy is created on the original object being modified. In this way, the connection will not be broken.

Multiple attributes

Closures and loops are used to do this when you have multiple variables/attributes in your data.

new Vue()

What does Vue do with Data?

When you create an instance

const vm = new Vue({data: myData})
Copy the code
  1. Vue makes the VM a proxy for myData.
  2. Vue monitors all properties of myData.

purpose

What is the purpose of this?

  1. You can use this to query the VM. This. N = = = myData. N.
  2. The reason for monitoring is to prevent vue from being able to learn about property changes in myData.
  3. Vue can use Render (data) to update the UI and render the page when the property changes.

Data response

  • Responsivity is a form of response to changes in the outside world.
const vm = new Vue({data: {n: 0}})
Copy the code
  • When changing vm.n or data.n, render(data…) N will respond to that response.
  • This linkage process is the data response of vUE.
  • Vue currently implements data responsiveness through Object.defineProperty.

Add attributes to data

Vue does listen and proxy for properties in data (or properties in objects), but it has no way to listen and proxy in advance.

What if you add attributes after initializing data?

General object

For general objects, all possible attributes can be written out in advance in data, so that there is no need to add attributes, only need to change it.

Attributes can also be added in other ways.

With that in mind, let’s look at an API that Vue provides:

Vue. Set (object, key, value) orthis.$set(object, key, value)
Copy the code

role

  1. Add new attributes to data.
  2. Automatically create creates agents and listeners for it if none have been created.

The sample

const Vue = window.Vue

new Vue({
    data: {
        obj: {
            a: 0}},template: ` 
      
{{obj.b}}
`
.methods: { one() { Vue.set(this.obj, 'b'.1) $set(this.obj, 'b', 1); } } }).$mount('#app') Copy the code

An array of

Because arrays are inherently special: the length of an array is unpredictable (the usernames of all users, for example, are in the array), you can’t use undefined to hold space for each item, or use the vue.set () method all the time.

You can use the push method this.array.push(‘value’), but the array is already wrapped in Vue’s new push method. The idea is to declare a new class to inherit an array. For the various special case methods used in Vue instances, see array variation methods. There are seven apis. These methods (apis) automatically handle listening and proxying on array items and trigger view updates.