simple vue2


function hasOwn(obj, key) {
    return Object.prototype.hasOwnProperty.call(obj, key)
}

function def(obj, key, value) {
    Object.defineProperty(obj, key, {
        enumerable: false,
        get() {
            return value
        }
    })
}

function isDef(value) {
    return value != null && value != undefined
}

function defaultStrat(parVal, childVal, key) {
    return childVal ? childVal : parVal
}

function mergeOptions(parent, children, vm) {
    let res = {}
    function mergeKey(parVal, childVal, key) {
        let res = strats[key] ? strats[key](parVal, childVal, vm) : defaultStrat(parVal, childVal, vm)
        return res
    }
    for (let key in parent) {
        res[key] = mergeKey(parent[key], children[key], key)
    }
    for (let key in children) {
        if (!hasOwn(children[key])) {
            res[key] = mergeKey(parent[key], children[key], key)
        }
    }
    return res
}

class Vnode {
    constructor(context, tag, data, children, text, componentOption) {
        this.tag = tag
        this.key = data ? data.key : null
        this.data = data
        this.children = children
        this.text = text
        this.context = context
        this.componentOption = componentOption
    }
}
function createComponentVnode(vm, tag, data, ctor) {
    return new Vnode(
        vm,
        'component ' + tag,    //随便拼的
        {
            hooks: {
                i(vnode, parent) {                    
                    let instance = vnode.componentInstance = new vnode.componentOption.ctor({
                        _isComponent: true,
                        parentVnode: vnode,    //这个vnode是myComponent自己的vnode,上面有自己的信息
                    })
                    instance.$mount()   //手动调用
                    vnode.elm = instance.$el
                    parent.appendChild(vnode.elm)
                }
            }
        },
        [],
        '',
        {
            ctor,
            listners: data.on
        }
    )
}

let reservedHTML = ['div', 'span', 'p', 'h1', 'input', 'h2']

var _c, _v, _s, _l, _e

function normalizeChildren(children) {
    if (!children) {
        return null
    }
    let res = []
    if (Array.isArray(children)) {
        for (let i = 0; i < children.length; i++) {
            let c = children[i]
            let arr = normalizeChildren(c)   //递归一下,防止nested
            res.push.apply(res, arr)    //这里是apply,所以能够一个个push进去
        }
    } else {
        res.push(children)
    }
    return res
}
function _createElement(vm, tag, data, children) {
    children = normalizeChildren(children)
    //这里有好几种情况
    if (typeof tag === 'string') {
        if (reservedHTML.indexOf(tag) > -1) {
            return new Vnode(vm, tag, data, children)
        } else {
            //用户注册的组件名称,去components对象上找,因为挂载在上面
            return createComponentVnode(vm, tag, data, vm.$options.components[tag])
        }
    } else {
        return createComponentVnode()
    }
}
function createElement(tag, data, children) {
    let vm = this
    if (Array.isArray(data)) {
        children = data
        data = {}
    }
    return _createElement(vm, tag, data, children)
}

function createTextNode(str) {
    return new Vnode(this, null, null, null, str)
}

function renderList(arr, render) {
    let ret = []
    for (let i = 0; i < arr.length; i++) {
        let vnode = render(arr[i], i)
        ret.push(vnode)
    }
    return ret
}

_l = renderList
function initRender(vm) {
    vm._c = createElement.bind(vm)
    vm._v = createTextNode.bind(vm)
    _s = str => {
        if (typeof str === 'object') {
            return JSON.stringify(str)
        } else {
            return str
        }
    }
    _e = () => {
        return createTextNode('')
    }
}

function getProxy(obj, target, key) {
    Object.defineProperty(obj, key, {
        get() {
            return obj[target][key]
        },
        set(newVal) {
            obj[target][key] = newVal
        }
    })
}

class Depend {
    constructor() {
        this.subs = []
    }
    depend() {
        if (Depend.target) {
            Depend.target.addDep(this)
        }
    }
    notify() {
        this.subs.forEach(sub => {
            sub.update()
        })
    }
    addSub(sub) {
        this.subs.push(sub)
    }
}

let activeStack = []
function pushTarget(watcher) {
    activeStack.push(watcher)
    Depend.target = watcher
}
function popTarget() {
    activeStack.pop()
    Depend.target = activeStack[activeStack.length - 1]
}

const proto = Array.prototype
const ArrayMethods = Object.create(proto)
const MethodKeys = ['splice', 'push', 'pop', 'shift', 'unshift']
MethodKeys.forEach(key => {
    let method = proto[key]
    ArrayMethods[key] = function (...args) {
        let ob = this.__ob__
        let res = method.call(this, ...args)
        let inserted
        switch (key) {
            case 'push':
            case 'unshift':
                inserted = args
                break;
            case 'splice':
                inserted = args.splice(2)
                break;
            default:
                break;
        }
        observe(inserted)
        ob.deps.notify()         //Vue.set里面对数组操作调用的就是hack过后的方法
        return res
    }
})

