Giao Tiếp Sự Kiện Và Đồng Bộ Hóa Trạng Thái Giữa Các Cửa Sổ Sciter

Sciter là một thư viện giao diện người dùng nhúng được tối ưu cho hiệu suất và kích thước tệp, phù hợp với các ứng dụng desktop cần tích hợp chặt chẽ với nền tảng gốc. Framework này cung cấp mô hình lập trình kết hợp HTML/CSS với JavaScript, cho phép xây dựng giao diện động mà không cần trình duyệt bên thứ ba. Bài viết tập trung vào cơ chế truyền thông liên cửa sổ, bao gồm kích hoạt sự kiện tùy chỉnh, phân phối dữ liệu toàn cục và chuyển giao cấu hình khởi tạo.

Để minh họa các kỹ thuật này, môi trường phát triển sử dụng hệ điều hành Windows 10, công cụ IDE tích hợp bộ biên dịch MinGW 11.0 và SDK Sciter-JS phiên bản 6.0.2.28.

1. Cơ Chế Gửi và Lắng Nghe Sự Kiện

Trong kiến trúc Sciter, các cửa sổ hoạt động như các tiến trình hoặc ngữ cảnh độc lập. Việc đồng bộ trạng thái giữa chúng được thực hiện thông qua hệ thống bus sự kiện. Tùy thuộc vào mức độ tham chiếu, ta có hai cách tiếp cận chính.

1.1 Truyền thông định danh (Direct Targeting)

Khi cửa sổ cha khởi tạo và lưu trữ tham chiếu đến đối tượng cửa sổ con, việc truyền dữ liệu trở nên trực tiếp. Sciter phân biệt rõ ràng giữa gửi đồng bộ và gửi bất đồng bộ.

