簡易Vue源碼實現

index.html文件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文件vue

// 定義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   用途:編譯器node

// 遍歷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') } }
相關文章
相關標籤/搜索