class Observer {
    constructor(data) {
        this.deps = new Depend()
        def(data, "__ob__", this)
        if (Array.isArray(data)) {
            data.__proto__ = ArrayMethods      //对数组进行hack操作
            this.observeArray(data)
        }
        else {
            this.walk(data)
        }
    }
    walk(data) {
        Object.keys(data).forEach(key => {
            this.defineReactive(data, key)
        })
    }
    observeArray() {             //对数组进行hack操作
        for (let i = 0, l = data.length; i < l; i++) {
            this.observe(data[i])
        }
    }
    defineReactive(obj, key) {
        let childOb = observe(obj[key])     //如果是个object的话,我们要收集这个对象的依赖,给Vue.set用
        let dep = new Depend()
        let value = obj[key]
        Object.defineProperty(obj, key, {
            get() {
                if (Depend.target) {           //依赖收集
                    dep.depend(Depend.target)
                    childOb && childOb.deps.depend()
                }
                return value
            },
            set(newVal) {
                childOb = observe(newVal)
                value = newVal
                dep.notify()
            }
        })
    }
}

function observe(data) {
    if (typeof data != 'object') {  //普通类型
        return
    }
    let ob
    if (hasOwn(data, '__ob__')) {   //有__ob__这个属性,说明已经ob过了
        ob = data.__ob__
    } else {
        ob = new Observer(data)
    }
    return ob
}

function getExpr(expr) {

}

let queue = []
let pending = false
let flushing = false

function flushQueue() {
    while (queue.length > 0) {
        let watcher = queue.pop()
        watcher.run()
    }
    flushing = false
}
let taskStack = []

function flushCallbacks() {
    pending = false
    let copies = taskStack.slice()
    for (let i = 0; i < copies.length; i++) {
        copies[i]()
    }
}

function nextTick(fn) {
    let timerFunc = function (cb) {
        Promise.resolve().then(() => {
            cb()
        })
    }
    taskStack.push(fn)
    if (!pending) {
        pending = true
        timerFunc(flushCallbacks)
    }
}

function queueWatcher(watcher) {
    queue.push(watcher)
    if (!flushing) {        //这里的flushing控制只会触发flushQueue一次
        flushing = true
        nextTick(flushQueue)
    }
}

class Watcher {
    constructor(vm, fnOrExp, callback, options = {}) {
        this.get = typeof fnOrExp === 'string ' ? getExpr(fnOrExp) : fnOrExp
        this.deps = []
        this.vm = vm
        this.computed = this.dirty = options.computed
        this.value = options.computed ? null : this.getter()
    }
    getter() {
        pushTarget(this)
        let value = this.get.call(this.vm)
        popTarget()
        return value
    }
    update() {
        if (this.computed) {
            this.dirty = true
        } else {
            //this.run()
            queueWatcher(this)
        }
    }
    evaluate() {
        if (this.dirty) {
            this.value = this.getter()
        }
    }
    depends() {
        console.log(this.deps)
        if (this.deps) {
            this.deps.forEach(dep => {
                dep.depend(Depend.target)
            })
        }
        debugger
    }
    run() {
        let value = this.getter()
        if (this.value !== value) {
            this.vlaue = value
        }
    }
    addDep(dep) {
        if (this.deps.indexOf(dep) < 0) {
            dep.addSub(this)
            this.deps.push(dep)
        }
    }
}

function initData(vm) {
    let data = vm.$options.data
    data = vm._data = typeof data === 'function' ? data() : data
    for (let key in data) {
        getProxy(vm, "_data", key)
    }
    observe(data)
}

Vue.prototype.$emit = function (name, ...args) {
    let events = this._events[name]
    let vm = this
    debugger
    events.forEach(event => {
        event.call(vm, ...args)
    })
}

//这个步骤的用处是跟data是一样的,都需要把它绑定到vm实例上去,这样的话当render.call(vm)的时候才能
//把正确的数据给传到render上面去
function initMethods(vm) {
    let methods = vm.$options.methods
    Object.keys(methods).forEach(key => {
        vm[key] = methods[key].bind(vm)
    })
}

