vue源码学习一

本篇主要为阅读源码做一些准备,相关代码参考了网上相关资料。

本篇主要讲述数据的动态动态绑定与更新:

 

1:数据的动态绑定与更新核心方法是Object.defineProperty(),下面是相关介绍。

Object.definePropety(obj,property,descriptor)  
/
 *  obj 需要操作的对象
 *  property  需要修改的属性
 *  descriptor  属性描述符,属性描述符有两种类型的取值 1:数据描述符,2:存取描述符,属性描述符是这两者之一,不能同时存在
 *  
    数据描述符和存取描述符均具有的可选键有:

      configurable:(true||false) 默认false, 当且仅当该属性值为true时,属性描述符的内容才能被改变
      enumerable: (true||false)  默认false, 当该属性为true时,操作的(property)属性才是枚举类型
   
    数据描述符独有的属性
       value: 默认为undefined    值为property属性对应的值。
       writable: 默认false        property是否可写入,仅当为true时,操作的属性才能进行赋值操作
		
    存取描述符独有的属性

	get: 默认值undefined。给property提供geter方法的属性,当访问property时,就会触发get属性,	
	
	set: 默认值undefined。 给property提供setter方法的属性,当修改property时会触发该方法。


/

2:这里我们想实现和vue一样的格式: 

window.onload = function(){
    let app = new Vue ({
    el:'#app', //根节点
    data:{           //数据
        number:1,
        count:1
    },
    methods:{        //方法
        addN: function(){
            this.number+=2
        },
        addC: function(){
            this.count++
        }
    }
})    }

首先我们需要先定义函数Vue()

    //新建Vue函数
    function Vue (options){
         //初始化实例
        this._init(options);  
    };
    Vue.prototype._init = function (options){
        this.$el = document.querySelector(options.el); //根节点
        this.$data = options.data;   //数据
        this.$methods = options.methods;    //方法
        this._var = {};                    //为动态更新数据设置的变量
        this._obverse(this.$data);        //为数据设置get set 方法
        this._directive(this.$el);         // 循环遍历文档,找寻v指令进行初始化
    };

再看_obverse()方法: 

 Vue.prototype._obverse = function(data){
        //循环数据$data,对每个数据执行Object.defineProperty()
        var _this = this;
        Object.keys(data).forEach((item)=>{
            if(data.hasOwnProperty(item)){
                var value = data[item];
                _this._var.dire= [];
                if(typeof value === 'Object'){
                    this._obverse(value)
                } 
                Object.defineProperty (data,item,{
                    configurable: true,
                    enumerable: true,
                    get(){
                        return value
                    },
                    set(newValue){
                        if(newValue!=value){
                            value = newValue;
                        //需要明白的是input中的数据对应的是实例data的数据,然后通过v指定标记进行恰当的赋值操作
                        //如何让一个确定的节点与数据发生关系,方法是给实例设置变量,将节点作为对象添加到变量中,然后
                        //通过某一标记进行循环判断即可获得节点。
                        _this._var.dire.forEach(item=>{
                            item._update();
                        })
                        }
                    }
              })
            }
        })
    };

接着看_directive()方法 : 该方法循环遍历整个文档节点,然后找出所有v指令,并对各个指令进行逻辑赋值,v-click增加点击事件,v-model增加input事件, v-bind:进行数据显示。

//对v指令进行逻辑赋值
    Vue.prototype._directive = function (el) {
        let _this = this;
        let root = el.children
        //childNodes与children的区别
         let node=Array.prototype.slice.call(root);
        node.forEach((item)=>{
            if(item.children.length>0){
                _this._directive(item);
            }
            if(item.hasAttribute('v-click')){
                //v-click触发点击事件
                let attrValue = item.getAttribute('v-click');
                 item.onclick = function(){
                     //调用实例的methods方法
                    _this.$methods[attrValue].apply(_this.$data)
                     
                 }
            }
            if(item.hasAttribute('v-model')&&(item.nodeName === 'INPUT'||item.nodeName === 'TEXTAREA')){
                //v-model触发input事件
                let attrValue = item.getAttribute('v-model'); //自执行函数为了初始化input值
                item.addEventListener('input',(function(){
                    let value = item.Value;
                  _this._var.dire.push(new Node({
                      content:'value',
                      name: attrValue,
                      node: item,
                      vm: _this
                  }));
                  return function(){
                      _this.$data[attrValue] = this.value==''?0:parseInt(this.value);
                  }
                })(),false)
            }
            if(item.hasAttribute('v-bind')){
                let attrValue = item.getAttribute('v-bind');
                //查询实例对象的数据对节点进行赋值
                
                _this._var.dire.push(new Node({
                    content:'innerHTML',
                    name: attrValue,
                    node: item,
                    vm: _this
                }))
                // item.innerHTML = _this.$data[attrValue]
            }
        }) 
    }

这里有一个问题:如何对某一节点进行数据的动态交互呢?