Cửa sổ chính (Parent)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Bảng Điều Khiển Chính</title>
    <style>
        body { font-family: system-ui, sans-serif; padding: 24px; line-height: 1.5; }
        button { padding: 10px 16px; margin-right: 8px; cursor: pointer; background: #007bff; color: #fff; border: none; border-radius: 4px; }
        button:hover { background: #0056b3; }
        #logArea { margin-top: 16px; padding: 12px; background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 4px; min-height: 24px; }
    </style>
</head>
<body>
    <h2>Quản Lý Cửa Sổ Con</h2>
    <button id="btnInit">Khởi Tạo</button>
    <button id="btnSync">Gửi Đồng Bộ</button>
    <button id="btnAsync">Gửi Hàng Đợi</button>
    <div id="logArea">Sẵn sàng...</div>

    <script>
        let sequenceId = 0;
        let childRef;

        const log = (msg) => document.getElementById('logArea').textContent = msg;

        document.getElementById('btnInit').onclick = () => {
            childRef = new Window({
                url: __DIR__ + 'child_panel.html',
                width: 320,
                height: 240
            });
            log('Đã khởi tạo đối tượng cửa sổ con.');
        };

        document.getElementById('btnSync').onclick = async () => {
            const evt = new Event('sync-update', { data: `Dữ liệu đồng bộ #${sequenceId++}` });
            await childRef.dispatchEvent(evt);
            log(`Đã đẩy đồng bộ: ${evt.data}`);
        };

        document.getElementById('btnAsync').onclick = () => {
            const evt = new Event('async-update', { data: `Dữ liệu bất đồng bộ #${sequenceId++}` });
            childRef.postEvent(evt);
            log(`Đã thêm vào hàng đợi: ${evt.data}`);
        };
    </script>
</body>
</html>

Cửa sổ phụ (Child)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Giao Diện Phụ</title>
    <style>
        body { font-family: system-ui, sans-serif; padding: 24px; }
        #recvOutput { font-weight: 600; color: #2c3e50; margin-top: 12px; }
    </style>
</head>
<body>
    <h2>Cửa Sổ Thu Nhận</h2>
    <p id="recvOutput">Chờ dữ liệu từ cửa sổ cha...</p>

    <script>
        const outputEl = document.getElementById('recvOutput');

        Window.this.on('sync-update', (evt) => {
            outputEl.textContent = `Nhận [Sync]: ${evt.data} | Event: ${evt.type}`;
        });

        Window.this.on('async-update', (evt) => {
            outputEl.textContent = `Nhận [Async]: ${evt.data} | Event: ${evt.type}`;
        });
    </script>
</body>
</html>

1.2 Truyền thông toàn cục (Global Broadcasting)

Khi không nắm được tham chiếu trực tiếp, Sciter cho phép phát sóng sự kiện đến không gian tên toàn cục của tiến trình. Phương thức tĩnh Window.post sẽ phân phối sự kiện đến tất cả các người lắng nghe, trong khi Window.send hoạt động theo cơ chế first-match và dừng ngay khi tìm thấy người tiêu thụ đầu tiên.

Thành phần Phát sóng

<script>
    let broadcastSeq = 0;
    document.getElementById('btnGlobal').onclick = () => {
        const globalEvt = new Event('app-notification', {
            data: `Thông báo hệ thống: ${broadcastSeq++}`
        });
        // Phân phối đến mọi cửa sổ đang lắng nghe trong cùng tiến trình
        Window.post(globalEvt);
    };
</script>

Thành phần Thu nhận

<script>
    Window.this.on('app-notification', (evt) => {
        console.log(`[${Window.this.title}] Nhận toàn cục:`, evt.data);
        // Áp dụng logic cập nhật UI cục bộ tại đây
    });
</script>

1.3 Tổng kết API Truyền Thông

  • targetWindow.dispatchEvent(event): Thực thi ngay lập tức trên thread đích (đồng bộ).
  • targetWindow.postEvent(event): Đẩy vào hàng đợi vòng lặp sự kiện của cửa sổ đích (bất đồng bộ).
  • Window.post(event): Phát sóng bất đồng bộ đến toàn bộ cửa sổ trong tiến trình.
  • Window.send(event): Gửi đến cửa sổ đầu tiên đăng ký lắng nghe, hủy tìm kiếm sau đó.

Để kiểm tra hoặc lặp qua danh sách các cửa sổ đang hoạt động, có thể truy cập bộ sưu tập tĩnh Window.all:

for (const activeContext of Window.all) {
    console.log('Tiêu đề:', activeContext.title);
}

2. Truyền Tham Số Khởi Tạo

Song song với hệ thống sự kiện, Sciter hỗ trợ chuyển cấu hình hoặc trạng thái ban đầu ngay khi实例 hóa đối tượng Window. Dữ liệu được định dạng dưới dạng đối tượng JavaScript và có thể đọc ngay lập tức bên trong ngữ cảnh mới thông qua thuộc tính parameters.

Cấu hình từ cửa sổ mẹ

<script>
    const sessionConfig = {
        userId: 8842,
        region: 'VN',
        enableCache: true
    };

    document.getElementById('btnLaunch').onclick = () => {
        new Window({
            url: __DIR__ + 'dashboard.html',
            parameters: sessionConfig,
            alwaysOnTop: false
        });
    };
</script>

Truy xuất trong cửa sổ con

<script>
    document.addEventListener('DOMContentLoaded', () => {
        const cfg = Window.this.parameters;
        if (cfg) {
            const infoBox = document.getElementById('cfgDisplay');
            infoBox.innerText = JSON.stringify(cfg, null, 2);
        }
    });
</script>

Sử dụng parameters giúp loại bỏ độ trễ chờ đợi sự kiện đồng bộ từ phía cửa sổ mẹ, đồng thời giữ cho logic khởi tạo trang HTML trở nên độc lập và dễ kiểm thử. Việc kết hợp truyền cấu hình ban đầu với cơ chế lắng nghe sự kiện thời gian chạy tạo nên mô hình giao tiếp hai chiều hoàn chỉnh cho các ứng dụng Sciter phức tạp.

Thẻ: Sciter Desktop-UI JavaScript event-loop ipc

Đăng vào ngày 8 tháng 9 lúc 16:53