1. What is it?
MVC is a pattern/framework decouple application into 3 parts: modal, view, and controller.
- Modal: the single source of truth of your app, responsible for data input and data output;
- View: the UI part displayed to users, responsible for display;
- Controller: bind views to modal organically in a desired way, responsible for handling Modal(automatically resulting in View update)
[Note]: MVC is based on Observer Pattern
2. Why use it?
As app grow, the business logic between data and view become complicated. There could be a good chance that several component use same piece of data. It would be painful to call vanilla JS to update every component one by one.
It would be better if there is a way that we can update all those components in a batch automatically when that data piece changes. Here come in the MVC pattern.
- Viewis html code snippet actually on frontend;
- Modaldefines all single source of true for their views
- Controllerwill- binds all Viewsto their correspondingModal, so that Views will update automatically when the Modal changes.
- via callback passed in, it updatesModal, leading toViewsget updated
 
- binds all 
3. How to use it?
The general way:
- Declare modalclass prototype, and its APIs, which will be called bycontroller
- Specify to-be-bound modalforviewcomponents
- pass intention callback tocontrollerto specify how to updatemodal
4. Evolve to MVC pattern (eg: Timer)
We will implement a very simple widget to show how to evolve an app to MVC pattern. The widget is to display whatever we get from API or from callback, and show it inside a div.
- No Pattern - 1 
 2
 3
 4
 5
 6
 7- // View Part: 
 <div id="div1"></div>
 // Logic Part:
 const data = "This is the data"
 const div = document.getElementById('div1');
 div.innerHTML = data;
- Using - Observer Pattern- Now we don’t want to update view by ourselves. Instead, we hope data update will trigger view change automatically. - Observer Patterncan be used here to let the view observe the modal, so whenever modal changes, view will get updated automatically.- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33- function Model(value) { 
 this._value = typeof value === 'undefined' ? '' : value;
 this._listeners = [];
 }
 Model.prototype.set = function (value) {
 var self = this;
 self._value = value;
 // model value change will notify regisered callbacks
 // According to JS running principle, we apply callback asynchronously: WON'T Freeze UI
 // or use requestAnimationFrame, rather than setTimout
 setTimeout(function () {
 self._listeners.forEach(function (listener) {
 listener.call(self, value);
 });
 });
 };
 Model.prototype.watch = function (listener) {
 // register callback
 this._listeners.push(listener);
 };
 // View:
 <div id="div1"></div>
 // Logic:
 (function () {
 var model = new Model();
 var div1 = document.getElementById('div1');
 model.watch(function (value) {
 div1.innerHTML = value;
 });
 model.set('hello, this is a div');
 })();
- Bind - Viewand- Modal- There is a drawback in the above approach: we need to make modal to - watcheach related views manually. But the truth is that all those work are duplicated, and conveys the same functionality: bind views to modal, or let modal watch related views.- We can encapsulate this feature onto modal actually, as follows: - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18- Model.prototype.bind = function (node) { 
 // put `watch` and `common callback` inside bind
 this.watch(function (value) {
 node.innerHTML = value;
 });
 };
 // View:
 <div id="div1"></div>
 <div id="div2"></div>
 // Logic:
 (function () {
 var model = new Model();
 model.bind(document.getElementById('div1'));
 model.bind(document.getElementById('div2'));
 model.set('this is a div');
 })();
- MVC Pattern - Now, it is better. We bind view and modal😄~ And we can bind as many as views to a modal as we want😱. - But still, we need to manually bind view to modal. Is there a way our code can handle it for us, as long as we tell the code which modal the view want to bind. - Yeah, You might already get it! We can specify modal piece for the view by using HTML attribute, like - <div bind='modal1' />. Thus, the code can know the desired modal current view wants to bind.- We will create an util object that can - firstly, automatically bind views to their modals,
- secondly, receive a callback to update modal in programmer’s desired way
 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26- function Controller(callback) { 
 var models = {};
 // find all views with `bind` attr
 var views = document.querySelectorAll('[bind]');
 // convert views into array
 views = Array.prototype.slice.call(views, 0);
 views.forEach(function (view) {
 var modelName = view.getAttribute('bind');
 // get the modal for this view, or create new one for it
 models[modelName] = models[modelName] || new Model();
 // bind view and its modal
 models[modelName].bind(view);
 });
 // update modals using specified callback
 callback.call(this, models);
 }
 // View:
 <div id="div1" bind="model1"></div>
 <div id="div2" bind="model1"></div>
 // Controller:
 new Controller(function (models) {
 var model1 = models.model1;
 model1.set('this is a div');
 });
- The whole MVC - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30- function Model(value) { 
 this._value = typeof value === 'undefined' ? '' : value;
 this._listeners = [];
 }
 Model.prototype.set = function (value) {
 var self = 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) {
 var models = {};
 var views = Array.prototype.slice.call(document.querySelectorAll('[bind]'), 0);
 views.forEach(function (view) {
 var modelName = view.getAttribute('bind');
 (models[modelName] = models[modelName] || new Model()).bind(view);
 });
 callback.call(this, models);
 }
5. Test our MVC framework
We will use our MVC framework to implement a Timer as follows
| 1 | // View: | 
6. Thoughts
For almost all frameworks like Redux, Flux. It use similiar ways to handle View and Modal. Here we use Observer Pattern to implement MVC. 
Based on my understanding, Redux and Flux use Publisher-Subscriber Pattern will handles events better than Observer Pattern.