function initState(vm) {
    if (vm.$options.data) {
        initData(vm)
    }
    if (vm.$options.methods) {
        initMethods(vm)
    }
    if (vm.$options.computed) {
        for (let key in vm.$options.computed) {
            let handler = vm.$options.computed[key]
            Object.defineProperty(vm, key, {
                get() {
                    let w = new Watcher(vm, handler, null, { computed: true })
                    w.evaluate()
                    w.depends()
                    return w.value
                }
            })
        }
        debugger
    }
}
//这步的作用是在当我们_c(div,{on:{'fn':fn}})的时候,这个
function initEvents(vm) {
    vm._events = Object.create(null)
    let listners = vm.$options._parentListenrs
    if (listners) {
        //把回调函数挂到当前vm的_events上面
        for (let name in listners) {
            let events = vm._events[name] || []
            events.push(listners[name])
            vm._events[name] = events
        }
    }

}
function initMixin(Vue) {
    Vue.prototype.init = function (options) {
        let vm = this
        if (options._isComponent) {
            let parentVnode = options.parentVnode
            let parentComponentOptions = parentVnode.componentOption
            let _parentListenrs = parentComponentOptions.listners
            let opts = vm.$options = Object.create(vm.constructor.options)
            opts._parentListenrs = _parentListenrs   //需要把组件vnode的事件给到实例vm的选项上
        } else {
            vm.$options = mergeOptions(this.constructor.options, options, vm)
        }
        initRender(vm)               //设置模板渲染函数,让render()之后能拿到vnode
        initEvents(vm)
        callHook('beforeCreate', vm)
        initState(vm)              //对数据进行监听
        callHook('created', vm)
        //测试
        // new Watcher(vm, function () {
        //     console.log("this is " + this.aa)
        // })
        if (vm.$options.el) {
            vm.$mount(vm.$options.el)
        }
    }
}
function createChildren(vnode, children) {
    if (!children) return
    if (Array.isArray(children)) {
        for (let i = 0, l = children.length; i < l; i++) {
            createElm(children[i], vnode.elm)
        }
    } else {
        createElm(children, vnode.elm)
    }
}
//组件在不同生命周期的时候对不同属性的处理,如果是createElm说明是第一次进入到组件中
//那么就去调用cbs.create的处理函数
//如果是patchVnode估计就会调用cbs.update里面的处理函数
let cbs = {}

function updateAttrs() {

}
attrs = {
    create: updateAttrs,
    update: updateAttrs
}

function updateDirectives(oldVnode, Vnode) {
    Vnode.data = Vnode.data || {}
    //给所有的指令放上def属性,也就是指令的定义
    if (oldVnode.data.directives || Vnode.data.directives) {
        let newDirs = Vnode.data.directives.map(dir => {
            let { key, value } = dir
            let def = Vnode.context.$options.directives[key]
            dir.def = def
            return dir
        })
        console.log(newDirs)
        //经过了上一步,我们拿到了所有的指令的定义,然后去调用并传入指令的参数
        newDirs.forEach(newDir => {
            if (newDir.def) {
                newDir.def.bind(Vnode.elm, newDir)
            }
        })
    }

}

directives = {
    create: updateDirectives,
    update: updateDirectives,
}
events = {
    create: function createEvents() { },
    update: function updateEvents() { }
}

let modules = [attrs, events, directives]
let hooks = ['create', 'update']

for (let i = 0, l = hooks.length; i < l; i++) {
    let hook = hooks[i]
    modules.forEach(modul => {
        cbs[hook] = cbs[hook] ? cbs[hook].concat(modul[hook]) : [modul[hook]]
    })
}

function createComponent(vnode, parent) {
    if (vnode.data && (hooks = vnode.data.hooks) && (i = hooks.i)) {
        i(vnode, parent)
        return true
    }
}

function createElm(vnode, parent, ref) {
    if (createComponent(vnode, parent)) {
        return
    }
    if (vnode.tag) {
        vnode.elm = document.createElement(vnode.tag)
    } else {
        vnode.elm = document.createTextNode(vnode.text)
    }
    createChildren(vnode, vnode.children)
    //insert
    console.log(vnode.context.$options.directives)
    if (vnode.data && vnode.data.domProps) {
        Object.keys(vnode.data.domProps).forEach(key => {
            vnode.elm[key] = vnode.data.domProps[key]
        })
    }
    if (vnode.data && vnode.data.on) {
        Object.keys(vnode.data.on).forEach(key => {
            vnode.elm.addEventListener(key, vnode.data.on[key])
        })
    }
    for (let i = 0; i < cbs.create.length; i++) {
        let fn = cbs.create[i]
        let EmptyNode = { tag: '', data: {} }
        fn(EmptyNode, vnode)
    }
    if (parent) {
        if (ref) {
            parent.insertBefore(vnode.elm, ref)
        } else {
            parent.appendChild(vnode.elm)
        }
    }
    return vnode.elm
}

function isSameNode(node1, node2) {
    return node1.tag === node2.tag && node1.key === node2.key
}

