手写一个简单的vue实现双向数据绑定

本文详细介绍了如何从零开始手写一个简单的Vue实现双向数据绑定,包括编译模板、数据劫持、观察者、订阅器和数据代理等关键步骤。通过创建Compiler解析器、Observer监听数据、Watcher作为通信桥梁以及Dep管理观察者,最终实现Vue核心功能的模拟。
摘要由CSDN通过智能技术生成

手写一个简单的vue实现双向数据绑定

1. 准备两个文件index.html和mvvm.js
index.html内容如下:

<body>
    <div id="app">
        <input type="text" v-model="person.name">
        {
   {
   person.name}}
        <div>
            <span>我是{
   {
   person.name}}</span>
        </div>
        <div>就是那么任性</div>
        <div>年龄:{
   {
   person.age}}</div>
    </div>
</body>
<script src="MVVM.js"></script>
<script>
    let vm = new Vue({
   
        el: "#app",
        data: {
   
            person: {
   
                name: 'jinhong姐',
                age: '保密'
            }
        }
    });
</script>

mvvm.js内容如下:

// 基类 调度
class Vue {
   
    constructor(options) {
   
        this.$el = options.el;
        this.$data = options.data;
    }
}

在这里插入图片描述
2. 实现编译模板部分(解析器) Compiler
编译模板的主要功能是找出html中的 v- 开头的指令和文本中的 { {}} 然后将其替换成data里面相应的数据。
(1) 在MVVM.js中新建一个class Compiler,接收elVue实例本身(this)
(2) 判断基类Vue中el是否存在,存在的话 new 一个Compiler。
MVVM.js

// 基类 调度
class Vue {
   
    constructor(options) {
   
        this.$el = options.el;
        this.$data = options.data;
        // 判断根元素是否存在
        if (this.$el) {
   
            // 编译模板
            new Compiler(this.$el, this);
        }
    }
}

// 编译工具
const CompilerUtil = {
   
    // 根据表达式取到对应的数据
    getVal(vm, expr) {
   
        let value = expr.split('.').reduce((data, current) => {
   
            return data[current];
        }, vm.$data);
        return value;
    },
    setValue(vm, expr, value) {
   
        let newValue = expr.split('.').reduce((data, current, index, arr) => {
   
            if (index == arr.length - 1) {
   
                return data[current] = value;
            }
            return data[current];
        }, vm.$data)
        return newValue;
    },
    text(node, expr, vm) {
   
        let fn = this.updater['textUpdater'];
        // 给表达时中的每个{
   {}}都替换成文本
        let content = expr.replace(/\{\{(.+?)\}\}/g, (...args) => {
   
            return this.getVal(vm, args[1]);
        });
        fn(node, content);
    },
    model(node, expr, vm) {
   
        let fn = this.updater['modelUpdater'];
        let value = this.getVal(vm, expr);
        node.addEventListener('input', (e) => {
   
            // 获取用户输入的内容
            let value = e.target.value; 
            this.setValue(vm, expr, value);
        });
        fn(node, value);
    },
    updater: {
   
        // 处理文本节点
        textUpdater(node, value) {
   
            node.textContent = value;
        },
        modelUpdater(node, value) {
   
            node.value = value;
        }
    }
}

class Compiler {
   
    constructor(el, vm) {
   
        // 判断el属性是不是一个属性,如果不是元素,那就获取它
        this.el = this.isElementNode(el) ? el : document.querySelector(el);
        this.vm = vm;
        // 把当前节点中的元素获取放到内存中
        let fragment = this.nodefragment(this.el);
        // 把节点中的内容进行替换
        this.compiler(fragment);
        // 把内容从内存再塞到页面中
        this.el.appendChild(fragment);
    }
    // 判断是不是元素节点
    isElementNode(node) {
   
        return node.nodeType === 1;
    }
    // 将节点放入内存当中
    nodefragment(node) {
   
        let fragment = document.createDocumentFragment();
        let firstChild;
        while (firstChild = node.firstChild) {
   
            // appendChild具有移动性
            fragment.appendChild(firstChild);
        }
        return fragment;
    }
    // 编译数据
    compiler(node) {
   
        // 用来编译内存中的dom节点
        let childNodes = node.childNodes;
        // 类数组转化成数组
        [...childNodes].forEach(child => {
   
            // 判断是不是元素节点
            if (this.isElementNode(child)) {
   
                this.compilerElement(child);
                // 如果是元素的话还需要把自己传进去,再去遍历子节点
                this.compiler(child);
            } else {
   
                this.
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值