MVVM в Vue.js

внешний интерфейс Vue.js MVVM

Статья впервые опубликована на:GitHub.com/US TB-Вуд, умри, о ты…

Понимание МВВМ

MVVM делится на Model-View-ViewModel, который состоит из трех частей: View, ViewModel и Model. Слой представления представляет представления и шаблоны и отвечает за преобразование модели данных в пользовательский интерфейс для отображения. Уровень модели представляет модели и данные, а бизнес-логика модификации данных и операций может быть определена на уровне модели. Слой ViewModel соединяет Model и View.

В архитектуре MVVM уровень View и уровень Model не связаны напрямую, а взаимодействуют через уровень ViewModel. Слой ViewModel соединяет слой View и уровень Model через двустороннюю привязку данных, так что синхронизация между уровнем View и Model происходит полностью автоматически. Таким образом, разработчикам нужно обращать внимание только на бизнес-логику и не нужно вручную управлять DOM.Сложное обслуживание состояния данных передается MVVM для унифицированного управления. Воплощение MVVM на Vue.js:

Принцип МВВМ

В разных фреймворках принципы реализации MVVM разные:

Грязный механизм проверки:

Angular.js — это механизм грязной проверки.Когда происходит какое-либо событие (например, ввод), Angular.js проверяет, изменилась ли новая структура данных и предыдущая структура данных, чтобы решить, следует ли обновлять представление.

захват данных

Метод реализации Vue.js перехватывает данные (модель), когда данные изменяются, данные запускают метод привязки во время перехвата для обновления представления.

Та же точка

Грязная проверка и перехват данных имеют много общего, например, они оба состоят из трех этапов:

  • Разобрать шаблон
  • Аналитические данные
  • Привязка шаблонов и данных

Реализовать МВВМ

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Two-way data-binding</title>
</head>
<body>
    <div id="app">
        <input type="text" v-model="text">
        {{ text }}
    </div>
    <script>
        function observe (obj, vm) {
            Object.keys(obj).forEach(function (key) {
                defineReactive(vm, key, obj[key]);
            });
        }
        function defineReactive (obj, key, val) {
            var dep = new Dep();
            Object.defineProperty(obj, key, {
                get: function () {
                    if (Dep.target) dep.addSub(Dep.target);
                    return val
                },
                set: function (newVal) {
                    if (newVal === val) return
                    val = newVal;
                    dep.notify();
                }
            });
        }
        function nodeToFragment (node, vm) {
            var flag = document.createDocumentFragment();
            var child;
            while (child = node.firstChild) {
                compile(child, vm);
                flag.appendChild(child);
            }
            return flag;
        }
        function compile (node, vm) {
            var reg = /\{\{(.*)\}\}/;
            // 节点类型为元素
            if (node.nodeType === 1) {
                var attr = node.attributes;
                // 解析属性
                for (var i = 0; i < attr.length; i++) {
                    if (attr[i].nodeName == 'v-model') {
                        var name = attr[i].nodeValue; // 获取v-model绑定的属性名
                        node.addEventListener('input', function (e) {
                            // 给相应的data属性赋值,进而触发该属性的set方法
                            vm[name] = e.target.value;
                        });
                        node.value = vm[name]; // 将data的值赋给该node
                        node.removeAttribute('v-model');
                    }
                }
                new Watcher(vm, node, name, 'input');
            }
            // 节点类型为text
            if (node.nodeType === 3) {
                if (reg.test(node.nodeValue)) {
                    var name = RegExp.$1; // 获取匹配到的字符串
                    name = name.trim();
                    new Watcher(vm, node, name, 'text');
                }
            }
        }
    
        function Watcher (vm, node, name, nodeType) {
        //  this为watcher函数
            Dep.target = this;
        //  console.log(this);
            this.name = name;
            this.node = node;
            this.vm = vm;
            this.nodeType = nodeType;
            this.update();
            Dep.target = null;
        }
        Watcher.prototype = {
            update: function () {
                this.get();
                if (this.nodeType == 'text') {
                    this.node.nodeValue = this.value;
                }
                if (this.nodeType == 'input') {
                    this.node.value = this.value;
                }
            },
            // 获取daa中的属性值
            get: function () {
                this.value = this.vm[this.name]; // 触发相应属性的get
            }
        }
        function Dep () {
            this.subs = []
        }
        Dep.prototype = {
            addSub: function(sub) {
                this.subs.push(sub);
            },
            notify: function() {
                this.subs.forEach(function(sub) {
                    sub.update();
                });
            }
        };
        function Vue (options) {
            this.data = options.data;
            var data = this.data;
            observe(data, this);
            var id = options.el;
            var dom = nodeToFragment(document.getElementById(id), this);
            // 编译完成后,将dom返回到app中
            document.getElementById(id).appendChild(dom);
        }
        var vm = new Vue({
            el: 'app',
            data: {
                text: 'hello world'
            }
        });
    </script>
</body>
</html>

Можете обратить внимание на мой паблик-аккаунт «Muchen Classmate», фермера на гусиной фабрике, который обычно записывает какие-то банальные мелочи, технологии, жизнь, инсайты и срастается.