Chuyển đổi phản ứng thành html tĩnh

Có nhiều lý do khác nhau cho việc này. Một mặt, React tạo ra các thành phần mà không cần nhiều nghi lễ và thỏa hiệp. Mô hình này cũng khá độc lập với React. Với nhà máy JSX mới, bạn thậm chí sẽ không thấy việc nhập React trong TypeScript hoặc Babel

Rõ ràng, phải có một lưu ý khi xây dựng mọi ứng dụng giao diện người dùng độc quyền trong React. Bên cạnh cuộc thảo luận tôn giáo rõ ràng về việc liệu React có thực sự là cách phù hợp để làm giao diện người dùng hay không, con voi trong phòng là React… sau tất cả… chỉ là JavaScript

Đó là một thư viện JavaScript và nó yêu cầu JavaScript để chạy. Điều này có nghĩa là thời gian tải xuống lâu hơn, trang cồng kềnh hơn và có lẽ là xếp hạng SEO không tốt lắm

Tùy chọn sử dụng React không có JavaScript

Vậy chúng ta có thể làm gì để cải thiện tình hình?

“Ồ vâng, chúng ta hãy sử dụng Node. thay vào đó, máy chủ js cũng đang thực hiện SSR. ”

“Không có thứ gì đó tạo ra đánh dấu tĩnh sao?

“Hmm, tôi muốn tất cả, nhưng rõ ràng một khung SSR làm những thứ giống như Gatsby sẽ tốt hơn. Đây là tiếp theo. js, phải không?”

“Nghe phức tạp quá. Hãy sử dụng lại Jekyll. ”

"Tại sao?. Hãy chào mừng mọi người đến với năm 2021 — phục vụ bất kỳ trang nào dưới dạng SPA thật tuyệt. ”

Có thể bạn có xu hướng xác định với một trong những. Cá nhân, tùy thuộc vào vấn đề, tôi sẽ chọn một trong hai cái sau. Chắc chắn, tôi đã thực hiện khá nhiều thao tác kết xuất phía máy chủ (SSR) với React, nhưng tôi thường thấy rằng sự phức tạp bổ sung không đáng để nỗ lực

Tương tự như vậy, tôi có thể là một trong số ít người thực sự không thích Gatsby. Đối với tôi, ít nhất, nó phức tạp hóa hầu hết mọi thứ và tôi không thấy nhiều lợi ích

Vì vậy, đây là nó? . Nếu chúng tôi đặt ứng dụng của mình trên một kiến ​​trúc vững chắc, chúng tôi có thể chỉ cần viết một tập lệnh nhỏ và thực sự tự mình thực hiện tạo trang tĩnh (SSG) — không cần Gatsby hay bất kỳ thứ gì khác. Điều này sẽ không phức tạp;

Bây giờ chúng ta đang thực sự mong đợi điều gì ở đây?

Đặt đúng kỳ vọng

Trong bài đăng này, chúng tôi sẽ xây dựng một giải pháp đơn giản để chuyển đổi trang của chúng tôi được tạo bằng React thành một tập hợp các trang web tĩnh được tạo trước đầy đủ. Chúng tôi vẫn có thể hydrat hóa thứ này và để trang web của chúng tôi hoạt động

Mục tiêu của chúng tôi là cải thiện hiệu suất kết xuất ban đầu. Trong thử nghiệm Ngọn hải đăng của chúng tôi, chúng tôi thấy rằng trang chủ của chúng tôi không phải lúc nào cũng được nhận thức tốt như chúng tôi mong đợi


Chuyển đổi phản ứng thành html tĩnh
Chuyển đổi phản ứng thành html tĩnh

Hơn 200 nghìn nhà phát triển sử dụng LogRocket để tạo ra trải nghiệm kỹ thuật số tốt hơn

Chuyển đổi phản ứng thành html tĩnh
Chuyển đổi phản ứng thành html tĩnh
Tìm hiểu thêm →


Chuyển đổi phản ứng thành html tĩnh
Chuyển đổi phản ứng thành html tĩnh

Những gì chúng tôi sẽ không làm là tối ưu hóa các trang tĩnh để chúng chỉ có các đoạn JavaScript nhỏ. Chúng tôi sẽ luôn cấp nước cho trang bằng JavaScript đầy đủ (có thể vẫn tải chậm, nhưng sẽ bổ sung thêm về điều đó sau)

Mặc dù kết xuất trước tĩnh của SPA có thể có lợi cho hiệu suất nhận thức, nhưng chúng tôi sẽ không tập trung vào tối ưu hóa hiệu suất. Nếu bạn quan tâm đến việc tối ưu hóa hiệu suất, bạn nên xem hướng dẫn chuyên sâu này để tối ưu hóa hiệu suất với webpack

Một ứng dụng React cơ bản

Để chuẩn bị cho bài viết này, chúng tôi sử dụng một loại ứng dụng React khá đơn giản nhưng khá phổ biến. Chúng tôi cài đặt một loạt các phụ thuộc dành cho nhà phát triển (vâng, chúng tôi sẽ sử dụng TypeScript cho mục đích dịch mã)

