Thứ năm, 12/12/2019 | 00:00 GMT+7

Xây dựng ứng dụng SSR với bộ định tuyến Preact, Unistore và Preact

Ứng dụng trang đơn là cách phổ biến để xây dựng các ứng dụng web hiện đại. Khi nói đến SPA, có hai cách để bạn có thể hiển thị nội dung của ứng dụng cho user của bạn : hiển thị phía client hoặc kết xuất phía server .

Với kết xuất phía client , khi nào user mở ứng dụng, một yêu cầu sẽ được gửi để tải lên bố cục, HTML, CSS và JavaScript. Trong trường hợp nội dung của ứng dụng phụ thuộc vào việc tải thành công các tập lệnh JS, thì đây có thể là một vấn đề. Điều này nghĩa là user sẽ bị buộc phải xem trình tải trước trong khi đợi các tập lệnh tải xong.

Kết xuất phía server hoạt động khác nhau. Với SSR, yêu cầu ban đầu của bạn sẽ tải trang, bố cục, CSS, JavaScript và nội dung. SSR đảm bảo dữ liệu được khởi tạo đúng cách tại thời điểm kết xuất. Kết xuất phía server cũng phù hợp hơn để tối ưu hóa công cụ tìm kiếm.

Trong hướng dẫn này, bạn sẽ khám phá cách xây dựng một ứng dụng được hiển thị phía server với Preact . preact-router sẽ được sử dụng để định tuyến, unistore để quản lý trạng thái và Webpack để đóng gói JS.
Có thể cần một số kiến thức hiện có về Preact, Unistore và Webpack.

Công nghệ

Trong hướng dẫn này, bạn sẽ sử dụng công nghệ sau để xây dựng Ứng dụng được kết xuất từ phía server :

  • Preact - Một giải pháp thay thế cho React với cùng một API. Nó nhằm mục đích cung cấp trải nghiệm phát triển tương tự như React, mặc dù với một số tính năng bị loại bỏ như PropTypes và Children .
  • Unistore - Một containers trạng thái tập trung với các liên kết thành phần cho React và Preact.
  • Preact Router - Giúp quản lý tuyến đường trong các ứng dụng Preact. Cung cấp thành phần <Router /> hiển thị có điều kiện thành phần con của nó khi URL trùng với đường dẫn của chúng.
  • Webpack - Trình gói giúp gói các file JavaScript để sử dụng trong trình duyệt.

Xây dựng ứng dụng SSR với Preact

Việc xây dựng ứng dụng này sẽ được chia thành hai phần. Trước tiên, bạn sẽ xây dựng phía server của mã sẽ có trong Node và Express. Sau đó, bạn sẽ mã phần Preact của mã.

Ý tưởng là tạo một ứng dụng Preact như ban đầu và kết nối nó với một server Node bằng cách sử dụng preact-render-to-string . Nó cho phép hiển thị các thành phần JSX và Preact thành một chuỗi HTML mà sau đó được dùng trong server . Điều này nghĩa là ta sẽ tạo các thành phần Preact trong folder src và sau đó kết nối nó với file server Node.

Điều đầu tiên cần làm là tạo folder cho dự án và các folder khác nhau mà bạn cần. Tạo một folder có tên preact-unistore-ssr và chạy lệnh npm init --y bên trong folder . Điều đó tạo ra một package.json tối thiểu và một package-lock.json đi kèm.

Tiếp theo, cài đặt một số công cụ bạn sẽ sử dụng cho dự án này. Mở file package.json và chỉnh sửa bằng mã bên dưới, sau đó chạy lệnh npm i .

{   "name": "preact-unistore-ssr",   "version": "1.0.0",   "description": "",   "main": "index.js",   "scripts": {     "test": "echo \"Error: no test specified\" && exit 1"   },   "keywords": [],   "author": "",   "license": "ISC",   "devDependencies": {     "babel-cli": "^6.26.0",     "babel-core": "^6.26.0",     "babel-loader": "^7.1.2",     "babel-plugin-transform-react-jsx": "^6.24.1",     "babel-preset-env": "^1.6.1",     "file-loader": "^1.1.11",     "url-loader": "^1.0.1",     "webpack": "^3.11.0",     "webpack-cli": "^2.0.13"   },   "dependencies": {     "express": "^4.16.2",     "preact": "^8.2.6",     "preact-render-to-string": "^3.7.0",     "preact-router": "^2.6.0",     "unistore": "^3.0.4"   } } 

