# my-vue **Repository Path**: duans/my-vue ## Basic Information - **Project Name**: my-vue - **Description**: 模拟vue核心原理代码实现 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-01-20 - **Last Updated**: 2024-10-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # vue核心原理演示 ## vue.js ```jsx import Compiler from './compiler.js'; import Observer from './observe.js'; class Vue { // 构造器方法 constructor(options) { this.$el = options.el; this.$data = options.data || {}; // 处理数据 this._proxyData(options.data); // 处理方法 this._proxyMethods(options.methods); // 劫持数据 new Observer(this.$data); // 执行模板编译 this.compiler = new Compiler(this); } // 劫持data中的数据 _proxyData(data) { Object.keys(data).forEach(key => { Object.defineProperty(this, key, { get() { return data[key]; }, set(newVal) { data[key] = newVal; } }); }); /* const obj = new Proxy(data, { get(target, value) { return Reflect.get(target, value); }, set(target, key, value) { return Reflect.set(target, key, value); }, deleteProperty(target, key) { // 将删除操作反射到原始对象上 return Reflect.deleteProperty(target, key); } }); Object.keys(obj).forEach(key => { this[key] = obj[key]; }) */ } // 将methods直接挂在到this对象上 _proxyMethods(methods) { Object.keys(methods).forEach(key => { if (methods[key]) { this[key] = methods[key]; } }); } } window.Vue = Vue; export default Vue; ``` ## 数据劫持 ```jsx // Observer.js import Dep from './dep.js'; // 劫持数据 export default class Observer{ constructor(data){ this.data=data; // 实现批量劫持数据 this.walk(this.data); } // 批量劫持数据 walk(data){ if(!data||typeof data !=='object'){ return false; } Object.keys(data).forEach(key=>{ this.defineReactive(data,key,data[key]); }); } // 实现数据劫持 defineReactive(data,key,value){ const dep=new Dep(); Object.defineProperty(data,key,{ get(){ // 添加订阅者 Dep.target && dep.addSub(Dep.target) return value; }, set(newVal){ value=newVal; // 执行消息发布通知订阅者 dep.notify(); } }); this.walk(value); } } ``` ## 观察者 ```jsx // Watcher.js import Dep from './dep.js'; let $uid = 0 export default class Watcher { constructor(exp, scope, callback) { this.exp = exp; this.scope = scope; this.callback = callback; this.uid = $uid++; // 执行更新操作 this.update(); } // 更新方法 update() { // 获取表达式最新的状态数据 const value = this.get(); // 执行回调函数更新节点内容 this.callback(value); } // 获取表达式的最新值 get() { Dep.target = this; let newValue = Watcher.computedExpression(this.scope,this.exp); Dep.target = null; return newValue; } static computedExpression(scope,exp){ const fn=new Function('scope','with(scope){ return '+exp+'}'); return fn(scope); } } ``` ## 模板编译 ```js import Watcher from './watcher.js'; export default class Compiler { constructor(context) { this.$el = document.querySelector(context.$el); this.$data = context.$data; this.context = context; // 将dom节点转换成虚拟文档片段 this.$fragment = this.nodeToFragment(this.$el); // 编译虚拟文档片段 this.compiler(this.$fragment); // 将虚拟文档片段挂载到dom树上 this.$el.appendChild(this.$fragment); } // 将dom节点转换成虚拟文档片段 nodeToFragment(node) { // 创建一个虚拟文档片段(对其进行操作不会触发dom回流) let fragment = document.createDocumentFragment(); // 将页面中元素节点添加到虚拟文档片段上 node.childNodes.forEach(child => { // 元素节点和非空文本节点, 都需要追加到虚拟文档片段中 if(child.nodeType===3&&child.textContent.trim()!==''||child.nodeType===1){ fragment.appendChild(child); } }); // 将虚拟文档片返回 return fragment; } // 执行模板编译 compiler(node) { // 通过循环遍历, 执行正则表达式 node.childNodes.forEach(child => { this.compilerTextNode(child); }); } // 编译文本内容 compilerTextNode(node) { // 匹配插值表达式的正则表达式 const reg = /\{\{(.+?)\}\}/g; // 执行模板编译 const pieces = node.textContent.split(reg); const matches = node.textContent.match(reg); let tokens = []; pieces.forEach(item => { if (matches && matches.indexOf(`{{${item}}}`) !== -1) { tokens.push('(' + item + ')'); } else { tokens.push('`' + item + '`'); } }); new Watcher(tokens.join('+'), this.context, value => { // 更新节点内容 node.textContent = value; }); } } ``` ## 发布者订阅者模式 ```js // 观察者模式中 通知 export default class Dep { constructor() { // 存放所有watcher // this.subs = {0:{update},1:{update}} this.subs = {} } addSub(target) { this.subs[target.uid] = target } notify() { for (let uid in this.subs) { this.subs[uid].update() } } } ```