ReactJS การแสดงผลฝั่งเซิร์ฟเวอร์เทียบกับการแสดงผลฝั่งไคลเอ็นต์


120

ฉันเพิ่งเริ่มศึกษา ReactJS และพบว่ามี 2 วิธีในการเรนเดอร์เพจ: ฝั่งเซิร์ฟเวอร์และฝั่งไคลเอ็นต์ แต่ฉันไม่เข้าใจวิธีใช้ร่วมกัน มันเป็น 2 วิธีในการสร้างแอปพลิเคชันแยกกันหรือสามารถใช้ร่วมกันได้หรือไม่?

หากเราสามารถใช้ร่วมกันได้จะต้องทำอย่างไร - เราต้องทำซ้ำองค์ประกอบเดียวกันในฝั่งเซิร์ฟเวอร์และฝั่งไคลเอ็นต์หรือไม่? หรือเราสามารถสร้างส่วนคงที่ของแอปพลิเคชันของเราบนเซิร์ฟเวอร์และชิ้นส่วนไดนามิกในฝั่งไคลเอ็นต์โดยไม่ต้องเชื่อมต่อกับฝั่งเซิร์ฟเวอร์ที่แสดงผลล่วงหน้าแล้วได้หรือไม่?


1
คำตอบสั้น ๆ ไม่ - คุณสามารถแยกส่ง html แบบคงที่และเปลี่ยนได้อย่างสมบูรณ์ในการแสดงผลไคลเอนต์ ได้เพิ่มรายละเอียดในคำตอบของฉัน
Kira

คำตอบ:


108

สำหรับให้เว็บไซต์ / เว็บแอพลิเคชันที่คุณสามารถตอบสนองการใช้งานได้ทั้งฝั่งไคลเอ็นต์ , ฝั่งเซิร์ฟเวอร์หรือทั้งสอง

ด้านลูกค้า