npm i webpack webpack-dev-server webpack-cli typescript ts-loader file-loader html-webpack-plugin @types/react @types/react-dom @types/react-router @types/react-router-dom --save-dev

Và, tất nhiên, một số phụ thuộc thời gian chạy

npm i react react-dom react-router-dom react-router --save

Bây giờ chúng tôi thiết lập một

module.exports = {
  mode: 'production',
  devtool: 'source-map',
  entry: './src/index.tsx',
  output: {
    filename: 'app.js',
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js'],
  },
  module: {
    rules: [
      { test: /\.tsx?$/, loader: 'ts-loader' },
      { test: /\.(png|jpe?g|gif)$/i, loader: 'file-loader' },
    ],
  },
};
6 thích hợp để đóng gói ứng dụng

module.exports = {
  mode: 'production',
  devtool: 'source-map',
  entry: './src/index.tsx',
  output: {
    filename: 'app.js',
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js'],
  },
  module: {
    rules: [
      { test: /\.tsx?$/, loader: 'ts-loader' },
      { test: /\.(png|jpe?g|gif)$/i, loader: 'file-loader' },
    ],
  },
};

Điều này sẽ hỗ trợ TypeScript và các tệp dưới dạng nội dung. Lưu ý rằng đối với một ứng dụng web lớn hơn, chúng tôi có thể cần nhiều thứ khác, nhưng điều này là đủ cho bản demo của chúng tôi

Chúng tôi cũng nên thêm một số trang có một chút nội dung để thấy điều này hiệu quả. Trong cấu trúc của chúng tôi, chúng tôi sẽ sử dụng tệp

module.exports = {
  mode: 'production',
  devtool: 'source-map',
  entry: './src/index.tsx',
  output: {
    filename: 'app.js',
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js'],
  },
  module: {
    rules: [
      { test: /\.tsx?$/, loader: 'ts-loader' },
      { test: /\.(png|jpe?g|gif)$/i, loader: 'file-loader' },
    ],
  },
};
7 để tổng hợp mọi thứ. Tập tin này có thể đơn giản như

import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';

const HomePage = React.lazy(() => import('./pages/home'));
const FirstPage = React.lazy(() => import('./pages/first'));
const NotFoundPage = React.lazy(() => import('./pages/not-found'));

const App = () => (
  
    
        
        
        
    
  
);

render(, document.querySelector('#app'));

Vấn đề với phương pháp này là chúng tôi sẽ cần chỉnh sửa tệp này cho mỗi trang mới. Nếu chúng ta sử dụng quy ước lưu trữ các trang trong thư mục

module.exports = {
  mode: 'production',
  devtool: 'source-map',
  entry: './src/index.tsx',
  output: {
    filename: 'app.js',
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js'],
  },
  module: {
    rules: [
      { test: /\.tsx?$/, loader: 'ts-loader' },
      { test: /\.(png|jpe?g|gif)$/i, loader: 'file-loader' },
    ],
  },
};
8, chúng ta có thể nghĩ ra thứ gì đó tốt hơn. Chúng tôi có hai lựa chọn

  1. Sử dụng phép thuật
    module.exports = {
      mode: 'production',
      devtool: 'source-map',
      entry: './src/index.tsx',
      output: {
        filename: 'app.js',
      },
      resolve: {
        extensions: ['.ts', '.tsx', '.js'],
      },
      module: {
        rules: [
          { test: /\.tsx?$/, loader: 'ts-loader' },
          { test: /\.(png|jpe?g|gif)$/i, loader: 'file-loader' },
        ],
      },
    };
    
    9 của webpack để “đọc to” thư mục và nhận danh sách động mà chúng tôi có thể sử dụng
  2. Sử dụng một mô-đun được tạo theo thời gian biên dịch đặc biệt giúp chúng tôi tải chậm và định tuyến

Cách tiếp cận thứ hai mang lại lợi thế lớn là các tuyến đường có thể là một phần của trang. Đối với phương pháp này, chúng tôi sẽ cần một trình tải webpack khác

________số 8_______

Điều này bây giờ cho phép chúng tôi cấu trúc lại tệp

module.exports = {
  mode: 'production',
  devtool: 'source-map',
  entry: './src/index.tsx',
  output: {
    filename: 'app.js',
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js'],
  },
  module: {
    rules: [
      { test: /\.tsx?$/, loader: 'ts-loader' },
      { test: /\.(png|jpe?g|gif)$/i, loader: 'file-loader' },
    ],
  },
};
7 để trông giống như

import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';
import Layout from './Layout';

const pages = require('./toc.codegen');
const [notFound] = pages.filter((m) => m.route === '*');
const standardPages = pages.filter((m) => m !== notFound);

const App = () => (
  
    
      
        {standardPages.map((page) => (
          
        ))}
        {notFound && }
      
    
  
);

render(, document.querySelector('#app'));

Bây giờ tất cả các trang được xác định đầy đủ bởi

import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';

const HomePage = React.lazy(() => import('./pages/home'));
const FirstPage = React.lazy(() => import('./pages/first'));
const NotFoundPage = React.lazy(() => import('./pages/not-found'));

const App = () => (
  
    
        
        
        
    
  
);

