Bạn viết this.myBox.el.focus() trong setup, chạy thử, và nhận TypeError: Cannot read property 'focus' of null. DOM chưa có ở đó. Một component OWL không "sinh ra" tức thì — nó đi qua một vòng đời: khởi tạo, nạp dữ liệu, dựng DOM, gắn vào trang. Mỗi việc phải làm đúng chặng của nó. Bài này đo thật thứ tự các chặng và chỉ ra: nạp dữ liệu ở đâu, chạm DOM ở đâu.
Bốn chặng, hai hook chính
onWillStart để nạp dữ liệu, onMounted để chạm DOM — vì hai việc khả dụng ở hai thời điểm khác nhau:

Hình 1: setup() chạy đầu tiên — khởi tạo state, service, useRef, nhưng chưa có DOM. onWillStart(async ...) chạy tiếp, là nơi await nạp dữ liệu trước lần render đầu (component không hiện cho tới khi nó xong). Sau khi OWL render và gắn DOM vào trang, onMounted chạy — lúc này this.hopRef.el mới là node thật, chạm được. Còn onWillUnmount để dọn dẹp (huỷ timer, gỡ listener) khi component bị gỡ.
Đo thật: thứ tự qua Console
Tôi đặt một console.log ở mỗi hook rồi mở dashboard, bắt log bằng trình duyệt tự động. Thứ tự in ra không đổi:
HOOK 1 · setup (khởi tạo, chưa có DOM)
HOOK 2 · onWillStart (nạp dữ liệu, TRƯỚC render)
HOOK 4 · onMounted (DOM đã gắn -> chạm được)

Hình 2: Dòng thời gian. setup → onWillStart → render (OWL dựng DOM) → onMounted. Con số trong Console (1, 2, 4) khớp đúng — chặng 3 là render, không phải hook để bạn viết code. Về sau còn onWillUpdateProps (khi props đổi) và onWillUnmount (khi gỡ, để dọn dẹp).
Bằng chứng onMounted chạm được DOM
Trong onMounted, tôi ghi đè nội dung một node qua useRef. Nếu hook này chạy trước khi DOM tồn tại, ref sẽ null và không có gì đổi. Nhưng nó chạy sau render, nên node có thật:

Hình 3: Template ban đầu vẽ ra một dòng giữ chỗ, nhưng onMounted đã ghi đè nó thành "onMounted đã ghi vào DOM sau khi render xong." Việc này chỉ thành công vì this.hopRef.el trỏ tới một node có thật — bằng chứng onMounted chạy sau khi DOM đã gắn. Thử cùng đoạn code đó trong setup là nhận null.
Quy tắc dùng hook
- Nạp dữ liệu ở
onWillStart. Nóawaitđược và chặn render cho tới khi xong, nên component hiện ra đã có sẵn dữ liệu — không nhấp nháy "trống rồi mới đầy". - Chạm DOM / khởi tạo thư viện ngoài ở
onMounted. Vẽ biểu đồ (Chart.js), focus ô nhập, đo kích thước element — tất cả cần node thật, chỉ có từonMounted. - Lấy node bằng
useRef+t-ref.useRef("hop")trongsetup,t-ref="hop"trong template;.elchỉ có giá trị từonMountedtrở đi. - Dọn dẹp ở
onWillUnmount. ĐặtsetInterval, thêm listener toàn cục, mở kết nối? Huỷ chúng ở đây, kẻo rò rỉ khi component bị gỡ.
Ba ý mang về
- Component OWL có vòng đời:
setup(khởi tạo, chưa DOM) →onWillStart(nạp dữ liệu,await, trước render) → render →onMounted(DOM đã gắn) — đo thật thứ tự này qua Console. - Nạp dữ liệu ở
onWillStart, chạm DOM ởonMounted— dùnguseRef+t-ref,.elchỉ có từonMounted; chạm DOM trongsetupluôn null. - Dọn dẹp ở
onWillUnmount(timer, listener, kết nối);onWillUpdatePropschạy khi props đổi.
Hook lo khi nào code chạy. Còn phản ứng với người dùng — bấm, gõ, di chuột — thì gắn thế nào? Phần sau đi vào bắt sự kiện với t-on: t-on-click, truyền tham số cho handler, và những cái bẫy khi lặp trong t-foreach.