Response principle

DefineProperty (obj, key, {}) of ES5.

Reactive: when a data variable is changed, the view is also re-rendered accordingly; This requires listening for changes in the data, intercepting the changes and rerendering them.

The basic principle of

let data = {}

let name = 'Joe'

Object.defineProperty(data, 'name', {
    get: function(){
        console.log('get')
        return name
    },
    set: function(newValue){
        console.log('set')
        name = newValue
        // Re-render the view}})console.log(data.name) // Trigger get interception to print s3s
data.name = 'bill'      // The view is rerendered in the set
console.log(data.name) // Trigger get interception to print li Si
Copy the code

Response principle

let person = {
  name: 'Joe'.age: 18.habby: {
    ball: 'badminton'.sing: 'singing'
  },
  colors: ['red'.'blue'.'pink']}/ /! Array method responsive thinking: Clone the array prototype and modify it appropriately
const protoMethod = Array.prototype
const protoTemp = Object.create(protoMethod)

let methodList = ['push'.'pop'.'reserve'.'shift']

methodList.forEach(method= > {
    protoTemp[method] = function(){
        protoMethod[method].call(this. arguments) renderView() } }) observer(person)function observer(target) {
  // If it is a basic type, return it directly
  if (typeoftarget ! = ='object' || typeof target === null) {
    return target
  }
  // If it is an array
  if (Array.isArray(target)){
      target.__proto__ = protoTemp
  }

  // If it is a normal object
  for (let key in target) {
    defineReactive(target, key, target[key])
  }
}

function defineReactive(target, key, value) {
  // Deep listen until it is not an object. As a person. Habby)
  // If the initial data structure is deeply nested and complex, the initial rendering may be stuck, because it starts with a recursive deep listening until the deepest common value is reached (this problem is solved by proxy in VUe3, only when the data is used).
  observer(value)

  // Reactive redefinition
  Object.defineProperty(target, key, {
    get() {
      return value
    },
    set(newValue) {
      Person. age = {num:10}
      observer(newValue)
      if(value ! == newValue) { value = newValue renderView() } } }) }function renderView(){
    console.log('View Update')
}


person.age = { num: 10 }     // View update
person.age.num = 20          // View update
person.colors.push('white')  // View update

Copy the code

Disadvantages of object responsiveness:

  1. Unable to listen to the addition and deletion of object attributes (vUE can be added and deleted by vue. delete and vue. set in response.)

  2. If the data structure of Person is deeply nested, the first rendering can be time-consuming and potentially jammed. (Because the recursive deep listening is directly carried out at the beginning, until the deepest common value (this problem is solved in VUe3 proxy, only to use the data will do the corresponding listening)


Disadvantages of array responsiveness:

  1. Arrays that change data by subscript are not reactive (reactive can be enforced in VUE via vue.set)
  2. Array modification length is not reactive

Tip: When rewriting a function, an outer function can pass arguments to an inner function, either directly as a named argument or by binding this+arguments to the inner function

Depend on the collection

Source: juejin. Cn/post / 684490…

Now that we can listen for changes in the data, how do we handle notification views updating?

Dep is to help us collect [exactly where to notify]. For example, in the following code case, we find that although data has text and Message attributes, only message is rendered to the page. As for the text, no matter how it changes, it does not affect the display of the view. Therefore, we can only collect messages to avoid some useless work.

The MESSAGE Dep collects a dependency that manages message changes in data.

<div>
    <p>{{message}}</p>
</div>

data: {
    text: 'hello world',
    message: 'hello vue',
}
Copy the code

When the watch property is used, it is the developer’s custom to listen for property changes in a data. For example, when message changes, we need to notify the watch hook to execute the callback function.

At this point, message’s Dep collects two dependencies, the second of which is used to manage message changes in the Watch.

watch: {
    message: function (val, oldVal) {
        console.log('new: %s, old: %s', val, oldVal)
    },
} 
Copy the code

When developers customize computed attributes, the following messageT attributes depend on changes in message. So if message changes, we also want to notify computed and have it perform a callback.

At this point, message’s Dep collects three dependencies that are used to manage changes to Message in computed.

computed: {
    messageT() {
        return this.message + '! '; }}Copy the code

How do YOU collect dependencies?

How do we know that a property in data is used? The answer is Object.defineProperty, because reading a property triggers the GET method. The code can be modified as follows:

function defineReactive (obj, key, val) {
    let Dep; / / rely on

    Object.defineProperty(obj, key, {
        enumerable: true.configurable: true.get: () = > {
            console.log('I'm being read. Should I do something about it? ');
            // This dependency is collected
            Dep.depend(); // This time
            return val;
        },
        set: newVal= > {
            if (val === newVal) {
                return;
            }
            val = newVal;
            // Changed, notification dependency to update
            Dep.notify(); // This time
            console.log("The data has been changed and I'm going to render the new value onto the page!"); }})}Copy the code

reference

www.bilibili.com/video/BV1d7…

www.bilibili.com/video/BV1VA…

Juejin. Cn/post / 684490…