最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • vue2核心原理(简易)-watch笔记

    正文概述 掘金(小桂summer)   2021-01-06   473

    前言

    • watch 中每个属性都会new一个用户watcher(new Watcher)
    • 在数据初始化得时候 开始new Watcher, Dep.target 指向此时的用户watcher, 此时该属性中的加入用户watcherdep.addSub.push(watcher)
    • 当data中的数据发生变化时, 调用该数据的所有watcher
    • Watcher先将老值存起来 数据发生变化时 将新值与老值 返回给表达式(cb)

    html 和 javascript 模板

    <div id="app">{{ name }}</div>
    
    <script src="dist/vue.js"></script>
    <script>
        var vm = new Vue({
            data: {
                name: 'one'
            },
            /** 用户watcher 几种方式 */
            watch: {
                // name(newVal, oldVal) {
                //     console.log(newVal, oldVal)
                // },
                // name: [
                //     function(newVal, oldVal) {
                //         console.log(newVal, oldVal)
                //     },
                //     function(a, b) {
                //         console.log(a, b)
                //     }
                // ],
                // 'obj.n'(newVal, oldVal) {
                //     console.log(newVal, oldVal)
                // }
            }
        })
    
        vm.$mount('#app')
    
        vm.$watch('name', function(newValue, oldValue) {
            console.log(newValue, oldValue)
        })
        setTimeout(() => {
            vm.name = 'two'
        }, 2000)
    

    vue2核心原理(简易)-watch笔记

    正题

    $watch

    export function stateMixin(Vue) {
        Vue.prototype.$watch = function(key, handler, options={}) {
            // 用户创建的watcher
            options.user = true
    
            new Watcher(this, key, handler, options)
        }
    }
    

    options.watch 初始化开始执行

    export function initState(vm) {
        const opts = vm.$options
    
        if (opts.watch) {
            initWatch(vm, opts.watch)
        }
    
    }
    
    /** initWatche module */
    /**
     * @description 调用$watch
     */
    function createWatcher(vm, key, handler) {
        return vm.$watch(key, handler)
    }
    
    /**
     * @description 初始化 watch 将观察属性的function拆分出来
     * @description 每个观察属性 创建new watcher
     */
    function initWatch(vm, watch) {
        for (const key in watch) {
            const handler = watch[key]
            if (Array.isArray(handler)) {
                for (let i = 0; i < handler.length; i++) {
                    createWatcher(vm, key, handler[i])
                }
            } else {
                createWatcher(vm, key, handler)
            }
        }
    }
    

    Watcher 类 (关键)

    import { pushTarget, popTarget } from './dep'
    import { queueWatcher } from './scheduler'
    
    /** 给new Watcher一个id */
    let id = 0
    
    /**
     * @description 数据劫持时 期望一个属性对应多个watcher 同时一个watcher对应多个属性
     */
    class Watcher {
        constructor(vm, exprOrFn, cb, options) {
            this.vm = vm
            this.exprOrFn = exprOrFn
            this.cb = cb
            this.options = options
    
            
            this.id = id++
            
            this.deps = []
            this.depsId = new Set()
            
            /** 用户watcher */
            this.user = !!options.user
            // 看这里 将获取的函数名 封装成表达式 
            if (typeof exprOrFn == 'string') {
                this.getter = function() {
                    // vm取值 'obj.n' -> ['obj', 'n'] -> vm['obj']['n']
                    let path = exprOrFn.split('.')
                    let obj = vm
                    for (let i = 0; i < path.length; i++) {
                        obj = obj[path[i]]
                    }
                    return obj
                }
            } else {
                this.getter = exprOrFn
            }
    
            /** 用户watcher 默认取得第一次值 */
            this.value = this.get()
        }
    
        /** render生成vnode时 dep.push(watcher) 并更新视图 */
        get() {
            pushTarget(this)
            //  看这里 在VM上寻找劫持的数据 可将该属性上push用户Watcher
            const value = this.getter.call(this.vm)
            popTarget()
    
            return value
        }
    
        update() {
             queueWatcher(this)
        }
    
        run() {
            /** 考虑1 渲染组件watcher */
            /** 考虑2 用户watcher(newValue oldValue) */
            let newValue = this.get()
            let oldValue = this.value
            this.value = newValue
    
            /** 看这里 用户watcher 将值返回给cb, 并调用 */
            if (this.user) {
                this.cb.call(this.vm, newValue, oldValue)
            }
        }
    
        /** 存储dep 并排除生成vnode时多次调用一样属性 只存一个 dep 或 watcher */
        /** 其次当属性发生变化时将不再存储dep 和 watcher */
        addDep(dep) {
            let id = dep.id
            if (!this.depsId.has(id)) {
                this.depsId.add(id)
                this.deps.push(dep)
                dep.addSub(this)
            }
        }
    
    }
    
    export default Watcher
    
    

    Dep 类

    /** 每个劫持的属性 加上唯一的标识 */
    let id = 0
    
    /**
     * @description 每个劫持的属性 new Dep
     */
    class Dep {
        constructor() {
            this.id = id++
            this.subs = []
        }
    
        /** dep传给watcher */
        depend() {
            if (Dep.target) {
                Dep.target.addDep(this)
            }
        }
    
        addSub(watcher) {
            this.subs.push(watcher)
        }
    
        notify() {
            this.subs.forEach(watcher => watcher.update())
        }
    
    }
    
    Dep.target = null
    let stack = []
    
    export function pushTarget(watcher) {
        Dep.target = watcher
        stack.push(watcher)
    }
    
    export function popTarget() {
        stack.pop()
        Dep.target = stack[stack.length - 1]
    }
    
    export default Dep
    


    起源地下载网 » vue2核心原理(简易)-watch笔记

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元