Original is not easy, I hope you can pay attention to us, and then easily point a praise ~~
This post was first published by the Front end team of the Political Cloud blog: Talk about Vue3.0 responsive data over lunch
Unfortunately, the authors of Vue released a pre-APLHA version of Vue3.0 during the National Day holiday, which means that Vue3.0 is coming to us soon. Take it as it comes. Help me up so I can start talking. Vue3.0 has been refactored in many ways to make it faster, smaller, easier to maintain, more native, and more developer-friendly:
- Use the Typescript
- Abandon class in favor of the function-based API
- Refactoring complier
- Refactoring virtual DOM
- New responsive mechanisms
Today we’re going to talk about refactoring responsive data.
Early adopters
There is a big difference in writing between the reconstructed Vue3.0 and the previous one. Earlier, there was a discussion about the radical reconstruction of Vue3.0 on the network, and opinions differ. Without further ado let’s see how radical Vue3.0 is written.
<! DOCTYPE html><html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src=".. /packages/vue/dist/vue.global.js"></script>
</head>
<body>
<div id="app"></div>
<script>
const { reactive, computed, effect, createApp } = Vue
const App = {
template: `
`,
setup() {
const state = reactive({
count: 0
})
function add() {
state.count++
}
effect((a)= > {
console.log('the count change', state.count);
})
return {
state,
add
}
}
}
createApp().mount(App, '#app')
</script>
</body>
</html>
Copy the code
It’s a little bit different from Vue2. X, with a setup. However, my first impression was not the difference in writing React. After all, there is nothing special about writing React. The point is that this kind of reactive data writing seems like something you’ve seen before? People who have written the React project will recognize mobx, the React responsive state management plugin
import {observable,computed,autorun} from "mobx"
var numbers = observable([1.2.3]);
var sum = computed((a)= > numbers.reduce((a, b) = > a + b, 0));
var disposer = autorun((a)= > console.log(sum.get()));
/ / output '6'
numbers.push(4);
/ / output '10'
numbers.push(5);
Copy the code
Take a look at some of the methods that Vue3.0 exposes related to responsive data:
-
reactive(value)
Create an observable variables, parameters can be JS primitive types, reference, pure object, class instance, array, collection (Map | Set).
-
effect(fn)
Effect means side effect, and this method is executed once by default. This callback is fired again if there are dependent observable property changes in FN
-
computed(()=>expression)
Create a computed value. A computed implementation is based on effect because functions in computed do not execute immediately, multiple values are cached, and expression should not have any side effects, but only return a value. When the expression depends on observable properties that change, the expression is recalculated.
Similar to Mobx.
I’m a big fan of Vue3.0, which takes the creation of reactive objects out of component instance initialization and gives the developer the freedom to decide where and when to create reactive objects by exposing the API.
What changes did the refactoring of the responsive mechanism bring?
Every major release means new features and features, so how has the refactoring of responsive data changed from the pre-3.0 release? Listen to me:
Full listening on arrays
Vue2. Was poking fun at most x is only for array push, pop, shift, unshift, splice, sort, reverse ‘the seven methods of listening, before when changing values by the array subscript is not triggered view update. DefineProperty can’t listen for array index changes. Object defineProperty can also listen for array index changes
const arr = ["2019"."Cloud"."Habitat"."Sound"."Le"."Day"];
arr.forEach((val,index) = >{
Object.defineProperty(arr,index,{
set(newVal){
console.log("Assignment");
},
get(){
console.log("Value");
returnval; }})})let index = arr[1];
/ / value
arr[0] = "2050";
/ / assignment
Copy the code
Nothing’s wrong. Everything’s under control. So if you don’t understand this code, let me pretend that you don’t understand this code, let me post a picture
Object.defineproperty (); Object defineProperty (); Object defineProperty (); In fact, Vue2. X does not implement omnidirectional array listening for two main reasons:
-
JS arrays are much more “mutable” than ordinary objects. For example: arr. Length =0, can instantly empty an array; Arr [100]=1 can also instantly change the length of an array to 100 (other places are filled with empty elements), and so on. For a normal object, we’re just going to change the Value of the Key, not the Key itself, which is the case with arrays where both the Key and the Value are constantly increasing and decreasing, Therefore, after each change, we need to recurse all the keys of the entire array using object.defineProperty plus setters and getters, and we have to exhaust every possible array change, which inevitably introduces performance overhead. Some people might think that this is an x, but performance problems grow from small to large, and this can be a big problem if the array has a large amount of data and is operated on frequently. React16.x is reported to have improved performance by many percentage points by removing meaningless SPAN tags when optimizing textNode, so the performance problem is not to be underestimated.
-
Array was operating in used often, but usually push and pop, shift, unshift, splice, sort, reverse the 7 kinds of operation can achieve a goal. Therefore, Vue2. X is a trade-off for performance.
So why did Vue3.0 go backwards to implement full listening on arrays? The answer is the emergence of a pair of native CPS, Proxy and Reflet. Vue3.0 uses Proxy as the core of a responsive data implementation, using Proxy to return a Proxy object that collects dependencies and triggers updates. The idea is something like this:
const arr = ["2019"."Cloud"."Habitat"."Sound"."Le"."Day"];
let ProxyArray = new Proxy(arr,{
get:function(target, name, value, receiver) {
console.log("Value")
return Reflect.get(target,name);
},
set: function(target, name, value, receiver) {
console.log("Assignment")
Reflect.set(target,name, value, receiver);; }})const index = ProxyArray[0];
/ / value
ProxyArray[0] ="2050"
/ / assignment
Copy the code
The effect is the same as object.defineProperty, and it looks fresh and refined, right? And Proxy can be used for any object, as we’ll see later. Of course, Vue3.0 has new love, but it also has old love. It still supports listening on several methods of array in previous versions.
Inert to monitor
What is “lazy listening “?
In a nutshell, it is “lazy”, allowing developers to selectively generate observable objects. There are often scenarios like this in daily development. Some data on a page does not change during the life cycle of the page, and this part of the data does not need to be responsive, which was not optional before Vue3.0. All data used in templates need to be defined in data. The component instance turns the entire data object into an observable when initialized.
What are the benefits of lazy listening?
-
Improved component instance initialization speed
Before Vue3.0, component instances initialized the entire data Object as an observable, recursively adding getters and settters to each Key using Object.defineProperty. If it is an array, override the seven methods of the proxy array object. In Vue3.0, reactive object creation is handed over to the developer. The developer can customize the data that requires the reactive capability through the exposed reactive, compted, and Effect methods. The instance does not need to recurse to the data object during initialization. This reduces component instantiation time.
-
Reduced running memory usage
Prior to Vue3.0, responsive objects were deeply traversed, and a DEF object was generated for each Key to store all dependencies of the Key. When the Value corresponding to the Key changed, the dependency was notified to update. However, if these dependencies do not need to be updated throughout the life of the page, then the def object collects dependencies that are not only useless but also consume memory. It would be nice to ignore these values when initializing data without changing them. Vue3.0 exposes the reactive approach, allowing developers to selectively create observables to reduce dependency retention and runtime memory usage.
Monitoring of Map, Set, WeakSet and WeakMap
As mentioned earlier, Proxy can be used to represent all objects, which is immediately reminiscent of ES6’s new collection Map and Set. The support for aggregation types comes from Proxy and Reflect. To be honest, I did not know that Proxy could be used for everything before this, so I tried using Proxy to create a map without saying anything
let map = new Map([["name"."zhengcaiyun"]])
let mapProxy = new Proxy(map, {
get(target, key, receiver) {
console.log("Values.",key)
return Reflect.get(target, key, receiver)
}
})
mapProxy.get("name")
Copy the code
Uncaught TypeError: Method Map.prototype.get called on incompatible receiver [object Object]
A basin of cold water was thrown, and the report was wrong. The Map and Set object assignments and values are related to their internal reference to this, but this refers to a Proxy object, so it is necessary to do so
let map = new Map([['name'.'wangyangyang']])
let mapProxy = new Proxy(map, {
get(target, key, receiver) {
var value = Reflect.get(... arguments)console.log("Values.". arguments)return typeof value == 'function' ? value.bind(target) : value
}
})
mapProxy.get("name")
Copy the code
When retrieving a function, bind the original object to a Map or Set object using scoped binding.
How does Vue3.0 implement collection type data listening?
If you look at the above code, you will find that there is no set method, but the set assignment is add. Let’s take a look at how Vue3.0 works. The previous simplified source code
function reactive(target: object) {
return createReactiveObject(
target,
rawToReactive,
reactiveToRaw,
mutableHandlers,
mutableCollectionHandlers
)
}
function createReactiveObject(target: any, toProxy: WeakMap
, toRaw: WeakMap
, baseHandlers: ProxyHandler
, collectionHandlers: ProxyHandler
,>
,>) {
//collectionTypes = new Set<Function>([Set, Map, WeakMap, WeakSet])
const handlers = collectionTypes.has(target.constructor)
? collectionHandlers
: baseHandlers
// Generate a proxy object
observed = new Proxy(target, handlers)
toProxy.set(target, observed)
toRaw.set(observed, target)
if(! targetMap.has(target)) { targetMap.set(target,new Map()}return observed
}
Copy the code
Depending on the target type, collectionHandlers are used for collections (Map, Set) and baseHandlers are used for other types. Now let’s look at collectionHandlers
export const mutableCollectionHandlers: ProxyHandler<any> = {
get: createInstrumentationGetter(mutableInstrumentations)
}
export const readonlyCollectionHandlers: ProxyHandler<any> = {
get: createInstrumentationGetter(readonlyInstrumentations)
}
Copy the code
There is no accident but get, SAO SAO is here:
// Variable data staking object, and a series of corresponding staking methods
const mutableInstrumentations: any = {
get(key: any) {
return get(this, key, toReactive)
},
get size() {
return size(this)
},
has,
add,
set,
delete: deleteEntry,
clear,
forEach: createForEach(false)}// Iterator-dependent methods
const iteratorMethods = ['keys'.'values'.'entries'.Symbol.iterator]
iteratorMethods.forEach(method= > {
mutableInstrumentations[method] = createIterableMethod(method, false)
readonlyInstrumentations[method] = createIterableMethod(method, true)})// Create a getter function
function createInstrumentationGetter(instrumentations: any) {
return function getInstrumented(target: any, key: string | symbol, receiver: any) {
target =
hasOwn(instrumentations, key) && key in target ? instrumentations : target
return Reflect.get(target, key, receiver)
}
}
Copy the code
Due to the Proxy traps with Map | Set Set do not match the original method, so can’t through the Proxy to hijack the Set, so the author “bait-and-switch” in here, here to create a new and collection objects with the same properties and methods of common objects, Replaces the target object with the newly created normal object during the collection object GET operation. So when you call get Reflect reflects on the new object, and when you call set it directly calls the method on the new object that triggers the response. Isn’t that neat? So read the source code benefits, you can learn more people SAO operation.
What about IE?
I hate to mention it, but I can’t get around it. IE is the devil in front end developers’ eyes. Prior to Vue3.0, the implementation of responsive data relied on ES5’s Object.defineProperty, so any browser that supports ES5 supports Vue, meaning that Vue2. X supports IE9. Vue3.0 relies on Proxy and Reflect, a pair of new-age CP’s, and cannot be translated into ES5 or provided with compatibility via Polyfill, which is awkward. According to the developer Tech Front, there will be official compatibility with IE11 before the final version is released, and a lower version of IE will have to be a cool one.
In fact, there is no need to tangle too much with IE, because even Microsoft themselves have given up the treatment of IE to embrace Chromium, why should we tangle?
conclusion
When using an open source framework, it is important to remember that we are able to try it for free thanks to the efforts of the maintainers. Hopefully we’ll find out more about the benefits it brings and the programming ideas the author wants to convey through it. Finally, we look forward to the official version of Vue3.0.
, recruiting
ZooTeam, more than 50 partners are waiting for you to join the wave ~ If you want to change things have been torturing, I hope to start torturing things; If you want to change, you’ve been told you need more ideas, but you don’t have a solution. If you want change, you have the power to make it happen, but you don’t need it. If you want to change what you want to accomplish, you need a team to support you, but you don’t have the position to lead people. If you want to change “5 years of work and 3 years of experience”; If you want to change the original savvy is good, but there is always a layer of fuzzy window… If you believe in the power of believing, believing that ordinary people can achieve extraordinary things, believing that you can meet a better version of yourself. If you want to be a part of the growth of a front end team that has deep business understanding, technology systems, value creation, and impact spillover as the business takes off, I think we should talk. Any time, waiting for you to write something and send it to [email protected]
Recommended reading
Visual Construction System for Front-end Engineering Practice (PART 1)
Move the universe! React can also “use” computed properties
Automated Web performance optimization analysis solution