1. What is MVC

  • M is mode, namely data model, responsible for data-related tasks, including adding, deleting, modifying and checking data
  • V is the view, the view layer, the interface that the user can see
  • C is the Controller, the Controller that listens for user events and then calls M and V to update data and views

Next, pseudo-code will be used to represent the work content of the three parts

1.1 Model Data Model

let Model={
    data:{data source},create:{add data},delete:{delete data},update(data){
        Object.assign(m.data,data)  // Replace old data with new data
        eventBus.trigger('m:update')  // eventBus triggers the 'M :update' message, notifying the View to refresh the interface
    },
    get:{get data}}Copy the code

1.2 View View layer

let View={
    el: The element to refresh, HTML:'Refresh content to display on the page'
    init(){v.l: Initialize the element to be refreshed},render(){refresh page}}Copy the code

1.3 Controller Controller

By binding events, the controller calls M and V to update data and views according to user operations

let Controller={
    init(){
        v.init()  // Initialize the View
        v.render()  // Render the page for the first time
        c.autoBindEvents()  // Automatic event binding
        eventBus.on('m:update'.() = >{v.render()}  // When enentsBus triggers 'm:update', the View is refreshed},events:{events are stored as hash table records},/ / such as:
   events: {
    'click #add1': 'add'.'click #minus1': 'minus'.'click #mul2': 'mul'.'click #divide2': 'div',},add() {
      m.update({n: m.data.n + 1})},minus() {
      m.update({n: m.data.n - 1})},mul() {
      m.update({n: m.data.n * 2})},div() {
      m.update({n: m.data.n / 2})},method(){data= new data m.pdate (data)// Controller notifies model to update data
    },
    autoBindEvents(){
      for (let key in c.events) { // Iterate through the Events table and automatically bind events
      const value = c[c.events[key]]
      const spaceIndex = key.indexOf(' ')
      const part1 = key.slice(0, spaceIndex) / / get a 'click'
      const part2 = key.slice(spaceIndex + 1)  / / get '# add1'
      v.el.on(part1, part2, value)
    }
}
Copy the code

1.4 the sample

The value changes each time the corresponding button is clicked and retains the previous value after refreshing. The basic idea is to listen for the CLICK event MVC action as follows

import './app1.css'
import $ from 'jquery'

const eventBus = $(window)
// All data related to m
const m = {
  data: {
    n: parseInt(localStorage.getItem('n'))},create() {},
  delete() {},
  update(data) {
    Object.assign(m.data, data)
    eventBus.trigger('m:updated')
    localStorage.setItem('n', m.data.n)
  },
  get(){}}// All views are placed in v
const v = {
  el: null.html: ` 
      
{{n}}
< button id = "minus1" > 1 < / button > < button id = "mul2" > * 2 < / button > < button id = "divide2" > present 2 < / button > < / div > < / div > `
.init(container) { v.el = $(container) }, render(n) { if(v.el.children.length ! = =0) v.el.empty() $(v.html.replace('{{n}}', n)) .appendTo(v.el) } } // all others are c const c = { init(container) { v.init(container) v.render(m.data.n) // view = render(data) c.autoBindEvents() eventBus.on('m:updated'.() = > { console.log('here') v.render(m.data.n) }) }, events: { 'click #add1': 'add'.'click #minus1': 'minus'.'click #mul2': 'mul'.'click #divide2': 'div',},add() { m.update({n: m.data.n + 1})},minus() { m.update({n: m.data.n - 1})},mul() { m.update({n: m.data.n * 2})},div() { m.update({n: m.data.n / 2})},autoBindEvents() { for (let key in c.events) { const value = c[c.events[key]] const spaceIndex = key.indexOf(' ') const part1 = key.slice(0, spaceIndex) const part2 = key.slice(spaceIndex + 1) v.el.on(part1, part2, value) } } } export default c Copy the code

In external references

import x from './app1.js'
x.init('#app1') // Pass in the container div
// Then write one in HTML
<div id="app1"> </div>   
// as a container
Copy the code

2. EventBus

2.1 What is EventBus

  • EventBus is primarily used for communication between objects, such as in the example above, where the Model data Model and the View Model are unaware of each other’s existence but need to communicate
  • Conclusion: Using eventBus can meet the minimum knowledge principle, m and V do not know each other’s details, but can call each other’s functions

2.2 What apis does EventBus provide

  • EventBus provides apis such as ON, off, and trigger. On is used to listen for events and trigger is used to trigger events. For example, in the MVC model above, when the M data model is updated, trigger triggers an event
const m = {
  ....
  update(data) {
    Object.assign(m.data, data)
    eventBus.trigger('m:updated')  // Notify the View layer that I have updated the data and the view should start working
    localStorage.setItem('n', m.data.n)
  },
  ....
}
Copy the code

Then in controller, the Controller listens for events with ON and tells the View model to re-render the page

const c = {
  init(container) {
    v.init(container)
    v.render(m.data.n) // view = render(data)
    c.autoBindEvents()
    eventBus.on('m:updated'.() = > {   // Controller will use on to listen for events,
      // Tell the view model to re-render the page
      console.log('here')
      v.render(m.data.n)
    })
  },
  ... 
}
Copy the code

3. Table driver programming

When we need to judge more than three cases and make corresponding things, we often need to write a lot of If and else, which is not readable enough. In order to improve the readability of the code, we can use table-driven programming, save the value used to judge the If condition into a hash table, and then take an example from the table: In the example above, we add, subtract, multiply and divide the four buttons to determine which one was clicked and then modify the output value. Traditionally, we would bind the click event to each of the four buttons and then write the four callback functions to modify the value

$button1.on('click'.() = > {
    let n = parseInt($number.text())
    n += 1
    localStorage.setItem('n', n)
    $number.text(n)
})

$button2.on('click'.() = > {
    let n = parseInt($number.text())
    n -= 1
    localStorage.setItem('n', n)
    $number.text(n)
})

$button3.on('click'.() = > {
    let n = parseInt($number.text())
    n = n * 2
    localStorage.setItem('n', n)
    $number.text(n)
})

$button4.on('click'.() = > {
    let n = parseInt($number.text())
    n = n/2
    localStorage.setItem('n', n) $number.text(n)}) -------- after the event delegate -------const c = {
    init(container) {
        v.init(container)
        v.render(m.data.n)
        c.BindEvents()
    }
    BindEvents() {
        v.el.on('click'.'#add1'.() = > {
            m.data.n += 1
            v.render(m.data.n)
        })
        v.el.on('click'.'#minus1'.() = > {
            m.data.n -= 1
            v.render(m.data.n)
        })
        v.el.on('click'.'#mul2'.() = > {
            m.data.n *= 2
            v.render(m.data.n)
        })
        v.el.on('click'.'#divide2'.() = > {
            m.data.n /= 2
            v.render(m.data.n)
        })
    }
}
Copy the code

But this is too much trouble, update the measures:

  1. Bind the parent element of the add, subtract, multiply and divide buttons with only one event listener
  2. Use hash tables to store buttons and their corresponding operations
const c = {
  events: {
    'click #add1': 'add'.'click #minus1': 'minus'.'click #mul2': 'mul'.'click #divide2': 'div',},add() {
    m.update({n: m.data.n + 1})},minus() {
    m.update({n: m.data.n - 1})},mul() {
    m.update({n: m.data.n * 2})},div() {
    m.update({n: m.data.n / 2})},autoBindEvents() {
    for (let key in c.events) {
      const value = c[c.events[key]]
      const spaceIndex = key.indexOf(' ')
      const part1 = key.slice(0, spaceIndex)
      const part2 = key.slice(spaceIndex + 1)
      v.el.on(part1, part2, value)
    }
  }
Copy the code

4. The modular

  • Modularity is the extraction of relatively independent code from a large piece of code into small and concise modules
  • Each module is independent of each other, which facilitates future maintenance and modification
  • ES6 syntax introduces Import and export to implement modularity when we package the Controller model in app1.js and export controller:
export default c  // Export by default
export {c} // The other way to export must be curly braces
Copy the code

In main.js we want to use controller:

import x from './app1.js'
/ / equivalent to the
import {default as x} from './app1.js'
x.init('#app1')
Copy the code

More examples of rename exports:

// inside module.mjs
export { function1, function2 };

// inside main.mjs
import { function1 as newFunctionName,
         function2 as anotherNewFunctionName } from '/modules/module.mjs';
Copy the code

Resources: MDNmodules modularization