美文网首页
Vue 源码分析

Vue 源码分析

作者: pop_club | 来源:发表于2019-01-17 20:09 被阅读0次
    vue.jpg
    <div id="app" :class="myclass" @click="show">
        <p>{{ num }}</p>
    </div>
    

    一、处理模板

    1、获取 template

    
    var dom = document.querySelector(id);
    var template = dom ? dom.outerHTML : '<div></div>'
    ...
    <div id="app" :class="myclass" @click="show">
        <p>{{ num }}</p>
    </div>
    
    

    2、template 转 ast

    
    var startRe = /^<(\w+)/
    var bindRe = /^(:|v-bind:)/
    var eventRe = /^(@|v-on:)/
    var textRe = /\s*\{\{\s*([^\{\}]*)\s*\}\}\s*/
    
    function parseHTML(ops){
        var html = ops.html
        while(html){
            var pos = html.indexOf('<')
            
            if(pos == 0){
                var start = html.match(startRe)
                if(start){
                    // 开始标签
                    continue
                }
                
                var end = html.match(endRe)
                if(end){
                    // 结束标签
                    continue
                }
            }
            
            if(pos >= 0){
                // 文本内容
                advance(pos)
            }
            
            if(pos < 0){
                // 处理完毕
                html = ''
            }
        }
        
        function advance(len){
            html = html.substr(len)
        }
    }
    ...
    var ast = {
        tag: 'div',
        attrs: {
            id: 'app'
        },
        bindClass: 'myclass',
        events: {
            click: 'show'
        },
        children: [...ast]
    }
    
    

    3、ast 转 render

    
    function render(ast){
        var code = 'with(this){ return '
        code += renderCode(ast)
        return code +' }'
    }
    
    function renderCode(ast){
        if(ast.for && !ast.fored){
            return renderFor(ast)
        } else if(ast.if && !ast.ifed){
            return renderIf(ast)
        } else {
            var code = ''
            if(ast.type == 1){
                // dom 元素
                var data = renderData(ast)
                var children = renderChildren(ast.children)
                code += '_c("'+ ast.tag +'",'+ data +','+ children +')'
            } else if(ast.type == 2){
                // {{ ...}} 表达式
                code += renderText(ast.exp)
            } else {
                // 纯文本
                code += renderText(ast.text)
            }
            return code
        }
    }
    ...
    var render = 'with(this){ return _c("div",{bindClass:myclass,attrs:{"id":"app"},on:{click:show}},[_t("\n\t\t"),_c("p",{},[_t(_s(num))]),_t("\n\t")]) }'
    
    

    4、render 转 vNode

    
    function bind(fn, ctx){
        function bindFn(){
            arguments.length ? fn.apply(ctx, arguments) : fn.call(ctx)
        }
        return bindFn
    }
    
    this.$render = new Function(render)
    var vNode = this.$render.call(this)
    ...
    var vNode = {
        tag: 'div',
        attrs: {
            id: 'app'
        },
        bindClass: 'active',
        on: {
            click: bindFn()
        },
        elm: div#app.active,
        children: [...vNode]
    }
    
    

    二、初始化生命周期

    1、构造函数

    
    var did = 0
    
    function Dep(){
        this.id = did++
        this.subs = [...Watcher]
    }
    
    Dep.prototype.notify = function(){
        var subs = this.subs
        for(var i=0, l=subs.length; i<l; i++){
            subs[i].update()
        }
    }
    
    Dep.target = null
    
    var wid = 0
    
    function Watcher(ctx, exp, cb){
        this.id = wid++
        this.cb = cb
        this.ctx = ctx
        this.getter = exp
        this.value = this.get()
    }
    
    Watcher.prototype.get = function(){
        var value
        var ctx = this.ctx
        var exp = this.getter
    
        Dep.target = this
        if(isFun(exp)){
            try {
                value = this.getter.call(ctx)
            } catch(e) {
                warn(e)
            }
        } else {
            value = ctx[exp]
        }
    
        this.value = value
        Dep.target = null
        return value
    }
    
    Watcher.prototype.update = function(){
        var old = this.value
        var now = this.get()
    
        if(old != now){
            this.cb()
        }
    }
    
    var oid = 0
    
    function Observer(data){
        this.id = oid++
        if(isArray(data)){
            walkArray(data)
        } else {
            walkObject(data)
        }
        data.__ob__ = this
    }
    
    function defineReactive(obj, key, val){
        var dep = new Dep()
        Object.defineProperty(obj, key, {
            enumerable: true,
            configurable: true,
            get: function(){
                dep.depend()
                return val
            },
            set: function(value){
                val = value
                dep.notify()
            }
        })
    }
    
    

    2、初始化

    var vid = 0
    
    function V(ops){
        this.id = vid++
        this.$ops = ops
        this.$init()
    }
    
    V.prototype.$init = function(){
        initHook(this)
        initWorth(this)
        initRender(this)
    }
    
    function initWorth(ctx){
        initData(ctx, ops.data)
        initMethod(ctx, ops.methods)
        initFilter(ctx, ops.filters)
        initComputed(ctx, ops.computed)
        initWatch(ctx, ops.watch)
        callHook(ctx, 'created')
    }
    
    function initComputed(v, computed){
        for(var k in computed){
            defineComputed(v, k, computed[k])
        }
    }
    
    function defineComputed(obj, key, userDef){
        var getter = isFun(userDef) ? userDef : userDef.get
        var setter = isFun(userDef) ? noop : userDef.set
        Object.defineProperty(obj, key, {
            enumerable: true,
            configurable: true,
            get: bind(getter, obj),
            set: bind(setter, obj)
        })
    }
    
    function initWatch(v, watch){
        for(var k in watch){
            watchHandle(v, k, watch[k])
        }
    }
    
    function watchHandle(obj, key, userDef){
        var fn = obj[userDef]
        var cb = isFun(fn) ? fn : noop
        new Watcher(obj, key, cb)
    }
    
    function initRender(ctx){
        ctx.$render = new Function(code)
        ctx.$watcher = new Watcher(ctx, ctx.$update, noop)
        removeNode(query(ops.el))
    }
    
    V.prototype.$update = function(){
        var node = this.$render.call(this)
        
        if(this.$node){
            patch(this.$node, node)
            callHook(this, 'updated')
        } else {
            initNode(node)
            callHook(this, 'mounted')
        }
        this.$node = node
    }
    
    

    三、渲染视图

    1、初次渲染

    
    function initNode(node, parent, ref){
        parent = parent || document.body
        foundElm(node, parent, ref)
    }
    
    function foundElm(node, parent, ref){
        var i = null
        if(node.tag){
            node.elm = makeElm(node.tag)
        } else if(node.annotate){
            node.elm = makeAnnotate(node.text)
        } else if(node.text){
            node.elm = makeText(node.text)
        }
        if(node.data){
            foundData(node)
        }
        // 挂载 dom
        makeNode(node.elm, parent, ref)
        if(isDef(i = node.data) && isDef(i = i.html)){
            node.elm.innerHTML = i
        } else if(isDef(i = node.data) && isDef(i = i.text)){
            node.elm.innerText = i
        } else if(node.children){
            for(var i=0, l=node.children.length; i<l; i++){
                foundElm(node.children[i], node.elm)
            }
        }
    }
    
    

    2、更新

    
    function patchNode(a, b){
        var c = a.children
        var d = b.children
        var elm = b.elm = a.elm
        
        if(isUndef(b.text)){
            if(isDef(c) && isDef(d)){
                // diff 算法
                updateChildren(c, d, elm)
            } else if(isDef(c)){
                removeChildren(c, 0, c.length, elm)
            } else if(isDef(d)){
                addChildren(d, 0, d.length, elm)
            } else if(isDef(c.text)){
                elm.nodeValue = ''
            }
        } else if(a.text != b.text) {
            elm.nodeValue = b.text
        }
    }
    
    

    相关文章

      网友评论

          本文标题:Vue 源码分析

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