要想解决该问题可以将某一节点设置数据需要的必要条件保存下来,然后供vue数据使用,这也是Node函数存在的理由。

 //设置节点对象
    function Node(opt){
        this.$content = opt.content; //内容输出到哪里  innerHTML  或者 value 
        this.$name = opt.name;      //节点对应的属性值   v-bind= 'name'
        this.$node = opt.node;     //节点本身
        this._this = opt.vm    // Vue实例this
        this._update();
    }
    Node.prototype._update = function(){
        //通过该函数对节点内容进行赋值,由该句话可知我们所需要的节点信息
        this.$node[this.$content] = this._this.$data[this.$name]
    }

最后说明一下大致流程:

文档加载完毕 > 执行_init()函数进行初始化>>首先执行_obverse()方法遍历整个data数据,为每个数据添加get与set方法,注意

set方法内循环保存有节点信息的数组来对含有v-bind指令的节点进行数据展示。>>接着执行_directive()方法,该方法遍历整个文档节点,当发现节点存在v-click就绑定onclick事件与vue实例方法相关联,当发现v-model就绑定input事件,注意该句柄是一个

自执行函数,目的为了使该节点初始时就有值,之后又返回一个函数作为input事件该函数是为了触发数据的set方法,

当发现v-bind时,将节点信息保存到数组中同时会运行update()初始化节点的值。

 

最后附上完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>vue源码一</title>
</head>
<body>
   <div id="app">
        <form>
            <input type="text"  v-model="number" class="in">
            <button type="button" v-click="addN">增2</button>
        </form>
            <h3 v-bind="number"></h3>
        <form>
            <input type="text"  v-model="count" class="in">
            <button type="button" v-click="addC">增1</button>
        </form>
            <h3 v-bind="count"></h3>
   </div>
    <script>
        //新建Vue函数
    function Vue (options){
        this._init(options);
    };
    Vue.prototype._init = function (options){
        this.$el = document.querySelector(options.el);
        this.$data = options.data;
        this.$methods = options.methods;
        //定义观察者函数
        this._var = {};
        this._obverse(this.$data);
        this._directive(this.$el);
    };
    Vue.prototype._obverse = function(data){
        //循环数据$data
        var _this = this;
        Object.keys(data).forEach((item)=>{
            console.log(item)
            if(data.hasOwnProperty(item)){
                var value = data[item];
                _this._var.dire= [];
                if(typeof value === 'Object'){
                    this._obverse(value)
                } 
                Object.defineProperty (data,item,{
                    configurable: true,
                    enumerable: true,
                    get(){
                        return value
                    },
                    set(newValue){
                        if(newValue!=value){
                            value = newValue;
                        //编写代码对h3中的数据进行实时监控
                        //需要明白的是input中的数据对应的是实例data的数据,然后通过v指定标记进行恰当的赋值操作
                        //如何让一个确定的节点与数据发生关系,方法是给实例设置变量,将节点作为对象添加到变量中,然后
                        //通过某一标记进行循环判断即可获得节点。
                        _this._var.dire.forEach(item=>{
                            item._update();
                        })
                        }
                    }
              })
            }
        })
    };
    //对v指令进行逻辑赋值
    Vue.prototype._directive = function (el) {
        let _this = this;
        let root = el.children
        //childNodes与children的区别
         let node=Array.prototype.slice.call(root); //抓化为真正的数组
        node.forEach((item)=>{
            if(item.children.length>0){
                _this._directive(item);
            }
            if(item.hasAttribute('v-click')){
                //v-click触发点击事件
                let attrValue = item.getAttribute('v-click');
                 item.onclick = function(){
                    console.log(1111111111)
                     //调用实例的methods方法
                    _this.$methods[attrValue].apply(_this.$data)
                     
                 }
            }
            if(item.hasAttribute('v-model')&&(item.nodeName === 'INPUT'||item.nodeName === 'TEXTAREA')){
                //v-model触发input事件
                let attrValue = item.getAttribute('v-model'); //自执行函数为了初始化input值
                item.addEventListener('input',(function(){
                    let value = item.Value;
                  _this._var.dire.push(new Node({
                      content:'value',
                      name: attrValue,
                      node: item,
                      vm: _this
                  }));
                  return function(){
                      _this.$data[attrValue] = this.value==''?0:parseInt(this.value);
                  }
                })(),false)
            }
            if(item.hasAttribute('v-bind')){
                let attrValue = item.getAttribute('v-bind');
                //查询实例对象的数据对节点进行赋值
                
                _this._var.dire.push(new Node({
                    content:'innerHTML',
                    name: attrValue,
                    node: item,
                    vm: _this
                }))
                // item.innerHTML = _this.$data[attrValue]
            }
        }) 
    }
    //设置节点对象
    function Node(opt){
        this.$content = opt.content; //内容输出到哪里  innerHTML  或者 value 
        this.$name = opt.name;      //节点对应的属性值   v-bind= 'name'
        this.$node = opt.node;     //节点本身
        this._this = opt.vm    // Vue实例this
        this._update();
    }
    Node.prototype._update = function(){
        //通过该函数对节点内容进行赋值
        this.$node[this.$content] = this._this.$data[this.$name]
    }
   window.onload = function(){
    let app = new Vue ({
    el:'#app',
    data:{
        number:1,
        count:1
    },
    methods:{
        addN: function(){
            this.number+=2
        },
        addC: function(){
            this.count++
        }
    }
})    
   }
    </script>
</body>
</html>














 

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值