从零构建MVVM:手把手教你实现数据驱动的UI更新

452 阅读4分钟

引言 在前端开发中,我们常常需要实现数据的动态更新,让UI能够实时响应数据的变化。数据绑定是构建动态界面的核心技术之一。MVVM(Model-View-ViewModel)模式因其能够简化数据管理与视图更新而备受推崇。本文将通过以下内容,来探索如何利用JavaScript的Object.defineProperty方法实现基础的数据绑定功能,为各位读者提供一些简易版数据绑定框架的设计思路。

阶段一——单向数据流的初次尝试

首先我们创建一个简易html结构,包含一个显示数值的spanbutton按钮,要实现的效果是:通过点击按钮实现数值加一的变化效果。 页面效果如下: image.png 代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>最受欢迎的mvvm 开发框架</title>
</head>
<body>

    <span id="container">1</span>
    <button id="btn">点击加1</button>
</body>
</html>

接着我们开始实现数据的动态变化:

  • 先定义一个obj对象,包含一个属性value,初始化为1
  • 再定义两个变量oSpanoBtn,分别对应HTML中的<span><button>元素。
  • oBtn添加了第一个点击事件监听器,直接通过修改oSpaninnerHTML属性来实现数值的增加,通过直接操作了DOM,来实现视图更新。
  • 随后为oBtn添加第二个点击事件监听器,通过修改obj.value的值来间接更新视图。
  • 最后使用Object.definePropertyobjvalue属性创建了一个getter和setter。getter返回value的当前值,setter则负责更新value的值,并在更新后同步修改oSpaninnerHTML,从而实现视图的响应式更新。

代码如下:

    <script>
        var obj={
            value:1
        }
        var value=1;
        const oSpan=document.getElementById('container')
        const oBtn=document.getElementById('btn')

        oBtn.addEventListener('click',function(){
            oSpan.innerHTML=parseInt(oSpan.innerHTML)+1
        })
        // 监控对象
        oBtn.addEventListener('click',function(){
            obj.value++
        })
        Object.defineProperty(obj,"value",{
            get:function(){
                return value
            },
            set:function(newValue){
                value=newValue
                oSpan.innerHTML=newValue
            }
        })
    </script>

最后实现效果如下:

image.png

但该段代码存在着瑕疵,其代码中的事件监听器直接修改了DOM,而非通过修改模型数据,其违背了MVVM模式的核心原则,即数据模型与视图之间的解耦。如果日常开发采用此类方法可能会影响后期开发与维护。因此,我们进入下一阶段,看看如何优化和扩展这段代码。

阶段二——进阶版响应式数据绑定

同样的,我们先创建一个简易的html结构,如下:

<!DOCTYPE html>
2<html lang="en">
3<head>
4    <!-- Meta tags omitted -->
5    <title>Document</title>
6</head>
7<body>
8    <!-- HTML structure -->
9    <span id="container">1</span>
10    <span id="txt">hi</span>
11    <button id="btn">点击加1</button>
12    <button id="btn2">点击</button>
13    <!-- Script goes here -->
14</body>
15</html>

页面效果如下:

image.png 接着我们来实现响应式数据绑定,代码如下:

<script>
        // 不用dom,针对数据状态业务
        var obj={
            value:1,
            text:'hi'
        }
        ;(function(){
            function watch(obj,key,func){
                var value=obj[key]
                // 数据拦截
                Object.defineProperty(obj,key,{
                    get: function(){
                        return value
                    },
                    set:function(newValue){
                        value=newValue
                        func(newValue)
                    }
                })
            }
            this.watch=watch
        })();
        // 数据可以被监听 每次只能监听一个属性
        watch(obj,'value',function(newValue){
            document.getElementById('container').innerHTML=newValue
        });
        watch(obj,'text',function(newValue){
            document.getElementById('txt').innerHTML=newValue
        });
        document.getElementById('btn').addEventListener('click',()=>{
            obj.value++
        });
        document.getElementById('btn2').addEventListener('click',()=>{
            obj.text=obj.text=='hi'?'你好':'hi'
        })
    </script>

在这段代码中,

  • 我们首先在obj对象中定义了两个属性:valuetext,它们分别与页面上的<span>元素相关联。
  • 紧接着创建数据监听器,通过watch 函数接收三个参数,分别为要监听的对象、要监听的属性名以及回调函数。该函数利用Object.defineProperty将指定属性转换为响应式的,即每次属性值改变时都会执行给定的回调函数。
  • 接下来为value 和 text 属性分别注册监听器,当属性值改变时,对应的<span>元素的innerHTML会被更新,从而实现实时的数据绑定效果。
  • 最后当用户点击id="btn"按钮时,value 属性会递增,导致#container元素的内容更新。同样地,当用户点击id="btn2"按钮时,text 属性会在“hi”和“你好”之间切换,更新#txt元素的内容。
    最终实现的效果也如图所示:

image.png

总结

    通过上述例子构建的一个非常基础的数据绑定系统,可以展示数据驱动视图更新的基本原理。虽然它功能有限且未涵盖MVVM模式的全部细节,但它这种简易版的数据绑定框架或许可以帮助一些初学者理解响应式编程的概念,为进一步学习如Vue.js、React等现代前端框架奠定坚实的基础。同时通过以上两段代码的对比和分析,我们不仅理解了MVVM模式的精髓,还学会了如何用纯JavaScript实现响应式数据绑定。虽然这些代码远不及成熟的框架那么强大和灵活,但它们为我们揭开了MVVM神秘面纱的一角,让我们得以窥见数据驱动UI更新的魅力所在。看完后赶紧动手试试吧,尝试修改上述代码,增加更多的数据和交互,甚至可以尝试自己编写一个更复杂的MVVM系统。记住,前端开发的乐趣在于不断探索和创新,愿你在这条道路上越走越远!

    最后,如果觉得文章不错的话,别忘了点赞、收藏、关注,三者任选其三即可。