- Writing JavaScript Views the Hard Way : यह लेख बताता है कि framework के बिना शुद्ध JavaScript से view कैसे बनाए जाएँ
- सीधे imperative approach के ज़रिए performance, maintainability और portability हासिल की जा सकती है
- state update और DOM update को स्पष्ट रूप से अलग रखा जाता है, और हर भूमिका के लिए सख्त naming rules और structural patterns अपनाए जाते हैं
- इस तरीके का बड़ा फायदा यह है कि debugging आसान होती है, सभी browsers के साथ compatibility मिलती है, और 0 dependencies रहती हैं
- शुरुआती लोगों के लिए यह कठिन हो सकता है, लेकिन सीखने पर सिस्टम वास्तव में कैसे काम करता है इसकी गहरी समझ मिलती है
JavaScript व्यू को 'Hard Way' से लिखना
यह क्या है?
- यह तरीका React, Vue, lit-html जैसे framework के बिना सिर्फ JavaScript से view बनाने का एक pattern है
- यह किसी खास library या tool की बजाय एक coding pattern है, जो spaghetti code की समस्या से बचाता है
- सीधा imperative तरीका इस्तेमाल करके abstraction कम किया जाता है और सहजता बढ़ाई जाती है
framework के मुकाबले फायदे
- Performance: imperative code होने के कारण यह बिना अनावश्यक computation के काम करता है, और hot path व cold path दोनों के लिए उपयुक्त है
- 0 dependencies: library upgrade या compatibility problems की चिंता नहीं रहती
- Portability: लिखा गया code किसी भी framework में port किया जा सकता है
- Maintainability: स्पष्ट section structure और naming rules के कारण code में चीज़ों का स्थान ढूँढना आसान होता है
- Browser support: IE9 और उसके बाद के ज़्यादातर browsers के साथ compatible है, और थोड़े बदलाव के साथ IE6 तक support किया जा सकता है
- Debugging में आसानी: बीच की layers न होने के कारण shallow stack trace मिलती है
- Functional structure: immutability नहीं है, लेकिन सारे components function-based होते हैं
संरचना की व्याख्या
पूरी संरचना
template → clone() → init() function से मिलकर बनी होती है
init() function एक view instance बनाता है जिसमें state variables, DOM references, update functions, event listeners आदि शामिल होते हैं
उदाहरण code संरचना (Hello World)
const template = document.createElement('template');
template.innerHTML = `<div>Hello <span id="name">world</span>!</div>`;
function clone() {
return document.importNode(template.content, true);
}
function init() {
let frag = clone();
let nameNode = frag.querySelector('#name');
let name;
function setNameNode(value) {
nameNode.textContent = value;
}
function setName(value) {
if(name !== value) {
name = value;
setNameNode(value);
}
}
function update(data = {}) {
if(data.name) setName(data.name);
return frag;
}
return update;
}
init() function की आंतरिक संरचना
1. DOM variables
frag वह template fragment है जो clone() से बनता है
- अंदर के elements को
querySelector() से reference किया जाता है, और variable नाम fooNode रूप में रखे जाते हैं
2. DOM views
- वे हिस्से जो दूसरे views को शामिल करते हैं (reusable subviews)
- उदाहरण:
let updateChildView = childView();
- view update functions का नाम
updateFoo रूप में रखा जाता है
3. state variables
- वे data values जो view के भीतर बदल सकती हैं
- DOM update को efficient रखने के लिए current value से तुलना करके केवल ज़रूरत पड़ने पर ही DOM बदला जाता है
4. DOM update functions
- DOM elements की state बदलने के लिए इस्तेमाल होती हैं
- उदाहरण:
function setNameNode(value) {
nameNode.textContent = value;
}
- DOM manipulation केवल इन्हीं functions के भीतर करनी चाहिए
5. state update functions
- इनमें state change logic और उसके अनुसार DOM को reflect करना शामिल होता है
- जो values नहीं बदली हैं उन्हें ignore किया जाता है ताकि अनावश्यक DOM changes रोके जा सकें
- उदाहरण:
function setName(value) {
if(name !== value) {
name = value;
setNameNode(value);
}
}
template और clone() function
template
<template> element से static HTML structure बनाया जाता है
- इसे सीधे DOM में insert नहीं किया जाता, बल्कि clone बनाकर उसकी copy तैयार की जाती है
clone()
document.importNode(template.content, true) से clone किया जाता है
- ज़रूरत पड़ने पर
.firstElementChild का इस्तेमाल करके root element लौटाया जा सकता है
interaction का तरीका
parent → child data flow
- parent, child के
init() को call करके update function प्राप्त करता है, और उसे update({ name: 'foo' }) रूप में call करता है
event-based data propagation
- मूल रूप से props down, events up model का पालन किया जाता है
- नीचे के views communication के लिए events को ऊपर dispatch करते हैं
React से तुलना
constructor() (React) → init() (Hard Way)
- component की initial setup संभालता है
render() (React) → update(data) (Hard Way)
- screen refresh और UI update का काम करता है
this.setState() (React) → setX(value) (Hard Way)
- इसे state values को सीधे set करने वाले तरीके से बदला जाता है
props (React) → update(data) से दिए गए values (Hard Way)
- parent component से आए data को संभालने का तरीका
- JSX / Virtual DOM (React) → HTML templates + DOM API (Hard Way)
- declarative UI की जगह manual DOM manipulation और templates का उपयोग
निष्कर्ष
- परिचित framework की तुलना में इस तरीके में शुरुआती entry barrier ज़्यादा हो सकती है, लेकिन इसके ये फायदे हैं:
- Performance optimization
- पूरा control
- सीखने से गहरी समझ
- भूमिका-आधारित function separation और naming rules के ज़रिए, framework के बिना भी maintainable UI बनाया जा सकता है
compatibility
- आधुनिक उदाहरण modern browsers के API का उपयोग करते हैं, लेकिन function-based alternatives के ज़रिए IE9 और उससे नीचे तक भी support संभव है
- events की जगह props के रूप में functions पास करने का तरीका अपनाकर इसे IE6 तक भी बढ़ाया जा सकता है
3 टिप्पणियां
आखिरकार वेब कंपोनेंट्स तक ही बात पहुँचती है..
बधाई हो। एक और js फ्रेमवर्क का जन्म हो गया है।
Hacker News टिप्पणियाँ
कई JS डेवलपर्स के लिए यह विधर्म जैसा हो सकता है, लेकिन मेरा मानना है कि
stateवेरिएबल एक anti-pattern हैstateवेरिएबल जोड़ने के बजाय DOM element केvalue/textContent/checkedआदि को ही single source of truth के रूप में इस्तेमाल करता हूँदस्तावेज़ में बताया गया है कि यह approach बहुत maintainable है, लेकिन मैं सहमत नहीं हूँ
हाल में मैं vite के साथ शुद्ध 'vanilla' TypeScript में application लिख रहा हूँ, और frontend की 'best' practices पर धीरे-धीरे और ज़्यादा सवाल उठने लगे हैं
यह approach पुराने backbone js लाइब्रेरी की याद दिलाती है
हाल में मैंने भी कुछ ऐसा ही सोचा, लेकिन template element का उपयोग नहीं करता
innerHTMLमें डालता हूँ या नयाdivelement बनाकर उसमें रखता हूँयह कोड ठीक वैसा ही दिखता है जैसा reactive view libraries replace करने की कोशिश करती हैं: manual update code
लगभग 20 साल से programming कर रहा हूँ, लेकिन frontend framework की आदत अब तक नहीं पड़ी
मैं
React.createElementजैसा एक helper इस्तेमाल करता हूँHTML-आधारित tools के लिए JS toolkit बनाने की कोशिश के तौर पर deja-vu.junglecoder.com पर काम कर रहा हूँ
कॉलेज से निकलने के बाद अपनी पहली औपचारिक नौकरी में Delphi software का web version बनाने पर काम किया था