菜鸡手写vue(三)-初始渲染

217 阅读1分钟

组件挂载入口

Vue.prototype.$mount = function(el){
    const vm = this;
    const opt = vm.$options;
    el = document.querySelector(el);

    // 如果不存在render属性
    if(!opt.render){
        let template = opt.template;
        // 如果不存在tempalte,但是有el;
        if(!template && el){
            template = el.outerHTML;
        }

        // 最终还是需要将template模版转化成render函数
        if(template){
            let render = complileToFunction(template);
            opt.render = render;          
        }
    }
    mountComponent(vm, el);
}

组件挂载核心方法

export function mountComponent(vm, el){
    console.log(vm, el)
    // 实现页面的挂载流程
    vm.$el = el;
    const updateComponent = () => {
        // 调用render函数,获取虚拟节点,生成真实dom
        vm._update(vm._render())
    };
    // 如果数据发生变化,也调用这个函数
    updateComponent();
}

render函数转换虚拟dom方法

export function lifecycleMixin(Vue){
    Vue.prototype._c = function(){
        return createElement(this, ...arguments);
    }
    Vue.prototype._v = function(){
        return createTextNode(this, ...arguments);
    }
    Vue.prototype._s = function(value){
        if(Object.prototype.toString.call(value) === '[object Object]'){
            return JSON.stringify(value)
        }
        return value
    }

    Vue.prototype._render = function(){
        const vm = this;
        const render = vm.$options.render;
        let vnode = render.call(vm);
        return vnode;
    }
    Vue.prototype._update = function(vnode){     // 将虚拟节点变成真实节点
        // 将vnode渲染到元素中
        const vm = this;
        const el = vm.$el;
        vm.$el = patch(vm.$el, vnode);
    }
}

创建虚拟dom

export function createElement(vm, tag, data={}, ...children){
    return vnode(vm, tag, data, children, data.key, null);
}

export function createTextNode(vm, text){
    return vnode(vm, null, null, null, null, text);
}

function vnode(vm, tag, data, children, key, text){
    return {
        vm,
        tag,
        data,
        children,
        key,
        text,
    }
}

创建真实dom

export function patch(oldVnode, vnode){     // 后续做两个虚拟节点的比对
    const isRealElement =  oldVnode.nodeType; // 如果有nodeType说明是一个dom元素

    if(isRealElement){
        const oldEle = oldVnode;

        // 需要获取父节点,将当前节点的下一个元素作为参照参照物,之后删除老节点
        const parentNode = oldEle.parentNode;
        const el = createElm(vnode);
        parentNode.insertBefore(el, oldEle.nextSibling);
        parentNode.removeChild(oldEle);
    }else{

    }
}

// 根据虚拟节点创建真实节点
function createElm(vnode){
    let {tag, data, children, text} = vnode;
    if(typeof tag === 'string'){    // 元素
        // 后续需要diff算法 拿虚拟节点比对更新dom
        vnode.el = document.createElement(tag);
        children.forEach(child => {
            vnode.el.appendChild(createElm(child)); // 递归渲染
        })
    }else{                          // 文本
        vnode.el = document.createTextNode(text);
    }
    // 虚拟节点创建真实节点
    return vnode.el;    
}