function createKeyToIdx(oldChild, oldStart, oldEnd) {
    let map = Object.create(null)
    for (let i = oldStart, key; i <= oldEnd; i++) {
        isDef(key = oldChild[i].key) && (map[key] = i)
    }
    return map
}

function updateChildren(parentElm, oldChild, children) {
    let oldStart = 0
    let newStart = 0
    let oldEnd = oldChild.length - 1
    let newEnd = children.length - 1
    let VnodeToMove
    let keyToIdx
    while (oldStart <= oldEnd && newStart <= newEnd) {
        if (isSameNode(oldChild[oldStart], children[newStart])) {
            patchVnode(oldChild[oldStart], children[newStart])
            oldStart++
            newStart++
        }
        else if (isSameNode(oldChild[oldEnd], children[newEnd])) {
            patchVnode(oldChild[oldEnd], children[newEnd])
            oldEnd--
            newEnd--
        }
        else if (isSameNode(oldChild[oldStart], children[newEnd])) {
            //把老开头移动到老尾巴后面去
            patchVnode(oldChild[oldEnd], children[newEnd])
            parentElm.insertBefore(oldChild[oldStart].elm, oldChild[oldEnd].elm.nextSibling)
            oldStart++
            newEnd--
        } else if (isSameNode(oldChild[oldEnd], children[newStart])) {
            //把老尾巴移到老开头前面去
            patchVnode(oldChild[oldEnd], children[newStart])
            parentElm.insertBefore(oldChild[oldEnd].elm, oldChild[oldStart].elm)
            oldStart++
            newEnd--
        } else {
            //4个方向都没找到,去新节点头尾中间去找一个跟老的头一样的节点,找到了就放到老头的后面
            if (!keyToIdx) {
                keyToIdx = createKeyToIdx(oldChild, oldStart, oldEnd)
            }
            let index = keyToIdx[newStart]
            VnodeToMove = isDef(index) ? children[index] : null
            if (isDef(VnodeToMove)) {     //找到了老头对应节点插入到老头前面去
                parentElm.insertBefore(vnode.elm, oldChild[oldStart])
            } else {
                createElm(children[newStart], parentElm, oldChild[oldStart].elm)
            }
            newStart++
        }
    }
    if (oldStart <= oldEnd) {
        //老节点要删掉
        removeVnodes(parentElm, oldChild, oldStart, oldEnd)
    }
    if (newStart <= newEnd) {
        //还剩下新的节点没加上去,暂时没ref,因为没看懂
        addVnode(parentElm, children, newStart, newEnd)
    }
}

function addVnode(parentElm, children, start, end) {
    for (let i = start; i <= end; i++) {
        createElm(children[i], parentElm)
    }
}

function removeVnodes(parentElm, children, start, end) {
    for (let i = start; i <= end; i++) {
        parentElm.removeChild(children[i].elm)
    }
}

//patchVnode->updateChildren->pathVnode
function patchVnode(oldVnode, vnode) {
    let elm = vnode.elm = oldVnode.elm
    let oldChild = oldVnode.children
    let children = vnode.children
    if (!isDef(vnode.text)) {
        if (isDef(oldChild) && isDef(children)) {
            updateChildren(elm, oldChild, children)
        } else if (children) {
            addVnodes(elm, children)
        } else if (oldChild) {
            remoreVnodes(elm, oldChild)
        }
    } else {
        if (oldVnode.text !== vnode.text) {
            elm.textContent = vnode.text
        }
    }
    if (vnode.data && vnode.data.domProps) {
        Object.keys(vnode.data.domProps).forEach(prop => {
            elm[prop] = vnode.data.domProps[prop]
        });
    }
}

Vue.prototype.__patch__ = function (oldVnode, vnode) {
    if (!oldVnode) {
        createElm(vnode)
    } else {
        //二者都存在
        if (isSameNode(oldVnode, vnode)) {
            patchVnode(oldVnode, vnode)       //更新diff算法
        } else {
            if (isDef(oldVnode.nodeType)) {
                createElm(vnode, oldVnode.parentNode, oldVnode)
            }
        }
    }
    return vnode.elm
}

Vue.prototype._update = function (vnode) {
    console.log(vnode)
    let vm = this
    let prevNode = vm._vnode
    vm._vnode = vnode
    if (!prevNode) {
        vm.$el = vm.__patch__(vm.$el, vnode)
        console.log(vm.$el)
    } else {
        vm.$el = vm.__patch__(prevNode, vnode)
    }
}

function mountComponent(vm, el) {
    callHook('beforeMount', vm)
    function updateComponet() {
        let render = vm.$options.render
        vm._update(render.call(vm))
    }
    new Watcher(vm, updateComponet)
    return vm.$el
}

