Недавно я начал заниматься разработкой собственного JS фреймворка подобного Angular'у, тут конечно я описывать не буду как он работает и как на нем писать SPA, но хотелось бы поделиться примером, как я реализовал управление состоянием и связывание DOM и JS.
Начнем с HTML странички:
<h1 bind="name">name</h1>
<input type="text" placeholder="Your name..." bind="name" />
<h1 bind="age">age</h1>
<input type="text" placeholder="Your age..." bind="age" />
Из компонента мы имеем состояние по умолчанию (initial state) в виде константы componentState.
const componentState = {
name: 'no name',
age: 0
}
const target = {};
const handler = {
get: function(target, prop) {
return target[prop];
},
set: function(target, prop, value) {
updateElements(prop, value)
return target[prop] = value
}
};
const state = new Proxy(target, handler);
function updateElements(prop, value) {
var els = document.querySelectorAll(`[bind='${prop}']`)
els.forEach(el => {
if(el.nodeName === 'INPUT' || el.nodeName === 'TEXTAREA') el.value = value
else el.textContent = value
})
}
function initBinds(prop, value){
var elems = document.querySelectorAll(`[bind='${prop}']`)
elems.forEach(el => {
if(el.nodeName === 'INPUT' || el.nodeName === 'TEXTAREA') {
el.value = value
el.addEventListener('input', (e) => {
state[prop] = e.target.value
})
}
else el.textContent = value
})
}
function reactivity(){
var keys = Object.keys(componentState)
keys.forEach(key => {
initBinds(key, componentState[key])
})
}
reactivity()
Из приведенного выше кода мы видим, что в константе handler, объявлены сеттер и геттер, которые через класс Proxy реагируют на чтение и запись данных, а далее объявлена константа state, которая как раз и будет творить чудеса. Как мы видим, что геттер просто возвращает содержимое, а вот сеттер как раз выполняет обновление данных как в объекте JS, так и в DOM с помощью функции updateElements, соответственно чтобы все заработало, нам необходимо связать объект state и объект состояния из компонента, что и делает функция initBinds. Ну а функция reactivity просто переберает первичные состояния из компонента и кормит их функции initBinds.
Тем самым у нас выходит, что когда например мы вводим в поле ввода
<input type="text" placeholder="Your name..." bind="name" />
у нас изменяется state, ну и само собой и заголовок.
<h1 bind="name">name</h1>
Вот и вся магия JS и кастомных атрибутов HTML.
P.S. Говорю сразу, что опыта в написании статей у меня нет и стаж в программировании у меня небольшой, т.ч. камнями не бросайте. У кого есть желание, то предлагайте решения по оптимизации или улучшению кода.