vue watch val newval_Vue设计原理

概念

Vue是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue被设计为可以自底向上逐层应用。Vue的核心库只关注视图层,不仅易于上手,还方便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue也完全能够为复杂的单页面应用提供驱动。

当你把一个普通的JavaScript对象传入Vue实例作为data选项,Vue将遍历此对象所有的属性,并使用Object.defineProperty把这些属性全部转为getter/setter。Object.defineProperty是ES5中一个无法shim的特性,这也就是Vue不支持IE8以及更低版本浏览器的原因。

这些getter/setter对用户来说是不可见的,但是在内部它们让Vue能够追踪依赖,在属性被访问和修改时通知变更。这里需要注意的是不同浏览器在控制台打印数据对象时对getter/setter的格式化不同,所以建议安装vue-devtools来获取对检查数据更加友好的用户界面。

每个组件实例都对应一个watcher实例,它会在组件渲染的过程中把“接触”过的数据属性记录为依赖。之后当依赖项的setter触发时,会通知watcher,从而使它关联的组件重新渲染。

6fe193af430e6e58d2d597f031926b49.png

主要模块:

依赖Dep、观察者Observer、监视者Watcher、编译Compile、Vue

1、定义Vue对象,声明data里面的属性值,准备初始化observe实例方法。

2、在Observe定义响应式方法Object.defineProperty()的属性,在初始化的时候,通过Watcher对象进行addDep的操作。即每定义一个data属性值,会添加到一个Watcher对象到订阅者里面。

3、每当形成一个Watcher对象的时候,定义它的响应式。即Object.defineProperty()定义。这导致了一个Observer里面的getter/setter方法与订阅者形成一种依赖关系。

4、由于依赖关系的存在,每当数据的变化后,会导致setter方法,从而触发notify通知方法,通知订阅者数据改变了,需要更新。

5、订阅者会触发内部的update方法,从而改变vm实例的值,以及每个Watcher里面对应node的nodeValue,即视图上面显示的值。

6、Watcher里面接收到了消息后,会触发改变对应对象里面的node的视图的value值,而改变视图上面的值。视图的值改变了,形成了双向绑定MVVM的效果。

1、Dep和Observer代码,即发布订阅模式

class Dep{    constructor(){        this.subs = [];    }    addSub(sub){        this.subs.push(sub);    }    notify(){        this.subs.forEach(function(sub){            sub.update();        })    }}class Observer{    constructor(data){        this.data = data;        this.walk(data);    }    walk(data){        let _this = this;        Object.keys(data).forEach(function(key){            _this.defineReactive(data, key, data[key]);        });    }    defineReactive(data, key, val){        let dep = new Dep();        let childObj = observe(val);        Object.defineProperty(data, key, {            enumerable: true,            configurable: true,            get(){                if(Dep.target){                    dep.addSub(Dep.target);                }                return val;            },            set(newVal){                if(val === newVal){                    return;                }                val = newVal;                dep.notify();            }        })    }}function observe(value, vm){    if(!value || typeof value !== 'object'){        return;    }    return new Observer(value);}Dep.target = null;

2、Watcher监视者

class Watcher{    constructor(vm, exp, cb){        this.cb = cb;        this.vm = vm;        this.exp = exp;        this.value = this.get();    }    update(){        this.run();    }    run(){        let value = this.vm.data[this.exp];        let oldVal = this.value;        if(value!==oldVal){            this.value = value;            this.cb.call(this.vm, value, oldVal);        }    }    get(){        Dep.target = this;        let value = this.vm.data[this.exp];        Dep.target = null;        return value;    }}

3、Compile编译

