整体流程图

在10~15分钟完成的自定义Vue
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8" /> |
| <title>EasyFrame</title> |
| <script src="./easy-frame.js"></script> |
| </head> |
| <body> |
| <div id="app"> |
| <p>{{count}}</p> |
| <button @click="add">+1</button> |
| </div> |
| <script> |
| const app = new EasyFrame({ |
| el: "#app", |
| data: { |
| count: 0, |
| }, |
| methods: { |
| add() { |
| console.log("add"); |
| this.count++; |
| }, |
| }, |
| }); |
| </script> |
| </body> |
| </html> |
EasyFrame
| function EasyFrame(options) { |
| this.$options = options; |
| this.$el = this.$options.el; |
| this.$data = this.$options.data; |
| this.$methods = this.$options.methods; |
| observe(this.$data); |
| proxy(this); |
| compile(this.$el, this); |
| } |
observe
| function observe(obj) { |
| function defineReactive(obj, key, val) { |
| observe(val); |
| const dep = new Dep(); |
| Object.defineProperty(obj, key, { |
| get() { |
| |
| Dep.target && dep.addDep(Dep.target); |
| console.log("getter"); |
| return val; |
| }, |
| set(newVal) { |
| if (newVal != val) { |
| console.log("setter"); |
| observe(val); |
| val = newVal; |
| dep.notify(); |
| } |
| }, |
| }); |
| } |
| |
| Object.keys(obj).forEach((key) => defineReactive(obj, key, obj[key])); |
| } |
compile
| function compile(el, vm) { |
| function traverse(nodes) { |
| nodes.forEach((node) => { |
| if (node.nodeType === Node.ELEMENT_NODE) { |
| Array.from(node.attributes).forEach((attr) => { |
| if (attr.name.startsWith("@")) { |
| const attrName = attr.name.substring(1); |
| const attrValue = attr.value; |
| node.addEventListener(attrName, vm.$methods[attrValue].bind(vm)); |
| } |
| }); |
| } |
| if ( |
| node.nodeType === Node.TEXT_NODE && |
| /\{\{(.*)\}\}/.test(node.textContent) |
| ) { |
| node.textContent = vm[RegExp.$1]; |
| new Watcher(vm, RegExp.$1, function (val) { |
| node.textContent = val; |
| }); |
| } |
| if (node.childNodes && node.childNodes.length > 0) { |
| traverse(node.childNodes); |
| } |
| }); |
| } |
| const childNodes = document.querySelector(el).childNodes; |
| traverse(childNodes); |
| } |
Watcher&Dep
| function Dep() { |
| this.watchers = []; |
| this.addDep = function (watcher) { |
| this.watchers.push(watcher); |
| }; |
| this.notify = function () { |
| this.watchers.forEach((watcher) => watcher.update()); |
| }; |
| } |
| |
| function Watcher(vm, key, fn) { |
| |
| Dep.target = this; |
| vm[key]; |
| Dep.target = null; |
| |
| |
| this.update = function () { |
| fn && fn(vm[key]); |
| }; |
| } |
proxy
| function proxy(vm) { |
| Object.keys(vm.$data).forEach((key) => { |
| Object.defineProperty(vm, key, { |
| get() { |
| return vm.$data[key]; |
| }, |
| set(val) { |
| vm.$data[key] = val; |
| }, |
| }); |
| }); |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)