Các ví dụ của Vanilla JSX
Nếu JSX trả về phần tử DOM thì sao?
- Hàm
ClickMe tạo một nút bấm và hiển thị số lần được nhấp
- Mỗi khi nhấp vào nút, nội dung văn bản sẽ được cập nhật
export default function ClickMe() {
let i = 0;
const el = Click me as HTMLButtonElement;
el.onclick = (e) => {
el.textContent = `Clicked ${++i} times`;
};
return el;
}
Khả năng tái sử dụng
- Có thể dùng component
ClickMe nhiều lần để mỗi thành phần giữ trạng thái riêng
import ClickMe from "./sample1.js";
export default () =>
;
Tạo cây DOM tương tác
- Có thể quản lý danh sách việc cần làm bằng các lớp
TodoInput và TodoList
- Có thể thêm mục và nhấp để xóa chúng
function TodoInput(attrs: { add: (v: string) => void }) {
const input = as HTMLInputElement;
input.placeholder = 'Add todo item...';
input.onkeydown = (e) => {
if (e.key === 'Enter') {
attrs.add(input.value);
input.value = '';
}
};
return input;
}
class TodoList {
ul = as HTMLUListElement;
add(v: string) {
const item =
- {v}
as HTMLLIElement;
item.onclick = () => item.remove();
this.ul.append(item);
}
}
export default () => {
const list = new TodoList();
list.add('foo');
list.add('bar');
return
list.add(v)} />
{list.ul}
;
};
Xử lý dữ liệu quy mô lớn
- Hàm
FindNames xử lý và lọc lượng dữ liệu lớn để hiển thị kết quả
- Các mục khớp sẽ được cập nhật theo thời gian thực dựa trên giá trị đầu vào
import { data } from "../fetch-dataset.js";
export default function FindNames() {
const status = as HTMLParagraphElement;
const results = as HTMLUListElement;
const input = as HTMLInputElement;
updateMatches();
function updateMatches() {
const matched = (data.entries().filter(([k]) => k.match(input.value)).toArray());
const matches = (Iterator.from(matched).map(match => ).take(30));
results.replaceChildren(...matches);
status.textContent = `${matched.length} / ${data.size}`;
}
return
{input}
{status}
{results}
;
}
function Item(attrs: { match: [string, number], regex: string }) {
const [name, count] = attrs.match;
const total = ({count});
return
-
{total}
;
}
function highlight(str: string, regex: string) {
if (!regex) return str;
const r = new RegExp(`(${regex})`, 'gi');
return str.replace(r, '$1');
}
Giới thiệu về imlib
imlib là thư viện được phát triển cho immaculatalibrary.com
- Được dùng để xây dựng minigamemaker.com và website bạn đang đọc
- Nó được tạo ra vì các trạng thái hiện có chưa đủ đáp ứng, và là cách được ưa thích nhất để xây dựng ứng dụng
Tóm tắt của GN⁺
- Bài viết này giải thích cách dùng JSX để trực tiếp tạo và tương tác với các phần tử DOM
- Đưa ra cách xử lý dữ liệu quy mô lớn hiệu quả mà không cần dùng virtual DOM
- Thư viện
imlib giúp phát triển ứng dụng theo cách đơn giản và trực quan
- Các dự án khác có tính năng tương tự gồm React, Vue.js, v.v.
1 bình luận
Ý kiến trên Hacker News
Nếu trả về nút DOM thực tế thì gần như làm suy yếu lợi thế lớn của các thư viện không dùng JSX như JSX và Lit: API kiểu immediate mode và mô hình
UI=f(state)Cần trả về mô tả của DOM chứ không phải DOM thật thì mới có thể đánh giá lại template với trạng thái mới và cập nhật hiệu quả DOM được render từ template đó
Các ví dụ đang cập nhật bằng DOM API kiểu mệnh lệnh như
input.onkeydown = ...haythis.ul.append(item), trong khi việc tránh loại mã này và lặp qua các mục trong template có lẽ là lợi ích chính của virtual DOMTuy vậy, vấn đề của virtual DOM là việc tính diff chậm, và có thể giải quyết bằng template tách riêng phần tĩnh với phần động như Lit
Ngược lại, nó còn khiến người ta nghĩ “cứ render lại cả UI lớn thoải mái, virtual DOM sẽ lo”, tạo tác dụng ngược; hiệu năng thực tế thì kém nhưng cấu trúc ứng dụng lại bị cố định đến mức khó sửa nếu không viết lại
Nhóm của bạn đã thấy đúng luồng đó, và với tư cách kiến trúc sư, bạn từng đề xuất kiến trúc vanilla chú trọng hiệu năng dựa trên những vấn đề sẽ gặp ở React, nhưng không được tiếp nhận vì kiểu lập luận “có nhiều cách để tránh mà”
Cuối cùng có lẽ trong nhiều trường hợp người ta phải tự vất vả mới học được
createElement, còn các lợi ích được mô tả đến từ framework và runtimeNói ngắn gọn hơn, JSX là cú pháp thay thế cho lời gọi hàm, hữu ích trong một số ngữ cảnh
Ở công ty cũ, JSX được dùng cho factory dữ liệu kiểm thử; trông như XML nhưng ở bước tạo phần tử có thể đưa ra các quyết định theo ngữ cảnh để phù hợp với dữ liệu cuối cùng bằng builder pattern
Nó không liên quan gì đến React hay DOM, cũng không có nghĩa là nếu không có JSX thì không thể biểu đạt theo kiểu khai báo
Nhưng cá nhân tôi thích cập nhật DOM theo kiểu mệnh lệnh hơn. Bạn có thể kiểm soát cực kỳ chi tiết chuyện gì đang diễn ra và thiết kế theo cấu trúc rất hiệu quả
Đồng thời có thể khiến ứng dụng dễ thêm, thay đổi, xóa và sửa tính năng mà không cần phải suy nghĩ theo một hệ phương pháp cứng nhắc của ai đó
UI=f(state)ở JSX và các thư viện không dùng JSX như Lit, nhưng điều này lại giống một abstraction bị rò rỉ vào hàng bậc nhất trong toàn bộ ngành tính toánCó lý do khiến quanh React hình thành cả một hệ sinh thái về cách tránh render nhiều lần và ngăn các lần refresh không cần thiết
Tôi làm dự án này vì đã thấy bức bối với tình trạng của trình tạo site tĩnh suốt 10 năm qua
Vì chủ yếu làm website tĩnh nên tôi muốn công cụ đơn giản, trực quan; JSX có vẻ phù hợp nhưng tôi đã mệt với sự phức tạp quá mức của các framework JSX như React
Vì thế tôi tạo một trình tạo site tĩnh render JSX thành chuỗi, rồi mở rộng sang phía trình duyệt để render JSX thành phần tử DOM, điều này diễn ra khá tự nhiên
Trong một số trường hợp, đặc biệt là layout, nó cũng rất hợp với các component dùng chung. Nhìn chung tôi hài lòng với kết quả, dù một số phần khá hacky và hỗ trợ IDE vẫn có thể tốt hơn
Ngoài ra, cách này cũng hợp với SEO. Đây là vấn đề mà tôi chưa thấy các framework JSX khác thực sự giải quyết ổn thỏa
Trong tài liệu có lẽ nên viết đầy đủ “static site generator” thay vì “SSG”. Dù đã từng dùng trình tạo site tĩnh, tôi vẫn không biết SSG là gì và phải hỏi AI
Tôi chuyển từ Vue sang nên khá ngạc nhiên: chỉ với template vanilla JS đơn giản, lit-html có thể làm được nhiều việc mà template Vue làm, bao gồm cả gắn sự kiện
Nếu dùng extension lit cho VSCode, bạn còn có thể nhận được syntax highlighting và kiểm tra kiểu đầy đủ ngay trong template
Tôi biết đến nó sau khi Marc Grabanski tweet rằng anh ấy dùng lit-html với vanillajs chứ không phải Lit, và khi tự thử thì thấy nó hoạt động rất tốt. Nó trông khá giống với điều bạn đang cố giải quyết
Nếu dùng gói template
lit-htmlthì có thể làm hầu hết những gì mô tả ở phần template: https://lit.dev/docs/templates/overview/Thậm chí không cần abstraction component của
lit-element: https://lit.dev/docs/libraries/standalone-templates/#renderi...Lý do những giả định kiểu “nếu như” này thú vị là vì có thể lần ngược nguồn gốc của JSX về tận XHP của Facebook, và XHP đã được truyền cảm hứng một cách rõ ràng từ chuẩn JS đời đầu E4X, vốn trông và hoạt động tương tự thư viện được mô tả ở đây
[1] https://engineering.fb.com/2010/02/09/developer-tools/xhp-a-...
[2] https://en.m.wikipedia.org/wiki/ECMAScript_for_XML
Đó cũng là lý do JSX đã vượt qua E4X, và sau đó không còn đề xuất nghiêm túc nào về template HTML bên trong JS nữa
Dù vậy, có lẽ vẫn có thể hồi sinh ý tưởng chung này theo cách hiện đại hơn: https://github.com/WICG/webcomponents/issues/1069
for(of)thay vìfor eachlà vì E4XCòn chuyện không có được
for (:)thì còn ngớ ngẩn hơn, vì nó có thể xung đột với chú thích:typemà một số thành viên TC39 từng tin rằng một ngày nào đó sẽ tự nhiên đi vào ngôn ngữTôi từng xây dựng một thư viện UI dựa trên ý tưởng biểu thức template JSX tạo ra các node DOM thực
Nó cũng cho phép bind object model vào thuộc tính để loại bỏ bớt phần boilerplate của event handler kiểu mệnh lệnh. Có thể đoán được là tôi sẽ nghĩ vậy, nhưng tôi thấy đây là ý tưởng hay
https://github.com/tomtheisen/mutraction
Bạn có thể viết code kiểu tạo model bằng
track({ clicks: 0 }), tăngmodel.clickskhi bấm, rồi phản ánh ngay giá trị đó vào DOMỞ đây cũng đáng nhắc đến VanJS: https://vanjs.org/
Các thư viện JSX khác như Solid.JS cũng trả về các node DOM, và tôi thích việc ý tưởng này đang dần được chú ý
Theo tôi, càng tiến gần hơn tới nền tảng mà chúng ta đang dùng thì càng tốt. Tất nhiên, lớp trừu tượng đôi khi vẫn hữu ích, nhưng trên thực tế tôi vẫn chưa thấy cần phải che giấu nền tảng
Có thể những dự án khổng lồ như ở Facebook sẽ hưởng lợi, nhưng tôi chưa từng làm ở quy mô như vậy
Các ví dụ thì rất hay, nhưng điều quan trọng có vẻ là chúng không cho thấy props của component có thể thay đổi theo thời gian
Có vẻ như khả năng đó thậm chí còn chưa được mô hình hóa. Nếu không cần thì đây là cách làm thông minh, nhưng tôi khó hình dung nó mở rộng lên các ứng dụng phức tạp hơn ra sao
Khác biệt chỉ là nó viết gọn hơn phần code khoảng 10 dòng để tạo và cấu hình phần tử DOM theo cách thông thường
Thực ra, phần thật sự thú vị trong framework này là gần như mọi thứ khác ngoài đó. Có phần JSX trả về chuỗi ở phía static site generator, phần lấy raw source directory rồi thao tác với
string|Buffertại thời điểm SSG, và một hệ thống module rất hiệu quả, rất nhanh tự xây trênchokidarvàswcPhần dùng JSX làm DOM chỉ là thứ nổi bật nhất về mặt hình ảnh, nhưng thực ra gần như chỉ là một màn biểu diễn
Ví dụ, source của vanillajsx.com rất gọn, rõ ràng và ngắn. Tôi viết toàn bộ nó chỉ trong hôm nay, không có dependency nào ngoài
imlib, và demo JSX-as-DOM lại là phần kém đột phá nhất: https://github.com/sdegutis/vanillajsx.com/tree/main/siteNếu bạn thích cách JSX trả về các phần tử DOM cụ thể này thì Solid hoạt động chính xác như vậy, và có thêm một lớp reactivity bài bản trên đó
setTimeouthaysetIntervalnhư thế nàoTuy vậy, tôi đồng ý rằng các dự án kiểu này thường hoạt động tốt ở các trường hợp nhỏ, nhưng nhanh chóng sụp đổ trong những kịch bản “thế giới thực”
Có vẻ việc triển khai cũng không quá khó
Cái này rất giống Vanilla TSX: https://github.com/wisercoder/uibuilder
Có ví dụ ứng dụng viết bằng Vanilla TSX ở đây: https://github.com/wisercoder/eureka/tree/master/webapp/Clie...
Nó làm tôi nhớ tới ActionScript 3, nơi XML nằm trong lõi ngôn ngữ
Đây là một ngôn ngữ rất vui để viết, nhưng nổi tiếng là đã thất bại trong việc trở thành ES4. Rốt cuộc phải mất hơn 10 năm mới quay lại được điểm tương tự với TypeScript và JSX
Nó bị vô hiệu hóa cho trang web từ Firefox 17, tức 6 tháng trước bản phát hành React đầu tiên
Không biết có ai khác cũng gặp việc ví dụ cuối không chạy trên Firefox không. Trên Edge thì được nhưng Firefox thì lỗi
Uncaught (in promise) TypeError: Map.groupBy(...).entries().map is not a functionTôi không thật sự hiểu vì sao JSX lại hấp dẫn hơn cách viết như dưới đây
h("div", {}, [ h("p", {}, "this is easy"), ...list.map((l) => h("li", {}, l)) ])Làm như vậy thì tự động có được loop và nội suy biến mà không cần compiler hay tạo ra cú pháp mới. Mong ai đó giải thích giúp
Nói công bằng thì trong JSX cũng vẫn dùng loop và nội suy thông thường.
{}nhận các biểu thức JavaScript thuầnChỉ là cần một bước compile để hạ các thẻ phần tử thành lời gọi
createElementMỗi người xử lý thông tin theo cách khác nhau, nhưng ví dụ đó có quá nhiều dấu câu nên rất khó nắm bắt nhanh
Nếu không tự phân rã cú pháp trong đầu thì khó mà thấy được cấu trúc DOM được tạo ra; điều quan trọng hơn nhiều so với việc có cần compiler hay không là hiểu được cấu trúc DOM trong nháy mắt
Nhân tiện, tôi là người thuộc phổ thần kinh đa dạng, mong là cách giải thích này hữu ích
Nó khá giống những gì bạn thấy trong công cụ devtools của trình duyệt nên dễ so sánh và debug hơn
Nhưng trong các app phức tạp, khi số lượng component lên tới hàng trăm và bắt đầu import qua lại, cách viết bằng thẻ JSX sẽ cho cảm giác là cú pháp đơn giản và dễ hiểu
Sự phản ứng ngược với React cũng phần lớn đến từ việc nó quá nặng cho những việc đơn giản, và đúng là như vậy
Nhưng app mỗi người xây dựng, độ phức tạp và yêu cầu đều khác nhau, nên không thể đơn giản hóa rằng một bên hấp dẫn hơn. Đó là những công cụ khác nhau cho những bài toán khác nhau.