VUE双向绑定的原理

一、数据绑定

AngularJS 使用双向绑定, Vue在不同组件间强制使用单向数据流。

二、VUE框架中MVVM实现原理深度解读 

1、什么是MVVM?

MVVM实现了数据层和视图层的关联。

VUE主要靠的,就是MVVM, 实现了数据绑定和模板渲染。

先来个小例子来感受一下双向绑定:

初始化package.json:

npm init -y

安装:

npm install vue --save

安装的vue的版本为@2.6.7

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div id="app">
        <!--双向数据绑定,靠的是表单-->
        <input type="text" v-model="msg">
        {{msg}}
    </div>
    <script src="node_modules/vue/dist/vue.js"></script>
    <script>
        // 我们的数据一般都挂载在vm上
        let vm = new Vue({
            el: '#app',
            data: {
                msg: 'hello'
            }
        })
    </script>
</body>
</html>

 

compile.js:

// compile.js 负责编译模板
// 把数据渲染到页面上
class Compile {
    constructor(el, vm) {
        this.el = this.isElementNode(el) ? el : document.querySelector(el);
        this.vm = vm;
        if (this.el) {
            // 如果这个元素能获取到,我们才开始编译
            // 1.先把这些真实的DOM移入到内存中,fragment(文档碎片)
            let fragment = this.node2fragment(this.el);
            // 2.编译,提取想要的元素节点v-model 和 文本节点 {{}}
            this.compile(fragment);
            // 3.把编译好的fragment再塞回到页面里去
        }

    }

    // 专门写一些辅助的方法
    isElementNode(node) {
        return node.nodeType === 1;
    }

    // 核心的方法
    // 编译
    compile(fragment) {
        let childNodes = fragment.childNodes;
        // console.log(childNodes);
        // 需要递归
        Array.from(childNodes).forEach(node => {
            if (this.isElementNode(node)) {
                // 是元素节点,还需要继续深入的检查
                console.log('element', node);
                // 这里需要编译元素
                this.compileElement(node);
                this.compile(node);
            } else {
                // 是文本节点
                // 这里需要编译文本
                console.log('text', node);
                // 编译文本
                this.compileText(node);
            }
        });
    }

    // 需要将el中的内容全部放到内存中
    node2fragment(el) {
        // 创建内存中的文档碎片
        // 文档碎片, 内存中的dom节点
        let fragment = document.createDocumentFragment();
        let firstChild;
        while (firstChild = el.firstChild) {
            fragment.appendChild(firstChild);
        }
        // 内存中的节点
        return fragment;
    }

    // 编译元素
    compileElement(node) {
        // 比如,编译带 v-model 的
        // 判断元素上有没有v-model属性
        // 取出当前元素节点的属性
        let attrs = node.attributes;
        console.log(attrs);
        Array.from(attrs).forEach(attr => {
            console.log(attr.name);
            console.log(attr.value);  // 取的是值
            // 判断属性名字是不是包含 v-
            let attrName = attr.name;
            if (this.isDirective(attrName)) {
                // 取到对应的值放到节点中
                let expr = attr.value;
                let [,type] = attrName.split('-');
                console.log(type);
                // node
                // 编译
                CompileUtil[type](node, this.vm, expr);
            }
        })
    }

    // 编译文本
    compileText(node) {
        // 比如,编译带 {{}} 的
        let expr = node.textContent;   // 取文本中的内容
        console.log(expr);
        let reg = /\{\{([^}]+)\}\}/g;
        if (reg.test(expr)) {
            // node,  this.vm.$data
            CompileUtil['text'](node, this.vm, expr);
        }
    }

    // 判断是不是指令
    isDirective(name) {
        // 如果name里面包含'v-',那么返回true。
        return name.includes('v-');
    }
}


// 编译的方法
CompileUtil = {
    getVal(vm, expr){
        // 获取实例上对应的数据
        expr.split('.');
        expr.reduce((prev,next) => {
            return prev[next];
        }, vm.$data);
    },
    // 编译文本
    text(node, vm, expr) {
        let updateFn = this.updater['textUpdater'];
        vm.$data[expr];
        updateFn && updateFn(node);
    },
    // 编译 v-model
    model(node, vm, expr) {
        let updateFn = this.updater['modelUpdater'];
        updateFn && updateFn(node,this.getVal(vm, expr));
    },
    // 编译 v-html
    html(node, vm, expr) {
        //...
    },
    updater: {
        // 文本更新
        textUpdater(node, value) {
            node.textContent = value;
        },
        // 输入框更新
        modelUpdater(node, value) {
            node.value = value;
        },
        // html更新
        htmlUpdater(node, value) {
            node.innerHTML = value;
        }
    }
}


// 01:09:37

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值