VUE原理

作者: 我叫Aliya但是被占用了 | 来源:发表于2020-05-09 11:33 被阅读0次

vue源码实现

  1. 模板解析
    • 拿到模板内每一个元素,放入内存中(AST抽象语法树)
    • 解析(深度遍历)每一个元素,及其属性
    • 抽离数据渲染部分准备复用
  2. 双向绑定
    • 数据劫持
    • 创建被观察者(beauty)和观察者
    • 在模板解析时,每次cmdHandler时为节点和属性创建观察者
    • 在数据劫持时,为每个对象创建被观察者(beauty),在cmdHandler访问数据get时(观察者刚刚创建),为beauty添加观察者,在set时改变beauty状态
  3. computed 和 methods,主要注意this指向问题

2.2 被观察者和观察者比作老师和家长更合适。老师(被观察者)要记录所有家长(观察者),并在学校有事的时候主动通知他们。

// 被观察者,存储每个数据的老值,发生变化后notify所有观察者
class Beauty {
    constructor (state, name) {
        this.watchers = []
        this.state = state
        this.name = name
    }

    setState(newval) {
        if (this.state == newval) return;
        this.state = newval
        this.notify()
    }

    addWatcher (observer) {
        this.watchers.push(observer)
    }

    notify () {
        // console.log('00000。', this.watchers)
        this.watchers.forEach(o => {
            o.update(this.state)
        })
    }
}
// 利用js单线程,存刚刚创建的watcher,给beauty用
let temp_target = null
// 观察者,存储每个节点上的第个命令,接到Beauty通知后,update node的上的属性(或文本)
class Watcher {
    constructor (vm, expr, cb) {
        this.vm = vm
        this.expr = expr
        this.cb = cb

        // 创建watcher,马上访问数据,触发数据劫持的get
        temp_target = this
        this.oldval = vm._getdata(expr)
        temp_target = null
    }

    update (newval) {
        if (this.oldval != newval) this.cb(newval)
    }
}
// 数据劫持
class DefineProperty {
    constructor (data) {
        this.observer(data)
    }

    observer (data) {
        if (data && typeof data == 'object') { 
            for (let key in data) {
                this.defineReactive(data, key, data[key])
            }
        }
    }

    defineReactive (data, key, value) {
        this.observer(value) 
        let beauty = new Beauty(value, key) // 为每个数据创建被观察者
        Object.defineProperty(data, key, {
            get () {
                // 有 temp_target 为刚刚创建的观察者,观察的就是当前数据
                // 为beauty添加 刚刚创建的watcher
                temp_target && beauty.addWatcher(temp_target)   
                return value
            },
            set: (newval) => { 
                if (value == newval) return;
                this.observer(newval)   // 防止普通值变为对象
                value = newval
                beauty.setState(newval) // 变更beauty状态
            }
        })
    }
}

class Vue {
    constructor (options) {
        window.vm = this
        // 数据劫持
        this.data = options.data()
        new DefineProperty(this.data)

        // 拿到模板内每一个元素,放入内存中
        this.$el = document.querySelector(options.el)
        this.fragment = document.createDocumentFragment()
        while (this.$el.firstChild) {
            this.fragment.append(this.$el.firstChild)
        }
        // 渲染节点
        this.fragment = this.render(this.fragment) 
        this.$el.append(this.fragment)
        // this.methods = options.methods
    }

    render (fragment) {
        fragment.childNodes.forEach(item => {
            if (item.nodeType == 3) {   // 文本节点
                cmdHandler['text'](item, this)
            } else if (item.nodeType != 8) { // 8是注释
                [...item.attributes].forEach(attr => {
                    if (!attr.name.startsWith('v-')) return false;

                    let [cmd] = attr.name.substr(2).split(':')
                    cmdHandler[cmd] && cmdHandler[cmd](item, this, attr)
                })
            }
            
            item.hasChildNodes() && this.render(item)
        })
        return fragment
    }

    _getdata (keystr) {
        return keystr.split('.').reduce((data, key) => data[key], this.data)
    }
}

let cmdHandler = {
    model (node, vm, attr) { 
        new Watcher(vm, attr.value, newval => {
            this.update.model(node, newval)
        })
        this.update.model(node, vm._getdata(attr.value), 'value')
        node.removeAttribute(attr.name)
    },
    bind (node, vm, attr) {
        let [, attr_truename] = attr.name.substr(2).split(':')
        new Watcher(vm, attr.value, newval => {
            this.update.bind(node, newval, attr_truename)
        }) 
        this.update.bind(node, vm._getdata(attr.value), attr_truename)
        node.removeAttribute(attr.name)
    }, 
    text (node, vm) {
        let expr = node.textContent
        let newtext = this._getContext(vm, expr, item_expr => {
            new Watcher(vm, item_expr, newval => {
                this.update.text(node, this._getContext(vm, expr))  // 重新算一遍
            })
        })
        this.update.text(node, newtext)
    },
    _getContext (vm, expr, cb) {
        return expr.replace(/\{\{(.+?)\}\}/g, (...args) => {
            cb && cb(args[1])
            return vm._getdata(args[1])
        })
    },
    click () {

    },
    update: {
        model (node, value) {
            node.setAttribute('value', value)
        },
        bind (node, value, key) {
            node.setAttribute(key, value)
        },
        text (node, value) {
            node.textContent = value
        }
    }
}

vm.data.obj.age = 44后,页面上的节点对应也变成了44

下面完成v-model

class Vue {
    ...
    _setvalue (keystr, val) {
        let arr = keystr.split('.')
        arr.reduce((data, key, i) => {
            if (arr.length - 1 == i)
                data[key] = val
            return data[key]
        }, this.data)
    }
}
let cmdHandler = {
    model (node, vm, attr) { 
        ...

        node.addEventListener('input', e => { vm._setvalue(attr.value, e.target.value) })
    },

相关文章

  • vue总结

    vue路由守卫 vue实现双向绑定原理 Vue生命周期: vue跨域方式 vue-router实现原理 vue-r...

  • 面试总结之基础(2)

    Vue2响应式原理 Vue3响应式原理

  • vue原理面试题资源整理

    vuex面试题 Vue生命周期 周期2 Vue通信 父子 兄弟 Vue响应式原理 MVVM Vue axios原理...

  • VUE(面试题)

    1、vue双向绑定原理vue2.x:vue双向绑定的原理是利用了Object.defineProperty()这个...

  • Vue面试题集锦

    VUE的双向绑定原理 原理:在创建Vue实例时,Vue会遍历data选项的属性,利用Object.definePr...

  • Vue响应式原理

    Vue2.x 核心响应式原理 Vue3.0 核心响应式原理 Vue 自定义事件 Vue 发布订阅模式 发布/订阅模...

  • Vue 父组件 传值 子组件 不生效

    原理 父组件.Vue 子组件.Vue 拓展

  • 前端TODO

    Vue.js 等框架原理了解 webpack 原理了解 browserify 插件开发 Vue.js 等框架原理学习

  • vue系列--- vue响应式原理

    vue响应式原理 要说vue响应式原理首先要说的是Object.defindProperty(),这个是响应式原理...

  • 关于vue常见的题目

    1. 简述vue响应式原理 2. 计算属性的原理 3. Vue.nextTick 原理 用法: 理解:nextTi...

网友评论

    本文标题:VUE原理

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