From Vue2 to Vue3 [7] - the implementation of the responsive principle in Vue2 and its defects

Series Article Directory

content Link
From Vue2 to Vue3 [zero] Introduction to Vue3
From Vue2 to Vue3 [1] Composition API (Chapter 1)
From Vue2 to Vue3 [2] Composition API (Chapter 2)
From Vue2 to Vue3 [3] Composition API (Chapter 3)
From Vue2 to Vue3 [4] Composition API (Chapter 4)
From Vue2 to Vue3 [5] From Vue2 to Vue3 [5] - new components (Fragment, Teleport, Suspense)
From Vue2 to Vue3 [6] From Vue2 to Vue3 [6] - Changes in Vue3 (book at the end of the article)


foreword

Vue is a popular JavaScript framework that has attracted widespread attention for its concise and easy-to-use syntax and powerful responsiveness. Vue's responsive data mechanism is one of its core features, which can automatically track data changes and update related views in real time. However, the responsive data mechanism in Vue 2 is not perfect. This article will discuss the principle of Vue 2 responsiveness and its defects.


1. Responsive principle in Vue2

  • Responsive principle of Vue 2:
    In Vue 2, responsiveness is achieved by using the Object.defineProperty() method .
    During component instantiation, Vue recursively traverses the data object (data) , converts each property into a getter/setter , and creates a dependency tracking system for each property. When a property is accessed or modified, the getter/setter will trigger the dependency tracking system to perform dependency collection and dispatch updates to ensure the synchronization of data and views.
  • The specific implementation steps are as follows:
    1. Create an Observer object: By recursively converting the properties of the data object into responsive properties, use Object.defineProperty() to add getter and setter methods for each property . Vue2中 通过使用 Object.defineProperty() 方法,将对象的属性转换成 getter 和 setter,当数据发生变化时,会自动触发相应的更新函数,实现数据的响应式。
    2. Create a Dep object: used to manage Watcher, which is used to collect dependencies, delete dependencies, and send messages to dependencies. Dependency collection and dispatch update operations for decoupled properties.
    3. Create a Watcher object: The Watcher object is used to connect the bridge between the view and the data. When the dependent property changes , the Watcher object will receive a notification and update the view . When data changes, it notifies components subscribed to that data to update the view. Watcher will add itself to Dep when it is instantiated, and when the data changes, it will trigger the corresponding update function.
    4. Template compilation: Vue will parse the template, and translate the data binding instructions in the template into the corresponding update function, so that it can be called when the data changes.

When the value of the object is modified, the corresponding setter will be triggered, and the setter will notify the Watcher in the Dep obtained by relying on the collection before, telling it that its own value has changed, and the view needs to be re-rendered. At this time, these Watchers will start to call update to update the view, and the corresponding getter will trigger tracking and re-render the new value to the view

input用v-model绑定数据,我们需要在input元素上添加事件监听,每当input事件被触发时,就修改对应的data,data里的数据又会响应式更新回视图

2. Simulate the simple version of the responsive principle

Implementation idea:
Define an Observe constructor for data hijacking of the properties of the data object . We use the Object.defineProperty() method to hijack each property of the data object , and define the getter and setter methods of the property.
In the getter method, we return the value of the property. In the setter method, we judge whether the new value is different from the old value, and if so, update the value of the property and trigger a dependency update .

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>Document</title>
	</head>
	<body>
		<script type="text/javascript" >

			let data = {
      
      
				name:'前端百草阁',
				age:21,
			}

			function Observer(obj){
      
      
				//汇总对象中所有的键形成一个数组
				const keys = Object.keys(obj)
				//遍历
				keys.forEach((k)=>{
      
      
					Object.defineProperty(this,k,{
      
      
						get(){
      
      
							return obj[k]
						},
						set(val){
      
      
							console.log(`${ 
        k}被改了,我要通知Vue重新去解析模板.....`)
							obj[k] = val
						}
					})
				})
			}
			
			//创建一个监视的实例对象,用于监视data中属性的变化
			const obs = new Observer(data)		
	
			//准备一个vm实例对象
			let vm = {
      
      }
			vm._data = data = obs
		</script>
	</body>
</html>

Please add a picture description
insert image description here
at this time,原先data里的属性就会各自有一个为他们服务的getter和setter,变成了具有响应式的属性

  • Shortcomings of the simplified version
    • Defect 1: A data proxy will be made in normal vue. When accessing vm.name, the access is actually vm._data.name. After doing this, it is more convenient to use the data proxy
      insert image description here
    • Defect 2: The simple version does not take into account the fact that the attribute value in data is still an object, and uses a recursive method in Vue to recursively convert all attributes in data into responsive attributes (even if the attribute value is an array, Objects are hidden in the array, and the corresponding properties can still be converted into responsive properties)

Here is a small tip, use this to point to obs, access the properties in this (obs), the getter returns the properties in obj (data proxy), why is this so? If you access the properties in obj, I really return the corresponding properties in obj to you through getters, and the returned properties in obj have to trigger their own getters, so is it stuck in an endless loop? The problem is that whether you trigger the getter or the setter will cause the error of exceeding the maximum call stack

insert image description here
Another way to solve this problem is to use the closure to pass the initial value to the value and save it. The subsequent getter and setter are all for the value in the closure, which is isolated from the original obj. When you access or When setting obj.key, the corresponding val will be modified (since the closure val will not be recycled by the garbage mechanism), there will be no maximum call stack overflow