class Compile{    constructor(el, vm){        this.vm = vm;        this.el = document.querySelector(el);        this.fragment = null;        this.init();    }    init(){        if(this.el){            this.fragment = this.nodeToFragment(this.el);            this.compileElement(this.fragment);            this.el.appendChild(this.fragment);        }else{            console.log(`Dom元素不存在`);        }    }    nodeToFragment(el){        let fragment = document.createDocumentFragment();        let child = el.firstChild;        while(child){            fragment.appendChild(child);            child = el.firstChild;        }        return fragment;    }    compileElement(el){        let childNodes = el.childNodes;        let _this = this;        [].slice.call(childNodes).forEach(function(node){            let reg = /\{\{(.*)\}\}/;            let text = node.textContent;            if(_this.isElementNode(node)){                _this.compile(node);            }else if(_this.isTextNode(node) && reg.test(text)){                _this.compileText(node, reg.exec(text)[1]);            }            if(node.childNodes && node.childNodes.length){                _this.compileElement(node);            }        })    }    compile(node){        let nodeAttr = node.attributes;        let _this = this;        Array.prototype.forEach.call(nodeAttr,function(attr){            let attrName = attr.name;            if(_this.isDirective(attrName)){                let exp = attr.value;                let dir = attrName.substring(2);                if(_this.isEventDirective(dir)){                    _this.compileEvent(node, _this.vm, exp, dir);                }else{                    _this.compileModel(node, _this.vm, exp, dir);                }                node.removeAttribute(attrName);            }        })    }    compileText(node, exp){        let _this = this;        let initText = this.vm[exp];        this.updateText(node, initText);        new Watcher(this.vm, exp, function(value){            _this.updateText(node, value);        })    }    compileEvent(node, vm, exp, dir){        let eventType = dir.split(':')[1];        let cb = vm.methods && vm.methods[exp];        if(eventType && cb){            node.addEventListener(eventType, cb.bind(vm), false);        }    }    compileModel(node, vm, exp, dir){        let _this = this;        let val = this.vm[exp];        this.modelUpdater(node, val);        new Watcher(this.vm, exp, function(value){            _this.modelUpdater(node, value);        })        node.addEventListener('input', function(e){            let newVal = e.target.value;            if(val === newVal){                return;            }            _this.vm[exp] = newVal;            val = newVal;        })    }    updateText(node, value){        node.textContent = typeof value == 'undefined'?'':value;    }    modelUpdater(node, value, oldVal){        node.value = typeof value == 'undefined'?'':value;    }    isDirective(attr){        return attr.indexOf('v-') == 0;    }    isEventDirective(dir){        return dir.indexOf('on:') == 0;    }    isElementNode(node){        return node.nodeType == 1;    }    isTextNode(node){        return node.nodeType == 3;    }}

4、Vue

class Vue{    constructor(options){        this.data = options.data;        this.methods = options.methods;        Object.keys(this.data).forEach(key=>{            this.proxy(key);        })        observe(this.data);        new Compile(options.el, this);        options.mounted.call(this);    }    proxy(key){        Object.defineProperty(this, key, {            enumerable: true,            configurable: true,            get(){                return this.data[key];            },            set(newVal){                this.data[key] = newVal;            }        })    }}

5、测试例子

<html lang="cn"><head>    <meta charset="UTF-8">    <title>前端咖 Vue设计原理title>head><body>    <div id="app">        <h2>{{title}}h2>        <input v-model="name">        <h1>{{name}}h1>        <button v-on:click="clickMe">click me!button>    div>    <script>        new Vue({            el: '#app',            data: {                title: 'hello world',                name: 'canfoo'            },            methods: {                clickMe: function () {                    this.title = 'hello world';                 }            },            mounted: function () {                window.setTimeout(() => {                    this.title = '你好';                }, 1000);              }        }); script>body>html>

83f4e8bcf9c897712a76b81260ac84dc.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue中,我们可以使用watch来监听数组和对象的变化。对于数组,我们可以在watch选项中配置需要监听的数组,然后使用handler函数来处理数组变化时的逻辑。handler函数接收两个参数,分别是变化后的数组和变化前的数组。我们可以在handler函数内部根据需要进行相应的操作。例如: ``` watch: { arr: { handler(newVal, oldVal) { console.log(newVal); console.log(oldVal); } } } ``` 对于对象,我们可以使用deep属性来进行深度监听。在watch选项中配置需要监听的对象,并将deep属性设置为true。这样监听器会一层层地遍历对象的所有属性,并为每个属性添加监听器。例如: ``` watch: { obj: { handler(val, oldval) { console.log(val); console.log(oldval); }, deep: true } } ``` 另外,需要注意的是,由于JavaScript的限制,Vue无法检测以下数组变动:当直接通过索引设置数组的某一项时,或者当修改数组的长度时。为了解决这个问题,我们可以使用deep属性进行深度监听。 例如: ``` watch: { items: { handler(val, oldVal) { // 处理数组变化的逻辑 }, deep: true } } ``` 通过配置watch的选项,我们可以方便地监听数组和对象的变化,并进行相应的处理操作。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [vue怎么监听数组和对象的变化](https://blog.csdn.net/bzf0482/article/details/106791126)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [VUE深度监听一个对象数组](https://blog.csdn.net/flybone7/article/details/104532403)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值