render(, document.querySelector('#app'));
1, đây là một mô-đun được tạo nhanh chóng trong quá trình đóng gói. Ngoài ra, chúng tôi đã thêm một thành phần
import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';

const HomePage = React.lazy(() => import('./pages/home'));
const FirstPage = React.lazy(() => import('./pages/first'));
const NotFoundPage = React.lazy(() => import('./pages/not-found'));

const App = () => (
  
    
        
        
        
    
  
);

render(, document.querySelector('#app'));
2 để cung cấp cho các trang của chúng tôi một chút cấu trúc được chia sẻ

Trình tạo mã cho mô-đun

import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';

const HomePage = React.lazy(() => import('./pages/home'));
const FirstPage = React.lazy(() => import('./pages/first'));
const NotFoundPage = React.lazy(() => import('./pages/not-found'));

const App = () => (
  
    
        
        
        
    
  
);

render(, document.querySelector('#app'));
3 trông như thế này

const { getPages } = require('./helpers');

module.exports = () => {
  const pageDetails = getPages();
  const pages = pageDetails.map((page) => {
    const meta = [
      `"content": lazy(() => import('./${page.folder}/${page.name}'))`,
      `${JSON.stringify('route')}: ${JSON.stringify(page.route)}`,
    ];

    return `{ ${meta.join(', ')} }`;
  });

  return `const { lazy } = require('react');
module.exports = [${pages.join(', ')}];`;
};

Chúng tôi chỉ lặp lại trên tất cả các trang và tạo một mô-đun mới, xuất một mảng các đối tượng có thuộc tính

import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';

const HomePage = React.lazy(() => import('./pages/home'));
const FirstPage = React.lazy(() => import('./pages/first'));
const NotFoundPage = React.lazy(() => import('./pages/not-found'));

const App = () => (
  
    
        
        
        
    
  
);

render(, document.querySelector('#app'));
4 và
import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';

const HomePage = React.lazy(() => import('./pages/home'));
const FirstPage = React.lazy(() => import('./pages/first'));
const NotFoundPage = React.lazy(() => import('./pages/not-found'));

const App = () => (
  
    
        
        
        
    
  
);

render(, document.querySelector('#app'));
5

Tại thời điểm này, mục tiêu của chúng tôi là kết xuất trước ứng dụng đơn giản (nhưng hoàn chỉnh) này

Khái niệm cơ bản về SSG với React

Nếu bạn biết SSR với React, bạn đã biết mọi thứ bạn cần để thực hiện một số SSG cơ bản. Về cốt lõi, chúng tôi sử dụng hàm

import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';

const HomePage = React.lazy(() => import('./pages/home'));
const FirstPage = React.lazy(() => import('./pages/first'));
const NotFoundPage = React.lazy(() => import('./pages/not-found'));

const App = () => (
  
    
        
        
        
    
  
);

render(, document.querySelector('#app'));
6 từ
import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';

const HomePage = React.lazy(() => import('./pages/home'));
const FirstPage = React.lazy(() => import('./pages/first'));
const NotFoundPage = React.lazy(() => import('./pages/not-found'));

const App = () => (
  
    
        
        
        
    
  
);

render(, document.querySelector('#app'));
7 thay vì
import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';

const HomePage = React.lazy(() => import('./pages/home'));
const FirstPage = React.lazy(() => import('./pages/first'));
const NotFoundPage = React.lazy(() => import('./pages/not-found'));

const App = () => (
  
    
        
        
        
    
  
);

render(, document.querySelector('#app'));
8 từ
import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';

const HomePage = React.lazy(() => import('./pages/home'));
const FirstPage = React.lazy(() => import('./pages/first'));
const NotFoundPage = React.lazy(() => import('./pages/not-found'));

const App = () => (
  
    
        
        
        
    
  
);

render(, document.querySelector('#app'));
9. Giả sử chúng ta có một trang được lồng trong một bố cục, đoạn mã sau có thể hoạt động rồi

const element = (
  
    
       
    
  
);
const content = renderToString(element);

Trong đoạn mã trên, chúng tôi giả sử rằng bố cục được cung cấp đầy đủ trong thành phần

import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';

const HomePage = React.lazy(() => import('./pages/home'));
const FirstPage = React.lazy(() => import('./pages/first'));
const NotFoundPage = React.lazy(() => import('./pages/not-found'));

const App = () => (
  
    
        
        
        
    
  
);

render(, document.querySelector('#app'));
2. Chúng tôi cũng giả định rằng React Router được sử dụng để định tuyến phía máy khách. Do đó, chúng ta cần cung cấp ngữ cảnh định tuyến thích hợp. May mắn thay, việc chúng ta sử dụng
npm i parcel-codegen-loader --save-dev
1,
npm i parcel-codegen-loader --save-dev
2 hay
npm i parcel-codegen-loader --save-dev
3 không thực sự quan trọng — tất cả đều cung cấp ngữ cảnh định tuyến


