iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >Vue2.x响应式简单讲解及示例
  • 139
分享到

Vue2.x响应式简单讲解及示例

2024-04-02 19:04:59 139人浏览 八月长安
摘要

一、回顾Vue响应式用法 ​ vue响应式,我们都很熟悉了。当我们修改vue中data对象中的属性时,页面中引用该属性的地方就会发生相应的改变。避免了我们再去操作dom,

一、回顾Vue响应式用法

​ vue响应式,我们都很熟悉了。当我们修改vue中data对象中的属性时,页面中引用该属性的地方就会发生相应的改变。避免了我们再去操作dom,进行数据绑定。

二、Vue响应式实现分析

对于vue的响应式原理,官网上给了出文字描述 https://cn.vuejs.org/v2/guide/Reactivity.html

vue内部主要是通过数据劫持和观察者模式实现的

数据劫持:

vue2.x内部使用Object.defineProperty Https://developer.mozilla.org/zh-CN/docs/WEB/javascript/Reference/Global_Objects/Object/defineProperty

vue3.x内部使用的Proxy https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy

观察者模式:https://www.jb51.net/article/219790.htm

内部成员示意图

各个成员的功能

Vue:

把data中的成员注入到Vue实例中,并把data中的成员转换为getter和setter

Observer:

对data对象中的简单类型数据及对象进行监听,当数据发生变化时通知Dep

Compiler:

解析每个元素中的指令/差值表达式,并替换成相应的数据

Dep:

观察者模式中的通知者,添加观察者,当数据变化时通知观察者

Watcher:

每个引用data中的属性的地方都有一个watcher对象,负责更新视图

附:data对象中的属性充当被观察者,引用data对象中属性的地方充当观察者

三、Vue响应式源码实现

Vue对象实现

功能

  • 负责接受初始化的参数
  • 把data中的属性注入到data实例,转换成getter和setter
  • 调用Observer监听data中所有属性的变化
  • 调用compiler解析指令、差值表达式.

class Vue{
    constructor(options){
        // 1、通过属性保存穿进来的属性
        this.$options= options||{};
        this.$data= options.data||{};
        this.$el = typeof options.el ==='string' ? document.querySelector(options.el) : options.el;
        // 2、把data参数中的数据转换为getter和setter 挂载到Vue实例上
        this._proxyData(this.$data)
        // 3、调用observe对象监视data数据的变化
        new Observer(this.$data)
        // 4、调用compiler对象渲染页面
        new Compiler(this)
    }
    _proxyData(data){
        if (data&&Object.keys(data).length>0){
             for (const key in data) {
                Object.defineProperty(this,key,{
                    configurable:true,
                    enumerable:true,
                    get(){
                        return data[key]
                    },
                    set(value){
                        if (data[key]===value) {
                            return;
                        }
                        data[key]=value;
                    }
                })
             }
        }
    }
}

Observer对象实现

功能

  • 把data选项中的属性进行数据劫持
  • data中的某个属性也是对象的话,进行递归转换成响应式对象
  • 数据变化发送通知
//数据劫持   class Observer {
    constructor(data) {
        this.walk(data)
    }
    walk(data) { 
        //1、判断data是否是对象    
        if (!data || typeof data !== 'object') {     
            return
        }
        //2、循环调用defineReactive进行数据劫持
        Object.keys(data).forEach(key => {
            this.defineReactive(data, key, data[key])
        })
    }
    defineReactive(obj, key, val) {
        //创建通知者
        const dep = new Dep()
        //使用walk把引用对象中的属性变成响应式的
        this.walk(val)
        const that=this;
        Object.defineProperty(obj, key, {
            configurable: true,
            enumerable: true,
            get() {
                //通知者收集观察者
                Dep.target && dep.addSub(Dep.target)
                return val;
            },
            set(newVal) {
                if (newVal === val) {
                    return;
                }
                val = newVal;
                that.walk(newVal)
                //被观察者发生变化的时候,通知者对象给每个观察者发送通知
                dep.notify()
            }
        })
    }
}

Compile对象实现

功能

  • 负责编译模板,解析指令、差值表达式
  • 负责页面首次渲染
  • 当数据发生改变后,负责重新渲染视图