var ncname = '[a-zA-Z_][\\w\\-\\.]*';   //这里的\\w是因为当我们去用字符串去new的时候,\也需要转义
//这里的意思是必须要大小写字母和_开头
var tagStart = /^<([a-zA-Z]\w*)/     //这里要注意的是如果要区分start和end,比如<div>和</div>,一定要主要用^大头,因为html的后面会有其他的开头和结尾
//如果不加的话就会匹配到后面的开头
var StartCloseTag = /^\s*(\/?)>/;
var enTag = /^<\/(\w+)>/
var delimmiter = /\{\{(\w+)\}\}/
var attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/;
var forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/;
var forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/;
var stripParensRE = /^\(|\)$/g;
var dirRe = /v-|^@|^:/
var modifierRE = /\.[^\.]+/g
var bindRE = /:|v-bind:/
var onRE = /@|v-on:/
var simplePathRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/;

var root
var currentParent
var stack = []   //stack的作用主要是用来当子标签闭合,获取当前的currentParent
function getASTElement(tag, attrsList, attrsMap, unary) {
    return {
        tag,
        attrsList,
        attrsMap: attrsMap,
        unary,
        parent,
        children: []
    }
}
let match

function getAttrMap(attrs) {
    let res = {}
    for (let i = 0, l = attrs.length; i < l; i++) {
        res[attrs[i].key] = attrs[i].value
    }
    return res
}

function getAndRemoveAttr(el, key) {
    delete el.attrsMap[key]
    let res = []
    for (let i = 0; i < el.attrsList.length; i++) {
        if (el.attrsList[i].key !== key) {
            res.push(el.attrsList[i])
        }
    }
    el.attrsList = res
}

function processFor(element) {
    let keys = Object.keys(element.attrsMap)
    let expr
    for (let i = 0, l = keys.length; i < l; i++) {
        let key = keys[i]
        if (key === 'v-for') {
            expr = element.attrsMap[key].replaceAll("'", "").replaceAll('"', "")
            //todo
            getAndRemoveAttr(element, key)
            break
        }
    }
    if (expr) {
        let forMatch = expr.match(forAliasRE)
        element.for = forMatch[2]
        let alias = forMatch[1]
        element.alias = alias = alias.replace(stripParensRE, "")
        //a,b
        let iterMatch = alias.match(forIteratorRE)
        if (iterMatch) {
            element.alias = alias.replace(forIteratorRE, "").trim()
            element.iter1 = iterMatch[1].trim()
        }
    }
}

function addIfConditions(ast, condition) {
    if (!ast.ifConditions) {
        ast.ifConditions = []
    }
    ast.ifConditions.push(condition)
}

// function addIfConditions(ast) {
//     if (ast.elseif || ast.else) {
//         let prev = findPrev()
//         prev.ifConditions = prev.ifConditions || []
//         prev.ifConditions.push({
//             expr: ast.elseif || ast.else,
//             block: ast
//         })
//     } else {
//         ast.ifConditions.push({
//             expr: ast.if,
//             block: ast
//         })
//     }
// }

function processIf(element) {
    let expr
    let attrsMap = element.attrsMap
    let ifConditions = element.ifConditions
    Object.keys(attrsMap).forEach(key => {
        if (key === 'v-if') {
            expr = attrsMap[key]
            element.ifConditions = ifConditions ? element.ifConditions : []
            element.if = expr
            getAndRemoveAttr(element, key)
        } else if (key === 'v-else-if') {
            expr = attrsMap[key]
            element.elseif = expr
            getAndRemoveAttr(element, key)
        } else if (key === 'v-else') {
            element.else = true
            getAndRemoveAttr(element, key)
        }
    })
}

