A, MVVM

The design of Vue is largely inspired by the MVVM model, so the variable name VM is often used to represent component instances in official documentation.

I’m going to introduce you to MVVM, but before I start, I can give you a brief introduction to the MVC design pattern. In the MVC design pattern:

  • M (Mode, model) : It is used to encapsulate data related to the application’s business logic and how it is processed. The Model has direct access to data, such as database access, independent of “View” and “Controller”, that is, The Model doesn’t care how it will be displayed or manipulated. However, changes to the data in the Model are typically published through a refresh mechanism. In order to implement this mechanism, the views used to monitor the Model must be registered with the Model in advance so that the View can learn about changes to the data Model. (E.g. Observer mode)

  • V (View) : The ability to display data purposefully (theoretically, this is not required). There is generally no procedural logic in a View. In order to refresh on a View, the View needs access to the data Model it is monitoring, so it should register with the data it is monitoring beforehand.

  • C (Controller) : forwards and processes requests. As a bridge between “Modal” and “View,” it processes and responds to events, including user behavior and changes to the data Model.

Simple understanding is, when the data changes, a notification is sent to the controller to refresh the view, the same controller to monitor the user action, when need to modify the data controller will inform data model to refresh the data, here you can see that the view and model does not directly interact, but by of the controller to realize a butt joint, the MVC and MVVM similar.

MVVM (Model-view-ViewModel) is a design mode.

Among them:

  • Model represents the data Model.

  • View indicates a View.

  • The ViewModel listens for data changes in the Model and controls the update of the view, and handles user interactions. Similar to the controller in the MVC design pattern, the ViewModel is essentially an instance of a Vue.

Note: The Model and View are not directly related, but through the ViewModel to contact, Model and ViewModel have two-way data binding relationship. So when data changes in the Model, it triggers a refresh of the View layer, and data changes in the View due to user interactions are synchronized in the Model.

This mode automatically synchronizes the data between the Model and View, so developers can only focus on the maintenance of the data instead of manipulating the DOM themselves.

Two, Vue response

1. Responsive attributes

In VUE, data can be bound to a view with double curly braces, such as:

<script setup lang="ts"> </script> <template> <! - bound data - > < div > count: {{count}} < / div > < / template >Copy the code

Page output: count: 0, next we define a button in the template to try to change the value of count and see if the view has changed:

<script setup lang="ts"> Const increment = () => {count++; }; </script> <template> <! </div> <button type="button" @click="increment">increment</button> </template>Copy the code

In the above example, @click represents adding a click event to the button. The event handler is increment. In the event handler, we increment the count variable. This is because the variable count we defined is not reactive (although you can render it on the view, count is not added to the reactive system).

Next, let’s look at several forms of adding variables to a responsive system in vue3.x:

1.1. ref()

Takes an internal value and returns a reactive and mutable REF object. The ref object has a single property.value pointing to an internal value.

Next we modify the example by wrapping the count value with ref:

<script setup lang="ts"> import { ref } from 'vue'; // let count = ref(0); Const increment = () => {count.value++; }; </script> <template> <! </div> <button type="button" @click="increment">increment</button> </template>Copy the code

Click the button to see that the count value has been updated successfully.

Tip: Ref generally wraps around basic data types, such as strings, values, Booleans, and so on. If you want to add an object to a reactive, use Reactive.

Ref () can also be used to get a single DOM element, as follows:

<script setup lang="ts"> import { onMounted, ref } from 'vue'; const domRef = ref(null); onMounted(() => { console.log(domRef.value); // <div>Hello</div> }) </script> <template> <! -- ref --> <div ref="domRef">Hello</div> </template>Copy the code

1.2. reactive()

Returns a reactive copy of the object.

<script setup lang="ts"> import { reactive } from 'vue'; Reactive ({count: 0,}); Const increment = () => {obj.count++; }; </script> <template> <! Obj. Count: {{obj. Count}}</div> <button type="button" @click="increment">increment</button> </template>Copy the code

Tip: Reactive General package object type.

1.3. toRefs()

Converts a reactive object to a normal object, where each property of the resulting object is a REF pointing to the corresponding property of the original object.

<script setup lang="ts"> import { reactive, toRefs } from 'vue'; const state = reactive({ name: 'Li-HONGYAO', age: 18, }); const stateAsRefs = toRefs(state); /** stateAsRefs: {name: Ref<string>, age: Ref<number>}*/ console.log(stateAsRefs.age.value); // 19 stateAsRefs.age.value++; console.log(state.age); // 20 </script>Copy the code

Small coup: A page usually has multiple states, such as user information, login status, etc. You may define multiple ref or Reactive variables to hold this information. In VUe2. If you also want to define the state of a page in a state variable, you can do this:

const state = reactive({
  loginStatus: 0.user: {
    name: 'Li-HONGYAO'.job: 'Front End Engineer'.address: 'Chengdu High-tech Zone',}});Copy the code

Then access it in the template:

<div>loginStatus: {{state. LoginStatus? 'Logged in' : 'not login'}} < / div > < div > Name: {{state. The user. The Name}} < / div > < div > job: {{state. The user. The job}} < / div > < div > address: {{ state.user.job }}</div>Copy the code

But you might think that every time you access a property, you need to go through state.xxx. Is it possible to access the property directly in some form? The answer is yes, we can do it with toRefs.

const { loginStatus, user } = toRefs(state);
Copy the code

LoginStatus and User can then be accessed directly from within the template.

2. Responsivity principle

Vue data bidirectional binding mainly refers to: data changes update view, view changes update data

2.1. @ 2. X

When a Vue instance is created, you can pass a plain JavaScript object into the Vue instance as the data option, and the Vue will iterate over the data option’s properties, Use Object.defineProperty to turn them all into getters/setters and track the dependencies internally, notifying the properties of changes when they are accessed and modified. Each component instance has a corresponding Watcher program instance, which records properties as dependencies during component rendering and then, when setters for dependencies are called, tells Watcher to recalculate, causing its associated components to be updated.

Deep understanding:

  • Monitor the Observer: Traverses the data object, including the attributes of the subattribute object, usingObject.defineProperty()Add setters and getters to all properties. So, if you assign a value to this object, it will trigger the setter, so you can listen for changes in the data.
  • Compile: parse Vue template instructions, replace variables in the template with data, and then initialize render page view, bind the corresponding node of each instruction to update function, add subscribers to listen to the data, once the data changes, receive notification, call update function for data update.
  • Subscriber Watcher: The Watcher subscriber is the bridge between the Observer and Compile. Its main task is to subscribe to the message of the change of attribute value in the Observer. When the message of the change of attribute value is received, the corresponding update function in the parser Compile is triggered. Each component instance has a corresponding Watcher instance object, which records properties as dependencies during component rendering and then, when setters for dependencies are called, informs Watcher to recalculate, causing its associated components to be updated — a typical observer pattern
  • Subscriber Dep: The subscriber uses a public-subscribe design pattern to collect subscriber Watcher and manage listener Observer and subscriber Watcher uniformly.

2.2. @ 3. X

Vue3.x uses Proxy instead of Object.defineProperty because Proxy can directly listen for changes to objects and arrays and has up to 13 intercepting methods. And as a new standard, browser vendors will continue to focus on performance optimization.

Proxy only proxies the first layer of an object. How does Vue3 handle this?

Check if reflect. get is Object, and reactive is used to proxy it.

It is possible to get/set multiple times while monitoring arrays, so how do you prevent multiple get/set triggers?

We can determine whether key is the property of the current proxied object target itself or whether the old value is equal to the new value. Trigger can be executed only when one of the above two conditions is met.

Life cycle

Before each Vue instance is created, it goes through a series of initialization procedures, during which functions called lifecycle hooks are run, giving users the opportunity to add their own code in certain scenarios.

# # @ 2. X

@3. x

Be replaced

  1. beforeCreate -> setup()
  2. created -> setup()

rename

  1. beforeMount -> onBeforeMount
  2. mounted -> onMounted
  3. beforeUpdate -> onBeforeUpdate
  4. updated -> onUpdated
  5. beforeDestroy -> onBeforeUnmount
  6. destroyed -> onUnmounted
  7. errorCaptured -> onErrorCaptured

The addition of

Add the following two callback hooks to facilitate debugging:

  1. onRenderTracked
  2. onRenderTriggered

@ Code Examples

app.vue

<! -- script --> <script setup lang="ts"> import {onBeforeMount, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted, onUpdated } from 'vue'; console.log('__setup__'); onBeforeMount(() => { console.log('__onBeforeMount__'); }); onMounted(() => { console.log('__onMounted__'); }); onBeforeUpdate(() => { console.log('__onBeforeUpdate__'); }); onUpdated(() => { console.log('__onUpdated__'); }); onBeforeUnmount(() => { console.log('__onBeforeUnmount__'); }); onUnmounted(() => { console.log('__onUnmounted__'); }); </script> <! <template> <div class="app">Hello, Vue3! </div> </template> <! <style scoped></style>Copy the code