Vue的编译与原理实现

test.html

<!--
 * @Description: 
 * @Autor: wangDuJuan
 * @Date: 2020-08-11 06:38:09
-->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id="app">
        <p>{{name}}</p>
        <p k-text="name"></p>
        <p>{{age}}</p>
        <p>
            {{doubleAge}}
        </p>
        <input type="text" k-model="name">
        <button @click="changeName">呵呵</button>
        <div k-html="html"></div>
    </div>
    <script src="./vueyyg.js"></script>
    <script src='./compile.js'></script>
    
    

    <script>
        const kaikeba = new Vue({
            el: '#app',
            data: {
                name: "I am test.",
                age: 12,
                html: '<button>这是一个按钮</button>'
            },
            created() {
                console.log('开始啦')
                setTimeout(() => {
                    this.name = '我是测试'
                }, 1500)
            },
            methods: {
                changeName() {
                    this.name = '哈喽'
                    this.age = 1
                }
            }
        })
    </script>
</body>

</html>

vueyyg.js

/*
 * @Author: yang
 * @Date: 2020-08-10 14:47:28
 * @LastEditors: yang
 * @LastEditTime: 2020-08-18 11:22:06
 * @FilePath: \demo3\vueyyg.js
 */
class Vue{
    constructor(options){
        this.$options = options
        this.$data = options.data
        this.observe(this.$data)
        // new Watcher()
       new Compile(options.el,this)
       if(options.created){
           options.created.call(this)
       }
    }
    observe(data){
        if(!data || typeof data != 'object'){
            return
        }
        Object.keys(data).forEach(key=>{
            this.defineRective(key,data[key],data)
            //代理到vm上
            this.proxyData(key)
        })
    }
    proxyData(key){
        // let dep = new Dep()
        Object.defineProperty(this,key,{
            get(){
                return this.$data[key]
            },
            set(newValue){
                    this.$data[key] = newValue  
            }
        })
    }
    defineRective(key,value,obj){
        let dep = new Dep()
        Object.defineProperty(obj,key,{
            get(){
                Dep.target&&dep.addDep(Dep.target)
                return value
            },
            set(newValue){
                if(newValue!==value){
                    value = newValue 
                    // console.log(`${key}更新了${newValue}`)
                    // 每一个依赖,一定有一个单独的dep
                    dep.notify()
                }
               
            }
        })
        //递归
        this.observe(value)
    }
    //数据更改的 依赖的视图对应去做更新 Dep数组里面有n个watcher,对应对数据依赖的视图
}
class Dep{
    constructor(){
        this.deps = []
    }
    addDep(dep){
        this.deps.push(dep)
    }
    notify(){
        this.deps.forEach(dep=>dep.update())
    }
}
class Watcher{
    constructor(vm,key,cb){
        this.vm = vm
        this.key = key
        this.cb = cb
        Dep.target = this
        this.vm[this.key];
        Dep.target = null
    }
    update(){
        // console.log('数据更新')
        this.cb.call(this.vm,this.vm[this.key])
    }
}

compile.js

/*
 * @Author: yang
 * @Date: 2020-08-13 13:51:08
 * @LastEditors: yang
 * @LastEditTime: 2020-08-21 09:23:05
 * @FilePath: \demo3\compile.js
 */
class Compile {
    constructor(el,vm){
        this.$el = document.querySelector(el)
        this.$vm = vm
        if(this.$el){
            // 提取宿主中模板的内容到fragment标签,dom操作会提高效率
            this.$fragment = this.node2Fragment(this.$el)
            this.compile(this.$fragment)
            this.$el.appendChild(this.$fragment)
        }
    }
    node2Fragment(el){
        let fragment = document.createDocumentFragment()
        let child;
        while(child = el.firstChild){
            fragment.appendChild(child)
        }
        return fragment
    }
    compile(el){
       var childNodes = el.childNodes
       Array.from(childNodes).forEach(node=>{
           if(node.nodeType === 1){
               this.compileElement(node)
               console.log('编译节点',node.nodeName)
           } else if(this.isInterpolation(node)){
               //插值表达式
               this.compileText(node)
               console.log('插值表达式',node.textContent)
           }
        //    遍历子节点
           if(node.childNodes&&node.childNodes.length>0){
               this.compile(node)
           }
       })
    }
    compileText(node){
        node.textContent = this.$vm[RegExp.$1]
        this.update(node,this.$vm,RegExp.$1,'text')
    }
    compileElement(node){
        var nodeAttributes = node.attributes
        Array.from(nodeAttributes).forEach(attr=>{
            const attrName = attr.name
            const exp = attr.value
            if(this.isDirective(attrName)){
                const dir = attrName.substring(2)
                this[dir]&&this[dir](node,this.$vm,exp)
            }
            if(this.isEvent(attrName)){
                let dir = attrName.substring(1)
                this.eventHandler(node,this.$vm,exp,dir)
            }
        })
    }
    isDirective(name){
         return name.indexOf('k-') === 0
    }
    isEvent(name){
        return name.indexOf('@') === 0
    }
    update(node,vm,exp,dir){
        let updateFn =  this[dir + 'Updator']
        updateFn&&updateFn(node,vm[exp])
        //收集依赖
        new Watcher(vm,exp,function(val){
            updateFn&&updateFn(node,val)
        })
    }
    text(node,vm,exp){
        this.update(node,vm,exp,'text')
    }
    html(node,vm,exp){
        this.update(node,vm,exp,'html')
    }
    model(node,vm,exp){
        // data -> view
        this.update(node,vm,exp,'model')
        // view -> data
        node.addEventListener('input',e=>{
            vm[exp] = e.target.value
        })
    }
    
    htmlUpdater(node,value){
        node.innerHTML = value
    }
    modelUpdater(node,value){
        node.value = value
    }
    textUpdator(node,val){
        node.textContent = val
    }
    isInterpolation(node){
        console.log(RegExp.$1)
        return node.nodeType ==3&&/\{\{(.*)\}\}/.test(node.textContent)
    }
    eventHandler(node,vm,exp,dir){
        let fn = vm.$options.methods&&vm.$options.methods[exp]
        if(dir&&fn){
            node.addEventListener(dir,fn.bind(vm))
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值