Các bài viết hay khác từ LogRocket

  • Đừng bỏ lỡ một khoảnh khắc nào với The Replay, một bản tin được tuyển chọn từ LogRocket
  • Tìm hiểu cách Galileo của LogRocket loại bỏ tiếng ồn để chủ động giải quyết các sự cố trong ứng dụng của bạn
  • Sử dụng useEffect của React để tối ưu hóa hiệu suất ứng dụng của bạn
  • Chuyển đổi giữa nhiều phiên bản của Node
  • Khám phá cách tạo hoạt ảnh cho ứng dụng React của bạn với AnimXYZ
  • Khám phá Tauri, một khuôn khổ mới để xây dựng các tệp nhị phân
  • So sánh NestJS với. Thể hiện. js

Bây giờ,

npm i parcel-codegen-loader --save-dev
4 là gì? . Bạn có thể có nhiều thành phần hơn mà bạn muốn đưa vào trên mỗi trang. Một ví dụ điển hình sẽ là thành phần
npm i parcel-codegen-loader --save-dev
6, thường được tích hợp như


Nhưng đây sẽ là một thành phần mà chúng tôi sẽ để lại cho quá trình hydrat hóa khi trang tĩnh của chúng tôi trở nên động. Không cần kết xuất trước

Mặt khác, bản thân ứng dụng thường trông giống như

const app = (
  
    
    
      
        {pages
          .map(page => (
            
          ))}
        
      
    
  
);

hydrate(app, document.querySelector('#app'));

Điều này khá gần với đoạn trích để tạo tĩnh. Sự khác biệt chính là ở đây chúng tôi bao gồm tất cả các trang (và hydrat hóa), trong khi ở kịch bản SSG ở trên, chúng tôi giảm toàn bộ nội dung trang thành một trang cố định

Cạm bẫy của SSG

Chà, cho đến nay mọi thứ nghe có vẻ đơn giản và dễ dàng, phải không?

Hỗ trợ tài sản

Làm thế nào để chúng ta đề cập đến tài sản?

npm i react react-dom react-router-dom react-router --save
0

Điều này có thể hoạt động nếu

npm i parcel-codegen-loader --save-dev
7 tồn tại trên máy chủ của chúng tôi. Sử dụng URL đầy đủ sẽ đáng tin cậy hơn một chút, e. g. ,
npm i parcel-codegen-loader --save-dev
8. Tuy nhiên, sau đó chúng tôi sẽ từ bỏ một số tính linh hoạt liên quan đến môi trường

Khá thường xuyên, dù sao thì chúng tôi cũng để lại những nội dung đó cho các gói như webpack. Trong trường hợp này, chúng tôi có mã như

npm i react react-dom react-router-dom react-router --save
1

Ở đây,

npm i parcel-codegen-loader --save-dev
7 được giải quyết cục bộ tại thời điểm xây dựng, sau đó được băm, tối ưu hóa và sao chép vào thư mục đích. Tất cả đều ổn. Tuy nhiên, đối với quy trình được mô tả ở trên, chỉ yêu cầu mô-đun trong Nút. js sẽ không hoạt động. Chúng tôi sẽ đánh một ngoại lệ rằng
npm i parcel-codegen-loader --save-dev
7 không phải là một mô-đun hợp lệ

Giải quyết vấn đề này thực ra không phức tạp. May mắn thay, chúng ta có thể đăng ký các tiện ích mở rộng bổ sung cho các mô-đun trong Node. js

npm i react react-dom react-router-dom react-router --save
2

Đoạn mã trên giả định rằng tệp đã/sẽ được sao chép vào thư mục gốc. Vì vậy, chúng tôi sẽ biến đổi

import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';
import Layout from './Layout';

const pages = require('./toc.codegen');
const [notFound] = pages.filter((m) => m.route === '*');
const standardPages = pages.filter((m) => m !== notFound);

const App = () => (
  
    
      
        {standardPages.map((page) => (
          
        ))}
        {notFound && }
      
    
  
);

render(, document.querySelector('#app'));
1 thành
import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';
import Layout from './Layout';

const pages = require('./toc.codegen');
const [notFound] = pages.filter((m) => m.route === '*');
const standardPages = pages.filter((m) => m !== notFound);

const App = () => (
  
    
      
        {standardPages.map((page) => (
          
        ))}
        {notFound && }
      
    
  
);

render(, document.querySelector('#app'));
2

Còn phần băm thì sao?

npm i react react-dom react-router-dom react-router --save
3

Điều này sẽ cố gắng tìm một tệp bắt đầu bằng “foo” và kết thúc bằng “png”, chẳng hạn như

import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';
import Layout from './Layout';

const pages = require('./toc.codegen');
const [notFound] = pages.filter((m) => m.route === '*');
const standardPages = pages.filter((m) => m !== notFound);

const App = () => (
  
    
      
        {standardPages.map((page) => (
          
        ))}
        {notFound && }
      
    
  
);

render(, document.querySelector('#app'));
3. Nghỉ ngơi như trên

Hỗ trợ TypeScript

Bây giờ chúng tôi đã biết cách xử lý nội dung chung, chúng tôi cũng có thể cung cấp cơ chế xử lý cho các tệp

import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';
import Layout from './Layout';

const pages = require('./toc.codegen');
const [notFound] = pages.filter((m) => m.route === '*');
const standardPages = pages.filter((m) => m !== notFound);

const App = () => (
  
    
      
        {standardPages.map((page) => (
          
        ))}
        {notFound && }
      
    
  
);

