美文网首页
一来进阶vue

一来进阶vue

作者: Josey11 | 来源:发表于2019-07-11 09:49 被阅读0次

    上图,无图无真相

    花了400大洋买了[frontedmasters](https://frontendmasters.com/)的一个月的会员,就是为了看男神的这份vue教程,没有中文字幕,痛苦地坚持啃完了,建议有钱并且英语好的人直接买会员去官网看视频,尤大神人长得帅,业务水平高,英语还超苏,对不起,忍不住犯了会儿花痴,如果没钱英语又渣的人只能看我的总结了,看我的文章的好处就是不用花钱,不用被英语折磨,坏处就是本人的文字水平和业务水平真的有限,不会一步步引导式地分析原理和代码的实现,只能贴代码和尽我所能来解释了,能理解多少就看大家的水平了,这算不算无良商家,不包售后啊,哈哈。

    这个系列文章将从下面几个方面来介绍vue

    1. reactivity(响应式)

    2. plugin(插件)

    3. render(渲染函数)

    4. routing(路由)

    5. state-management(状态管理)

    6. international(多语言支持)

    ### 1 reactivity

    相信了解过vue的同学都知道,vue是MVVM框架,最大的特点就是双向数据绑定,数据Model修改了之后视图View会自动更新, view也可以通过v-model的将ViewModel的变化同步到Model里面

    这篇文章要探讨的问题:vue是如何监控数据的变化从而自动更新视图,也就是vue响应式是如何实现的,我们一步步来看。

    #### 1.1. getter and setter

    考虑下面这段代码,一个简单的代数计算关系,b依赖a, b是a的10倍

    ```javascript

        var a= 10;

        var b = a*10;

        a = 20

        //b = ? 如何监听b的变化

    ```

    请认真思考一下再往下看:当每次改变a的值的时候,如何监听a的变化并且更新b,保证他们的数学关系b是a的10倍?

    给大家留了10行空白,不知道大家有没有认真思考上面的问题?

    不卖关子了,直接用两个数值,我们目前无法监控,可以换种思路,直接监控一个数值变量行不通,我们可以**监控一个对象属性的变化,在属性改变的时候收集它的依赖,就可以触发依赖的更新**

    js有一个方法Object.defineProperty可以劫持属性的读取器getter和setter, 来看个例子

    ```javascript

      let obj = {

            a:10

        };

        observer(obj, 'a');

        obj.a = 20;

        let b = obj.a;

        obj.a = 30;

        // 实现一个函数,可以监控到对象属性的变化

        function observer(obj, key) {

            Object.defineProperty(obj, key, {

                configurable: true, // 可以被delete

                enumerable: true, // for in迭代

                set(val) {

                    console.log(`对象的属性${key}被赋值了${val}`);

                },

                get() {

                    console.log(`对象的属性${key}被读取了`);

                }

            });

        }

        //对象的属性a被赋值了20

        //对象的属性a被读取了

        //对象的属性a被赋值了30

    ```

    从上面的例子可以看出, 我们只要用 Object.defineProperty重写对象的属性读写器getter和setter就可以监听到对象属性的变化,上面的代码只可以监控一个属性的变化,对于对象的所有属性,需要遍历转换,改造一下observer方法(这里只考虑一层对象,深层次的需要递归遍历,数组的监控也需要另写方法,有兴趣的去看vue的源码)

    ```javascript

    function observer(obj) {

            Object.keys(obj).forEach(key => {

                let internalValue = obj[key]

                Object.defineProperty(obj, key, {

                    get() {

                        console.log(`getting key "${key}": ${internalValue}`)

                        return internalValue

                    },

                    set(newVal) {

                        console.log(`setting key "${key}" to: ${internalValue}`)

                        internalValue = newVal

                    }

                })

            })

        }

    ```

    现在我们已经可以监听到一个对象所有属性的变化了,最开始提出的问题已经解决了一半了,接下来要解决的问题是当属性变化的时候,如何收集它的依赖,触发依赖的更新呢?也就是我们这篇文章的主题,如何实现一个简易的响应式系统。

    思路就是:在读取属性的时候收集依赖,在改变属性值的时候触发依赖的更新

    1. 实现一个observer,劫持对象属性的getter和setter

    2. 实现一个全局的订阅器Dep,可以追加订阅者,和通知依赖更新

    3. 在读取属性的时候追加当前依赖到Dep中,在设置属性的时候循环触发依赖的更新

    按照这个思路,实习一个简易的响应式系统。

    ```javascript

        // 全局的依赖收集器Dep

        window.Dep = class Dep {

            constructor() {

                this.subscribers = new Set(); // 保证依赖不重复添加

            }

            // 追加订阅者

            depend() {

                if(activeUpdate) { // activeUpdate注册为订阅者

                    this.subscribers.add(activeUpdate)

                }

            }

            // 运行所有的订阅者更新方法

            notify() {

                this.subscribers.forEach(sub => {

                  sub();

              })

            }

        }

        let activeUpdate

        // js单线程语言,任一时刻只能有一个函数执行,也就是任一时刻,只可能有一个依赖在更新 //用一个全局变量activeUpdate来标志,这里有点不好理解,大家多想想就会明白autorun的巧妙之处了

        // autorun接受一个更新函数

        function autorun(update) {

            function wrapperUpdate() {

                activeUpdate = wrapperUpdate

                update() // wrapperUpdate, 闭包

                activeUpdate = null;

            }

            wrapperUpdate();

        }

        function observer(obj) {

            Object.keys(obj).forEach(key => {

                var dep = new Dep(); // 为每个key创建订阅器Dep

                let internalValue = obj[key]

                Object.defineProperty(obj, key, {

                    get() {

                        // console.log(`getting key "${key}": ${internalValue}`)

                        // 将当前正在运行的更新函数追加进订阅者列表

                        if(activeUpdate) {

                            dep.depend() //收集依赖

                        }

                        return internalValue

                    },

                    set(newVal) {

                    //console.log(`setting key "${key}" to: ${internalValue}`)

                    // 加个if判断,数据发生变化再触发更新

                    if(internalValue !== newVal) {

                          internalValue = newVal

                            dep.notify() // 触发依赖的更新

                    }

                    }

                })

            })

        }

        let state = {

            count:0

        }

        observer(state);

        autorun(() => {

            console.log('state.count发生变化了', state.count)

        })

        state.count = state.count + 5;

        // state.count发生变化了 0

        // state.count发生变化了 5

    ```

      这篇文章先更到这里,后续内容有时间继续更新,虽然更新时间不定,但是计划月底更新完,希望喜欢的小伙伴多多点赞,关注和支持,谢谢。

    相关文章

      网友评论

          本文标题:一来进阶vue

          本文链接:https://www.haomeiwen.com/subject/ksxrkctx.html