我为同样的目的创建了一个库,你可以在这里找到它:
https://www.npmjs.com/package/object-to-html-renderer。我知道你不能在项目中使用任何库,但是由于这个库非常短(并且没有依赖关系),你可以只复制和调整代码,代码只有一个小文件:
(请参见
https://gitlab.com/kuadrado-software/object-to-html-renderer/-/blob/master/index.js)。
module.exports = {
register_key: "objectToHtmlRender",
register(key) {
const register_key = key || this.register_key;
window[register_key] = this;
},
setRenderCycleRoot(renderCycleRoot) {
this.renderCycleRoot = renderCycleRoot;
},
event_name: "objtohtml-render-cycle",
setEventName(evt_name) {
this.event_name = evt_name;
},
objectToHtml(obj) {
if (!obj) return document.createElement("span");
const objectToHtml = this.objectToHtml.bind(this);
const { tag, xmlns } = obj;
const node = xmlns !== undefined ? document.createElementNS(xmlns, tag) : document.createElement(tag);
const excludeKeys = ["tag", "contents", "style_rules", "state", "xmlns"];
Object.keys(obj)
.filter(attr => !excludeKeys.includes(attr))
.forEach(attr => {
switch (attr) {
case "class":
node.classList.add(...obj[attr].split(" ").filter(s => s !== ""));
break;
case "on_render":
if (!obj.id) {
node.id = `${btoa(JSON.stringify(obj).slice(0, 127)).replace(/\=/g, '')}${window.performance.now()}`;
}
if (typeof obj.on_render !== "function") {
console.error("The on_render attribute must be a function")
} else {
this.attach_on_render_callback(node, obj.on_render);
}
break;
default:
if (xmlns !== undefined) {
node.setAttributeNS(null, attr, obj[attr])
} else {
node[attr] = obj[attr];
}
}
});
if (obj.contents && typeof obj.contents === "string") {
node.innerHTML = obj.contents;
} else {
obj.contents &&
obj.contents.length > 0 &&
obj.contents.forEach(el => {
switch (typeof el) {
case "string":
node.innerHTML = el;
break;
case "object":
if (xmlns !== undefined) {
el = Object.assign(el, { xmlns })
}
node.appendChild(objectToHtml(el));
break;
}
});
}
if (obj.style_rules) {
Object.keys(obj.style_rules).forEach(rule => {
node.style[rule] = obj.style_rules[rule];
});
}
return node;
},
on_render_callbacks: [],
attach_on_render_callback(node, callback) {
const callback_handler = {
callback: e => {
if (e.detail.outputNode === node || e.detail.outputNode.querySelector(`#${node.id}`)) {
callback(node);
const handler_index = this.on_render_callbacks.indexOf((this.on_render_callbacks.find(cb => cb.node === node)));
if (handler_index === -1) {
console.warn("A callback was registered for node with id " + node.id + " but callback handler is undefined.")
} else {
window.removeEventListener(this.event_name, this.on_render_callbacks[handler_index].callback)
this.on_render_callbacks.splice(handler_index, 1);
}
}
},
node,
};
const len = this.on_render_callbacks.push(callback_handler);
window.addEventListener(this.event_name, this.on_render_callbacks[len - 1].callback);
},
renderCycle: function () {
const main_elmt = document.getElementsByTagName("main")[0] || (function () {
const created_main = document.createElement("main");
document.body.appendChild(created_main);
return created_main;
})();
this.subRender(this.renderCycleRoot.render(), main_elmt, { mode: "replace" });
},
subRender(object, htmlNode, options = { mode: "append" }) {
let outputNode = null;
const get_insert = () => {
outputNode = this.objectToHtml(object);
return outputNode;
};
switch (options.mode) {
case "append":
htmlNode.appendChild(get_insert());
break;
case "override":
htmlNode.innerHTML = "";
htmlNode.appendChild(get_insert());
break;
case "insert-before":
htmlNode.insertBefore(get_insert(), htmlNode.childNodes[options.insertIndex]);
break;
case "adjacent":
htmlNode.insertAdjacentElement(options.insertLocation, get_insert());
break;
case "replace":
htmlNode.parentNode.replaceChild(get_insert(), htmlNode);
break;
case "remove":
htmlNode.remove();
break;
}
const evt_name = this.event_name;
const event = new CustomEvent(evt_name, {
detail: {
inputObject: object,
outputNode,
insertOptions: options,
targetNode: htmlNode,
}
});
window.dispatchEvent(event);
},
};
这是整个库,可以像这样使用(还有更多功能,但至少对于基本用法):
const renderer = require("object-to-html-renderer");
class DataListComponent {
constructor() {
this.render_data = [];
this.list_id = "my-data-list";
}
async fetchData() {
const fetchData = await (await fetch(`some/json/data/url`)).json();
return fetchData;
}
renderDataItem(item) {
return {
tag: "div",
contents: [
],
};
}
renderDataList() {
return {
tag: "ul",
id: this.list_id,
contents: this.render_data.map(data_item => {
return {
tag: "li",
contents: [this.renderDataItem(data_item)],
};
}),
};
}
render() {
return {
tag: "div",
contents: [
{
tag: "button",
contents: "fetch some data !",
onclick: async () => {
const data = await this.fetchData();
this.render_data = data;
renderer.subRender(
this.renderDataList(),
document.getElementById(this.list_id),
{ mode: "replace" },
);
},
},
this.renderDataList(),
],
};
}
}
class RootComponent {
render() {
return {
tag: "main",
contents: [new DataListComponent().render()],
};
}
}
renderer.setRenderCycleRoot(new RootComponent());
renderer.renderCycle();
我仅使用这个工具就构建了整个Web应用程序,它的表现非常好。我认为它是React Vue等的一个很好的替代品。(当然,它比React更简单,并且不具备React的所有功能..)也许它对你或其他人有用。
innerHTML
。 - Daifunction h(tagName, attributes, children) { /* 返回一个tagName元素 */ }
- joews