zoukankan      html  css  js  c++  java
  • 简易Vue源码实现

    index.html文件

    <div id="app">
        <p>{{name}}</p>
        <p k-text="name"></p>
        <p>{{age}}</p>
        <input type="text" k-model="name">
        <button @click="changeName">呵呵</button>
        <div k-html="html"></div>
    </div>
    
    
    <script src="kvue.js"></script>
    <script src='compile.js'></script>
    
    <script>
        const kaikeba = new KVue({
            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>

    kvue.js文件  --> 类似vue.js文件

    // 定义KVue构造函数
    class KVue {
        constructor(options){
            // 保存选项
            this.$options = options
    
            // 传入data
            this.$data = options.data
    
            // 响应化处理--数据的拦截处理
            this.observe(this.$data)
    
            // new Watcher(this, 'foo')
            // this.foo // 读一次,触发依赖收集
            // new Watcher(this, 'bar.mua')
            // this.bar.mua
    
            new Compile(options.el, this)
    
            if(options.created){
                options.created.call(this)
            }
        }
    
        observe(value){
            if(!value || typeof value !== 'object'){
                return
            }
    
            // 遍历
            Object.keys(value).forEach(key => {
                // 响应式处理
                this.defineReactive(value, key, value[key])
    
                // 代理data中的数据到vue根上
                this.proxyData(key)
            })
        }
    
        defineReactive(obj, key, val){
            // 递归
            this.observe(val)
    
            // 定义了一个Dep
            const dep = new Dep() // 每个dep的实例和data中每个key有一对一关系
    
            // 给obj的每一个key定义拦截
            Object.defineProperty(obj, key, {
                get(){
                    // 依赖收集
                    Dep.target && dep.addDep(Dep.target)
                    return val
                },
                set(newVal){
                    if(newVal !== val){
                        val = newVal
                        // console.log(key + ':属性更新了');
                        dep.notify()
                    }
                }
            })
        }
    
        // 在vue根上定义属性代理data中的数据
        proxyData(key){
            // this指的就是KVue的实例
            Object.defineProperty(this, key, {
                get(){
                    return this.$data[key]
                },
                set(newVal){
                    this.$data[key] = newVal
                }
            })
        }
    }
    
    // 创建Dep:管理所有Watcher
    class Dep {
        constructor(){
            // 存储所有依赖
            this.watcher = []
        }
    
        addDep(watcher){
            this.watcher.push(watcher)
        }
    
        notify(){
            this.watcher.forEach(watcher => watcher.update())
        }
    }
    
    // 创建Watcher:保存data中数值和页面中的挂钩关系
    class Watcher{
        constructor(vm, key, cb){
            // 创建实例时,立刻将该实例指向Dep.target便于依赖收集
            Dep.target = this
            this.vm = vm
            this.key = key
            this.cb = cb
    
            Dep.target = this
            this.vm[this.key] // 触发依赖收集
            Dep.target = null
        }
    
        // 更新
        update() {
            // console.log(this.key + '更新了!');
            this.cb.call(this.vm, this.vm[this.key])
        }
    }

    compile.js   用途:编译器

    // 遍历dom结构,解析指令和插值表达式
    class Compile {
        // el->待编译的模板,vm->KVue实例
        constructor(el, vm){
            this.$vm = vm
            this.$el = document.querySelector(el)
    
            // 把模板中的内容移到片段中去操作
            this.$fragment = this.node2Fragment(this.$el)
            // 执行编译
            this.compile(this.$fragment)
            // 放回$el中
            this.$el.appendChild(this.$fragment)
        }
    
        node2Fragment(el){
            // 创建片段
            const fragment = document.createDocumentFragment()
            // 
            let child
            while(child = el.firstChild) {
                fragment.appendChild(child)
            }
            return fragment
        }
    
        compile(el){
            const childNodes = el.childNodes
            // childNodes是一个类数组
            Array.from(childNodes).forEach(node => {
                if(node.nodeType == 1){
                    // 元素
                    // console.log('编译元素'+node.nodeName);
    
                    // 编译元素
                    this.compileElement(node)
                }else if(this.isInter(node)){
                    // 只关心{{xxx}}
                    // console.log('编译插值文本' + node.textContent);
    
                    // 编译文本
                    this.compileText(node)
                }
    
                // 递归子节点
                if(node.children && node.childNodes.length > 0){
                    this.compile(node)
                }
            })
        }
    
        isInter(node){
            return node.nodeType == 3 && /{{(.*)}}/.test(node.textContent)
        }
    
        // 文本替换
        compileText(node){
            console.log(RegExp.$1);
            console.log(this.$vm[RegExp.$1]);
    
            // 表达式
            const exp = RegExp.$1
            this.update(node, exp, 'text') // 等同于t-text
    
            
        }
    
        compileElement(node){
            // 关心属性
            const nodeAttrs = node.attributes
            Array.from(nodeAttrs).forEach(attr => {
                // 规定:k-xxx="yyy"
                const attrName = attr.name // k-xxx
                const exp = attr.value // yyy
                if(attrName.indexOf('k-') == 0){
                    // 指令
                    const dir = attrName.substring(2) // xxx
                    // 执行
                    this[dir] && this[dir](node, exp)
                }
            })
        }
    
        update(node, exp, dir){
            const updator = this[dir+'Updator']
            updator && updator(node, this.$vm[exp]) // 首次初始化
            // 创建Watcher实例,依赖收集完成了
            new Watcher(this.$vm, exp, function(value){
                updator && updator(node, value)
            })
        }
    
        textUpdator(node, value){
            node.textContent = value
        }
    
        text(node, exp){
            this.update(node, exp, 'text')
        }
    }
  • 相关阅读:
    TextWatcher 监视EditText的输入
    【Android】SqLitOpenHelper操作数据库
    【第一个开源项目】最完整的Andoird联系人操作,完美支持联系人的各种属性操作。
    Ext.ux.grid.RowEditor更新
    『转』如何判断Store加载完毕
    MyEclipse8.6安装svn插件
    『转』Eclipse中设置编码的方式
    『转』jsonlib json 格式 Date
    Spring Security的关键组件
    Ext.grid之numbercolumn格式化输出
  • 原文地址:https://www.cnblogs.com/haishen/p/11328636.html
Copyright © 2011-2022 走看看