The code should be written in a way that others can’t understand (End of Chapter 28)

This article refers to the book "JavaScript Design Patterns"-Zhang Rongming

Preface

  Everyone, this section is the last article in the series I don’t understand. I’m very grateful to Mr. Zhang Rongming’s book "JavaScript Design Patterns". I have read it three times from start to finish. The improvement to me is really huge. I also recommend that you read it if you have the opportunity.

  The design pattern to be introduced in this section is also the last chapter in the book. It is currently recognized as the best framework pattern, MVVM . As you can see from the name, MVVM also evolved from the MVC pattern, to be precise, from the MVP pattern. Evolutionary.

  MVC evolved to MVP because each time a new requirement is added, the view layer and the controller layer have to be modified. So why did MVP evolve to MVVM ?

  There is demand for development. Everyone feels, what are the shortcomings of the MVP model in the previous section ?

  It seems that there are no shortcomings, except that it is not easy to understand, it can flexibly increase the demand as long as the manager is modified.

  In fact, "difficult to understand" is the disadvantage of the MVP model. The core of an excellent framework must meet rapid development. The reason why MVP cannot be applied on a large scale is that it must be based on the understanding of the principle to perform related operations at the manager layer, which leads to many beginners’ early investment The cost is too high, increasing the investment of the entire project.

  For this problem, the MVVM model is derived .
Picture from the Internet

MVVM mode

  MVVM stands for Model—View—ViewModel .

  Layer is a view ( View ) view of a tailor-made model ( the ViewModel ), and the model view ( the ViewModel create attributes and methods), the layer of the view ( View Binding Data () the Model ) and interact.

  Everyone is thinking about a question. In the previous section, we separated the view and used the manager to control the view. Can we turn it around and control the manager by creating a view?

  Of course, to create a view is to create a view in the page, so in essence, it is to write HTML in the page . If you improve the role of the view, you can create view components by directly writing HTML code in the page , let the controller or manager listen to these view components, and process these components to complete the expected functions. Is this implementation way for those who are only familiar with HTML? Developers can easily complete some functional requirements.

  This is the MVVM mode praised by heroes from all walks of life .

  Create an example to show and try, here we create a slider and progress bar.
Insert picture description here

  Our expected goal is to be achieved in the following ways.

<div class="first" data-bind="type : 'slider', data: demo1"></div>
<div class="second" data-bind="type : 'slider', data: demo2">test</div>
<div class="third" data-bind="type : 'progressbar', data: demo3"></div>

  We analyze the above code as follows. First, the HTML code is the view ( V ) layer in MVVM , and the three components can be divided into two categories.

  The first type is the first two, sliding components, and the second type is the last one, the progress bar component. It can be seen from the type value in the custom attribute data-bind . The format of the content in the data-bind attribute is very similar to that of the object, except for a pair of braces. The following data value represents the data model required by the component. Of course, these data must be given in the data model.

  Our core purpose is to bind JavaScript behaviors to elements through data-bind custom attribute values .

The elements in the   view ( V ) layer are to be monitored by the view model ( VM ) layer, so we implement the monitoring of these elements in the view model ( VM ) layer and bind behaviors for them. So what we have to do next is to create a VM object, but before that we have to create a VM environment.

View model layer

//屏蔽压缩报错
~(function() {
    
    
    //在闭包中获取全局变量
    var window = this || (0, eval)('this');
    //获取页面字体大小,作为创建页面 UI 尺寸参照物
    var FONTSIZE = function() {
    
    
        //获取页面 body 元素字体大小并转化成整数
        return parseInt(document.body.currentStyle ? document.body.currentStyle['fontSize'] :
        getComputedStyle(document.body, false)['fontSize']);
    } ();
    //视图模型对象
    var VM = function() {
    
    };
    //将视图模型对象绑定在 Window 上,供外部获取
    window.VM = VM;
}) ();

  In the VM environment, we first obtain the global variable Window (of course, the global variable is not Window in a non-browser environment ), and obtain the font size in the page body element as the reference size for creating the page UI component. Finally, the model view ( VM ) layer objects are bound to the Window for external operations.

  The next thing we have to do is to deal with the view model object VM . In this object, we save the method of instantiating the component. Since the method of creating each type of component is different, we will save the method of creating the UI component as the method of the strategy object, so that it is easier to expand other UI components in the future .

