Since around 09, MVC has grown in the forefront of the field, and finally in the past 2015 with the launch of react native to usher in a big outbreak:
AngularJS, Emberjs, Backbone, Reactjs, Riotjs, Vuejs ... The emergence and change of a series of names in a cursory style,
Some of them have faded out of sight, some are growing rapidly, and some have independently who but myself in certain ecological environments.
However, MVC has and will continue to profoundly influence the way in which front-end engineers think and how they work.
Many examples of MVC are starting with a concept of a specific framework, such as Backbone's collection or ANGULARJS model, which is certainly a good idea.
But frameworks are frameworks, not class libraries (jQuery) or toolsets (underscore), because they have a lot of good design ideas and best practices behind them,
The essence of these designs is complementary, interlocking and indispensable, and it is not an easy task to see the nature of a particular design pattern in a short period of time through a complex framework.
This is the origin of this essay--the prototype code that is born to help you understand the concept should be as simple as possible, just enough to understand the concept.
1. The basis of MVC is the Observer pattern, which is the key to achieving model and view synchronization
For the sake of simplicity, only one primitive value is included in each model instance.
function Model (value) { This. _value =typeofValue = ='undefined'?"': value; This. _listeners = [];} Model.prototype.Set=function (value) {varSelf = This; Self._value=value; //when the value in model changes, the registered callback function should be notified//in accordance with the general mechanism of JavaScript event handling, we call callback functions asynchronously//If you feel settimeout affects performance, you can also use RequestanimationframesetTimeout (function () {Self._listeners.foreach (function (listener) {Listener.call (self, value ); }); });}; Model.prototype.watch=function (listener) {//registering the listener's callback function This. _listeners.push (listener);};//HTML code:<div id="Div1"></div>//Logic Code:(function () {varModel =NewModel (); varDiv1 = document.getElementById ('Div1'); Model.watch (function (value) {div1.innerhtml=value; }); Model.Set('Hello, this is a div');}) ();
With the observer pattern, we have realized that when the set method of calling model changes its value, the template is also updated synchronously, but this implementation is very awkward,
Because we need to manually monitor the change of the model value (through the Watch method) and pass in a callback function, is there any way to make the view (one or more DOM node) and model more simple binding?
2. Implement the Bind method, bind model and view
Model.prototype.bind =function (node) {//put the logic of watch and the general callback function here This. Watch (function (value) {node.innerhtml=value; });};//HTML code:<div id="Div1"></div><div id="Div2"></div>//Logic Code:(function () {varModel =NewModel (); Model.bind (document.getElementById ('Div1')); Model.bind (document.getElementById ('Div2')); Model.Set('This is a div');}) ();
With a simple package, the bindings between the view and the model have taken shape, and it's easy to implement even if you need to bind multiple view.
Note that bind is a native method on the function class prototype, but it is not closely related to MVC, and I really like the word bind.
One language, concise, so simply in here the native method covered, we can ignore. And, even though the complexity of the bindings is reduced,
This step still relies on our manual completion, is it possible to completely decouple the logic of the binding from the business code?
3. Implementing the Controller, decoupling the bindings from the logic code
A careful friend may have noticed that, although it is about MVC, there are only model classes in the above, and the view class does not appear to be understandable,
After all, HTML is an out-of-the-box view (in fact, in this article from beginning to end only using HTML as View,javascript code does not appear in the View Class),
Why is the controller class invisible? Don't worry, the so-called "logic Code" is a framework logic (let's call this prototype toy framework) and the business logic of high-coupling code snippets,
Now we're going to break it down.
If you want to give the binding logic to the framework, you need to tell the framework how to complete the binding. Because JS is more difficult to complete annotation (annotations),
We can make this tag in view--using the HTML tag attribute is a simple and effective way.
function Controller (callback) {varModels = {}; //find all elements with the bind attribute varviews = Document.queryselectorall ('[bind]'); //treat views as normal arraysviews = Array.prototype.slice.call (views,0); Views.foreach (function (view) {varModelName = View.getattribute ('Bind'); //Remove or create a new model that the element is bound toModels[modelname] = Models[modelname] | |NewModel (); //completes the binding of the element and the specified modelmodels[modelname].bind (view); }); //invoking the specific logic of the controller, passing the models in to facilitate business processingCallback.call ( This, models);}//HTML:<div id="Div1"Bind="Model1"></div><div id="Div2"Bind="Model1"></div>//Logic Code:NewController (function (models) {varModel1 =Models.model1; Model1.Set('This is a div');});
Is that so simple? It's so simple. The essence of MVC is to complete the business logic in the controller and modify the model, and the model changes cause the view to be updated automatically.
These logic is reflected in the code above, and supports multiple view, multiple model. It's not enough for a production project, but it's a little bit helpful to everyone's MVC learning.
The "frame" code that removes the comment after finishing:
function Model (value) { This. _value =typeofValue = ='undefined'?"': value; This. _listeners = [];} Model.prototype.Set=function (value) {varSelf = This; Self._value=value; SetTimeout (function () {Self._listeners.foreach (function (listener) {Listener.call (self, value); }); });}; Model.prototype.watch=function (listener) { This. _listeners.push (listener);}; Model.prototype.bind=function (node) { This. Watch (function (value) {node.innerhtml=value; });}; function Controller (callback) {varModels = {}; varviews = Array.prototype.slice.call (Document.queryselectorall ('[bind]'),0); Views.foreach (function (view) {varModelName = View.getattribute ('Bind'); Models[modelname]= Models[modelname] | |NewModel (); Models[modelname].bind (view); }); Callback.call ( This, models);}
Postscript:
The author in the process of learning flux and redux, although mastered the use of tools, but only know it but do not know why,
The "Flux eschews MVC in favor of a unidirectional data flow", which has been emphasized in Reactjs's official documentation, is not very understanding,
Always feel that one-way data flow and MVC do not conflict, do not understand why in the Reactjs document that the two will be antagonistic, there is no me, I have no he (eschew, avoid).
Finally decided to go back to the definition of MVC to re-study, although the ordinary work of careless copy and paste, but we occasionally have to be willful one, a literal, right?
This kind of way also really helped me to understand this sentence, here can share their thoughts to everyone: The reason is that MVC and flux in the unidirectional data flow similar,
It is possible that the--MVC caused by the relationship between MVC and the Observer pattern is based on the observer pattern, and flux is the same, so the origin of this similarity is the Observer pattern,
Rather than MVC and flux itself. This understanding was also confirmed in the original design pattern of the four-person group:
"The first and perhaps best-known example of the Observer pattern appears in Smalltalk Model/view/controller (MVC),
The user interface framework in the SMALLTALK environment [KP88]. MVC ' s Model class plays the role of Subject,
While View is the base class for observers. ”。
If the reader is interested in expanding on the basis of such a prototype toy, you can refer to some of the following directions:
- Implement two-way binding to input class labels
- Enables precise control of the scope controlled by the controller, where a controller controls the entire DOM tree
- Implementing the View layer logic for DOM node hide/show, create/Destroy
- Integrates with virtual DOM to increase DOM diff functionality and improve rendering efficiency
- Provides dependency injection capabilities for control inversion
- Security checks on innerHTML's assigned content to prevent malicious injection
- Implement the Model collection logic, where each model has only one value
- Using the setter in ES5 to change the implementation of the set method makes it easier to modify the model.
- Adding control to attributes and CSS in the view layer
- Supports syntax similar to double curly braces in Angularjs, only partial HTML is bound
30 lines of code implement MVC in JavaScript