<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>My Vue from Scratch</title> </head> <body> <script> function reactive(obj) { const listeners = new Set(); const proxy = new Proxy(obj, { get(target, property, receiver) { if (typeof target[property] === 'object' && target[property] !== null) { return reactive(target[property]); } return Reflect.get(target, property, receiver); }, set(target, property, value, receiver) { const result = Reflect.set(target, property, value, receiver); listeners.forEach(fn => fn()); return result; } }); proxy.subscribe = function (fn) { listeners.add(fn); }; proxy.unsubscribe = function (fn) { listeners.delete(fn); }; return proxy; } class Component { constructor(options) { this.template = options.template; this.data = reactive(options.data()); this.methods = options.methods; this.style = options.style; this.rootId = options.rootId; // Ensure root element exists if (!document.getElementById(this.rootId)) { const rootElement = document.createElement('div'); rootElement.id = this.rootId; document.body.appendChild(rootElement); } this.data.subscribe(this.render.bind(this)); this.render(); } compileTemplate(template) { const match = template.match(/{{\s*(\w+)\s*}}/g); return () => { let compiledTemplate = template; if (match) { match.forEach(item => { const key = item.replace(/{{\s*|\s*}}/g, ''); compiledTemplate = compiledTemplate.replace(item, this.data[key]); }); } return compiledTemplate; }; } render() { const el = document.getElementById(this.rootId); if (el) { el.innerHTML = this.compileTemplate(this.template)(); this.applyMethods(el); } } applyMethods(el) { Object.keys(this.methods).forEach(methodName => { const matches = el.querySelectorAll(`[data-action="${methodName}"]`); matches.forEach(match => { match.onclick = this.methods[methodName].bind(this.data); }); }); } } const MyComponent = new Component({ template: ` <div class="my-component"> <div class="container"> <p class="message">Count: {{ count }}</p> <div class="buttons"> <button data-action="increment">Increment</button> <button data-action="decrement">Decrease</button> </div> </div> </div> `, data() { return { count: 0, }; }, methods: { increment() { this.count += 1; }, decrement() { this.count -= 1; }, }, style: ` .my-component { font-family: Arial, sans-serif; text-align: center; padding: 50px; background: #f0f0f0; } .my-component .container { background: white; padding: 20px; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.1); } .my-component .message { font-size: 24px; margin-bottom: 20px; } .my-component .buttons button { font-size: 16px; padding: 10px 20px; margin: 5px; cursor: pointer; border: none; border-radius: 5px; transition: background 0.3s; } .my-component .buttons button:hover { background: #ddd; } .my-component .buttons button:active { background: #ccc; } `, rootId: 'root' // Specify the ID for the root element }); // Ensure CSS is applied to the component const style = document.createElement('style'); style.textContent = MyComponent.style; document.head.appendChild(style); </script> </body> </html>