var VM = function() {
    
    
    //组件创建策略对象
    var Method = {
    
    
        //进度条组件创建方法
        progressbar: function() {
    
    };
        //滑动条组件创建方法
        slider: function() {
    
    }
    }
} ();

  Since there are only two components in the example, we only create two component strategy methods in the strategy object. The progressbar can create a progress bar, and the slider can create a sliding bar. Below we implement these two strategy methods in turn.

  This part belongs to the core of the VM . In the future, when we add other components, we only need to add the strategy method of creating the component to the strategy object.

  Of course, we can also add extension methods to the Method object, so that we can extend the strategy object externally to create more complex and diverse UI components.

/**
* 进度条组件创建方法
* dom   进度条容器
* data  进度条数据模型
*/
progressbar: function (dom, data) {
    
    
    //进度条完成容器
    var progress = document.createElement('div'),
        //数据模型数据,结构 {position: 50}
        param = data.data;
    //设置进度完成容器尺寸
    progress.style.width = (param.position || 100) + '%';
    //为进度条组件添加 UI 样式
    dom.className += ' ui-progressbar';
    //进度完成容器元素插入进度条容器中
    dom.appendChild(progress);
}

  With the experience of creating a progressbar component, it is much easier to create a slider component. We observed the sliders and found that they all have a dark gray sliding paddle. The sliding container and the dark gray progress container inside the sliding container, of course, for a complex sliding bar, it also has capacity prompt information and sliding paddle information, so we created the sliding bar according to the structure of the sliding bar in the example.

/**
 * 滑动条组件创建方法
 * dom   滑动条内容
 * data  滑动条数据模型
 */
slider: function(dom, data) {
    
    
        //滑动条拨片
    var bar = document.createElement('span'),
        //滑动条进度容器
        progress = document.createElement('div'),
        //滑动条容量提示信息
        totleText = null,
        //滑动条拨片提示信息
        progressText = null,
        //数据模型数据, 结构 {position: 60, totle: 200}
        param = data.data,
        //容器元素宽度
        width = dom.clientWidth,
        //容器元素横坐标
        left = dom.offsetLeft,
        //拨片位置(以模型数据中 position 数据计算)
        realWidth = (param.position || 100) * width / 100;
    //清空滑动条容器,为创建滑动条做准备
    dom.innerHTML = '';
    //如果模型数据中提供容器总量信息(param.totle),则创建滚动条提示文案
    if(param.totle) {
    
    
        //容器总量提示文案
        text = document.createElement('b');
        //拨片位置提示文案
        progressText = document.createElement('em');
        //设置容器总量提示文字
        text.innerHTML = param.totle;
        //将容器总量提示文案元素添加到滑动条组件中
        dom.appendChild(text);
        //将拨片位置提示文案元素添加到滑动条组件中
        dom.appendChild(progressText);
    }
    //设置滑动条
    setStyle(realWidth);
    //为滑动条组件添加样式
    dom.className += ' ui-slider';
    //将进度容器添加到滑动条组件中
    dom.appendChild(progress);
    //将拨片添加到滑动条组件中
    dom.appendChild(bar);
    //设置滑动条
    function setStyle(w) {
    
    
        //设置进度容器宽度
        progress.style.width = w + 'px';
        //设置拨片横坐标
        bar.style.left = w - FONTSIZE / 2 + 'px';
        //如果有拨片提示文案
        if(progressText) {
    
    
            //设置拨片提示文案横坐标
            progressText.style.left = w - FONTSIZE / 2 * 2.4 + 'px';
            //设置拨片提示文案内容
            progressText.innerHTML = parseFloat(w / width * 100).toFixed(2) + '%';
        }
    }
}

  After the scroll bar is created, we need to add interactive events to it so that it can be used. For example, when the scroll bar is dragged, the scroll bar prompts that the copy and the position of the paddle and the width of the progress container will change accordingly. When the mouse is released Stop the interaction when it is turned on.