render(, document.querySelector('#app'));
4 và
import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';
import Layout from './Layout';

const pages = require('./toc.codegen');
const [notFound] = pages.filter((m) => m.route === '*');
const standardPages = pages.filter((m) => m !== notFound);

const App = () => (
  
    
      
        {standardPages.map((page) => (
          
        ))}
        {notFound && }
      
    
  
);

render(, document.querySelector('#app'));
5. Rốt cuộc, những thứ này cũng có thể được dịch sang JS trong bộ nhớ. Tuy nhiên, tất cả công việc này là hoàn toàn không cần thiết, vì
import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';
import Layout from './Layout';

const pages = require('./toc.codegen');
const [notFound] = pages.filter((m) => m.route === '*');
const standardPages = pages.filter((m) => m !== notFound);

const App = () => (
  
    
      
        {standardPages.map((page) => (
          
        ))}
        {notFound && }
      
    
  
);

render(, document.querySelector('#app'));
6 đã làm việc đó rồi. Vì vậy, công việc của chúng tôi về cơ bản giảm xuống

npm i react react-dom react-router-dom react-router --save
4

Sau đó đăng ký trình xử lý cho các phần mở rộng tệp tương ứng bằng cách sử dụng

npm i react react-dom react-router-dom react-router --save
5

Bây giờ, điều này cho phép chỉ yêu cầu các mô-đun được xác định là tệp

import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';
import Layout from './Layout';

const pages = require('./toc.codegen');
const [notFound] = pages.filter((m) => m.route === '*');
const standardPages = pages.filter((m) => m !== notFound);

const App = () => (
  
    
      
        {standardPages.map((page) => (
          
        ))}
        {notFound && }
      
    
  
);

render(, document.querySelector('#app'));
5

tải chậm

Nếu chúng tôi đã viết một React SPA được tối ưu hóa, thì chúng tôi cũng sẽ chia nhóm ứng dụng của mình ở cấp định tuyến. Điều này có nghĩa là mỗi trang đều có gói phụ của riêng mình. Như vậy, sẽ có một gói lõi/chung (thường chứa cơ chế định tuyến, chính React, một số phụ thuộc được chia sẻ, v.v. ) và một gói cho mỗi trang. Nếu chúng tôi có 10 trang, chúng tôi sẽ có 11 (hoặc nhiều hơn) gói

Nhưng sử dụng phương pháp kết xuất trước, điều này không lý tưởng lắm. Rốt cuộc, chúng tôi đã kết xuất trước các trang riêng lẻ. Điều gì sẽ xảy ra nếu chúng ta có một thành phần phụ trên một trang (e. g. , một điều khiển bản đồ) sẽ được chia sẻ, nhưng quá lớn nên chúng tôi muốn đưa nó vào gói riêng?

Đó là, chúng ta có một thành phần như

npm i react react-dom react-router-dom react-router --save
6

Trường hợp thành phần thực tế sẽ được tải chậm. Trong kịch bản này, chúng tôi sẽ đụng phải một bức tường khá mạnh. React không biết cách kết xuất trước

import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';
import Layout from './Layout';

const pages = require('./toc.codegen');
const [notFound] = pages.filter((m) => m.route === '*');
const standardPages = pages.filter((m) => m !== notFound);

const App = () => (
  
    
      
        {standardPages.map((page) => (
          
        ))}
        {notFound && }
      
    
  
);

render(, document.querySelector('#app'));
8

May mắn thay, chúng ta có thể định nghĩa lại

import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';
import Layout from './Layout';

const pages = require('./toc.codegen');
const [notFound] = pages.filter((m) => m.route === '*');
const standardPages = pages.filter((m) => m !== notFound);

const App = () => (
  
    
      
        {standardPages.map((page) => (
          
        ))}
        {notFound && }
      
    
  
);