Điều đó sẽ cài đặt tất cả các gói cần thiết cho ứng dụng này. Trong đối tượng devDependencies , có một số gói babel sẽ giúp chuyển mã ES6. file-loaderurl-loader là các plugin Webpack giúp nhập file , nội dung, module , v.v.

Trong đối tượng dependencies , bạn cài đặt các gói như Express, Preact, preact-render-to-string, preact-router và unistore.

Tiếp theo, tạo một file cấu hình Webpack. Tạo một file có tên webpack.config.js trong folder root của dự án và chỉnh sửa nó bằng mã bên dưới:

const path = require("path");  module.exports = {     entry: "./src/index.js",     output: {         path: path.join(__dirname, "dist"),         filename: "app.js"     },     module: {         rules: [             {                 test: /\.js$/,                 loader: "babel-loader",             }         ]     } };  

Trong cấu hình webpack ở trên, bạn đã xác định điểm vào là src/index.js và kết quả là dist/app.js Bạn cũng đặt ra các luật để sử dụng Babel. Tệp điểm nhập chưa tồn tại nhưng bạn sẽ tạo nó sau.

Vì bạn đang sử dụng Babel, bạn cần tạo file .babelrc trong folder root của dự án và đưa vào cấu hình.

//.babelrc {     "plugins": [         ["transform-react-jsx", { "pragma": "h" }]     ],     "presets": [         ["env", {             "targets": {                 "node": "current",                 "browsers": ["last 2 versions"]             }         }]     ] }  

Xây dựng ứng dụng chính xác

Tiếp theo, bạn sẽ bắt đầu tạo file cho mặt Chính xác của mọi thứ. Tạo một folder src và tạo các file sau trong đó:

  • store/store.js
  • About.js
  • App.js
  • index.js
  • router.js

Đến đây bạn có thể chỉnh sửa các file với mã cần thiết. Bắt đầu với file store.js . Điều này sẽ chứa dữ liệu lưu trữ và các hành động.

import createStore from 'unistore'  export let actions = store => ({   increment(state) {     return { count: state.count + 1 }   },   decrement(state) {     return { count: state.count - 1 }   } })  export default initialState => createStore(initialState) 

Trong khối mã ở trên, bạn xuất một tập hợp các hành động làm tăng và giảm giá trị của count 1. Các hành động sẽ luôn nhận state là tham số đầu tiên và bất kỳ tham số nào khác có thể xuất hiện tiếp theo. Hàm createStore , được sử dụng để khởi tạo cửa hàng trong Unistore, cũng được xuất.

Tiếp theo, chỉnh sửa file router.js . Điều này chứa phần cài đặt cho các tuyến đường bạn sẽ sử dụng trong ứng dụng.

import { h } from 'preact' import Router from 'preact-router'  import { App } from "./App"; import { About } from "./About";  export default () => (   <Router>     <App path="/" />     <About path="/about" />   </Router> ) 

Mã này sử dụng preact-router để xác định các tuyến đường. Để thực hiện việc này, hãy nhập các tuyến và biến chúng trở thành con của thành phần Router . Sau đó bạn có thể cài đặt một prop của path để mỗi thành phần để preact-router biết mà thành phần để phục vụ cho một tuyến đường.

Có hai tuyến chính trong ứng dụng: thành phần App.js , đóng role là tuyến trang chủ và thành phần About.js , đóng role là trang giới thiệu.

Tiếp theo, chỉnh sửa About.js như sau:

import { h } from "preact"; import { Link } from "preact-router/match";  export const About = () => (     <div>         <p>This is a Preact app being rendered on the server. It uses Unistore for state management and preact-router for routing.</p>         <Link href="/">Home</Link>     </div> ); 

Đây là một thành phần có một mô tả ngắn và một thành phần Link dẫn đến tuyến nhà.

App.js đóng role là đường dẫn chính. Mở file đó và chỉnh sửa bằng mã cần thiết:

import { h } from 'preact' import { Link } from 'preact-router' import { connect } from 'unistore/preact'  import { actions } from './store/store'  export const App = connect('count', actions)(     ({ count, increment, decrement }) => (       <div class="count">         <p>{count}</p>         <button class="increment-btn" onClick={increment}>Increment</button>         <button class="decrement-btn" onClick={decrement}>Decrement</button>         <Link href="/about">About</Link>       </div>     )   ) 