function parseHTML(html, options = {}) {
    let stack = []
    html = html.trim()
    function advance(len) {
        html = html.substring(len)    //这个html很巧妙,在闭包里面
    }
    function parseStart() {
        let startMatch = html.match(tagStart)
        if (startMatch) {
            let tag = startMatch[1]
            advance(startMatch[0].length)
            let attr, end, attrs = []
            while (!(end = html.match(StartCloseTag)) && (attr = html.match(attribute))) {
                attrs.push(attr)
                advance(attr[0].length)
            }
            let match = {
                tag,
                attrs
            }
            if (end) {
                advance(end[0].length)
                match.unaryTag = end[1]
            }
            return match
        }
    }
    let unaryTag = 'area,base,br,col,embed,frame,hr,img,input,isindex,keygen,' +
        'link,meta,param,source,track,wbr'

    function handleStart(match) {
        let tagName = match.tag
        let attrs = match.attrs
        let attrsList = []
        for (let i = 0, l = attrs.length; i < l; i++) {
            let attr = attrs[i]
            let key = attr[1]
            let value = attr[3] || attr[4] || attr[5]
            attrsList.push({ key, value })
        }
        let unary = unaryTag.split(",").indexOf(tagName) > -1 || !!match.unaryTag
        if (!unary) {
            stack.push({
                tag: tagName,
                lowerCaseTag: tagName.toLowerCase()
            })
        }
        options.start(tagName, attrsList, unary)
    }

    function parseEnd(match) {
        let tag = match[1]
        let pos
        for (let i = stack.length - 1; i >= 0; i--) {
            let lowerCaseTag = stack[i].lowerCaseTag
            if (tag.toLowerCase() === lowerCaseTag) {
                pos = i
                break
            }
        }
        if (pos >= 0) {
            for (let i = stack.length - 1; i >= pos; i--) {
                if (pos > i) {
                    console.warn("no endTag for" + stack[i].tag)
                    options.end()
                } else {
                    options.end()
                }
            }
            stack = stack.slice(0, pos)
        }
        else {
            if (tag === 'br') {
                options.start(tag, [], true)
            }
            if (tag === 'p') {
                options.start(tag, [], false)
                options.end()
            }
        }
    }

    while (html) {
        let index = html.indexOf("<")
        if (index === 0) {
            let startMatch = parseStart()
            if (startMatch) {
                handleStart(startMatch)
                continue
            }
            //endTag  </div>
            if (match = html.match(enTag)) {
                parseEnd(match)
                //options.end()
                advance(match[0].length)
                continue
            }
        }
        //处理文本节点
        let text = html.substring(0, index)
        if (text.trim().length > 0) {
            options.chars(text)
        }
        advance(text.length)
    }
    return root
}

function getChildren(children) {
    if (children.length === 1 && children[0].for) {
        //这里是一个需要注意的点,这里如果发现ast上面有for的话,那么就不能外面再包一层'[]',因为如果是[_l(....)]就不对了
        //_l返回的本来就是一个数组
        let child = children[0]
        if (child.tag) {
            //是一个html节点
            return getElement(child)
        } else {
            //是一个str文字
            return genNode(child)
        }
    }
    let str = children.map(child => {
        if (child.tag) {
            //是一个html节点
            return getElement(child)
        } else {
            //是一个str文字
            return genNode(child)
        }
    })
    return `[${str.join(",")}]`
}

//_c('div', [  _c('p', [_v(_s(aa))])   , _c('span', [_v(_s(bb))] )   ])

function genFor(element) {
    element.forProcessed = true
    //这里需要把v-for拼装成一个_l(res,callback)的这样的一个形式,这样的话,在_l中就看可以通过for循环来_c拿到vnode,
    return `_l(${element.for},function(${element.alias},${element.iter1}){return ${getElement(element)}})`
}

function genIf(ast) {
    let ifConditions = ast.ifConditions
    if (ifConditions.length === 0) {
        return '_e()'
    }
    let condition = ifConditions.shift()
    let node = condition.block
    node.ifProcessed = true
    let res = `${condition.expr} ? ${genNode(node)}: ${genIf(ast)}`
    console.log(res)
    return res
}

function addProp(el, key, value) {
    if (!el.props) {
        el.props = []
    }
    el.props.push({
        key, value
    })
}

function addHandler(el, eventName, code) {
    let events = el.events || (el.events = {})
    events[eventName] = code       //暂时不考虑数组的情况了
    el.events = events
}
function model(el, dir) {
    function genAssignmentCode(value, assign) {
        return value += "=" + assign
    }
    //生成一个model的处理函数
    let modifiers = dir.modifiers || {}
    let event = modifiers.lazy ? "change" : "input"
    addProp(el, "value", dir.value)
    let code = genAssignmentCode(dir.value, "$event.target.value")
    addHandler(el, event, code)

}


function genDirective(el, state) {
    let directives = el.dirs
    if (!directives) {
        return ""
    }
    let res = "directives: ["
    let flag = false
    for (let i = 0; i < directives.length; i++) {
        let dir = directives[i]
        let key = dir.key
        let gen = state[key]
        if (gen) {
            gen(el, dir)
        } else {
            res += `{key:'${dir.key}',value:${dir.value},modifiers:${JSON.stringify(dir.modifiers)}}`
        }
    }
    if (flag) {
        res.slice(0, res.length - 1)
    }
    return res + "]"
}
function genData(el, state) {
    let data = "{"
    let dirs = genDirective(el, state)
    if (dirs) {
        data += (dirs + ",")
    }
    if (el.props) {
        let props = el.props.reduce((res, prop) => {
            return res += `${prop.key}:${prop.value},`
        }, "")
        data += "domProps:{" + props.slice(0, props.length - 1) + "},"
    }
    if (el.events) {
        let res = ""
        //对于每个事件种类,如input.click等
        Object.keys(el.events).forEach(key => {
            let handler = el.events[key]
            if (simplePathRE.test(handler)) {
                handler = handler      //如果是一个函数名字的话就直接返回
            } else {
                handler = "function($event){" + el.events[key] + "}"
            }
            res += (key + ":" + handler)
        })
        data += "on:{" + res + "},"
    }
    data += "}"
    console.log(data)
    return data
}

