前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >实现一个mvvm

实现一个mvvm

作者头像
Keller
发布2021-12-14 09:30:48
2370
发布2021-12-14 09:30:48
举报
文章被收录于专栏:Keller

最近在团队内做了一次vue原理分享,现场手写了一个乞丐版mvvm,这里记录一下这个mvvm实现的过程。

源码:https://github.com/keller35/mvvm

这个mvvm是基于发布订阅模式实现(也是vue本身的实现原理),最终达到的效果如下:

使用方式也跟vue一样:

代码语言:javascript
复制
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>mvvm</title>
</head>
<body>
    <div id="app">
        <input type="text" v-model="text">
        {{ text }}
        <button @click="reset" style="display:block;">重置</button>
    </div>
    <script src="./index.js"></script>
    <script>
        var vm = new Mvvm({
            el: 'app',
            data: {
                text: 'hello world'
            },
            methods: {
                reset() {
                    this.text = '';
                },
            },
        });
    </script>
</body>
</html>

实现很简单:

代码语言:javascript
复制
class Mvvm {
    constructor(options) {
        const { el, data, methods } = options;
        this.methods = methods;
        this.target = null;
        // 初始化dispatcher
        this.observe(this, data);
        // 初始化watcher
        this.compile(document.getElementById(el));
    }

    observe(root, data) {
        for (const key in data) {
            this.defineReactive(root, key, data[key]);
        }
    }

    defineReactive(root, key, value) {
        if (typeof value == 'object') {
            return this.observe(value, value);
        }
        const dep = new Dispatcher();
        Object.defineProperty(root, key, {
            set(newValue) {
                if (value == newValue) return;
                value = newValue;
                // 发布
                dep.notify(newValue);
            },
            get() {
                // 订阅
                dep.add(this.target);
                return value;
            }
        });
    }

    compile(dom) {
        const nodes = dom.childNodes;
        for (const node of nodes) {
            // 元素节点
            if (node.nodeType == 1) {
                const attrs = node.attributes;
                for (const attr of attrs) {
                    if (attr.name == 'v-model') {
                        const name = attr.value;
                        node.addEventListener('input', e => {
                            this[name] = e.target.value;
                        });
                        this.target = new Watcher(node, 'input');
                        this[name];
                    }
                    if (attr.name == '@click') {
                        const name = attr.value;
                        node.addEventListener('click', this.methods[name].bind(this));
                    }
                }
            }
            // text节点
            if (node.nodeType == 3) {
                const reg = /\{\{(.*)\}\}/;
                const match = node.nodeValue.match(reg);
                if (match) {
                    const name = match[1].trim();
                    this.target = new Watcher(node, 'text');
                    this[name];
                }
            }
        }
    }
}

class Dispatcher {
    constructor() {
        this.watchers = [];
    }
    add(watcher) {
        this.watchers.push(watcher);
    }
    notify(value) {
        this.watchers.forEach(watcher => watcher.update(value));
    }
}

class Watcher {
    constructor(node, type) {
        this.node = node;
        this.type = type;
    }
    update(value) {
        if (this.type == 'input') {
            this.node.value = value;
        }
        if (this.type == 'text') {
            this.node.nodeValue = value;
        }
    }
}

原理:

  1. 最根本的原理很简单,无非是基于发布订阅的消息通知模式,消息发出方来自mvvm中modal层的变法,而订阅方来自view层。
  2. modal层的变化,是通过对data设置setter来实现响应式,只要数据发生变化,通知所有订阅者。
  3. view层的订阅,则是在compile阶段,compile会对所有数据依赖进行收集,然后在getter中注册监听。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档