Trong mã này, chức năng connect được nhập, cũng như chức năng actions . Trong thành phần App , giá trị trạng thái count được hiển thị cũng như các hành động incrementdecrement . Các hành động incrementdecrement đều được kết nối với các node khác nhau bằng trình xử lý sự kiện onClick .

Tệp index.js là điểm nhập cho Webpack. Nó sẽ đóng role là thành phần mẹ cho tất cả các thành phần khác trong ứng dụng Preact. Mở file và chỉnh sửa bằng mã bên dưới.

// index.js import { h, render } from 'preact' import { Provider } from 'unistore/preact' import Router from './router'  import createStore from './store/store'  const store = createStore(window.__STATE__)  const app = document.getElementById('app')  render(   <Provider store={store}>     <Router />   </Provider>,   app,   app.lastChild ) 

Trong khối mã ở trên, thành phần Provider được nhập. Điều quan trọng là chỉ định môi trường làm việc nếu đó là Preact hay React. Ta cũng nhập thành phần Router từ file router.js và hàm createStore cũng được nhập từ file store.js .

Dòng const store = createStore(window.__STATE__) được sử dụng để chuyển trạng thái ban đầu từ server đến client vì bạn đang tạo ứng dụng SSR.

Cuối cùng, trong chức năng render , bạn bọc thành phần Router bên trong thành phần Provider để làm cho cửa hàng có sẵn cho tất cả các thành phần con.

Điều đó hoàn thành khía cạnh khách hàng của mọi thứ. Bây giờ ta sẽ chuyển sang phía server của ứng dụng.

Xây dựng server nút

Bắt đầu bằng cách tạo file server.js . Điều này sẽ chứa ứng dụng Node sẽ được sử dụng để hiển thị phía server .

// server.js const express = require("express"); const { h } = require("preact"); const render = require("preact-render-to-string"); import { Provider } from 'unistore/preact' const { App } = require("./src/App"); const path = require("path");  import Router from './src/router' import createStore from './src/store/store'  const app = express();  const HTMLShell = (html, state) => `     <!DOCTYPE html>     <html>         <head>             <meta charset="utf-8">             <meta name="viewport" content="width=device-width, initial-scale=1">             <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">             <title> SSR Preact App </title>         </head>         <body>             <div id="app">${html}</div>       <script>window.__STATE__=${JSON.stringify(state).replace(/<|>/g, '')}</script>             <script src="./app.js"></script>         </body>     </html>`  app.use(express.static(path.join(__dirname, "dist")));  app.get('**', (req, res) => {   const store = createStore({ count: 0, todo: [] })    let state = store.getState()    let html = render(     <Provider store={store}>       <Router />     </Provider>   )    res.send(HTMLShell(html, state)) })  app.listen(4000); 

Hãy chia nhỏ điều này:

const express = require("express"); const { h } = require("preact"); const render = require("preact-render-to-string"); import { Provider } from 'unistore/preact' const { App } = require("./src/App"); const path = require("path");  import Router from './src/router' import createStore from './src/store/store'  const app = express(); 

Trong khối mã ở trên, bạn nhập các gói cần thiết cho server Node, chẳng hạn như expresspath . Bạn cũng nhập preact , thành phần Provider từ unistore và quan trọng nhất là preact-render-to-string cho phép bạn thực hiện kết xuất phía server . Các tuyến đường và cửa hàng cũng được nhập từ các file tương ứng của chúng.

const HTMLShell = (html, state) => `     <!DOCTYPE html>     <html>         <head>             <meta charset="utf-8">             <meta name="viewport" content="width=device-width, initial-scale=1">             <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">             <title> SSR Preact App </title>         </head>         <body>             <div id="app">${html}</div>       <script>window.__STATE__=${JSON.stringify(state).replace(/<|>/g, '')}</script>             <script src="./app.js"></script>         </body>     </html>` 

Trong khối mã ở trên, bạn tạo HTML cơ sở sẽ được sử dụng cho ứng dụng. Trong mã HTML, trạng thái được khởi tạo trong phần script . Hàm HTMLShell chấp nhận hai tham số. Tham số html sẽ là kết quả nhận được từ preact-render-to-string , sau đó html được đưa vào bên trong mã HTML. Tham số thứ hai là trạng thái.