function getElement(ast) {
    let str
    if (ast.for && !ast.forProcessed) {
        str = genFor(ast)
    }
    else if (ast.if && !ast.ifProcessed) {
        str = genIf(ast)
    }
    else {
        let data = genData(ast, { model: model })
        str = `_c('${ast.tag}'${data ? ',' + data : ''} ${ast.children ? ',' + getChildren(ast.children) : ''})`
    }
    return str
}

function getText(ast) {
    return `_v(_s(${ast.expr}))`
}
function genNode(ast) {
    if (ast.type === 1) {
        return getElement(ast)
    } else if (ast.type === 2) {
        return getText(ast)
    } else if (ast.type === 3) {
        return `_v(_s('${ast.expr}'))`
    }
}

function generate(ast) {
    let fn = `with(this){
        return ${genNode(ast)}
    }`
    console.log(fn)
    return new Function(fn)
}

function findPrev() {
    let parent = currentParent
    console.log(parent)
    let index = parent.children.length - 1
    //这里直接找前一个就能找到if的那个,因为中间隔着的v-else的都没放到children里面
    return parent.children[index]
}

function addDirective(ast, key, value, modifiers) {
    let dirs = ast.dirs || (ast.dirs = [])
    dirs.push({
        key,
        value,
        modifiers
    })
    ast.dirs = dirs
}

function processAttrs(ast) {
    let attrs = ast.attrsList
    for (let i = 0, l = attrs.length; i < l; i++) {
        let attr = attrs[i]
        let key = attr.key
        let modifiers = {}
        let modifierMath = key.match(modifierRE)
        if (modifierMath) {
            key = key.replace(modifierRE, "")
            modifierMath.forEach(modifier => {
                modifiers[modifier.slice(1)] = true    //把modifier的.给去掉
            })
        }
        let value = attr.value
        //这里要分情况讨论,attr的情况比较多,可能是属性,可能是事件,可能是指令
        if (dirRe.test(key)) {
            if (bindRE.test(key)) {
                //1.可能是prop (匹配v-bind:或者:的情况)
                key = key.replace(bindRE, "")
                addProp(el, key, value)
            } else if (onRE.test(key)) {
                //1.可能是handlers v-on:或者@的情况
                let event = key.replace(onRE, "")
                addHandler(ast, event, value)
            } else {
                //normal Directives
                //常规指令,v-model,v-html等等
                key = key.replace(dirRe, "")
                addDirective(ast, key, value, modifiers)
            }
        }
    }
}

function processElement(ast) {
    processAttrs(ast)
}
Vue.prototype.$mount = function (el) {
    let vm = this
    root = null
    vm.$el = document.querySelector(el)
    if (vm.$options.render) {
        return mountComponent(vm, el)
    }
    var ast = parseHTML(vm.$options.template, {
        start(tag, attrsList, unary) {
            let attrsMap = getAttrMap(attrsList)
            let ast = getASTElement(tag, attrsList, attrsMap, unary)
            ast.type = 1
            processFor(ast)
            processIf(ast)
            processElement(ast)    //processAttrs->将attrs分别放入el.props,el.handlers,el.directives
            if (!root) {
                root = ast
            }
            if (currentParent) {
                if (ast.if) {
                    addIfConditions(ast, {
                        expr: ast.if,
                        block: ast
                    })
                }
                if (ast.elseif || ast.else) {
                    let prev = findPrev()
                    addIfConditions(prev, {
                        expr: ast.elseif || ast.else,
                        block: ast
                    })
                } else {
                    //如果不存在else,和elseif那么才把它放到children里面去
                    //这样方便获取prev然后获取ifconditions
                    currentParent.children.push(ast)
                    ast.parent = currentParent
                }
            }
            if (!unary) {
                //如果是一元标签,那么就不会成为父亲,并且也不会走到parseEnd里头去,因为正则不会匹配到</..>
                currentParent = ast
                stack.push(ast)
            }
        },
        end() {
            stack.pop()
            currentParent = stack[stack.length - 1]    //<div><p></p><p></p></div>,把第一个p闭合父亲回到外层div
        },
        chars(text) {
            if (match = text.match(delimmiter)) {
                currentParent && currentParent.children.push({
                    expr: `${match[1]}`,
                    test: match[0],
                    type: 2
                })
            } else {
                currentParent && currentParent.children.push({
                    expr: text,
                    test: text,
                    type: 3
                })
            }
        }
    })
    vm.$options.render = generate(ast)
    return mountComponent(vm, el)
}

