Những ý tưởng thú vị trong Observable Framework
(simonwillison.net)- Observable Framework là một công cụ mã nguồn mở nhằm vượt ra ngoài mô hình notebook vốn mạnh về khám phá dữ liệu tạm thời, để triển khai các ứng dụng dữ liệu, dashboard và báo cáo tải nhanh dưới dạng site tĩnh
- Các khối mã
jsvà biểu thức inline trong Markdown được chạy trên trình duyệt, và khi giá trị phản ứng nhưnowthay đổi, các phần hiển thị liên quan cũng tự động được cập nhật - Framework giữ lại tính phản ứng của Observable Notebooks, đồng thời cung cấp một file Markdown duy nhất, JavaScript tiêu chuẩn và quy trình làm việc thân thiện với Git
- Các thư viện như
Inputs,d3,Plotđược tải lười trong quá trình phát triển; khi build và triển khai, chỉ phần mã được tham chiếu mới được tự động tải từ jsdelivr CDN - Dùng Data loader có thể chuẩn bị dữ liệu bằng bất kỳ ngôn ngữ nào tại thời điểm build rồi đóng gói thành các file tĩnh như JSON, CSV, giúp triển khai dashboard với ít phụ thuộc backend hơn
Trình tạo site tĩnh cho ứng dụng dữ liệu
- Observable Framework là trình tạo site tĩnh biên dịch Markdown, JavaScript và nếu cần cả các ngôn ngữ khác thành trang tương tác
- Nó bao gồm một máy chủ hot reload đầy đủ tính năng, nên khi chỉnh sửa và lưu file trong trình soạn thảo, các thay đổi sẽ được phản ánh ngay trong trình duyệt
- Khi hoàn tất, có thể dùng lệnh build để tạo một tập các file tĩnh
- Các file này có thể được triển khai lên máy chủ
- Cũng có thể triển khai trực tiếp lên nền tảng chia sẻ đã xác thực của Observable bằng
npm run deploy
JavaScript chạy bên trong Markdown
- Thiết kế cốt lõi của Framework là đưa JavaScript vào tài liệu Markdown để tạo tài liệu tương tác
- Các khối mã Markdown được gắn nhãn
jssẽ được chạy như JavaScript trong trình duyệt của người dùng - Cũng có thể dùng biểu thức inline, chẳng hạn hiển thị thời gian hiện tại dưới dạng chuỗi với
${new Date(now)} nowlà biến đặc biệt cung cấp thời gian hiện tại tính bằng mili giây kể từ epoch và liên tục được cập nhật- Khi
nowthay đổi, các cell và biểu thức inline tham chiếu đến nó cũng được cập nhật theo
- Khi
- Trong Observable Notebooks, mã và Markdown được viết trong các cell riêng biệt, còn trong Framework thì cả hai nằm trong một tài liệu văn bản duy nhất
- Biểu thức inline và khối
jscó thể có cách hiển thị khác nhau- Biểu thức inline dùng biểu diễn chuỗi mặc định của đối tượng JavaScript
- Khối
jsdùng hàmdisplay()của Observable, còn quy tắc hiển thị nằm trong inspect/src/inspect.js
Duy trì mô hình thực thi phản ứng
- Tính phản ứng, chức năng cốt lõi của Observable Notebooks, cũng được duy trì trong tài liệu JavaScript Markdown của Framework
- Khi một cell thay đổi, các cell khác phụ thuộc vào cell đó sẽ tự động được đánh giá lại
- Đây là điểm khác biệt lớn so với Jupyter notebooks, và cũng là tính năng tiêu biểu của công cụ notebook Python marimo
- Hiệu quả càng rõ khi dùng cùng các input biểu mẫu
- Nếu thêm input vào trang và tham chiếu giá trị của nó ở phần khác trong tài liệu, có thể dễ dàng tạo tương tác thời gian thực
Ví dụ dashboard lượt tải PyPI
- Dashboard ví dụ hiển thị thống kê lượt tải PyPI theo từng package Python; phiên bản Observable Framework gồm một tài liệu Markdown dài 57 dòng
- Người dùng chọn package từ mảng
packagesbằngInputs.select()Inputs.select()là phương thức được bao gồm trong Framework và có thể xem trong tài liệu Observable Inputs- Hàm
view()là tính năng mới được thêm vào Framework, giúp thay đổi lựa chọn input được phản ánh trong các khối mã khác của tài liệu
packageNameđược định nghĩa bằngconstnên có thể dùng trong các khốijskhác trên trang- Dữ liệu được lấy bằng
d3.json()- Trong Framework có thể dùng toàn bộ D3
- URL bao gồm tên package đã chọn
- Nguồn dữ liệu là JSON API của Datasette
- Bảng SQLite nằm tại datasette.io/content/stats, được cập nhật mỗi ngày một lần bằng thống kê package PyPI mới nhất
- Workflow GitHub Actions liên quan đã được đề cập trong bài trước về baked data
- Thêm
.jsonvào URL sẽ trả về JSON- Chỉ yêu cầu các hàng của một package cụ thể
- Sắp xếp theo ngày giảm dần
- Nhận tối đa 1.000 hàng dưới dạng mảng đối tượng
- Chuỗi ngày của SQLite được chuyển thành đối tượng JavaScript
Datebằngd3.timeParse("%Y-%m-%d") - Biểu đồ được render bằng Observable Plot, được đóng gói cùng Framework
- Danh sách package được lấy bằng cách chạy truy vấn SQL trực tiếp trên cơ sở dữ liệu
/contentcủa Datasette- Truy vấn là
select package from stats group by package order by max(downloads) desc _shape=arrayfirstlà cách viết tắt để nhận cột đầu tiên của các hàng kết quả dưới dạng mảng JSON
- Truy vấn là
Chỉ bao gồm mã được dùng
- Dashboard ví dụ sử dụng các thư viện bổ sung như
Inputs,d3,Plot - Ở chế độ phát triển, tải lười được áp dụng
- Mã chỉ được tải khi lần đầu được dùng trong một cell
- Khi build và triển khai ứng dụng, Framework tự động chỉ tải phần mã thư viện được tham chiếu từ jsdelivr CDN
Cache dữ liệu tại thời điểm build
- Data loader của Framework là tính năng chuẩn bị dữ liệu dashboard tại thời điểm build
- Dashboard Framework có thể dùng
fetch()hoặc wrapper của nó ở runtime để lấy dữ liệu từ bất cứ đâu- Observable Notebooks cũng hoạt động theo cùng cách
- Với cách này, hiệu năng dashboard phụ thuộc vào backend được kết nối
- Framework khuyến nghị mô hình tạo dữ liệu cho dashboard tại thời điểm triển khai và chỉ đóng gói phần tập con dữ liệu cần thiết thành file tĩnh
- File dữ liệu tĩnh có thể được phục vụ nhanh từ cùng hệ thống hosting tĩnh với mã dashboard
- Data loader là script được viết bằng bất kỳ ngôn ngữ lập trình nào
- Khi build, Framework chạy script đó
- Kết quả xuất ra stdout của script được lưu thành file
- Ví dụ là đặt
curl https://earthquake.usgs.gov/earthquakes/feed/…trong filequakes.json.sh- Khi build, tên file cho Framework biết file đích là
quakes.jsonvà loader cần chạy là.sh
- Khi build, tên file cho Framework biết file đích là
- Miễn là có thể xuất JSON, CSV hoặc một định dạng hữu ích ra stdout, có thể lấy dữ liệu bằng bất kỳ công nghệ nào
Khác biệt với Observable Notebooks
- Observable Framework tái sử dụng nhiều ý tưởng và mã của Observable Notebooks, nhưng khác biệt lớn ở định dạng file và môi trường thực thi
- Observable Notebooks hiện có các đặc điểm sau khi so với Jupyter Notebooks
- Dùng JavaScript thay vì Python
- Bản thân trình chỉnh sửa notebook không phải mã nguồn mở, mà là sản phẩm hosted được cung cấp tại observablehq.com
- Có thể xuất notebook thành file tĩnh để chạy ở bất cứ đâu, nhưng trình chỉnh sửa là sản phẩm độc quyền
- Các cell có tính phản ứng; khi một cell thay đổi, các cell phụ thuộc vào nó được tự động đánh giá lại giống như Excel
- Để hỗ trợ mô hình phản ứng, Observable tạo từ khóa tùy biến
viewof, nên cú pháp JavaScript không hoàn toàn là tiêu chuẩn - Notebook có thể chỉnh sửa là một định dạng file độc quyền phức tạp và không phù hợp với các công cụ như Git, nên Observable triển khai hệ thống quản lý phiên bản và cộng tác riêng
- Observable Framework chuyển mô hình này sang định dạng file đơn giản hơn và môi trường thực thi mã nguồn mở
- Tài liệu là một file Markdown duy nhất có chứa các khối JavaScript
- Vẫn có tính phản ứng, nhưng có thể chỉnh sửa bằng bất kỳ trình soạn thảo văn bản nào và đưa vào Git
- Toàn bộ là mã nguồn mở theo giấy phép ISC, và có thể chạy toàn bộ stack chỉnh sửa trên máy local
- Chỉ dùng JavaScript tiêu chuẩn, không có cú pháp tùy biến
Sự thay đổi định hướng của Observable
- Observable Framework có vẻ cho thấy Observable đang nghiêng nhiều hơn về công cụ cho developer, thay vì công cụ cộng tác xoay quanh trình chỉnh sửa Observable Notebook độc quyền trước đây
- Dòng giới thiệu trên Twitter của Observable là “The end-to-end solution for developers who want to build and host dashboards that don’t suck”
- Bản lưu Internet Archive ngày 3/10/2023 ghi “Build data visualizations, dashboards, and data apps that impact your business — faster.”
- Việc sử dụng Observable Notebooks có thể bị hạn chế phần nào do tính độc quyền của nền tảng và các giới hạn của tài khoản miễn phí, đặc biệt là không có notebook riêng tư miễn phí
- Các thư viện mã nguồn mở như Observable Plot đã được đánh giá là những công nghệ có thể dùng tích cực
- Observable Framework triển khai lại những ý tưởng từng làm Observable Notebooks hấp dẫn theo mô hình mã nguồn mở, JavaScript tiêu chuẩn, một file văn bản duy nhất và triển khai tĩnh
1 bình luận
Ý kiến trên Hacker News
Theo một khía cạnh nào đó, Observable Framework giống như Avengers: Endgame của vũ trụ điện ảnh Mike Bostock vậy
Nó gom d3, Observable, Observable Plot, HTL lại với nhau, rồi còn thêm vào khá nhiều ý tưởng mới
Observable vốn đã có tích hợp AI, còn cái này trông giống một lớp wrapper giúp AI dễ kết hợp và tận dụng hơn. Phần đánh giá chiến lược mà không tính đến AI thì tôi thấy hơi gượng
Hôm nay tôi bắt đầu tìm cách host Jupyter Notebook tĩnh hoặc chạy tương tác bằng WASM, nhưng có vẻ Observable Framework sẽ phù hợp hơn với phần lớn mục đích sử dụng
Vấn đề của Observable là tuy trông như một kho ví dụ d3, code của nó lại được thiết kế để chạy bên trong framework đó nên không thể cứ copy-paste là dùng được
d3 vốn cũng không dễ dùng nếu thiếu ví dụ, đặc biệt là nhiều khi các thay đổi giữa các phiên bản không tương thích. Dù vậy, trên site có rất nhiều đồ họa đáng kinh ngạc
[0] https://observablehq.com/@d3/gallery
Nó đủ gần với ngôn ngữ gốc đến mức lẽ ra có thể chỉ dùng JavaScript bình thường, cộng thêm một chút API để hiển thị đồ họa
Chủ yếu là viết lại các định nghĩa cell ở cấp cao nhất
[0]: https://observablehq.com/@bumbeishvili/convert-observable-co...
Tôi đặc biệt bực chuyện đó với notebook ObservableHQ. Chúng vừa là ví dụ tuyệt vời, vừa trở thành tài liệu vô dụng. Tuy nhiên phía Framework lần này có vẻ mở hơn một chút, ít nhất cũng có thể tự host, nên tôi đang theo dõi thêm
Hơn nữa bài này nói rằng Observable Framework mới đã loại bỏ một phần các vấn đề của notebook Observable cũ, nên bình luận này hơi lệch khỏi bài viết. Giờ thì kiểu như “tất cả đều là JavaScript chuẩn, không có cú pháp tùy chỉnh”
Framework cũng rất dễ triển khai lên GitHub Pages
Tôi đã tổng hợp các bước và GitHub Action mẫu ở đây: https://notes.billmill.org/programming/observable_framework/...
Tác giả đã chỉ ra rất đúng về Framework
Tôi đã thử dùng Observable Framework để tạo một plot tương tác nhỏ (https://github.com/willmeyers/observable-ssta), và quá trình thiết lập rồi vẽ dữ liệu dễ đến khó tin. Phàn nàn duy nhất là tôi ước có thể cấu hình Python data loader dùng virtualenv
Sau khi tạo project Python, khi khởi động dev server, thay vì chạy
yarn run devthì chạypoetry run yarn run dev, như vậy Python sẽ chạy trong virtualenv. Cấu hình này cho phép định nghĩa phần code có thể tái sử dụng và unit test được trong data loader dưới dạng một package Python tùy chỉnh, rồi import nó trong các file*.json.pyđể giữ chúng cực kỳ đơn giảnCác công cụ như node và npm/yarn, cả JavaScript nữa, đều được giữ chung trong venv
.sh, rồi trỏ đến đường dẫn đầy đủ củabin/pythontrong thư mục môi trường ảo sao?Gần đây tôi vừa hoàn thành dự án “thực chiến” đầu tiên bằng notebook Observable
Công việc gồm học Observable Plot, Arquero, ôn lại một phần JavaScript, và tích hợp với simulator nền Rust là quy trình tạo dữ liệu. Nói thật là rất tuyệt. Tôi tốn khá nhiều năng lượng để học các công cụ, và khả năng tham số hóa bộ tạo dữ liệu vẫn còn chưa như ý, nhưng notebook cuối cùng đẹp và chạy tốt
Dùng Markdown và tính phản ứng khiến những notebook kiểu này thực sự có cảm giác dùng được. Định dạng tùy chỉnh của Jupyter khiến việc quản lý phiên bản khó hơn rất nhiều, còn nếu thiếu tính phản ứng thì các notebook được thiết kế lặp đi lặp lại rất dễ thành một mớ trạng thái hỗn độn: dễ viết nhưng khó đọc. Tôi cũng đã thử với Quarto và tích hợp Observable của nó, nhưng cảm giác như chắp vá tạm bợ
Thành thật mà nói, đây là lần đầu tiên tôi thấy việc viết notebook và chia sẻ với người khác vừa vui vừa đáng mong chờ. Chắc chắn về sau vẫn còn những góc cạnh sắc, nhưng sau dự án này, nó đã trở thành lựa chọn đầu tiên của tôi trong các công cụ notebook
[0]: https://living-papers.vercel.app/
Nếu muốn nhanh chóng thử và nghịch Framework ngay trong trình duyệt, tôi đã tạo một Codespace devcontainer tự động cấu hình môi trường Node và Python
[0]: https://github.com/dleeftink/observable-codespace
Có nên chuyển từ Jupyter Notebook sang Observable không? Hay bản thân việc chia hai thứ như vậy đã là một cách đặt vấn đề sai?
Nói cách khác về nội dung bài viết, mọi thứ trong code block có gợi ý nội dung
jssẽ được chạy ngay trong trình duyệt của người dùngNếu muốn hiển thị code, phải dùng gợi ý
js echo. Xét về tương thích ngược, chẳng phải làm ngược lại sẽ tốt hơn sao? Ví dụ, để code chạy trong trình duyệt người dùng là một gợi ý opt-in kiểujs exec, còn gợi ýjsvốn được dùng rộng rãi thì giữ nguyên. Với cấu trúc hiện tại, khi tích hợp renderer đó vào một app hiện có, bạn phải quản lý riêng xem chỗ nào được phép chạyCó thể làm theo từng trang trong front matter, hoặc áp dụng cho toàn bộ project bằng cấu hình project. Khi đó có thể đặt
js run=falselàm mặc định, và chỉ bật live code bằngjs runkhi muốn. Tuy vậy, use case chính của chúng tôi là live code nên đã chọn nó làm mặc địnhGiờ thì không thể chỉ hiển thị nguyên code block Mermaid nữa, trừ khi bỏ chú thích ngôn ngữ đi
Tôi đã dành cả một đêm đắm chìm trong Observable Framework và thấy rất tuyệt
Nó gần như không gây cản trở gì, và tôi có thể trực quan hóa cũng như khám phá chi tiết lịch sử Google Maps của mình. Phần liên quan đến môi trường data loader không thật rõ ràng, nhưng chạy Python trong môi trường poetry thì giải quyết được
Vì thích Kotlin nên tôi cũng thử tạo data loader cho Kotlin script, nhưng có vài chỗ còn thô. Kotlin kỳ vọng tên file script là
foo.main.kts, còn Observable lại kỳ vọng shebang loader có thể thực thi có phần mở rộngfoo.exe. Vì vậy tôi đã tạo một script exe proxy để gọi Kotlin script, nhưng như thế thì việc tự động tải lại dữ liệu không được kích hoạtMột điểm hơi bất tiện so với marimo hay Jupyter là phần dùng biến giữa data loader và notebook. Ví dụ, tôi muốn thay đổi phạm vi dữ liệu mà loader lấy về bằng một view component chọn ngày, nhưng không rõ phải làm thế nào. Vì vậy phân tích khám phá bị chậm đi một chút. Tôi biết điều này không hợp với paradigm, nhưng vẫn muốn nêu ra. Kết quả là trong quá trình khám phá, có thể bạn sẽ chuyển một phần đáng kể việc xử lý dữ liệu sang notebook, điều này không lý tưởng về mặt hiệu năng
Cuối cùng, sẽ rất tốt nếu có thể định nghĩa data loader inline. Tôi thích các file đơn lẻ, nên nếu chỉ cần thêm một code block Python và Framework trích xuất nó thành file thì đó sẽ là một cải thiện nhỏ về chất lượng trải nghiệm. Dù còn sớm, Framework trông rất hứa hẹn. Tôi mong có thể đưa tất cả ghi chú Markdown của mình lên đây để tạo một môi trường kiểu org-mode mà không cần đi đến mức dùng Emacs đầy đủ
.shhay.exeNó sẽ cho phép chỉ định interpreter gắn với một phần mở rộng file cụ thể. Ví dụ
.ktscho Kotlin. https://github.com/observablehq/framework/pull/935Cách điều khiển data loader bằng giá trị đầu vào hơi khác hướng, vì Framework ưu tiên snapshot dữ liệu tĩnh. Mục tiêu là để site đã build có tính tự chứa và hiệu năng tốt. Dù vậy, một kỹ thuật hoạt động tốt là tạo một file Parquet trong data loader chứa superset của dữ liệu bạn muốn tương tác, rồi ở client dùng DuckDB/SQL để lấy ra subset cần trực quan hóa. Thường thì hiệu năng tốt, nhưng tất nhiên còn tùy vào kích thước superset bạn muốn xử lý
Observable tích hợp rất tốt với ClickHouse thông qua REST API
Ví dụ ở đây: https://observablehq.com/@stas-sl/github-issues-survival-ana...
Tôi chưa thử Observable Framework mới, nhưng sẽ rất thú vị nếu thấy một ví dụ tương tự truy vấn cơ sở dữ liệu theo thời gian thực. Hy vọng nó không phải là kiến trúc chỉ cho phép tải trước và cache toàn bộ dữ liệu. Những app kiểu này cần có tính tương tác, nên lý tưởng là phải cho phép hiển thị SQL để chỉnh sửa live
Demo này tải dữ liệu bằng
fetch()lúc runtime: https://simonw.github.io/observable-framework-experiments/pa...