app.use(express.static(path.join(__dirname, "dist")));  app.get('**', (req, res) => {   const store = createStore({ count: 0})    let state = store.getState()    let html = render(     <Provider store={store}>       <Router />     </Provider>   )    res.send(HTMLShell(html, state)) })  app.listen(4000); 

Trong dòng đầu tiên của mã này, bạn nói với Express để sử dụng các dist khi phục vụ các file tĩnh. Như đã đề cập trước đó, app.js nằm trong folder dist .

Tiếp theo, bạn đặt lộ trình cho bất kỳ yêu cầu nào đến ứng dụng bằng app.get(**) . Điều đầu tiên cần làm là khởi tạo store và trạng thái của nó, sau đó tạo một biến chứa giá trị của trạng thái.

Sau đó, preact-render-to-string (được nhập dưới dạng render ) được sử dụng để kết xuất ứng dụng Preact phía client cùng với Router , giữ tuyến và Provider , cung cấp cửa hàng cho mọi thành phần con.

Sau khi hoàn thành, cuối cùng bạn có thể chạy ứng dụng và xem nó trông như thế nào. Trước khi bạn làm điều đó, hãy thêm khối mã bên dưới vào file package.json .

"scripts": {     "test": "echo \"Error: no test specified\" && exit 1",     "start:client": "webpack -w",     "start:server": "babel-node server.js",     "dev": "npm run start:client & npm run start:server"   }, 

Đây là những tập lệnh cho phép bạn cài đặt và chạy ứng dụng. Chạy lệnh npm run dev trong terminal của bạn và truy cập http://localhost:4000 . Ứng dụng sẽ được cài đặt và chạy và bạn sẽ nhận được một màn hình tương tự như bên dưới.

Ứng dụng tăng và giảm

Thêm CSS Styling

Bây giờ các chế độ xem đã xong và client được kết nối với server , bạn có thể thêm một số kiểu cho ứng dụng. Bạn cần cho Webpack biết rằng nó cần phải gói các file CSS.

Để làm điều đó, cần thêm style-loader css-loader vào ứng dụng. Cả hai đều có thể được cài đặt bằng cách chạy lệnh này:

  • npm i css-loader style-loader --save-dev

Sau khi cài đặt hoàn tất, hãy webpack.config.js file webpack.config.js và thêm mã bên dưới vào mảng rules .

{   test: /\.css$/,   use: [ 'style-loader', 'css-loader' ] } 

Đến đây bạn có thể tạo file index.css bên trong folder src và chỉnh sửa bằng mã sau:

body {   background-image: linear-gradient(to right top, #2b0537, #820643, #c4442b, #d69600, #a8eb12);   height: 100vh;   display: flex;   align-items: center;   justify-content: center;   text-align: center; } a {   display: block;   color: white;   text-decoration: underline; } p {   color: white } .count p {   color: white;   font-size: 60px; } button:focus {   outline: none; } .increment-btn {   background-color: #1A2C5D;   border: none;   color: white;   border-radius: 3px;   padding: 10px 20px;   font-size: 14px;   margin: 0 10px; } .decrement-btn {   background-color: #BC1B1B;   border: none;   color: white;   border-radius: 3px;   padding: 10px 20px;   font-size: 14px;   margin: 0 10px; } 

Trong index.js , hãy thêm mã này vào đầu file :

import './index.css';` ... 

Trang web bây giờ sẽ được cách điệu:
Trang ứng dụng cách điệu

Kết luận

Trong hướng dẫn này, bạn đã tạo ứng dụng Kết xuất phía server và khám phá những lợi thế của việc xây dựng các ứng dụng được kết xuất phía server . Bạn cũng đã sử dụng Unistore để quản lý trạng thái cơ bản và kết nối trạng thái từ server đến giao diện user bằng window.__STATE__ .

Đến đây bạn sẽ có ý tưởng về cách hiển thị ứng dụng Preact trên server . Tóm lại, ý tưởng ban đầu là hiển thị ứng dụng trên server trước tiên và sau đó hiển thị các thành phần trên trình duyệt.

Có thể xem mã cho hướng dẫn này trên GitHub .


Tags:

Các tin liên quan