This is the 22nd day of my participation in the August Wen Challenge.More challenges in August
MVVM
MVVM is a design pattern, or you can call it an architectural pattern. It stands for model-view-viewModel.
- Model stands for Model
- View stands for View
- The ViewModel represents a controller
In this case, we can think of the Model as the data returned by the back-end interface
{name: 'sunny ', age:'18',
mark: 'always 18'}Copy the code
A view is the page that the front end displays in the browser for the user to see
How is a piece of JSON data displayed on the page? That’s what the ViewModel is responsible for.
So, the relationship between them can be represented by a graph
The most common front-end FRAMEWORK for MVVM is probably
- AngularJs, but this framework, seems to be less used in China
- Especially bigvueWe often see this in vue demos
var vm = new Vue(...)
The VM is shorthand for ViewModel.
Response principle
The original intention of MVVM is to use data binding functions to remove all code related to interface data rendering logic from the view level.
So how do you use or write data binding functions to achieve this effect?
Let’s look at vue’s performance:
<div id="counter">
Counter: {{ counter }}
</div>
const Counter = {
data() {
return {
counter: 0
}
}
}
Vue.createApp(Counter).mount('#counter')
Copy the code
Of course this example has nothing to do with Object.defineProperty, I’m just saying that “data binding removes code from the view level related to the interface data rendering logic”
Obviously, we didn’t write it ourselves
document.getElementById(counter).innerText = counter
or
$('#counter).text(counter)
Rendering code like this.
You might think this is just a line of code, that simple.
Actually, no, let’s say we want counter to increment
mounted() {
setInterval(() = > {
this.counter++
}, 1000)}Copy the code
You can see how much effort MVVM saves us.
Ok, so now that I’ve shown you how MVVM behaves, how can I simply implement MVVM using Object.defineProperty?
In fact, the responsive principle of VUe2 is data hijacking, that is, when the data changes, the relevant page is automatically rerendered.
This requirement is pretty easy, but first understand a method called Object.defineProperty, and once you understand it most people can simulate a simple implementation.
It’s a far cry from a VUE, but the interview isn’t asking you to write a VUE.
DefineProperty data hijacking
The object.defineProperty method adds a new attribute to an Object or modifies an existing attribute of an Object before returning the Object.
The Object.defineProperty method can take three arguments
- Object (Required, the object for which attributes are to be defined)
- Propertyname (Required, name of the property to be defined or modified)
- Descriptor (required, attribute descriptor to define or modify)
Object.defineProperty(object, propertyname, descriptor)
Copy the code
For Descriptor, it is an object type that is used to configure the property descriptor of the propertyName. Therefore, the property of Descriptor can be one of the following:
- Data descriptor
key | Value types | describe | The default value |
---|---|---|---|
value | any | The value of the object. The propertyname | undefined |
writable | boolean | Whether object. Propertyname can be modified by the assignment operator | false |
configurable | boolean | Whether object. Propertyname can be modified or deleted | false |
enumerable | boolean | Whether object. Propertyname can be enumerated | false |
- Access descriptor
key | Value types | describe | The default value |
---|---|---|---|
get | function | Function called when reading Object.propertyName | undefined |
set | function | Function called when setting Object. propertyName | undefined |
configurable | boolean | Whether object. Propertyname can be modified or deleted | false |
enumerable | boolean | Whether object. Propertyname can be enumerated | false |
A data descriptor that has no value, writable, GET, or set inside it is a data descriptor by default.
The following example shows the use of access descriptors:
let data = {}, temp = 'aa'
Object.defineProperty(data, 'key1', {
set(value){
console.log('this is a new value: ' + value)
temp = value
//some code like $('div').html(value) will automatic execute when key1 changed
},
get(){
return temp
}
})
data.key1 = 'Jack'
Copy the code
When we run the above code in the browser, the console will print:
this is a new value: Jack
This is where access descriptors come in. They can be used for data hijacking.
A subscription model
The subscription and data hijacking we are trying to implement is via the access descriptor of Object.defineProperty.
A subscriber can simply be thought of as a queue filled with functions that will be executed at some point in time.
And, of course, for the sake of finding it, we can define it as an object type, where each property is an array type.
var a = {a: [].b: [].c: []}Copy the code
Let’s implement a subscriber:
let Deep = {
deepList: {},
listen(key, fn){
if(!this.deepList[key])
this.deepList[key] = []
this.deepList[key].push(fn)
},
trigger(){
let key = Array.prototype.shift.call(arguments)
let fnList = this.deepList[key]
if(! key || ! fnList || ! fnList.length)return false
for(let i=0, fn; fn = fnList[i++];) {
fn.apply(this.arguments)
}
}
}
Copy the code
Bind the subscriber to data hijacking
The idea here is to execute the corresponding code in the subscriber once data hijacking occurs.
This way, you can implement vUe-like changes to a message, and the page can render the latest results synchronously.
The logic of this part of the code is very simple:
- First, we bind the page tag to the content via deep.listen and put it into the subscriber
- We then call the trigger method of the subscriber in a data hijack, updating the data and updating the HTML simultaneously
let dataHijack = ({data, tag, datakey, selector}) = > {
let value = ' ', el = document.querySelector(selector);
Object.defineProperty(data, datakey, {
get(){
return value
},
set(newVlaue){
value = newVlaue
Deep.trigger(tag, newVlaue)
}
})
Deep.listen(tag, content= >{
el.innerHTML = content
})
}
Copy the code