render(, document.querySelector('#app'));
8 để chỉ hiển thị một số trình giữ chỗ (chúng ta cũng có thể phát điên ở đây và thực sự tải hoặc hiển thị trước nội dung, nhưng hãy giữ cho nó đơn giản và giả sử rằng bất kỳ thứ gì được tải chậm ở đây thực sự sẽ được tải chậm sau này

npm i react react-dom react-router-dom react-router --save
7

Phần khác không có sẵn để sử dụng trong

import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';

const HomePage = React.lazy(() => import('./pages/home'));
const FirstPage = React.lazy(() => import('./pages/first'));
const NotFoundPage = React.lazy(() => import('./pages/not-found'));

const App = () => (
  
    
        
        
        
    
  
);

render(, document.querySelector('#app'));
6 là
const { getPages } = require('./helpers');

module.exports = () => {
  const pageDetails = getPages();
  const pages = pageDetails.map((page) => {
    const meta = [
      `"content": lazy(() => import('./${page.folder}/${page.name}'))`,
      `${JSON.stringify('route')}: ${JSON.stringify(page.route)}`,
    ];

    return `{ ${meta.join(', ')} }`;
  });

  return `const { lazy } = require('react');
module.exports = [${pages.join(', ')}];`;
};
1. Chà, thành thật mà nói, việc thực hiện điều này sẽ không gây hại gì, nhưng chúng ta hãy tự làm điều đó

Tất cả những gì chúng ta cần làm ở đây là hành động như thể

const { getPages } = require('./helpers');

module.exports = () => {
  const pageDetails = getPages();
  const pages = pageDetails.map((page) => {
    const meta = [
      `"content": lazy(() => import('./${page.folder}/${page.name}'))`,
      `${JSON.stringify('route')}: ${JSON.stringify(page.route)}`,
    ];

    return `{ ${meta.join(', ')} }`;
  });

  return `const { lazy } = require('react');
module.exports = [${pages.join(', ')}];`;
};
1 hoàn toàn không có ở đó. Vì vậy, chúng tôi chỉ cần thay thế nó bằng một đoạn bằng cách sử dụng
const { getPages } = require('./helpers');

module.exports = () => {
  const pageDetails = getPages();
  const pages = pageDetails.map((page) => {
    const meta = [
      `"content": lazy(() => import('./${page.folder}/${page.name}'))`,
      `${JSON.stringify('route')}: ${JSON.stringify(page.route)}`,
    ];

    return `{ ${meta.join(', ')} }`;
  });

  return `const { lazy } = require('react');
module.exports = [${pages.join(', ')}];`;
};
3 được cung cấp

npm i react react-dom react-router-dom react-router --save
8

Bây giờ chúng tôi xử lý lazy loading khá hiệu quả, mặc dù, tùy thuộc vào tình huống, chúng tôi có thể (và thậm chí có thể muốn) làm nhiều hơn thế

Những thứ khác

Bên cạnh những thứ như

import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';
import Layout from './Layout';

const pages = require('./toc.codegen');
const [notFound] = pages.filter((m) => m.route === '*');
const standardPages = pages.filter((m) => m !== notFound);

const App = () => (
  
    
      
        {standardPages.map((page) => (
          
        ))}
        {notFound && }
      
    
  
);

render(, document.querySelector('#app'));
8 và
const { getPages } = require('./helpers');

module.exports = () => {
  const pageDetails = getPages();
  const pages = pageDetails.map((page) => {
    const meta = [
      `"content": lazy(() => import('./${page.folder}/${page.name}'))`,
      `${JSON.stringify('route')}: ${JSON.stringify(page.route)}`,
    ];

    return `{ ${meta.join(', ')} }`;
  });

  return `const { lazy } = require('react');
module.exports = [${pages.join(', ')}];`;
};
1, các phần khác cũng có thể bị thiếu trong React. Một ví dụ điển hình là
const { getPages } = require('./helpers');

module.exports = () => {
  const pageDetails = getPages();
  const pages = pageDetails.map((page) => {
    const meta = [
      `"content": lazy(() => import('./${page.folder}/${page.name}'))`,
      `${JSON.stringify('route')}: ${JSON.stringify(page.route)}`,
    ];

    return `{ ${meta.join(', ')} }`;
  });

  return `const { lazy } = require('react');
module.exports = [${pages.join(', ')}];`;
};
6. Tuy nhiên, vì
const { getPages } = require('./helpers');

module.exports = () => {
  const pageDetails = getPages();
  const pages = pageDetails.map((page) => {
    const meta = [
      `"content": lazy(() => import('./${page.folder}/${page.name}'))`,
      `${JSON.stringify('route')}: ${JSON.stringify(page.route)}`,
    ];

    return `{ ${meta.join(', ')} }`;
  });

  return `const { lazy } = require('react');
module.exports = [${pages.join(', ')}];`;
};
6 thường chỉ áp dụng trong thời gian chạy, nên cách dễ dàng để hỗ trợ nó là tránh nó hoàn toàn

Cách thực hiện khá đơn giản là thay thế nó bằng chức năng không hoạt động. Bằng cách này,

const { getPages } = require('./helpers');

module.exports = () => {
  const pageDetails = getPages();
  const pages = pageDetails.map((page) => {
    const meta = [
      `"content": lazy(() => import('./${page.folder}/${page.name}'))`,
      `${JSON.stringify('route')}: ${JSON.stringify(page.route)}`,
    ];

    return `{ ${meta.join(', ')} }`;
  });

  return `const { lazy } = require('react');
module.exports = [${pages.join(', ')}];`;
};
6 không cản trở chúng ta và đơn giản là bị bỏ qua

npm i react react-dom react-router-dom react-router --save
9

Một số mô-đun chúng tôi

const { getPages } = require('./helpers');

module.exports = () => {
  const pageDetails = getPages();
  const pages = pageDetails.map((page) => {
    const meta = [
      `"content": lazy(() => import('./${page.folder}/${page.name}'))`,
      `${JSON.stringify('route')}: ${JSON.stringify(page.route)}`,
    ];

    return `{ ${meta.join(', ')} }`;
  });

  return `const { lazy } = require('react');
module.exports = [${pages.join(', ')}];`;
};
9 thực sự sẽ không ở định dạng CommonJS. Đây là một vấn đề lớn. Tại thời điểm viết bài, Nút. js chỉ hỗ trợ các mô-đun CommonJS (các mô-đun ES vẫn đang thử nghiệm)

May mắn thay, có , cung cấp cho chúng tôi hỗ trợ cho các mô-đun ES bằng cách sử dụng các câu lệnh

const element = (
  
    
       
    
  
);
const content = renderToString(element);
0 và
const element = (
  
    
       
    
  
);
const content = renderToString(element);
1. Tuyệt vời

Giống như với

import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { render } from 'react-dom';
import Layout from './Layout';

const pages = require('./toc.codegen');
const [notFound] = pages.filter((m) => m.route === '*');
const standardPages = pages.filter((m) => m !== notFound);

const App = () => (
  
    
      
        {standardPages.map((page) => (
          
        ))}
        {notFound && }
      
    
  
);

render(, document.querySelector('#app'));
6, chúng tôi sẽ bắt đầu bằng cách cài đặt phần phụ thuộc

module.exports = {
  mode: 'production',
  devtool: 'source-map',
  entry: './src/index.tsx',
  output: {
    filename: 'app.js',
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js'],
  },
  module: {
    rules: [
      { test: /\.tsx?$/, loader: 'ts-loader' },
      { test: /\.(png|jpe?g|gif)$/i, loader: 'file-loader' },
    ],
  },
};
0

Và sau đó thực sự sử dụng nó. Trong trường hợp này, chúng ta cần thay thế

const { getPages } = require('./helpers');

module.exports = () => {
  const pageDetails = getPages();
  const pages = pageDetails.map((page) => {
    const meta = [
      `"content": lazy(() => import('./${page.folder}/${page.name}'))`,
      `${JSON.stringify('route')}: ${JSON.stringify(page.route)}`,
    ];

    return `{ ${meta.join(', ')} }`;
  });

  return `const { lazy } = require('react');
module.exports = [${pages.join(', ')}];`;
};
9 “bình thường” bằng một phiên bản mới của nó. mã đọc

module.exports = {
  mode: 'production',
  devtool: 'source-map',
  entry: './src/index.tsx',
  output: {
    filename: 'app.js',
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js'],
  },
  module: {
    rules: [
      { test: /\.tsx?$/, loader: 'ts-loader' },
      { test: /\.(png|jpe?g|gif)$/i, loader: 'file-loader' },
    ],
  },
};
1

Bây giờ chúng tôi cũng có hỗ trợ cho các mô-đun ES. Điều duy nhất còn thiếu có thể là một số chức năng liên quan đến DOM

Mặc dù thông thường chúng ta nên đặt các điều kiện trong mã của mình như vậy

module.exports = {
  mode: 'production',
  devtool: 'source-map',
  entry: './src/index.tsx',
  output: {
    filename: 'app.js',
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js'],
  },
  module: {
    rules: [
      { test: /\.tsx?$/, loader: 'ts-loader' },
      { test: /\.(png|jpe?g|gif)$/i, loader: 'file-loader' },
    ],
  },
};
2

Chúng ta cũng có thể làm giả một số quả địa cầu khác. Chẳng hạn, chúng tôi (hoặc một số người phụ thuộc mà chúng tôi sử dụng trực tiếp hoặc gián tiếp) có thể đề cập đến

const element = (
  
    
       
    
  
);
const content = renderToString(element);
4,
const element = (
  
    
       
    
  
);
const content = renderToString(element);
5 hoặc
const element = (
  
    
       
    
  
);
const content = renderToString(element);
6. Trong những trường hợp này, chúng ta có thể chế nhạo chúng bằng cách mở rộng đối tượng
const element = (
  
    
       
    
  
);
const content = renderToString(element);
7

Với cùng một logic, chúng ta có thể chế nhạo

const element = (
  
    
       
    
  
);
const content = renderToString(element);
8, hoặc ít nhất là một phần của nó. Tất nhiên, có gói
const element = (
  
    
       
    
  
);
const content = renderToString(element);
9, giúp với hầu hết chúng rồi. Nhưng bây giờ hãy giữ cho nó đơn giản và đi vào vấn đề
Chỉ là một ví dụ chế giễu

module.exports = {
  mode: 'production',
  devtool: 'source-map',
  entry: './src/index.tsx',
  output: {
    filename: 'app.js',
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js'],
  },
  module: {
    rules: [
      { test: /\.tsx?$/, loader: 'ts-loader' },
      { test: /\.(png|jpe?g|gif)$/i, loader: 'file-loader' },
    ],
  },
};
3

Bây giờ chúng tôi có mọi thứ để bắt đầu kết xuất trước ứng dụng của mình mà không gặp nhiều khó khăn

Mang tất cả lại với nhau

Có nhiều cách chúng ta có thể khái quát hóa và sử dụng các đoạn trích trên. Cá nhân tôi thích sử dụng chúng trong một mô-đun riêng biệt, được đánh giá/sử dụng trên mỗi trang bằng cách sử dụng


0. Bằng cách này, chúng tôi luôn nhận được các quy trình biệt lập, cũng có thể được xử lý song song

Một triển khai ví dụ được hiển thị bên dưới

module.exports = {
  mode: 'production',
  devtool: 'source-map',
  entry: './src/index.tsx',
  output: {
    filename: 'app.js',
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js'],
  },
  module: {
    rules: [
      { test: /\.tsx?$/, loader: 'ts-loader' },
      { test: /\.(png|jpe?g|gif)$/i, loader: 'file-loader' },
    ],
  },
};
4

Việc triển khai này có thể được sử dụng như một lib cũng như trực tiếp thông qua


1. Đó là lý do tại sao chúng tôi phân biệt giữa hai trường hợp bằng cách sử dụng

2 làm phân biệt đối xử. Trường hợp là lib thì ta export 2 hàm

3 và

4

Điều duy nhất còn lại ở đây là định nghĩa của


5. Đối với điều này, chúng tôi chỉ có thể sử dụng định nghĩa mà chúng tôi đã chỉ định trong phần giới thiệu về ứng dụng React cơ bản

Mô-đun


6 sẽ chứa mã ở trên. Được bọc trong một phong bì thích hợp để sử dụng trong quy trình rẽ nhánh, chúng tôi kết thúc với

module.exports = {
  mode: 'production',
  devtool: 'source-map',
  entry: './src/index.tsx',
  output: {
    filename: 'app.js',
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js'],
  },
  module: {
    rules: [
      { test: /\.tsx?$/, loader: 'ts-loader' },
      { test: /\.(png|jpe?g|gif)$/i, loader: 'file-loader' },
    ],
  },
};
5

Trong đó


7 sẽ thiết lập các sửa đổi cần thiết cho
const { getPages } = require('./helpers');

module.exports = () => {
  const pageDetails = getPages();
  const pages = pageDetails.map((page) => {
    const meta = [
      `"content": lazy(() => import('./${page.folder}/${page.name}'))`,
      `${JSON.stringify('route')}: ${JSON.stringify(page.route)}`,
    ];

    return `{ ${meta.join(', ')} }`;
  });

  return `const { lazy } = require('react');
module.exports = [${pages.join(', ')}];`;
};
9, trong khi

9 thực hiện tạo đánh dấu thực tế.
const app = (
  
    
    
      
        {pages
          .map(page => (
            
          ))}
        
      
    
  
);

hydrate(app, document.querySelector('#app'));
0 sử dụng đầu ra từ

9 để thực sự viết ra trang được tạo

Sử dụng thiết lập như vậy, chúng tôi có thể kết xuất trước trang web của mình và tăng hiệu suất đáng kể, như được xác nhận bởi Lighthouse

Chuyển đổi phản ứng thành html tĩnh
Chuyển đổi phản ứng thành html tĩnh

Như một tác dụng phụ thú vị, trang web của chúng tôi hiện cũng hoạt động — ở một mức độ nào đó — cho người dùng không có JavaScript. Ứng dụng ví dụ đầy đủ có thể được tìm thấy trên GitHub

Phần kết luận

Sử dụng React để tạo các trang tĩnh tuyệt vời thực sự không phải là vấn đề lớn. Không cần phải quay trở lại các khuôn khổ cồng kềnh và phức tạp. Bằng cách này, chúng ta có thể kiểm soát rất tốt những gì cần nhập và những gì cần loại bỏ.

Ngoài ra, chúng tôi tìm hiểu khá nhiều về nội bộ của Node. js, React và có khả năng là một số phần phụ thuộc mà chúng tôi sử dụng. Biết những gì chúng ta thực sự sử dụng không chỉ là một bài tập học thuật mà còn rất quan trọng trong trường hợp có lỗi hoặc các vấn đề khác

Hiệu suất đạt được khi kết xuất trước một SPA với kỹ thuật này có thể rất tốt. Quan trọng hơn, trang của chúng tôi trở nên dễ truy cập hơn và được xếp hạng cao hơn cho mục đích SEO

Bạn thấy kết xuất trước tỏa sáng ở đâu?

Tôi có thể chuyển đổi React sang HTML không?

Có, cả ứng dụng Angular và React đều có thể (và thường được) "xây dựng" thành HTML, CSS và JS tĩnh .

Cách sử dụng tĩnh trong HTML React?

Tạo Thành phần phản ứng trong chỉ mục. jsx , sau đó sao chép và dán phần nội dung của tệp HTML tĩnh của bạn vào câu lệnh trả về của Thành phần đó . Và nếu bạn có nhiều trang, tôi khuyên bạn nên tạo một thư mục trang riêng trong thư mục src và tạo. js cho mỗi trang (HTML) của trang web tĩnh của bạn.

Tôi có thể tạo trang web tĩnh bằng React không?

Đó là lý do tại sao ngày nay, nó cũng thường được sử dụng cho các trang web tĩnh. Khả năng kích hoạt các tệp HTML dựng sẵn giúp tăng tốc độ tải của React khiến nó trở thành một khung hoàn hảo để xây dựng SSG . React cho phép tiếp cận chi tiết trong việc thực hiện các thay đổi đối với trang web tĩnh của bạn bằng cách chỉ có thể chỉnh sửa một tệp thành phần.

Lệnh nào được sử dụng để chuyển đổi dự án React thành các tệp CSS & js HTML tĩnh?

Chạy npm build hoặc yarn build , thao tác này sẽ tạo thư mục bản dựng, trong đó bạn có thư mục tĩnh, chỉ mục. html, tất cả các tệp css, js của bạn, tất cả đã được rút gọn, sẵn sàng để triển khai.