//编译器   class Compiler {
    constructor(vm) {
        this.el = vm.$el;
        this.vm = vm;
        this.compile(this.el)
    }
    //编译模板 判断节点是文本节点还是元素节点
    compile(el) {
        let childnodes = el.childNodes;
        //处理第一层子节点
        Array.from(childNodes).forEach(node => {
            if (this.isTextNode(node)) {
                this.compileText(node)
            } else if (this.isElementNode(node)) {
                this.compileElement(node)
            }
            //如果当前节点还有子节点  递归调用编译指令
            if (node.childNodes && node.childNodes.length) {
                this.compile(node)
            }
        })
    }

    //编译元素节点,处理指令
    compileElement(node) {  
        //遍历所有的指令
        Array.from(node.attributes).forEach(attr => {
            //判断是不是指令节点
            if (this.isDirective(attr.name)) {
                const nodeName = attr.name;
                const key = attr.nodeValue;
                const directive = nodeName.substr(2)
                this.updater(directive,node,key)
            }
        }) 
    }
    updater(directive,node,key){
        const updaterFn = this[directive+"Updater"]
        updaterFn && updaterFn.call(this,node,this.vm[key],key)
    }
    //v-text
    textUpdater(node,value,key){
        node.textContent=value
        //使用v-text表达式的地方就是一个观察者
        new Watcher(this.vm,key,newValue => {
            node.textContent = newValue
        })
    }
    //v-model
    modelUpdater(node,value,key){
        node.value =value
        //使用v-model表达式的地方就是一个观察者
        new Watcher(this.vm,key,newValue => {
            node.value = newValue
        })
      //实现双向绑定
        node.addEventListener('input',()=>{
            this.vm[key] = node.value
        })
    }
    //v-html
    htmlUpdater(node,value,key){
        node.innerHTML = value
        //使用v-html表达式的地方就是一个观察者
        new Watcher(this.vm,key,newValue => {
            node.innerHTML = newValue
        })
    }

    //处理差值表达式
    compileText(node) {
        //匹配差值表达式的正则
        let reg = /\{\{(.+?)\}\}/
        //用正则匹配node的textContent,如果匹配到了 就替换
        if (reg.test(node.textContent)) {
            //获取插值表达式的key
            let key = RegExp.$1;
            let value = node.textContent;
            node.textContent = value.replace(reg, this.vm[key])

            //使用差值表达式的地方就是一个观察者
            new Watcher(this.vm,key,newValue => {
                node.textContent = newValue
            })
        }
    }

    //是否是指令
    isDirective(attrName) {
        return attrName.startsWith('v-')
    }

    //是否是文本节点
    isTextNode(node) {
        return node.nodeType === 3
    }

    //是否是元素
    isElementNode(node) {
        return node.nodeType === 1
    }
}

Dep对象实现

功能

  • 收集依赖,添加观察者
  • 通知所有观察者
//通知者类   class Dep {
    constructor() {
        //存储观察者
        this.subs = []
    }

    
    addSub(sub) {
        if (sub && sub.update) {
            this.subs.push(sub)
        }
    }

    
    notify() {
        this.subs.forEach(sub => {
            sub.update()
        })
    }
}

Watcher对象实现

功能

  • 当数据变化时,Dep通知所有Watcher实例更新视图
  • 自身实例化的时候往Dep对象中添加自己
//观察者类   class Watcher {
    constructor (vm,key,cb) {
        //Vue实例
        this.vm =vm;
        // data中的key对象
        this.key =key;
        // 更新视图的回调函数
        this.cb = cb
        //把当前观察者实例存放在Dep的target静态属性中
        Dep.target =this
        //触发Observe的getter方法,把当前实例存放在Dep.subs中
        //data中key对应的旧值
        this.oldValue = this.vm[this.key]
        Dep.target = null
    }
    //每个观察者都有一个update方法来改变状态
    update(){
        const newValue = this.vm[this.key]
        if ( this.newValue === this.oldValue ) {
            return
        }
        this.cb(newValue)
    }
}

测试


<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>index</title>
    <script src="./js/dep.js"></script>
    <script src="./js/watcher.js"></script>
    <script src="./js/compiler.js"></script>
    <script src="./js/observer.js"></script>
    <script src="./js/vue.js"></script>
</head>
<body>
    <p id="app">
        <h1>差值表达式</h1>
        <h3>{{msg}}</h3>
        <h3>{{count}}</h3>
        <h1>v-text</h1>
        <p v-text='msg'></p>
        <h1>v-model</h1>
        <input type="text" v-model="msg" attr="msg">
        <input type="text" v-model="count">
        <h1>v-html</h1>
        <p v-html="htmlText"></p>
    </p>
    <script>
        let vm = new Vue({
            el:"#app",
            data:{
                msg:'信息',
                count:'数量', 
		person:{name:'张三'},
                htmlText:"<p style='color:red'>你好</p>"
            }
        })
    </script>