ตรงนี้คุณกำลังเรียกใช้ ReactJS บนเบราว์เซอร์อย่างสมบูรณ์ นี่เป็นการตั้งค่าที่ง่ายที่สุดและมีตัวอย่างส่วนใหญ่ (รวมถึงตัวอย่างในhttp://reactjs.org ) HTML เริ่มต้นที่เซิร์ฟเวอร์แสดงผลเป็นตัวยึดตำแหน่งและ UI ทั้งหมดจะแสดงผลในเบราว์เซอร์เมื่อสคริปต์ทั้งหมดของคุณโหลด

ฝั่งเซิร์ฟเวอร์

คิดว่า ReactJS เป็นเครื่องมือสร้างเทมเพลตฝั่งเซิร์ฟเวอร์ที่นี่ (เช่นหยกแฮนด์บาร์ ฯลฯ ... ) HTML ที่แสดงโดยเซิร์ฟเวอร์มี UI ตามที่ควรจะเป็นและคุณไม่ต้องรอให้สคริปต์โหลด หน้าของคุณสามารถสร้างดัชนีได้โดยเครื่องมือค้นหา (หากไม่มีการเรียกใช้งานจาวาสคริปต์)

เนื่องจาก UI แสดงผลบนเซิร์ฟเวอร์ตัวจัดการเหตุการณ์ของคุณจะไม่ทำงานและไม่มีการโต้ตอบ (คุณมีหน้าคงที่)

ทั้งสอง

ที่นี่การเรนเดอร์เริ่มต้นอยู่บนเซิร์ฟเวอร์ ดังนั้น HTML ที่เบราว์เซอร์ได้รับจึงมี UI ตามที่ควรจะเป็น เมื่อโหลดสคริปต์แล้ว DOM เสมือนจะถูกแสดงผลอีกครั้งเพื่อตั้งค่าตัวจัดการเหตุการณ์ของคอมโพเนนต์ของคุณ

ที่นี่คุณต้องตรวจสอบให้แน่ใจว่าคุณได้เรนเดอร์ DOM เสมือน (root ReactJS component) ซ้ำกับpropsที่คุณใช้ในการแสดงผลบนเซิร์ฟเวอร์ มิฉะนั้น ReactJS จะบ่นว่า DOM เสมือนฝั่งเซิร์ฟเวอร์และฝั่งไคลเอ็นต์ไม่ตรงกัน

เนื่องจาก ReactJS แตกต่างระหว่าง DOM เสมือนระหว่างการแสดงผลซ้ำ DOM จริงจึงไม่กลายพันธุ์ เฉพาะตัวจัดการเหตุการณ์เท่านั้นที่ถูกผูกไว้กับองค์ประกอบ DOM จริง


1
ดังนั้นในกรณีของ "ทั้งคู่" ฉันต้องเขียนรหัสเดียวกันสองครั้ง "หนึ่งรหัสสำหรับการแสดงผลเซิร์ฟเวอร์และอีกรหัสหนึ่งในการสร้าง DOM นี้บนไคลเอนต์ใช่ไหม
Simcha

10
คุณต้องเรียกใช้รหัสเดียวกันสองครั้ง เมื่ออยู่บนเซิร์ฟเวอร์และอีกครั้งบนไคลเอนต์ อย่างไรก็ตามคุณต้องเขียนส่วนประกอบของคุณเพื่อคำนึงถึงสิ่งนี้เช่นคุณไม่ควรทำการดึงข้อมูลแบบ async ใด ๆcomponentWillMount()เนื่องจากจะเรียกใช้ทั้งไคลเอนต์และเซิร์ฟเวอร์ คุณจะต้องมีกลยุทธ์ในการดึงข้อมูลล่วงหน้าบนเซิร์ฟเวอร์และทำให้พร้อมใช้งานสำหรับการเรนเดอร์ครั้งแรกบนไคลเอนต์เพื่อให้แน่ใจว่าคุณจะได้รับผลลัพธ์เดียวกัน
Jonny Buchanan

3
คุณยังสามารถตรวจสอบได้ว่าโค้ดที่กำลังเรียกใช้นั้นอยู่บนฝั่งเซิร์ฟเวอร์หรือฝั่งไคลเอ็นต์โดยใช้typeof window == "undefined"จากนั้นดึงข้อมูลของคุณตามนั้น
Gautham Badhrinathan

คุณมีลิงค์ไปยังตัวอย่างที่เหมาะกับการใช้งานของคุณหรือไม่?
Sawtaytoes

1
@IanW โดยทั่วไปในกรณีนี้ HTML ที่เซิร์ฟเวอร์ส่งคืนจะเป็น "กระดูกเปล่า" มากเพียงแค่นำเข้า JavaScript และสไตล์ของคุณและมี<div>React ที่จะเขียนลงไป
Matt Holland

48

แหล่งที่มาของภาพ: บล็อกวิศวกรรมของ Walmart Labs

SSR

ความรับผิดชอบต่อสังคม

หมายเหตุ: SSR (การแสดงผลฝั่งเซิร์ฟเวอร์), CSR (การแสดงผลฝั่งไคลเอ็นต์)

ความแตกต่างหลักคือ SSR เซิร์ฟเวอร์ตอบสนองต่อเบราว์เซอร์ไคลเอนต์รวมถึง HTML ของเพจที่จะแสดงผล สิ่งสำคัญที่ควรทราบคือแม้ว่าด้วย SSR หน้าเว็บจะแสดงผลได้เร็วกว่า หน้านี้จะไม่พร้อมสำหรับการโต้ตอบของผู้ใช้จนกว่าจะดาวน์โหลดไฟล์ JS และเบราว์เซอร์ได้ดำเนินการตอบสนอง

ข้อเสียอย่างหนึ่งคือ SSR TTFB (Time to First Byte) อาจยาวกว่าเล็กน้อย เนื่องจากเซิร์ฟเวอร์ใช้เวลาในการสร้างเอกสาร HTML ซึ่งจะเพิ่มขนาดการตอบสนองของเซิร์ฟเวอร์


4

จริง ๆ แล้วฉันสงสัยว่าการค้นคว้าแบบเดียวกันนี้ค่อนข้างน้อยและในขณะที่คำตอบที่คุณกำลังมองหานั้นได้รับในความคิดเห็น แต่ฉันรู้สึกว่ามันควรจะโดดเด่นกว่านี้ดังนั้นฉันจึงเขียนโพสต์นี้ (ซึ่งฉันจะอัปเดตเมื่อฉันสามารถสร้าง วิธีที่ดีกว่าเมื่อฉันพบวิธีแก้ปัญหาทางสถาปัตยกรรมอย่างน้อยก็น่าสงสัย)

คุณจะต้องเขียนส่วนประกอบของคุณโดยคำนึงถึงทั้งสองวิธีดังนั้นโดยทั่วไปจะวางifสวิตช์ทุกที่เพื่อพิจารณาว่าคุณอยู่บนไคลเอนต์หรือเซิร์ฟเวอร์จากนั้นทำแบบสอบถาม DB (หรือสิ่งที่เหมาะสมบนเซิร์ฟเวอร์) หรือการเรียก REST (บน ลูกค้า) จากนั้นคุณจะต้องเขียนจุดสิ้นสุดที่สร้างข้อมูลของคุณและเปิดเผยต่อไคลเอนต์และไปที่นั่น

อีกครั้งยินดีที่ได้เรียนรู้เกี่ยวกับโซลูชันที่สะอาดกว่า


2

มันเป็น 2 วิธีในการสร้างแอปพลิเคชันแยกกันหรือสามารถใช้ร่วมกันได้หรือไม่?

สามารถใช้ร่วมกันได้

หากเราสามารถใช้ร่วมกันได้จะต้องทำอย่างไร - เราต้องทำซ้ำองค์ประกอบเดียวกันในฝั่งเซิร์ฟเวอร์และฝั่งไคลเอ็นต์หรือไม่? หรือเราสามารถสร้างส่วนคงที่ของแอปพลิเคชันของเราบนเซิร์ฟเวอร์และชิ้นส่วนไดนามิกในฝั่งไคลเอ็นต์โดยไม่ต้องเชื่อมต่อกับฝั่งเซิร์ฟเวอร์ที่แสดงผลล่วงหน้าแล้วได้หรือไม่?

ควรมีการแสดงเค้าโครงเดียวกันเพื่อหลีกเลี่ยงการดำเนินการจัดเรียงใหม่และทาสีใหม่การกะพริบ / กะพริบน้อยลงหน้าของคุณจะราบรื่นขึ้น อย่างไรก็ตามไม่ใช่ข้อ จำกัด คุณสามารถแคช SSR html ได้เป็นอย่างดี (สิ่งที่อิเล็กโทรดทำเพื่อลดเวลาตอบสนอง) / ส่ง html แบบคงที่ซึ่งถูกเขียนทับโดย CSR (การเรนเดอร์ฝั่งไคลเอ็นต์)

หากคุณเพิ่งเริ่มต้นด้วย SSR ฉันขอแนะนำให้เริ่มง่ายๆ SSR สามารถซับซ้อนได้อย่างรวดเร็ว ในการสร้าง html บนเซิร์ฟเวอร์หมายถึงการสูญเสียการเข้าถึงอ็อบเจ็กต์เช่นหน้าต่างเอกสาร (คุณมีสิ่งเหล่านี้ในไคลเอนต์) สูญเสียความสามารถในการรวมการดำเนินการแบบ async (นอกกรอบ) และโดยทั่วไปการแก้ไขโค้ดจำนวนมากเพื่อให้เข้ากันได้กับรหัส SSR ของคุณ ( เนื่องจากคุณจะต้องใช้ webpack เพื่อแพ็ค bundle.js ของคุณ) สิ่งต่างๆเช่นการนำเข้า CSS ต้องการเทียบกับการนำเข้าก็เริ่มกัดคุณ (นี่ไม่ใช่กรณีในแอป React เริ่มต้นที่ไม่มี webpack)

รูปแบบทั่วไปของ SSR มีลักษณะดังนี้ เซิร์ฟเวอร์ Express ที่ให้บริการคำขอ:

const app = Express();
const port = 8092;

// This is fired every time the server side receives a request
app.use(handleRender);
function handleRender(req, res) {
    const fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl;
    console.log('fullUrl: ', fullUrl);
    console.log('req.url: ', req.url);

    // Create a new Redux store instance
    const store = createStore(reducerFn);

    const urlToRender = req.url;
    // Render the component to a string
    const html = renderToString(
        <Provider store={store}>
            <StaticRouter location={urlToRender} context={{}}>
                {routes}
            </StaticRouter>
        </Provider>
    );
    const helmet = Helmet.renderStatic();

    // Grab the initial state from our Redux store
    const preloadedState = store.getState();

    // Send the rendered page back to the client
    res.send(renderFullPage(helmet, html, preloadedState));
}

ข้อเสนอแนะของฉันสำหรับคนที่เริ่มต้นด้วย SSR คือการให้บริการ HTML แบบคงที่ คุณสามารถรับ html แบบคงที่ได้โดยเรียกใช้แอป CSR SPA:

document.getElementById('root').innerHTML

อย่าลืมเหตุผลเดียวในการใช้ SSR ควรเป็น:

  1. SEO
  2. โหลดเร็วขึ้น (ฉันจะลดราคานี้)

แฮ็ค: https://medium.com/@gagan_goku/react-and-server-side-rendering-ssr-444d8c48abfc

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.