Mọi thứ vẫn sẽ hoàn hảo nếu như kích thước ứng dụng không tăng lên một cách nhanh chóng...

Giới thiệu

Bạn là một fan ruột của React và cuồng mọi thể loại Javascript stack đang hot hiện giờ? React, Redux, ES6, Babel, Webpack … đều là những món ưa thích của bạn. Bạn thông thạo chúng như lòng bàn tay? Chắc chắn là như thế sau khi bạn đọc bài viết này.

Mục đích của bài viết này không phải để hiểu thấu đáo mà là miêu tả một phương pháp hiện đại và rõ ràng nhằm giải quyết các vấn đề liên quan đến cách mà chúng ta viết mã.

Vấn đề

Sau đây là một ví dụ hay. Như bạn có thể thấy webpack tạo ra 2 file javascript: bundle.jsvendor.js. Đây là bước đầu tiên để tách code, tách biệt vendor với code của bạn. Điều này được trình bày rất tốt trong document của webpack.

Type yarn build
Gõ lệnh yarn build và xem điều gì xảy ra...

Nó là điều kiện tiên quyết cho bước tiếp theo, chia sẻ các vendor ( React, Redux ) với tất cả các component là rất cần thiết. Tuy nhiên chắc hẳn bạn cũng đã nhận ra, kích thước ứng dụng đã đạt xấp xỉ 2 Mb mà chưa tính các asset ( image, font,... ). Do đó ứng dụng sẽ mất thời gian để nạp và thậm chí còn lâu hơn với các kết nối di động sử dụng thuê bao giá rẻ. Hãy tự đặt câu hỏi rằng tại sao không chia nó thành nhiều phần nhỏ, khi nào cần thì mới được nạp?  Nghe thì có vẻ dễ tuy nhiên thực tế thì không toàn màu hồng như vậy.

Tham khảo các khóa học lập trình online, onlab, và thực tập lập trình tại TechMaster

Bắt đầu từ đâu?

Khi nói đến cải thiện tốc độ và hiệu năng thì có rất nhiều cách. Một trong số đó là Server Side Rendering, tuy nhiên nó không phải là chủ đề hôm nay 😜. Ở bài viết này, chúng ta hãy tìm hiểu cách tách code với Webpack và nơi tốt nhất để bắt đầu là webpack repository với rất nhiều các giải pháp. Ta sẽ chọn một giải pháp và đó là... system.import().

1. Sáng dạ lên nào

Chẳng có công cụ gì thần kì cả đâu, và để ổn thỏa nhất thì bạn sẽ cần đến thứ bạn dùng để đội mũ bảo hiểm 😕. Ví dụ nè: vendor không nên bao gồm mọi thư viện, trừ những cái thuộc tầng lớp "global" như React, Redux, moment,...

Đây không phải là file package.json đâu nhé

2. Bắt đầu tách code nào


// System.import() makes your old es6 import async
//        it simply return a ... Promise 😻

import MyComponent from './MyComponent'
System.import('./MyComponent').then(MyComponent => ...)
                                    
// 99% of the magic is just right here 👆

Load một component ( hoặc bất kỳ module ES nào ) theo cách này sẽ được hiểu là mốc tách code bởi Webpack. 
Bây giờ, hãy tưởng tượng chúng đang ở root của ứng dụng, vấn đề chính là Home component. Với các thư viện màu mè thì nó trở nên tương đối lớn so với phần còn lại ứng dụng. Do đó hãy tự nhắc mình rằng: ngay bây giờ mọi thứ được đóng gói trong cùng bundle và được load cùng lúc.

import Login from './Login'
import Signup from './Signup'
import Header from './Header'
import Home from './Home'

const App = ({ user }) => (
  <Body>
    <Header />
    {user.loggedIn ? <Route path="/" component={Home} /> : <Redirect to="/login" />}
    <Route path="/signup" component={Signup} />
    <Route path="/login" component={Login} />
  </Body>
)

Hãy tạo một component wrapper đơn giản load bất đồng bộ thành phần Home component và render nó, do đó Home sẽ chỉ được load khi mà bạn đã đăng nhập. 

import Login from './Login'
import Signup from './Signup'
import Header from './Header'

class Home extends React.Component {
  componentWillMount = () => {
    System.import('./Home').then(Component => {
      this.Component = Component
      this.forceUpdate()
    })
  }
  render = () => (
    this.Component ? <this.Component.default /> : null
  )
}

const App = ({ user }) => (
  <Body>
    <Header />
    {user.loggedIn ? <Route path="/" component={Home} /> : <Redirect to="/login" />}
    <Route path="/signup" component={Signup} />
    <Route path="/login" component={Login} />
  </Body>
)

Ta có thể khiến nó còn đơn giản hơn nữa chuẩn hóa phương thức này. Tôi sẽ xem nó như một phiên bản tách code React nhỏ. Và đây là kết quả cuối cùng:

import Async from 'react-code-splitting'
import Login from './Login'
import Signup from './Signup'
import Header from './Header'

const Home = () => <Async load={import('./Home')} />

const App = ({ user }) => (
  <Body>
    <Header />
    {user.loggedIn ? <Route path="/" component={Home} /> : <Redirect to="/login" />}
    <Route path="/signup" component={Signup} />
    <Route path="/login" component={Login} />
  </Body>
)

Nếu bạn muốn xem thêm về đoạn mã này trong đúng ngữ cảnh thì hãy xem thêm ở redux-react-starter.

3. Output

Webpack đã tạo ra một file mới tên là 0.[chunkhash].js và nó chính là phiên bản cũ của Home component 😎.

Đây là kết quả từ didierfranc/redux-react-starter

4. Tận hưởng thành quả 😎

Như bạn thấy Home component (0.bf87aaa616cea4a1ed40.js) đã được load ngay sau khi tôi đăng nhập. Chú ý rằng hiệu năng sẽ tốt hơn nữa nếu bạn chăm chút cho phần cache và http/2. Bạn có thể tạo lighthouse để báo cáo cho các tool bạn hay dùng, sau đó benchmark hiệu năng của ứng dụng.

Chrome > DevTools > Network Tab

Kết bài

Trong các bài viết kế, chúng ta sẽ tìm hiểu về cơ chế long-term cache, khả năng offline của ứng dụng,... Hay nói đơn giản hơn: cách tạo ra một ứng dụng web progressive.

Bài viết được dịch từ:  https://hackernoon.com/straightforward-code-splitting-with-react-and-webpack-4b94c28f6c3f#.tfd4vz3d0​