This is from the training I did before. I have deleted some relevant information and referred to a lot of materials (
List of resources), thank you, 😘

With the development of front-end technology, front-end framework is also changing.

Age of DOM manipulation

DOM (Document Object Model) expresses HTML documents as a tree structure and defines a standard way to access and manipulate HTML documents.

Front-end development basically involves HTML pages, which means dealing with the DOM.

In the earliest days of the Web front end, it was a static yellow Page where the content couldn’t be updated.

Gradually, users can perform simple operations on Web pages, such as submitting forms and uploading files. But the whole page part or the whole update, or by refreshing the page to achieve.

With the advent of AJAX, user operations on front-end pages became more and more complex, leading to the era of direct manipulation of DOM elements. To manipulate DOM elements, use the DOM API. Common DOM apis include:

type methods
Nodes in the query GetElementById, getElementsByName, getElementsByClassName, getElementsByTagName, querySelector, querySelectorAll
Node to create CreateElement, createDocumentFragment, createTextNode, cloneNode
Node to modify AppendChild, replaceChild, removeChild, insertBefore, innerHTML
Relationship between nodes ParentNode, previousSibling, and childNodes
Node properties InnerHTML, Attributes, getAttribute, setAttribure, getComputedStyle
Load content XMLHttpRequest, ActiveX

You can use the DOM API to do anything on the front page, but with the complexity of web applications, using native apis is inefficient. So jQuery, the interactive framework for manipulating the DOM, was born.

Why is jQuery the most popular framework in this era? Mainly because he solves so many problems for front-end developers:

  • Encapsulates the DOM API, providing a unified and convenient way to call.
  • Simplifies the selection of elements, you can quickly select the desired element.
  • Provides an AJAX interface for unified encapsulation of XMLHttpRequest and ActiveX.
  • Unified event handling.
  • Provides asynchronous processing.
  • Compatible with most major browsers.

In addition to addressing these issues, jQuery also has a great ecosystem and a huge number of plugins that make front-end development much smoother than it used to be. Especially in the IE6 and IE7 era, the lack of jQuery means infinite compatibility.

/ / the DOM API: document. QuerySelectorAll ('#container li');

// jQuery
$('#container').find('li');Copy the code

With the development of HTML5 technology, many of the methods provided by jQuery have been implemented in the native standard, and the need for jQuery has gradually decreased. youmightnotneedjquery.com/

Over time, SPA (Single Page Application), where the entire content of an Application is in a Single Page and different content is loaded entirely through asynchronous interaction, became unmanageable using jQuery to manipulate the DOM directly. The binding of events on a page can become messy, and in this case, a framework that automatically manages the interaction between DOM and data on a page is urgently needed.

The MV * mode

MVC, MVP, and MVVM are common Architectural patterns that improve the way code is organized by separating concerns.

Purely conceptually, it is difficult to distinguish and feel the difference between these three patterns in a front-end framework. Let’s take a look at an example: we have a component that can add and subtract values: it displays values, and two buttons can add and subtract values, and the values are updated.

The Model layer encapsulates the data associated with the application’s business logic and how it is processed. Here, we encapsulate the numerical variables needed in Model, and define add, sub, and getVal to manipulate the numerical methods.

var myapp = {}; // Create the application object myapp.model =function() { var val = 0; // This. Add = thisfunction(v) {
        if (val < 100) val += v;
    };

    this.sub = function(v) {
        if (val > 0) val -= v;
    };

    this.getVal = function() {
        return val;
    };
};Copy the code

View, as the View layer, is mainly responsible for the display of data.

myapp.View = function() {/* View element */ var$num = $('#num'),
        $incBtn = $('#increase'),
        $decBtn = $('#decrease'); /* Render data */ this.render =function(model) {
        $num.text(model.getVal() + 'rmb');
    };
};Copy the code

Here, the logic of data from the model layer to the view layer is completed through Model&View. But for an application, this is not enough. We need to respond to user actions and update the View and Model synchronously.

Front-end MVC pattern

MVC (Model View Controller) is a classic design pattern. The user’s operation on the View is handed over to the Controller. In the Controller, the interface of Model is called to operate on the data in response to the events of the View. Once the Model changes, relevant views will be notified to update.