function observe(obj) {
    
    
  if (!obj || typeof obj !== 'object') {
    
    
    return;
  }

  Object.keys(obj).forEach(function(key) {
    
    
    defineReactive(obj, key, obj[key]); 
  });
}

function defineReactive(obj, key, val) {
    
    
  observe(val);  // 递归地对data对象的属性进行数据劫持

  Object.defineProperty(obj, key, {
    
    
    get: function() {
    
    
      return val;
    },
    set: function(newValue) {
    
    
      if (newValue !== val) {
    
    
        val = newValue;
        // 触发依赖更新
        updateView();
      }
    }
  });
}

function updateView() {
    
    
  document.querySelector('h1').innerText = vm.message;
}

// 初始化数据劫持
observe(vm.$data);

In the above code, the observe function is used to recursively perform data hijacking on the properties of the data object. In the defineReactive function, we use the Object.defineProperty() method to hijack each property of the data object, and define the getter and setter methods of the property.
In the getter method, we return the value of the property. In the setter method, we judge whether the new value is different from the old value, and if so, update the value of the property and trigger a dependency update.
Finally, we call observe(vm.$data) to initialize the data hijacking, so that Vue can capture the access and modification operations on the properties of the data object, and trigger the corresponding dependency update.

3. Defects brought by Vue2 responsive data

Vue 2中的响应式数据存在一些缺陷,但通过使用Vue提供的补救办法,可以解决大部分响应式数据的问题。

3.1 The response problem of the new attribute

When Vue is initialized, it will hijack data on the properties of the data object, but for subsequent new properties, Vue cannot automatically perform responsive processing.
Vue cannot detect ordinary new attributes. For example this.myObject.saying = 'hi', this new saying attribute is not responsive , and Vue cannot detect it.

3.2 Response to array changes

Vue's changes to the array (such as modifying array elements through indexes, deleting or inserting elements through the splice method) cannot be directly processed in a responsive manner.

For example, these data are defined in data at this time

	data:{
    
    
		friends:[
			{
    
    name:'jerry',age:35},
			{
    
    name:'tony',age:36},
			'前端百草阁'
		]
	}

insert image description here
It is not difficult to find that the objects in the array are all responsive, but the ordinary elements in the array are not responsive, which means that if you directly modify the elements in the array, Vue cannot monitor them

If you modify the object properties through the array subscript, it can be monitored, because the properties in the object are all responsive, but if you modify the ordinary elements through the array subscript, it cannot be monitored

如果用一个新数组覆盖掉原先的数组,Vue是能监测到的Please add a picture description

3.3 Deletion of object properties

Vue cannot directly detect deletion of object properties.
利用delete删除对象的属性,无法被Vue监测到

Fourth, the solution to Vue2 responsive defects

4.1 Response issues for new attributes

Vue.set( target, propertyName/index, value )

Add a property to the reactive object and make sure the new property is also reactive and triggers a view update. It must be used to add new properties to reactive objects, because Vue cannot detect ordinary new properties (such as this.myObject.newProperty = 'hi')

Add an attribute to the student object in data, and it is responsive. There are two ways to write it, Vue.set or this.$set

// Vue.set(this._data.student,'sex','男') // 这里加不加_data实际上都可以,就是一个数据代理,访问谁都一样,那我们肯定选择偷懒啦
this.$set(this.student,'sex','男')  // this代表vm vue实例对象

insert image description here
Implemented the addition of the sex attribute in the student object, and this attribute has getters and setters for its own services (responsive)

However, the Vue official website clearly states: 注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。
Simply put, the first parameter target of the set method is not allowed to be vm (vue instance), nor is it allowed to be vm._data (root data object)

4.2 Response to array changes

The first solution is to use the array mutation method: Vue provides some array mutation methods (such as push, pop, shift, unshift, splice, sort, and reverse), which will trigger the responsive update of the array.
如果不是这七个方法的话,比如调用slice等数组方法的话,记得要把返回的新数组覆盖掉原来的旧数组,依然能触发响应式
insert image description here
The second solution is to use the set method. The set method can not only solve the problem of adding new attributes to the object, but also solve the problem of modifying the array (not used much)
insert image description here

4.3 Deletion of object properties

Vue.delete method: used to delete the properties of the object and trigger a responsive update. For example, you can use Vue.delete(vm.someObject, 'propertyToDelete') to delete a property.
Although the normal delete method does delete the property, it cannot be monitored. Use
insert image description here
Vue.delete to perfectly solve the problem that the property of the deleted object cannot be monitored (rarely used), orvm.$delete(vm.person,'name')
insert image description here


Summarize

The responsive data mechanism of Vue 2 can meet our needs in most cases, but there are also some defects.
First of all, Vue cannot directly respond to the new attributes, and needs to use specific methods to remedy. Secondly, for changes in arrays and deletions of object properties, Vue cannot directly perform responsive processing, and needs to use corresponding methods to trigger updates. These defects may cause some troubles in actual development.
But fortunately, Vue provides some remedies, such as Vue.set and Vue.delete methods, and array mutation methods. Through these remedial measures, we can make up for the shortcomings of Vue 2's responsive data mechanism, and improve development efficiency and user experience. Nevertheless, we also look forward to the improvement of the future version of Vue, which can be more intelligent and flexible in terms of responsive data to meet the needs of more complex scenarios.

Guess you like

Origin blog.csdn.net/m0_57524265/article/details/131989294