</body>

到此这篇关于Vue2.x响应式简单讲解及示例的文章就介绍到这了,更多相关Vue2.x响应式内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Vue2.x响应式简单讲解及示例

本文链接: https://www.lsjlt.com/news/132686.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Vue2.x响应式简单讲解及示例
    一、回顾Vue响应式用法 ​ vue响应式,我们都很熟悉了。当我们修改vue中data对象中的属性时,页面中引用该属性的地方就会发生相应的改变。避免了我们再去操作dom,...
    99+
    2024-04-02
  • java简单工厂模式实例及讲解
    简单工厂模式 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创...
    99+
    2024-04-02
  • Scrapy简单入门及实例讲解
    Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 其可以应用在数据挖掘,信息处理或存储历史数据等一系列的程序中。其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的, 也可以应用在获取API所返回的数据(例如 A...
    99+
    2023-01-31
    实例 入门 简单
  • vue3.x源码剖析之数据响应式的深入讲解
    目录前言什么是数据响应式数据响应式的大体流程vue2.x数据响应式和3.x响应式对比大致流程图实现依赖收集代码仓库结尾前言 如果错过了秋枫和冬雪,那么春天的樱花一定会盛开吧。最近一直...
    99+
    2024-04-02
  • html5响应式中px单位的示例分析
    这篇文章主要为大家展示了“html5响应式中px单位的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“html5响应式中px单位的示例分析”这篇文章吧。移动端h6响应式方案最近这几年用得最...
    99+
    2023-06-09
  • 运用示例简单讲解C#取消令牌CancellationTokenSource
    目录前言简单示例基础操作 定时取消关联取消 判断取消 源码探究构造入手 小插曲WaitHandle 注册操作 取消操作Cancel操作 CancelAfter操作 总结 前言 &nb...
    99+
    2024-04-02
  • TypeScript常见类型及应用示例讲解
    目录常见类型(Everyday Types)原始类型:数组(Array)anynoImplicitAny变量上的类型注解(Type Annotations on Variables)...
    99+
    2024-04-02
  • Vue响应式原理的示例详解
    Vue 最独特的特性之一,是非侵入式的响应系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。聊到 Vue 响应式实现原理,众多开发者都知道实现...
    99+
    2024-04-02
  • PyQt5简单读取以及显示图片的应用实例
    目录前言 一、程序界面简单设计二、通过下拉列表框读取指定路径上的图片1.设置路径2.将读取到的图片名称添加到下拉列表框内3.将读取到的图片展示到界面上四、退出事件1.定义一...
    99+
    2023-05-20
    pyqt5读取图片 pyqt5 显示图片 pyqt5读取文件
  • Vue响应式原理及双向数据绑定示例分析
    目录前言响应式原理双向数据绑定前言 之前公司招人,面试了一些的前端同学,因为公司使用的前端技术是Vue,所以免不了问到其响应式原理和Vue的双向数据绑定。但是这边面试到的80%的同学...
    99+
    2024-04-02
  • vue3响应式Object代理对象的读取示例详解
    目录正文读取属性xx in objfor ... in正文 从这一章开始,作者将更新深入的讲解响应式,尤其是vue3响应式的具体的实现。其实在前面一章,如果你仔细阅读,你是可以实现一...
    99+
    2022-11-13
    vue响应式Object读取 vue Object
  • Vue组合式API--setup中定义响应式数据的示例详解
    目录一、Options API(选项式API)的弊端1.1 什么是选项式API1.2 选项式API的弊端二、Composition API(组合式API)概述2.1 Composit...
    99+
    2024-04-02
  • vue3响应式Proxy与Reflect的理解及基本使用实例详解
    目录正文理解Proxy与ReflectProxyReflect实践示例正文 在第四章中,作者讲述了Vue.js中响应式系统的设计与实现,这一块其实是整个框架的基石,也是MVVM中,V...
    99+
    2022-11-13
    vue3响应式Proxy Reflect vue3响应式
  • 新浪开源轻量级分布式RPC框架motan简单示例解析
    目录前言概述功能简单调用示例在pom中添加依赖为调用方和服务方创建公共接口编写业务接口逻辑、创建并启动RPCServer创建并执行RPCClient集群调用示例使用CONSUL作为注...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作