Vue.options = Object.create(null)
Vue.config = Object.create(null)

Vue.config.mergeOptionsStrategies = Object.create(null)

let strats = Vue.config.mergeOptionsStrategies

const ASSETS_TYPE = ['component', 'directive']

ASSETS_TYPE.forEach((asset) => {
    Vue.options[asset + "s"] = Object.create(null)
})


ASSETS_TYPE.forEach((asset) => {
    //静态函数,组件挂载
    if (asset === 'component') {
        Vue[asset] = function (id, definition) {
            let ctor = this.options._base.extend(definition)   //this.options._base就是Vue
            this.options[asset + "s"][id] = ctor
        }
    }
})

var show = {
    bind(el, ref) {
        let value = ref.value
        let originDisplay = el.style.display
        el.style.display = value ? originDisplay : 'none'
    },
    update() {

    }
}

platFormDirectives = {
    show: show
}

//把自带的给extend上去,这样

function extend(to, from) {
    for (let key in from) {
        to[key] = from[key]
    }
}
extend(Vue.options.directives, platFormDirectives)

function mergeHook(parVal, childVal) {
    if (!parVal) {
        parVal = []
    }
    if (Array.isArray(childVal)) {
        parVal = parVal.concat(childVal)
    } else {
        childVal && (parVal = [...parVal, childVal])
    }
    return parVal
}
//生命周期策略
const lifeCycles = ["beforeCreate", "created", "beforeMount", "mounted"]

lifeCycles.forEach(cycle => {
    strats[cycle] = mergeHook
})

function callHook(hook, vm) {
    const handlers = vm.$options[hook]
    if (handlers) {
        for (var i = 0, l = handlers.length; i < l; i++) {
            handlers[i].call(vm)
        }
    }
}

function Vue(options) {
    this.init(options)
}

initMixin(Vue)

Vue.options._base = Vue

Vue.extend = function (extendedOptions = {}) {
    //通过组合继承的方式继承父类
    let Super = this
    let Sub = function (options) {
        this.init(options)
    }
    Sub.options = mergeOptions(Super.options, extendedOptions)
    Sub.prototype = Object.create(Super.prototype)
    Sub.prototype.constructor = Sub
    ASSETS_TYPE.forEach(asset => {
        //给静态方法
        Sub[asset] = Super[asset]
    })
    Sub.mixin = Super
    return Sub
}

 

测试用例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div id="app"></div>
    <!-- <input oninput="input(this)" /> -->
    <script src="./test.js"></script>
    <script>
        var data = {
            aa: '欢迎',
            bb: 'Vue',
            num: -1,
            test: [{ a: 1, key: 1 }, { b: 2, key: 2 }]
        }
        Vue.component(
            "myComponent", {
                template: `<div name='a' v-show="true">
                            <div v-for='(ele, index) in test'>
                                {{aa}}
                            </div>
                            <input v-model='aa'/>
                            <div @click="emit()">emit</div>
                            {{name}}
                        </div>`,
                data() {
                    return data
                },
                computed: {
                    name: function () {
                        return this.aa + "wayne"
                    }
                },
                created() {
                    console.log('this is created')
                },
                methods: {
                    emit() {
                        this.$emit("fn")
                    }
                }
            }
        )
        var app = new Vue({
            el: '#app',
            template: `
                        <div>
                            <myComponent @fn="fn"></myComponent>
                            <span>{{data1}}</span>
                        </div>
                        `,
            // render: new Function(`
            // with(this){
            //     return _c('div' ,{} ,[true ? _c('span' ,{} ,[_l(test,function(ele ,index){
            //     return _c('div' ,{} ,[_v(_s(aa))])
            // })]) : _e(),_c('input' ,{on:{click:function($event){aa=222}}} ),_c('input' ,{domProps:{value:aa}} )])
            // }
            // `),
            data() {
                return {
                    data1: 'data'
                }
            },
            created() {
                console.log('this is created')
            },
            methods: {
                fn() {
                    this.data1 = 'data11111'
                }
            }
        })
        //测试用例(这里没给key):
        //data.test = []   data.test.push({c:3}) 
        //data.test =[{ a: 3,key:3 }, { b: 2,key:2 }]
        function input(e) {
            console.log(e.value)
            data.aa = e.value
        }
    </script>
</body>

</html>

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值