Lazy as application
Bài đăng này đã không được cập nhật trong 4 năm
Tâm sự một chút, sau kì nghỉ Tết vừa rồi mình có review
lại hướng đi trong thời gian tới, nghĩ thì chưa ra mà vô tình sa vào trạng thái Chênh vênh
nên có stress tẹo, định cho bản thân thời gian F5 refresh
mà sinh ra lười biếng
hồi nào chẳng hay. Cũng may có buổi nói chuyện với một người anh nên vấn đề cũng đã được giải quyết, cái lười cũng sợ quá chạy mất tiêu
Cũng với cái chủ đề lười
này, với con người chúng ta ảnh hưởng là vậy, cơ mà với application
thì tốt lắm nhé ! Để mình kể cho mà nghe, chuyện ông Lazy loading
và bà Code-splitting
: )))
Bắt đầu thoyyyy !
Base
Với mỗi application
, trong quá trình phát triển, chúng ta luôn cố gắng chia kiến trúc code
nhỏ và chi tiết nhất để có thể dễ dàng xử lý logic
và maintain
sau này. Song, khi build
để deploy
lên server
, các files
này sẽ được Webpack
, Rollup
hay Browserify
đóng gói
lại.
Cùng nhau ngó qua cơ chế Webpack
thực hiện việc này một chút nhé !
How Webpack
bundling works?
Trong quá trình Bundling
, Webpack
sẽ tạo ra một thứ gọi là dependency graph
có dạng:
Root
của dependency graph
chính là entry point
được config
trong Webpack
(giả sử nó là main.js
).
Các modules
được import
trong main.js
sẽ trở thành các node tương ứng
của root
. Và dĩ nhiên, mỗi module
được import
trong các node
này sẽ trở thành các node con
của chúng.
Dependency graph
cũng tương tự nhưOriginal DOM tree
trongHTML
.
Nếu chúng ta sử dụng các Web APIs
để detect
các DOM nodes
thì thông qua Dependency graph
, Webpack
cũng detect
các modules
được đưa vào output bundle
.
Output bundle
là một file (hoặc nhiều nếu dùnglazy loading
) chứa cáccode vanila Javascript
được compiles từ các modules trongdependency graph
.
Muốn xem nội dung file Output bundle
trên brouser
, bạn chỉ cần mở cửa sổ Console
lên, trong Tab Network
:
sau đó click
vào file bundle.js
, sẽ thấy nó ...đáng yêu vô cùng luônnnn =)))
BONUS
Trong tab Network
, phía bên cột Waterfall
, nếu có đoạn progress
màu đỏ, nghĩa là có tài-nguyên-chưa-thực-sự-cần và nên thực hiện lazy load đó ! Có thể mình sẽ chia sẻ thêm về sử dụng các chức năng của Chrome dev tools
trong thời gian tới ^^
Để tóm tắt lại, quá trình bundling
của Webpack
có thể được hình dung theo sơ đồ sau:
Yayyyyy, bây giờ đã hiểu được Webpack làm gì rồi, ta lại nhận ra một vấn đề phát sinh:
Khi application càng nhiều modules, kích cỡ file bundle ban đầu càng to
Khi bundle càng to, thời gian client load càng lâu
Khi thời gian càng lâu, khả năng người dùng rời trang web càng cao.
Đó là chưa kể người dùng còn sử dụng máy tính cấu hình thấp, mobile internet data
hay các trường hợp có slow internet connections
nữa thì toang đúng không nào :v
Tổ lái sang UX
một xíuuu, theo phân tích của Google, 53% người dùng mobile quyết định rời khỏi trang web nếu thời gian phản hồi của nó lớn hơn 3s.
Như vậy, vấn đề bigger bundle = fewer users có thể trực tiếp làm mất potential revenue
(doanh thu từ những người khách hàng tiềm năng).
Một ví dụ cụ thể, việc delay 2s
sau khi hiển thị kết quả đã khiến Bing mất đi 4.3% potential revenue
.
Như chúng ta đã phân tích phía trên, Application
từ 1 bundle
ban đầu sẽ rất lớn, khi load
về sẽ chậm hơn khi bundle
càng ngày càng to, và có thể có nhiều phần code mà màn hình đó người dùng ko cần. Điều này cũng làm giảm performance
đi sương sương
. Lúc này đây, Lazy loading
và code splitting
được sinh ra cho đời bớt khổ (J4F) 🥰🥰
Main
Code-Splitting concept
Code splitting
is just a process of splitting the app into this lazily loadedchunks
.
Ý tưởng là ta sẽ tách file bundle.js
ban đầu ra thành 1 file bundle
nhỏ hơn và các file chunks nhỏ hơn
, chỉ tải những thứ client-thực-sự-cần
.
Điều này có thể làm giảm kích thước bundle
và các tài nguyên cần thiết tải lên cho lần khởi tạo đầu tiên; Việc tải các component hoặc modules khác chỉ diễn ra khi phía client-thực-sự-yêu-cầu
.
Và đó chính là Lazy loading
:
Lazy loading
Lazy loading
is technique of rendering only-needed or critical user interface items first, then quietly unrolling the non-critical items later.
Notes:
Code splitting
is process of splitting app into chunks.
Lazy loading
is process of loading chunks.
Pet demo
Observation
Quan sát kĩ các files được load trong tab Network nhé:
Architect
Root App component
chứa một button toggle
, click
vào toggle
thì bắt đầu load component Hello
. Hết : ))
Explaination
Khi sử dụng lazy loading
, bundle
ban đầu là một parent chunk
, trường hợp mình split route
hay render thêm một lazy component
(Hello component
), nó sẽ load
children chunk
tương ứng. Lúc này, trên thẻ head
sẽ được append
thêm file javascript
này:
<script src="/static/js/child-chunk.js"></script>
để chỉ dẫn cho thằng browser
sẽ đi load
children chunk
sau khi load
xong parent
.
Bạn có thể tìm hiểu sâu hơn về code splitting concept
của Webpack
trên trang chủ của nó nhé.
Notes
Mình gọi đây là một pet demo
vì nó chỉ có 2 component
, và ban đầu cột Waterfall
cũng không thể hiện việc có tài-nguyên-nào-đó
bị load
thừa cả. Mục đích của mình là để chúng ta có thể hiểu được cách hoạt động và dễ dàng quan sát cơ chế của nó ^^
Khi chưa thử code demo
mình đã nghĩ rằng các phương pháp làm tăng performance
nói chung cũng như Lazy loading
và Code splitting
nói riêng, có nhiều ý nghĩa như vậy, chắc code
cũng phải cũng đao to búa lớn
lắm. Thế nhưng NHẦM TO
các bạn à. Không tin thì đọc tiếp phần dưới xem nào : )))
How's ReactJS
lazy?
Phần này viết này mình sẽ giới thiệu một vài cách thực hiện lazy loading
trong ReactJS
nhé, các hotface
khác cùng với ReactJS
như Angular
, VueJS
cũng có các features
, library
tương ứng hỗ trợ , nếu có thắc mắc gì thêm các bạn có thể ping
cho mình ^^
Ý tưởng của 3 cách mình giới thiệu xoay quanh một điểm chốt: sử dụng dynamic import()
.
React.lazy()
feature
Đây là feature
mới của ReactJS version 16.6
:
const LazyComponent = React.lazy(() => import('./LazyComponent'));
const App = () => (
<div>
<Suspense fallback={<div>Loading...</div>}>
<section>
<LazyComponent />
</section>
</Suspense>
</div>
);
Notes:
Dù đã là một feature
khá hay ho của ReactJS
nhưng React.lazy
và Suspense
chưa hỗ trợ cho việc server-side rendering
.
Đó là lý có thêm 2 libraries
dưới đây:
react-loadable
library (RL)
import Loadable from 'react-loadable';
const LazyComponent = Loadable({
loader: () => import('./LazyComponent'),
loading: <div>Loading...</div>,
});
const App = () => (<LazyComponent/>)
Theo thông tin mình hóng
được thì react-loadable
đã từng được recommended
trong một thời gian dài. Song, tính tại thời điểm mình viết bài này (02/2020), điểm trừ duy nhất của RL
là vẫn chưa tương thích được với Webpack v4+
và Babel v7+
😭😭
Loadable component (LC)
import loadable from '@loadable/component';
const LazyComponent = loadable(() => import('./LazyComponent'))
const App = () => (
<div>
<LazyComponent />
</div>
);
Trong Loadable Component
, ta cũng có lazy() method
, Suspense component
, fallback
,... có cú pháp giống y React.lazy()
😄
Trường hợp muốn sử dụng fallback
thì chúng ta không nhất thiết dùng phải Suspense
:
// Type 1: Dùng Suspense (như Syntax của React.lazy())
// Type 2: Trong loadable() options
const LazyComponent = loadable(
() => import('./LazyComponent'),
{ fallback: <div>Loading...</div> }
);
// Type 3: Via fallback props
const App = () => (<LazyComponent fallback={<div>Loading...</div>} />);
Ngoài ra, Loadable component
còn cho phép ta truyền một dynamic value
vào dynamic import()
:
import loadable from '@loadable/component'
const AsyncPage = loadable(props => import(`./${props.page}`))
const App = () => (
<div>
<AsyncPage page="Home" />
<AsyncPage page="Contact" />
</div>
)
Bonus:
Ngoài syntax
cơ bản như trên, RL
và LC
còn cho phép xử lý khá nhiều case
: tùy chỉnh Loading component
trong trường hợp slow network conection
(hay thậm chí là rớt mạng) 🤣, customize
lại modules
được render
hay thậm chí là xoắn quẩy thêm Preloading
nữa... 😄 Ta có thể tìm hiểu chi tiết trên Github
của chúng.
Lazy as application
Cá nhân mình thấy rằng, việc application
load
các modules
cũng tương tự như cách chúng ta đối diện với các mục tiêu trong cuộc sống ấy:
Có bao giờ ở một thời điểm nào đó, bạn mong chờ ở bản thân
code
giỏi, cómindset full-stack...overflow
, muốn học một khóaUI - UX
, muốn tập chơiorgan
, muốn mở kênhyoutube
, viếtblog
chia sẻ với cộng đồngDevelopers
,... không? Và thế là không biết bắt đầu cái nào trước, lạireturn undefined; // Chênh vênh
.OKR
tràn từ quý này sang quý khác mà mục tiêu vẫn còn đó.
Và nếu như application
thực hiện lazy loading
: Load
những modules
thực sự cần thiết trước, sau đó load
tiếp nếu có request
, chúng ta cũng sẽ làm điều tương tự: đánh specify
cho các mục tiêu này (có thể phương pháp SMART), sau đó pick
ngay module
đầu tiên và thực hiện nó. Từ đó bạn sẽ cảm thấy dễ dàng hơn khi đưa ra quyết định ^^
Conclusion
Vậy là chúng ta đã cùng nhau tìm hiểu về Lazy Loading - Code splitting
: cách Webpack build files
, ý nghĩ files
bundle
, chunk
, pet demo
và một vài cách áp dụng cụ thể trong ReactJS
rồi 😄😄
Lazy Loading
tuyệt vời là vậy, nên nếu như ngày nọ ông dev
ngồi cạnh có bảo:
- Mày lười như phần mềm ấy >.<
thì hãy vui vẻ đón nhận, bởi vì đó là một lời khen mà 😉))
Song, mình chỉ muốn lưu ý thêm một điều:
Lazy loading
giúp tăngperformance
nhưng không phảiproject
nào xài nó thìperformance
cũng cao.
Pet demo
nho nhỏ trên giúp chúng ta trực quan hiểu rõ hơn về cơ chế của nó thôi, thực tế mà áp dụng thì quả là trường hợp dùng dao mổ trâu để giết gà
đó 🤣🤣 Dùng đúng mới thấy hiệu quả chứ k phải cái gì cũng code splitting
nha ^^
Hy vọng rằng bài viết này mang lại giá trị cho các bạn.
Cảm ơn các bạn đã đọc bài chia sẻ của mình. Tặng mình 1 upvote
để có thêm động lực cho những bài viết sắp tới nhé 😛😛
Đã đọc tới đây rồi, tiện ghé qua Blog của mình chơi một chút rồi về !
Chúc các bạn cuối tuần vui vẻ !
Happy coding !
Reference: CSS Tricks, Medium, VueSchool, Haodev.
All rights reserved