slider: function(dom, data) {
    
    
    //创建组件逻辑
    //...
    //按下鼠标拨片
    bar.onmousedown = function() {
    
    
        //移动拨片(光标在页面中滑动,事件绑定给 document 是为了优化交互体验,使光标可以在页面中自由滑动)
        document.onmousedown = function(event) {
    
    
            //获取事件源
            var e = event || window.event;
            //鼠标光标相对于滑动条容器位置原点移动的横坐标
            var w = e.clientX - left;
            //设置滑动条
            setStyle(w < width ? (w > 0 ? W : 0) : width);
        }
        //阻止页面滑动选取事件
        document.onselectstart = function() {
    
    
            return false;
        }
    }
    //停止滑动交互(鼠标按键松开)
    document.onmouseup = function() {
    
    
        //取消文档鼠标光标移动事件
        document.onmousemove = null;
        //取消文档滑动选取事件
        document.onselectstart = null;
    }
}

  The method of creating components is the logic of the VM object that focuses on the view. Of course, the complete VM object also needs to process the model data, so we need to have a method to obtain the data. getBindData obtains the corresponding data through the given data mapping identifier in the element. To render our view.

var VM = function() {
    
    
    var Method = {
    
    
        //...
    }
    /**
     * 获取视图层组件渲染数据的映射信息
     * dom    组件元素
     */
    function getBindData(dom) {
    
    
        //获取自定义属性 data-bing 值
        var data = dom.getAttribute('data-bind');
        //将自定义属性 data-bind 值转化为对象
        return !!data && (new Function("return ({" + data + "})")) ();
    }
} ();

  With the method of creating the view component and the method of obtaining the component mapping data , it is even more powerful for us to realize the function of the VM object. Therefore, in the VM , we only need to obtain the components that need to be rendered in the page.

var VM = function() {
    
    
    var Method = {
    
    
        //...
    }
    function getBindData(dom) {
    
    
    	//...
    }

    //组件实例化方法
    return function() {
    
    
            //获取页面中所有元素
        var doms = document.body.getElementsByClassName('*'),
            //元素自定义数据句柄
            ctx = null;
        //ui 处理是会向页面中插入元素,此时 doms.length 会改变,此时动态获取 dom.length
        for(var i = 0; i < doms.length; i++) {
    
    
            //获取元素自定义数据
            ctx = getBindData(doms[i]);
            //如果元素是 UI 组件,则根据自定义属性中组件类型,渲染该组件
            ctx.type && Method[ctx.type] && Method[ctx.type](doms[i], ctx);
        }
    }
} ();

  At this point, the VM part is basically completed, and finally we have to add some data to the M data model layer.

//数据模型层中获取到的组件渲染数据
    //带有提示文案的滑动条
var demo1 = {
    
    
        position: 60,
        totle: 200
    },
    //简易版滑动条
    demo2 = {
    
    
        position: 20
    },
    //进度条
    demo3 = {
    
    position: 50};

  Rendering components is easy.

window.onload = function() {
    
    
	//渲染组件
	VM();
}

  Of course, in order to improve the components, we can add scroll bar events and window size reset events to the page to optimize component interaction.

Summary

  The difference between MVVM and MVC and MVP modes is that the MVVM mode makes the view more flexible and can be modified independently of the data model layer and the view model layer, and can be created freely. Of course, this also allows the data model layer to change independently, and even one view model layer can correspond to multiple view layers or data model layers.

  One of the most important features of the MVVM development model is the independent development of the view layer. This allows those who are not familiar with JavaScript , as long as they understand HTML, they can complete the development of a complex page in accordance with the view layer specification format, and the test problem becomes very easy. , You only need to write test code for the view model layer.

  This is the end of this article, and the "Incomprehensible" series is over. Thank you for your company. The summary of design patterns is far from stopping. All these patterns are the skills and experience that the predecessors summed up in the actual development. These experiences are constantly enriched and developed. You can also summarize your own design patterns. As long as they are beautiful enough, they will become classics.

  The rivers and lakes are far away, goodbye by destiny~



Guess you like

Origin blog.csdn.net/EcbJS/article/details/112463575