The Model layer is used to store the data of the business, and the Model notifies the relevant views when the data changes.

// Model
myapp.Model = function() {
    var val = 0;

    this.add = function(v) {
        if (val < 100) val += v;
    };

    this.sub = function(v) {
        if (val > 0) val -= v;
    };

    this.getVal = function() {
        returnval; }; Var self = this, views = []; this.register =function(view) {
        views.push(view);
    };

    this.notify = function() {
        for(var i = 0; i < views.length; i++) { views[i].render(self); }}; };Copy the code

The observer pattern is used between the Model and the View, where the View registers with the Model in advance to observe the Model in order to update the data that changes on the Model.

The policy pattern is used between View and Controller, where the View introduces instances of Controller to implement specific response policies, such as the button click event in this chestnut:

// View
myapp.View = function(controller) {
    var $num = $('#num'),
        $incBtn = $('#increase'),
        $decBtn = $('#decrease');

    this.render = function(model) {
        $num.text(model.getVal() + 'rmb'); }; /* Bind event */$incBtn.click(controller.increase);
    $decBtn.click(controller.decrease);
};Copy the code

The Controller is the link between the model and the view. MVC encapsulates the response mechanism in the Controller object. When the user interacts with the application, the event trigger in the Controller starts to work.

// Controller
myapp.Controller = function() {
    var model = null,
        view = null;

    this.init = function() {/* Initialize Model and View */ Model = new myapp.model (); view = new myapp.View(this); /* Model. Register (View); model.notify(); }; /* Let Model update the value and tell View to update the View */ this.increase =function() {
        model.add(1);
        model.notify();
    };

    this.decrease = function() {
        model.sub(1);
        model.notify();
    };
};Copy the code

Here we instantiate the View and register it with the corresponding Model instance, notifying the View to update it when the Model changes.

It is obvious that the business logic of MVC mode is mainly focused on Controller, while the front View has the ability to process user events independently. When each event flows through Controller, this layer will become very bloated. In MVC, View and Controller are generally one-to-one corresponding, which represent a component together. The too close connection between View and Controller makes the reusability of Controller a problem. What should I do if I want to share a Controller with multiple views?

Front-end MVP mode

MVP (Model-View-Presenter) is an improvement of the MVC pattern. Similar to MVC: Controller/Presenter handles business logic, Model handles data, and View handles display.

In MVC, the View has direct access to the Model. The MVP View does not use the Model directly, but rather provides an interface for the Presenter to update the Model and then update the View in observer mode.

Compared with MVC, MVP mode decouples View and Model, completely separating View and Model to make responsibility division clearer. Since the View doesn’t depend on the Model, it can be spun off as a component that provides a set of interfaces to the upper level.

// Model
myapp.Model = function() {
    var val = 0;

    this.add = function(v) {
        if (val < 100) val += v;
    };

    this.sub = function(v) {
        if (val > 0) val -= v;
    };

    this.getVal = function() {
        return val;
    };
};Copy the code

The Model layer is still the data that is primarily relevant to the business and the corresponding way to process that data, very simple.

// View
myapp.View = function() {
    var $num = $('#num'),
        $incBtn = $('#increase'),
        $decBtn = $('#decrease');

    this.render = function(model) {
        $num.text(model.getVal() + 'rmb');
    };

    this.init = function() {
        var presenter = new myapp.Presenter(this);

        $incBtn.click(presenter.increase);
        $decBtn.click(presenter.decrease);
    };
};Copy the code

The MVP defines the interface between the Presenter and View, so that user operations on the View are transferred to the Presenter. For example, the View exposes the setter (render method) for the Presenter to call. When the Presenter notifies the Model of the update, the Presenter calls the interface provided by the View to update the View.

// Presenter
myapp.Presenter = function(view) {
    var _model = new myapp.Model();
    var _view = view;

    _view.render(_model);

    this.increase = function() {
        _model.add(1);
        _view.render(_model);
    };

    this.decrease = function() {
        _model.sub(1);
        _view.render(_model);
    };
};Copy the code

Presenters are the middleman between the View and the Model. In addition to basic business logic, there is a lot of code that needs to “manually synchronize” data from the View to the Model and from the Model to the View, which can be very heavy and difficult to maintain. If a Presenter needs to render more views, it has to focus too much on a particular view, and if the view needs to change, the Presenter needs to change too.

Front-end MVVM mode

MVVM (Model-view-ViewModel) was first proposed by Microsoft. ViewModel means “Model of View” — the Model of a View.

MVVM automates the synchronization logic between View and Model. The View and Model synchronization is no longer handled manually by the Presenter, but by the data binding function provided by the framework, which simply tells it which part of the Model the View displays.

We use Vue to complete the chestnut.

In MVVM, we can call the Model the data layer because it only cares about the data itself and doesn’t care about any behavior (formatting the data is the responsibility of the View), and we can think of it here as a JSON-like data object.

// Model
var data = {
    val: 0
};Copy the code

Unlike MVC/MVP, views in MVVM use template syntax to declaratively render data into the DOM, and when the ViewModel updates the Model, it updates it to the View via data binding.

<! -- View --> <div id="myapp">
    <div>
        <span>{{ val }}rmb</span>
    </div>
    <div>
        <button v-on:click="sub(1)">-</button>
        <button v-on:click="add(1)">+</button>
    </div>
</div>Copy the code

The ViewModel is basically MVC’s Controller and MVP’s Presenter, and is the focus of the whole pattern. The business logic is also focused here, and one of the core is data binding. Unlike MVP, without the interface provided by the View to The Presente, the data synchronization between the View and the Model, which was previously the responsibility of the Presenter, is handled by the data binding in the ViewModel. When the Model changes, the ViewModel automatically updates. As the ViewModel changes, the Model updates.

new Vue({
    el: '#myapp',
    data: data,
    methods: {
        add(v) {
            if(this.val < 100) {
                this.val += v;
            }
        },
        sub(v) {
            if(this.val > 0) { this.val -= v; }}}});Copy the code

Overall, it’s much simpler than MVC/MVP. Not only does it simplify business and interface dependencies, but it also solves the problem of frequently updating data (previously, DOM manipulation with jQuery was cumbersome). Because in MVVM, the View is unaware of the Model’s existence, and the ViewModel and Model are unaware of the View, this low-coupling pattern makes the development process easier and improves application reusability.

Data binding

In Vue, two-way data-binding technology is used, that is, the change of View can make the change of Model in real time, and the change of Model can be updated to the View in real time. Two-way data binding can simply be thought of as a template engine, but renders in real time based on data changes.

Someone had the audacity to file a patent:

Data change detection

Different MVVM frameworks have different techniques for implementing bidirectional data binding. At present, some mainstream data binding methods are as follows:

Manually triggered binding

Manual triggering instruction binding is a relatively direct implementation method. The main idea is to define get() and set() methods on data objects, and manually trigger get() or set() functions to obtain and modify data. After changing data, the re-rendering function of View layer in get() and set() functions will be actively triggered.

Dirty detection mechanism

Angularjs is a typical framework that uses dirty detection to update View layer operations by checking for dirty data.

The basic principle of dirty detection is to find all elements related to an attribute value of a ViewModel object when the value of the attribute changes, and then compare the data changes. If the changes are made, the Directive is called to rescan the element.

Front-end data object hijacking

Data hijacking is a widely used method at present. The basic idea is to use Object.defineProperty and Object.defineProperies to listen for properties get () and set() on ViewModel data objects and scan element nodes when data is read and assigned. Run the Directive specifying the corresponding node so that the ViewModel can be assigned with the generic equal sign.

Vue is a typical data hijacking and publish-subscribe framework.

  • Observer Data listener: Listens for all attributes of a data object (data hijacking) and notifies subscribers of changes to the data.
  • Compiler Instruction parser: scans the template, parses the instructions, and binds the specified events.
  • Watcher subscriber: associate Observer with Compile, subscribe to receive notification of property changes, perform instructions binding, and update views.

ES6 Proxy

Previously we talked about how Proxy implements data hijacking:

In summary, from direct DOM operation to MVC design pattern, then to MVP and then to MVVM framework, the improvement principle of front-end design pattern has been developing towards the basic direction of high efficiency, easy implementation, easy maintenance and easy expansion. Although various front-end frameworks have matured and started to iterate to higher versions, they are not finished yet. Our programming objects are still not divorced from the basic routines of DOM programming. The improvement of framework over and over has greatly improved the development efficiency, but the efficiency of DOM element operation remains unchanged. To solve this problem, some frameworks put forward the concept of Virtual DOM.

Virtual DOM

MVVM’s front-end interaction mode greatly improves programming efficiency, and automatic two-way data binding allows us to shift the core of page logic implementation to data layer modification operations, rather than directly manipulating the DOM in the page. Although MVVM has changed the logical way of front-end development, the rendering and change of the View layer reflected by the data layer on the page are still completed by corresponding instructions for DOM operation, and usually a change in the ViewModel may trigger multiple instructions on the page to operate the DOM change. Brings in a lot of page structure layer DOM manipulation or rendering.

For example, a piece of pseudocode:

<ul>
    <li repeat="list">{{ list.value }}</li>
</ul>

let viewModel = new VM({
    data:{
        list:[{value: 1}, {value: 2}, {value: 3}]}})Copy the code

Use the MVVM framework to generate a list of numbers, if the content to be displayed becomes [{value: 1}, {value: 2}, {value: 3}, {value: 4}], in the MVVM framework, the entire list is normally re-rendered, including the parts of the list that do not need to be changed. In fact, if you change the DOM directly, you just insert a new

  • element at the end of the
      child element. But in the normal MVVM framework, we usually don’t do this. Needless to say, MVVM’s View layer update mode in this case consumes more unnecessary performance.
  • So how do you improve the ViewModel so that the browser knows that you’re really just adding an element? By comparing the

    Value: the value: [{1}, {2}, {3} value:] and [{value: 1}, {value: 2}, {value: 3}, {4} value:]

    {value: 4} is added, so how do you reflect this added data to the View layer? You can compare the new Model data to the old Model data, and then record how and where the ViewModel changes, to see how the View layer should be updated this time, rather than just re-rendering the entire list.

    The data in the ViewModel is just another data structure identifier that describes the contents of the page View, but it needs to be compiled with the specific MVVM description syntax to generate the full DOM structure.

    The attribute hierarchy of JavaScript objects can be used to describe the structure of the ABOVE HTML DOM object tree. When the data is changed, a new modified Elements is generated and compared with the original Elemnets structure. After comparison, which DOM Elements can be changed can be decided.

    The ulElement object in the previous example can be understood as VirtualDOM. It is generally believed that Virtual DOM is a JavaScript object that can directly describe the structure of an HTMLDOM. The browser can create a unique HTMLDOM structure according to certain rules according to its structure. Overall, the interactive mode of the Virtual DOM reduces the number of DOM scans or manipulations in MVVM or other frameworks, and minimizes page DOM manipulation based on JavaScript objects where appropriate after data changes, avoiding massive re-rendering.

    The diff algorithm

    Virtual-dom execution process:

    Simulate the DOM tree with JS objects -> compare the differences between two virtual DOM trees -> apply the differences to the real DOM tree

    In Virtual DOM, the most important link is to find out the difference between two Virtual DOM through comparison and obtain a difference tree object.

    The comparison algorithm for Virtual DOM is actually the traversal algorithm for multi-fork tree structure. However, to find the smallest modification step between any two trees, the nodes are generally compared in sequence through cyclic recursion, and the algorithm complexity reaches O(n^3), which is very high. For example, to show more than 1000 nodes, the most pessimistic comparison will be performed a billion times in sequence. So the comparison algorithm used by different frameworks is actually a slightly simplified algorithm.

    Take React for example. Since moving a component to a different level is rare in Web applications, it is mostly horizontal. Therefore, React attempts to compare two trees layer by layer. Once there is inconsistency, the lower layer will no longer be compared, which significantly reduces the complexity of the comparison algorithm with a small loss.

    Front-end frameworks evolve very quickly, so only when we know the reasons for the evolution can we understand the advantages and disadvantages of each framework and choose the most appropriate framework according to the actual situation of the application. The same is true for other technologies.