URL ตอบกลับเราเตอร์จะไม่ทำงานเมื่อรีเฟรชหรือเขียนด้วยตนเอง


653

ฉันใช้ React-router และทำงานได้ดีในขณะที่ฉันคลิกที่ปุ่มลิงค์ แต่เมื่อฉันรีเฟรชหน้าเว็บของฉันมันไม่โหลดสิ่งที่ฉันต้องการ

ตัวอย่างเช่นฉันอยู่localhost/joblistและทุกอย่างดีเพราะฉันมาถึงที่นี่กดลิงค์ แต่ถ้าฉันรีเฟรชหน้าเว็บฉันจะได้รับ:

Cannot GET /joblist

โดยค่าเริ่มต้นมันไม่ทำงานเช่นนี้ ตอนแรกผมมี URL ของฉันเป็นlocalhost/#/และlocalhost/#/joblistและพวกเขาทำงานอย่างสมบูรณ์ดี แต่ฉันไม่ชอบ URL ประเภทนี้ดังนั้นพยายามที่จะลบมัน#ฉันเขียน:

Router.run(routes, Router.HistoryLocation, function (Handler) {
 React.render(<Handler/>, document.body);
});

ปัญหานี้ไม่ได้เกิดขึ้นlocalhost/สิ่งนี้กลับมาในสิ่งที่ฉันต้องการเสมอ

แก้ไข:แอพนี้เป็นหน้าเดียวดังนั้น/joblistไม่จำเป็นต้องถามอะไรเลยกับเซิร์ฟเวอร์ใด ๆ

EDIT2:เราเตอร์ทั้งหมดของฉัน

var routes = (
    <Route name="app" path="/" handler={App}>
        <Route name="joblist" path="/joblist" handler={JobList}/>
        <DefaultRoute handler={Dashboard}/>
        <NotFoundRoute handler={NotFound}/>
    </Route>
);

Router.run(routes, Router.HistoryLocation, function (Handler) {
  React.render(<Handler/>, document.body);
});

นอกจากว่าคุณจะใช้ htaccess เพื่อโหลดหน้าทัวร์หลักของคุณและบอกให้เราเตอร์ของคุณใช้ location.pathname มันจะไม่ทำงาน ..
Charles John Thompson III

คุณลบ#สัญลักษณ์นั้นได้อย่างไร ขอบคุณ!
SudoPlz

4
หากคุณมีพื้นที่ตอบสนองของคุณแอปในถัง S3 index.htmlคุณก็สามารถตั้งค่าเอกสารข้อผิดพลาด สิ่งนี้จะทำให้แน่ใจว่าindex.htmlถูกตีไม่ว่าจะเกิดอะไรขึ้น
เทรเวอร์ฮัตโต

ในกรณีของฉันมันใช้งานได้ดีใน windows แต่ไม่ใช่ใน linux
Ejaz Karim

นี่คือข้อมูลอ้างอิงที่ช่วยแก้ปัญหาของฉัน: github.com/facebook/create-react-app/blob/master/packages/…
jimbotron

คำตอบ:


1094

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

ฝั่งเซิร์ฟเวอร์กับฝั่งไคลเอ็นต์

สิ่งใหญ่แรกที่ต้องทำความเข้าใจเกี่ยวกับเรื่องนี้คือตอนนี้มี 2 แห่งที่ตีความ URL ในขณะที่เคยมีเพียง 1 ใน 'วันเก่า' ในอดีตเมื่อชีวิตเรียบง่ายผู้ใช้บางคนส่งคำขอhttp://example.com/aboutไปยังเซิร์ฟเวอร์ซึ่งตรวจสอบส่วนเส้นทางของ URL กำหนดว่าผู้ใช้ร้องขอหน้าเกี่ยวกับแล้วส่งกลับหน้านั้น

ด้วยการกำหนดเส้นทางฝั่งไคลเอ็นต์ซึ่งเป็นสิ่งที่ React-Router จัดให้มีสิ่งต่าง ๆ เรียบง่ายน้อยลง ตอนแรกลูกค้ายังไม่ได้โหลดรหัส JS ใด ๆ ดังนั้นการร้องขอแรกจะไปที่เซิร์ฟเวอร์เสมอ จากนั้นจะส่งคืนหน้าที่มีแท็กสคริปต์ที่จำเป็นในการโหลด React และ React Router เป็นต้นเฉพาะเมื่อสคริปต์เหล่านั้นโหลดแล้วจะเริ่มเฟส 2 ในเฟส 2 เมื่อผู้ใช้คลิกที่ลิงก์การนำทาง 'เกี่ยวกับเรา' URL จะถูกเปลี่ยนเฉพาะภายในเป็นhttp://example.com/about(ทำโดยAPI ประวัติ ) ที่เป็นไปได้แต่ไม่มีการร้องขอไปยังเซิร์ฟเวอร์. แต่เราท์เตอร์ React จะทำสิ่งนั้นในฝั่งไคลเอ็นต์พิจารณาว่ามุมมอง React ใดที่จะเรนเดอร์และแสดงผล สมมติว่าหน้าเกี่ยวกับของคุณไม่จำเป็นต้องทำการเรียกใช้ REST ใด ๆ ก็เสร็จสิ้นแล้ว คุณได้เปลี่ยนจากหน้าแรกเป็นเกี่ยวกับเราโดยไม่มีการร้องขอจากเซิร์ฟเวอร์

ดังนั้นโดยทั่วไปเมื่อคุณคลิกลิงค์บาง Javascript วิ่งปรุงแต่งว่า URL ในแถบที่อยู่ที่ไม่ก่อให้เกิดการฟื้นฟูหน้าซึ่งจะทำให้เกิดการตอบสนอง Router เพื่อดำเนินการเปลี่ยนแปลงหน้าบนฝั่งไคลเอ็นต์

แต่ตอนนี้ให้พิจารณาว่าจะเกิดอะไรขึ้นหากคุณคัดลอกวาง URL ในแถบที่อยู่และส่งอีเมลถึงเพื่อน เพื่อนของคุณยังไม่ได้โหลดเว็บไซต์ของคุณ ในคำอื่น ๆ ที่เธอยังอยู่ในขั้นตอนที่ 1 ยังไม่มีเราเตอร์ที่ทำงานอยู่ในเครื่องของเธอ ดังนั้นเบราว์เซอร์ของเธอจะทำให้คำขอเซิร์ฟเวอร์http://example.com/aboutไป

และนี่คือที่ที่ปัญหาของคุณเริ่มต้นขึ้น จนถึงตอนนี้คุณสามารถหลีกเลี่ยงได้ด้วยการวาง HTML คงที่ไว้ที่ webroot ของเซิร์ฟเวอร์ของคุณ แต่ที่จะให้404ข้อผิดพลาดสำหรับ URL อื่น ๆ ทั้งหมดเมื่อมีการร้องขอจากเซิร์ฟเวอร์ URL เดียวกันเหล่านั้นทำงานได้ดีในฝั่งไคลเอ็นต์เนื่องจากมี React Router กำลังทำการกำหนดเส้นทางให้คุณ แต่URL นั้นล้มเหลวที่ฝั่งเซิร์ฟเวอร์เว้นแต่คุณจะทำให้เซิร์ฟเวอร์ของคุณเข้าใจ

รวมการกำหนดเส้นทางเซิร์ฟเวอร์และฝั่งไคลเอ็นต์

หากคุณต้องการให้http://example.com/aboutURL ทำงานได้ทั้งบนเซิร์ฟเวอร์และฝั่งไคลเอ็นต์คุณต้องตั้งค่าเส้นทางสำหรับทั้งบนฝั่งเซิร์ฟเวอร์และฝั่งไคลเอ็นต์ เหมาะสมหรือไม่

และนี่คือที่ตัวเลือกของคุณเริ่มต้น โซลูชันมีตั้งแต่การข้ามปัญหาไปพร้อมกันผ่านเส้นทาง catch-all ที่ส่งคืน bootstrap HTML ไปยังวิธี isomorphic แบบเต็มซึ่งเซิร์ฟเวอร์และไคลเอนต์ใช้รหัส JS เดียวกัน

.

การข้ามปัญหาไปพร้อมกัน: ประวัติแฮช

ด้วยHash Historyแทนที่จะเป็นBrowser History URL ของคุณสำหรับหน้าเกี่ยวกับจะมีลักษณะดังนี้: http://example.com/#/about ส่วนหลัง#สัญลักษณ์hash ( ) จะไม่ถูกส่งไปยังเซิร์ฟเวอร์ ดังนั้นเซิร์ฟเวอร์จะเห็นhttp://example.com/และส่งหน้าดัชนีตามที่คาดไว้เท่านั้น React-Router จะหยิบชิ้น#/aboutส่วนขึ้นมาและแสดงหน้าที่ถูกต้อง

ข้อเสีย :

  • URL 'น่าเกลียด'
  • การแสดงภาพฝั่งเซิร์ฟเวอร์ไม่สามารถทำได้ด้วยวิธีการนี้ เท่าที่เกี่ยวข้องกับ Search Engine Optimization (SEO) เว็บไซต์ของคุณประกอบด้วยหน้าเดียวที่แทบไม่มีเนื้อหาใด ๆ

.

จับทั้งหมด

ด้วยวิธีการนี้คุณจะใช้ประวัติเบราว์เซอร์ แต่เพียงตั้งค่า catch-all บนเซิร์ฟเวอร์ที่ส่ง/*ไปindex.htmlให้คุณได้อย่างมีประสิทธิภาพเหมือนกับสถานการณ์ประวัติแฮช อย่างไรก็ตามคุณมี URL ที่สะอาดและคุณสามารถปรับปรุงรูปแบบนี้ได้ในภายหลังโดยไม่ต้องทำให้รายการโปรดของผู้ใช้ทั้งหมดไม่ถูกต้อง

ข้อเสีย :

  • การตั้งค่าที่ซับซ้อนมากขึ้น
  • ยังไม่มี SEO ที่ดี

.

เป็นลูกผสม

ในวิธีไฮบริดคุณขยายตามสถานการณ์ catch-all โดยการเพิ่มสคริปต์เฉพาะสำหรับเส้นทางที่เฉพาะเจาะจง คุณสามารถสร้างสคริปต์ PHP แบบง่าย ๆ เพื่อส่งคืนหน้าที่สำคัญที่สุดของไซต์ของคุณด้วยเนื้อหาที่รวมอยู่ดังนั้น Googlebot สามารถดูว่ามีอะไรอยู่บนหน้าของคุณ

ข้อเสีย :

  • ซับซ้อนยิ่งขึ้นในการตั้งค่า
  • SEO ที่ดีสำหรับเส้นทางเหล่านั้นที่คุณให้การดูแลเป็นพิเศษ
  • รหัสซ้ำสำหรับการแสดงผลเนื้อหาบนเซิร์ฟเวอร์และไคลเอนต์

.

ซึ่งมีรูปร่างสัณฐานเหมือนกัน

เกิดอะไรขึ้นถ้าเราใช้โหนด JS เป็นเซิร์ฟเวอร์ของเราเพื่อให้เราสามารถทำงานเดียวกันรหัส JS ที่ปลายทั้งสอง? ตอนนี้เรามีเส้นทางทั้งหมดที่กำหนดไว้ในการกำหนดค่า react-router เดียวและเราไม่จำเป็นต้องทำซ้ำรหัสการเรนเดอร์ของเรา นี่คือ 'จอกศักดิ์สิทธิ์' เพื่อที่จะพูด เซิร์ฟเวอร์ส่งมาร์กอัปเดียวกันกับที่เราจะได้รับหากการเปลี่ยนหน้าเกิดขึ้นกับลูกค้า โซลูชันนี้เหมาะสมที่สุดในแง่ของ SEO

ข้อเสีย :

  • เซิร์ฟเวอร์ต้อง (สามารถ) เรียกใช้ JS ฉันทดลองกับ Java icw Nashorn แต่มันไม่ได้ผลสำหรับฉัน ในทางปฏิบัติมันส่วนใหญ่หมายความว่าคุณต้องใช้เซิร์ฟเวอร์ที่ใช้ Node JS
  • ปัญหาสิ่งแวดล้อมที่ยุ่งยากมากมาย (ใช้windowกับฝั่งเซิร์ฟเวอร์ ฯลฯ )
  • โค้งการเรียนรู้ที่สูงชัน

.

ฉันควรใช้แบบไหน

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

โดยพื้นฐานแล้วสำหรับฉันนั่นจะเป็นปัจจัยในการตัดสินใจ ถ้าเซิร์ฟเวอร์ของฉันทำงานบน Node JS ฉันจะใช้ isomorphic ไม่เช่นนั้นฉันจะเลือกใช้โซลูชัน Catch-all และขยายให้มากขึ้น (โซลูชัน Hybrid) เมื่อเวลาผ่านไปและความต้องการ SEO ต้องการ

หากคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับการแสดงผล isomorphic (หรือที่เรียกว่า 'สากล') ด้วย React มีบทช่วยสอนที่ดีในหัวข้อ:

นอกจากนี้เพื่อให้คุณเริ่มต้นฉันขอแนะนำให้ดูชุดเริ่มต้นบางอย่าง เลือกอันที่ตรงกับตัวเลือกของคุณสำหรับสแต็คเทคโนโลยี (โปรดจำไว้ว่า React เป็นเพียง V ใน MVC คุณต้องการสิ่งเพิ่มเติมเพื่อสร้างแอปแบบเต็ม) เริ่มต้นด้วยการดูที่เผยแพร่โดย Facebook เอง:

หรือเลือกหนึ่งในหลาย ๆ แห่งโดยชุมชน มีเว็บไซต์ที่ดีในขณะนี้ที่พยายามจัดทำดัชนีทั้งหมด:

ฉันเริ่มต้นด้วยสิ่งเหล่านี้:

ขณะนี้ฉันกำลังใช้การเรนเดอร์สากลที่ให้แรงบันดาลใจจากชุดเลขหมายสองชุดด้านบน แต่ตอนนี้มันล้าสมัยแล้ว

ขอให้โชคดีกับภารกิจของคุณ!


2
Great post Stijn! คุณอยากแนะนำให้ใช้ชุดเริ่มต้นสำหรับแอป Isomorphic react หรือไม่? ถ้าเป็นเช่นนั้นคุณช่วยยกตัวอย่างคนที่คุณชอบได้หรือไม่?
Chris

1
@ Paulos3000 ขึ้นอยู่กับเซิร์ฟเวอร์ที่คุณใช้ โดยทั่วไปคุณกำหนดเส้นทาง/*และทำให้มันตอบสนองกับหน้า HTML ของคุณ สิ่งที่ยุ่งยากคือที่นี่เพื่อให้แน่ใจว่าคุณไม่ได้ขัดขวางการร้องขอไฟล์. js และ. css ด้วยเส้นทางนี้
Stijn de Witt

2
@ Paulos3000 ดูที่นี่สำหรับคำถามที่เกี่ยวข้องบางอย่างสำหรับ Apache / PHP , สำหรับเอ็กซ์เพรส / js , สำหรับ J2E
Stijn de Witt

1
สวัสดีฉันกำลังลองใช้วิธีแฮช แต่ก็ไม่มีโชค รับcreateMemoryHistory is not a functionข้อผิดพลาด ใจดูหรือไม่ stackoverflow.com/questions/45002573/…
Leon Gaban

5
@LeonGaban ดูเหมือนว่าตั้งแต่คำตอบนี้ถูกเขียนขึ้น React Router ได้เปลี่ยนการใช้งานของพวกเขา ตอนนี้พวกเขามีอินสแตนซ์เราเตอร์ที่แตกต่างกันสำหรับประวัติที่แตกต่างกันและพวกเขาทำการกำหนดค่าประวัติในพื้นหลัง หลักการพื้นฐานยังคงเหมือนเดิม
Stijn de Witt

115

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

devServer: {
   historyApiFallback: true,
   contentBase: './',
   hot: true
},

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

แก้ไข: ดูคำตอบของฉันที่นี่เพื่อดูรายละเอียดเพิ่มเติมว่าทำไมจึงมีความจำเป็น: https://stackoverflow.com/a/37622953/5217568


16
โปรดทราบว่าทีมงาน Webpack แนะนำไม่ให้ใช้เซิร์ฟเวอร์ dev ในการผลิต
Stijn de Witt

5
เพื่อการพัฒนาทั่วไปนี่เป็นทางออกที่ดีที่สุด historyApiFallbackเพียงพอแล้ว สำหรับทุกตัวเลือกอื่น ๆ ก็ยังสามารถตั้งค่าจาก CLI --history-api-fallbackกับธง
Marco Lazzeri

2
@ Kunok มันไม่ได้ นี่คือการแก้ไขด่วนสำหรับการพัฒนา แต่คุณยังคงต้องคิดอะไรบางอย่างสำหรับการผลิต
Stijn de Witt

contentBase: ': /' ทำให้ไฟล์แอปของคุณสามารถเข้าถึงได้จาก url
Nezih

85

คุณสามารถเปลี่ยน.htaccessไฟล์ของคุณและแทรกสิ่งนี้:

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_FILENAME} !-l
  RewriteRule . /index.html [L]
</IfModule>

ฉันกำลังใช้react: "^16.12.0"และreact-router: "^5.1.2" วิธีนี้คือ Catch-all และอาจเป็นวิธีที่ง่ายที่สุดในการเริ่มต้นใช้งาน


3
มันใช้งานได้ดี! และวิธีที่ง่ายที่สุดถ้าคุณไม่ต้องการปรับโครงสร้างแอปของคุณ
mhyassin

7
อย่าลืม RewriteEngine On เป็นบรรทัดแรก
Kai Qing

3
หมายเหตุคำตอบนี้อ้างถึงการกำหนดค่าบนเซิร์ฟเวอร์ Apache นี่ไม่ใช่ส่วนหนึ่งของแอป React
Colton Hicks

ฉันไม่เข้าใจคำตอบข้างต้น ไม่มีผู้สอนคนใดบอกว่าลิงก์ของฉันไม่ทำงานเลย อย่างไรก็ตามไฟล์ htaccess อย่างง่ายนี้ใช้งานได้ ขอบคุณ
Thomas Williams

สิ่งนี้ใช้ได้กับการส่งออกคงที่ของ next.js เช่นกัน เนื่องจาก tomcat ไม่คล้ายกับโหนดเราต้องการการกำหนดค่าเพิ่มเติมนี้สำหรับการส่งออกสแตติก next.js
giri-jeedigunta

62

สำหรับผู้ใช้เราท์เตอร์ V4 แบบโต้ตอบ :

หากคุณพยายามที่จะแก้ปัญหานี้ด้วยเทคนิคประวัติแฮชที่กล่าวถึงในคำตอบอื่น ๆ โปรดทราบว่า

<Router history={hashHistory} >

ไม่ทำงานใน V4 โปรดใช้HashRouterแทน:

import { HashRouter } from 'react-router-dom'

<HashRouter>
  <App/>
</HashRouter>

การอ้างอิง: HashRouter


คุณสามารถให้รายละเอียดเกี่ยวกับสาเหตุที่ HashRouter แก้ไขปัญหานี้ได้หรือไม่? ลิงก์ที่คุณระบุไม่ได้อธิบายสำหรับฉัน นอกจากนี้ยังมีวิธีซ่อนแฮชในเส้นทางหรือไม่ ผมใช้ BrowserRouter แต่วิ่งเข้าไปในปัญหานี้ 404 กับมัน ..
เอียนสมิ ธ

29

เราเตอร์สามารถเรียกได้สองวิธีขึ้นอยู่กับว่าการนำทางเกิดขึ้นบนไคลเอนต์หรือบนเซิร์ฟเวอร์ คุณได้กำหนดค่าไว้สำหรับการทำงานด้านไคลเอนต์ พารามิเตอร์ที่สำคัญคือหนึ่งในสองของวิธีการเรียกใช้สถานที่

เมื่อคุณใช้ส่วนประกอบ React Router Link มันจะบล็อกการนำทางเบราว์เซอร์และการเรียกการเปลี่ยนเส้นทางเพื่อทำการนำทางฝั่งไคลเอ็นต์ คุณกำลังใช้ HistoryLocation ดังนั้นจึงใช้ API ประวัติ HTML5 เพื่อทำให้การแสดงภาพเป็นการนำทางโดยการจำลอง URL ใหม่ในแถบที่อยู่ หากคุณใช้เบราว์เซอร์รุ่นเก่าจะไม่สามารถใช้งานได้ คุณจะต้องใช้องค์ประกอบ HashLocation

เมื่อคุณกดรีเฟรชคุณจะข้ามรหัส React และ React Router ทั้งหมด เซิร์ฟเวอร์ได้รับคำขอ/joblistและจะต้องส่งคืนบางสิ่ง บนเซิร์ฟเวอร์คุณต้องผ่านเส้นทางที่ถูกร้องขอไปยังrunวิธีการเพื่อให้มันแสดงผลมุมมองที่ถูกต้อง คุณสามารถใช้แผนที่เส้นทางเดียวกันได้ แต่คุณอาจต้องใช้การโทรRouter.runอื่น เมื่อ Charles ชี้ให้เห็นคุณสามารถใช้การเขียน URL ใหม่เพื่อจัดการสิ่งนี้ ตัวเลือกอื่นคือการใช้เซิร์ฟเวอร์ node.js เพื่อจัดการการร้องขอทั้งหมดและส่งค่าเส้นทางเป็นอาร์กิวเมนต์ตำแหน่ง

ยกตัวอย่างเช่นมันอาจมีลักษณะเช่นนี้:

var app = express();

app.get('*', function (req, res) { // This wildcard method handles all requests

    Router.run(routes, req.path, function (Handler, state) {
        var element = React.createElement(Handler);
        var html = React.renderToString(element);
        res.render('main', { content: html });
    });
});

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


2
ขอโทษคุณช่วยอธิบายคำสั่งต่อไปได้มั้ย "เมื่อคุณกดรีเฟรชคุณข้ามรหัส React และ React Router ทั้งหมด" ทำไมมันเกิดขึ้น
VB_

เราเตอร์ปฏิกิริยาปกติจะใช้เพื่อจัดการ 'เส้นทาง' ที่แตกต่างกันภายในเบราว์เซอร์เท่านั้น มีวิธีการทั่วไปสองวิธีดังนี้: เส้นทางแฮชแบบเก่าและ API ประวัติที่ใหม่กว่า การรีเฟรชเบราว์เซอร์จะทำการร้องขอเซิร์ฟเวอร์ซึ่งจะข้ามรหัสเราเตอร์ตอบกลับฝั่งไคลเอ็นต์ของคุณ คุณจะต้องจัดการกับเส้นทางบนเซิร์ฟเวอร์ (แต่เฉพาะในกรณีที่คุณใช้ API ประวัติ) คุณสามารถใช้ react router สำหรับสิ่งนี้ แต่คุณจะต้องทำสิ่งที่คล้ายกับที่ฉันอธิบายไว้ข้างต้น
ทอดด์

เข้าใจแล้ว แต่จะเป็นอย่างไรถ้าเซิร์ฟเวอร์ใช้ภาษาที่ไม่ใช่ JS (สมมุติว่า Java)
VB_

2
ขอโทษ…คุณหมายความว่าคุณต้องใช้เซิร์ฟเวอร์ด่วนเพื่อแสดงเส้นทางที่เป็น "ไม่รูท"? ฉันประหลาดใจเป็นครั้งแรกที่คำจำกัดความของ isomorphism ไม่ได้รวมถึงการดึงข้อมูลในแนวคิดของมัน (สิ่งต่าง ๆ มีความซับซ้อนอย่างยิ่งถ้าคุณต้องการที่จะแสดงมุมมองกับเซิร์ฟเวอร์ข้อมูลแม้ว่ามันจะเป็นความต้องการ SEO ที่เห็นได้ชัด ตอนนี้ฉันชอบ "WTF ตอบสนอง" อย่างจริงจัง ... แก้ไขให้ฉันถ้าฉันผิด Ember / angular / backbone ต้องการสิ่งที่เซิร์ฟเวอร์แสดงเส้นทางหรือไม่? ฉันไม่เข้าใจข้อกำหนดที่ป่องๆนี้เพื่อใช้เส้นทาง
เบ็น

1
หมายความว่าการรีเฟรชใน react-router ไม่ทำงานหากแอปพลิเคชันตอบโต้ของฉันไม่ได้ isomorphic?
แมตต์

28

ใน index.html ของคุณheadเพิ่มต่อไปนี้:

<base href="/">
<!-- This must come before the css and javascripts -->

จากนั้นเมื่อทำงานกับเซิร์ฟเวอร์ webpack dev ให้ใช้คำสั่งนี้

webpack-dev-server --mode development --hot --inline --content-base=dist --history-api-fallback

--history-api-fallback เป็นส่วนสำคัญ


มันใช้งานได้ดีมาก! ลิงก์ใด ๆ เพื่ออ่านเพิ่มเติมเกี่ยวกับวิธีที่คุณได้รับ / พบวิธีแก้ปัญหานั้น?
justDan

1
ขออภัยพี่ชาย พบได้โดยใช้เทคนิคการทดลองและข้อผิดพลาด
Efe Ariaroo

เป็นมูลค่าการกล่าวขวัญว่าถ้าคุณใช้ href ฐานที่ไม่ได้/แล้วHashRouterจะไม่ทำงาน (ถ้าคุณกำลังใช้เส้นทางแฮช)
Robbie Averill

แท็ก <base href = "/"> ทำเพื่อฉัน ขอบคุณสำหรับสิ่งนี้. เซิร์ฟเวอร์ Webpack dev พยายามดึงบันเดิลจากพา ธ / <bundle> และล้มเหลว
Malisbad

27

ฉันใช้ create-react-app เพื่อสร้างเว็บไซต์ในตอนนี้และมีปัญหาเดียวกันที่นำเสนอที่นี่ ฉันใช้BrowserRoutingจากreact-router-domแพ็คเกจ ฉันกำลังทำงานบนเซิร์ฟเวอร์ Nginx และสิ่งที่แก้ไขได้สำหรับฉันคือการเพิ่มสิ่งต่อไปนี้/etc/nginx/yourconfig.conf

location / {
  if (!-e $request_filename){
    rewrite ^(.*)$ /index.html break;
  }
}

ซึ่งสอดคล้องกับการเพิ่มรายการต่อไปนี้.htaccessในกรณีที่คุณใช้งาน Appache

Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]

นี่เป็นวิธีการแก้ปัญหาที่ Facebook แนะนำและสามารถพบได้ที่นี่


1
ว้าวนี่เป็นวิธีง่ายๆสำหรับ nginx; ใช้งานได้อย่างมีเสน่ห์เหมือนผู้ใช้ nginx ใหม่ เพียงคัดลอกวางและดีที่จะไป +1 สำหรับการเชื่อมโยงไปยังเอกสาร fb อย่างเป็นทางการ ทางที่จะไป.
3773048

สำหรับบันทึก: ฉันใช้ AWS อินสแตนซ์กับ nginx และแอพเชิงมุม วิธีนี้ใช้ได้ผล
Diego Sarmiento

คุณช่วยฉัน ฉันพยายามแก้ไขปัญหาของฉันเมื่อวานนี้ฉันเหนื่อยกับการแก้ปัญหา แต่ฉันล้มเหลว ในที่สุดคุณก็ช่วยฉันแก้ปัญหาเราเตอร์นี้
อ้า

มีอะไรที่จำเป็นสำหรับ "react": "^ 16.8.6", "react-router": "^ 5.0.1" หรือไม่? ฉันลองวิธีแก้ปัญหาเหล่านี้ (ทั้ง nginx และ apache) และพวกเขาไม่ทำงาน
Mark A. Tagliaferro

เนื่องจาก Facebook ไม่ได้เปลี่ยนเอกสารเกี่ยวกับสิ่งนี้ฉันจึงสามารถสรุปได้ว่าขั้นตอนนี้เหมือนกัน ฉันยังไม่ได้ลองสักพัก
Aidin

17

วิธีนี้สามารถแก้ปัญหาของคุณได้

ฉันยังประสบปัญหาเดียวกันในแอปพลิเคชัน ReactJS ในโหมดการผลิต นี่คือทางออกที่ 2 ของปัญหา

1. เปลี่ยนประวัติเส้นทางไปที่ "hashHistory" แทน browserHistory แทน

<Router history={hashHistory} >
   <Route path="/home" component={Home} />
   <Route path="/aboutus" component={AboutUs} />
</Router>

ตอนนี้สร้างแอปโดยใช้คำสั่ง

sudo npm run build

จากนั้นวางโฟลเดอร์ build ใน var / www / โฟลเดอร์ของคุณตอนนี้แอปพลิเคชันทำงานได้ดีโดยเพิ่ม # tag ในแต่ละ URL ชอบ

localhost / # / home localhost / # / aboutus

โซลูชันที่ 2: ไม่มี # tag โดยใช้ browserHistory

ตั้งค่าประวัติของคุณ = {browserHistory} ในเราเตอร์ของคุณตอนนี้สร้างมันโดยใช้ sudo npm run build

คุณต้องสร้างไฟล์ "conf" เพื่อแก้ไขหน้า 404 ที่ไม่พบหน้าไฟล์ conf ควรเป็นเช่นนี้

เปิดเทอร์มินัลของคุณพิมพ์คำสั่งด้านล่าง

cd / etc / apache2 / sites-available ls nano sample.conf เพิ่มเนื้อหาด้านล่างลงไป

<VirtualHost *:80>
    ServerAdmin admin@0.0.0.0
    ServerName 0.0.0.0
    ServerAlias 0.0.0.0
    DocumentRoot /var/www/html/

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
    <Directory "/var/www/html/">
            Options Indexes FollowSymLinks
            AllowOverride all
            Require all granted
    </Directory>
</VirtualHost>

ตอนนี้คุณต้องเปิดใช้งานไฟล์ sample.conf โดยใช้คำสั่งต่อไปนี้

cd /etc/apache2/sites-available
sudo a2ensite sample.conf

จากนั้นมันจะขอให้คุณรีโหลดเซิร์ฟเวอร์ apache โดยใช้ sudo service apache2 reload หรือรีสตาร์ท

จากนั้นเปิดโฟลเดอร์ localhost / build ของคุณและเพิ่มไฟล์. htaccess ที่มีเนื้อหาด้านล่าง

   RewriteEngine On
   RewriteBase /
   RewriteCond %{REQUEST_FILENAME} !-f
   RewriteCond %{REQUEST_FILENAME} !-d
   RewriteCond %{REQUEST_FILENAME} !-l
   RewriteRule ^.*$ / [L,QSA]

ตอนนี้แอพทำงานได้ตามปกติ

หมายเหตุ: เปลี่ยน 0.0.0.0 ip เป็นที่อยู่ IP ท้องถิ่นของคุณ

หากมีข้อสงสัยเกี่ยวกับเรื่องนี้อย่าลังเลที่จะแสดงความคิดเห็น

ฉันหวังว่ามันจะเป็นประโยชน์กับคนอื่น ๆ


วิธีแก้ปัญหาแรกจะใช้งานได้"react-router-dom": "^5.1.2"หรือไม่ฉันกำลังใช้<BrowserRouter>
151291

16

หากคุณกำลังโฮสต์แอปตอบโต้ผ่าน AWS Static S3 Hosting & CloudFront

ปัญหานี้นำเสนอตัวเองโดย CloudFront ตอบกลับด้วย 403 Access Denied message เพราะคาดว่า / บาง / อื่น ๆ / เส้นทางจะอยู่ในโฟลเดอร์ S3 ของฉัน แต่เส้นทางนั้นมีอยู่ภายในเฉพาะในเส้นทางของ React กับ react-router

วิธีแก้ไขคือการตั้งค่ากฎหน้าข้อผิดพลาดการแจกจ่าย ไปที่การตั้งค่า CloudFront และเลือกการกระจายของคุณ ถัดไปไปที่แท็บ "หน้าผิดพลาด" คลิก "สร้างการตอบกลับข้อผิดพลาดที่กำหนดเอง" และเพิ่มรายการสำหรับ 403 เนื่องจากเป็นรหัสสถานะข้อผิดพลาดที่เราได้รับ ตั้งค่า Path Page การตอบสนองเป็น /index.html และรหัสสถานะเป็น 200 ผลลัพธ์สุดท้ายทำให้ฉันประหลาดใจด้วยความเรียบง่าย หน้าดัชนีให้บริการ แต่ URL จะถูกเก็บไว้ในเบราว์เซอร์ดังนั้นเมื่อแอปตอบสนองโหลดมันจะตรวจจับเส้นทาง URL และนำทางไปยังเส้นทางที่ต้องการ

ข้อผิดพลาดหน้า 403 กฎ


1
นี่คือสิ่งที่ฉันต้องการ ขอบคุณ.
Jonathan

.. .. URL ทั้งหมดของคุณใช้งานได้ แต่ส่งคืนรหัสสถานะ 403 มันไม่ถูกต้อง รหัสสถานะที่คาดหวังควรอยู่ในช่วง 2xx
Stijn de Witt

@StijndeWitt ฉันไม่แน่ใจว่าคุณเข้าใจถูกต้อง CloudFront จะจัดการทุกอย่าง - ลูกค้าจะไม่เห็นรหัสสถานะ 403 ใด ๆ การจัดเส้นทางใหม่เกิดขึ้นที่ฝั่งเซิร์ฟเวอร์แสดงหน้าดัชนีรักษา URL และจัดเส้นทางที่ถูกต้องตามผลลัพธ์
th3morg

@ th3morg อ่าใช่ฉันเห็นแล้ว ฉันพลาดว่ามันยังช่วยให้คุณกรอกรหัสสถานะการตอบสนองเป็น 200 ดังนั้นโดยทั่วไปคุณกำลังทำแผนที่สถานะ 403 ถึงสถานะ 200 ... ดูดี ... ยกเว้นว่าบางทีถ้าคุณได้รับ 403 เนื่องจากเหตุผลอื่น คุณจะไม่สามารถดูได้เนื่องจากสิ่งนี้
Stijn de Witt

1
ขอบคุณสำหรับ @th3morg นี้ การตั้งค่านี้ใช้กับเส้นทางหลายระดับได้หรือไม่ มันใช้งานได้ดีสำหรับฉันถ้าเส้นทางใด ๆ ที่ระดับรูทเช่นโดเมน / การลงทะเบียนโดเมน / การเข้าสู่ระบบโดเมน / <documentId> แต่มันไม่สามารถใช้ได้กับเส้นทางหลายระดับเช่นโดเมน / เอกสาร / <documentId>
Pargles

15

Webpack Dev Server มีตัวเลือกให้เปิดใช้งานสิ่งนี้ เปิดขึ้นและเพิ่มpackage.json --history-api-fallbackโซลูชั่นนี้ใช้ได้สำหรับฉัน

ตอบสนองเตอร์-กวดวิชา


3
ไม่เป็นไรwebpack-dev-serverแต่ฉันจะแก้ไขสิ่งนี้สำหรับการสร้างการผลิตได้อย่างไร
ฮุสเซน

12

หากคุณโฮสต์แอปตอบโต้ของคุณบน IIS เพียงเพิ่มไฟล์ web.config ที่มี:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <httpErrors errorMode="Custom" existingResponse="Replace">
        <remove statusCode="404" subStatusCode="-1" />
        <error statusCode="404" path="/" responseMode="ExecuteURL" />
    </httpErrors>
  </system.webServer>
</configuration>

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


ขอบคุณครับ !!!! มันใช้งานได้!
Thanh Bao

12

เพิ่มไปที่webpack.config.js:

devServer: {
    historyApiFallback: true
}

นั่นยอดเยี่ยมสำหรับ dev แต่ไม่ได้ช่วยในการสร้างงานสร้าง
KFunk

11

กองการผลิต: ทำปฏิกิริยา, ทำปฏิกิริยาเราเตอร์ v4, BrowswerRouter, Express, Nginx

1) User BrowserRouter สำหรับ URL ที่ค่อนข้างสวย

// app.js

import { BrowserRouter as Router } from 'react-router-dom'

const App = () {
  render() {
    return (
        <Router>
           // your routes here
        </Router>
    )
  }
}

2) เพิ่ม index.html ไปยังคำขอที่ไม่รู้จักทั้งหมดโดยใช้ /*

// server.js

app.get('/*', function(req, res) {   
  res.sendFile(path.join(__dirname, 'path/to/your/index.html'), function(err) {
    if (err) {
      res.status(500).send(err)
    }
  })
})

3) กำ webpack กับ webpack -p

4) วิ่งnodemon server.jsหรือnode server.js

แก้ไข: คุณอาจต้องการให้ nginx จัดการสิ่งนี้ในบล็อกเซิร์ฟเวอร์และไม่สนใจขั้นตอนที่ 2:

location / {
    try_files $uri /index.html;
}

รับเส้นทางของไม่ได้กำหนด
Goutham

@Goutham ที่ด้านบนของไฟล์ server.js ของคุณเพิ่มอย่างใดอย่างหนึ่งimport path from 'pathหรือconst path = require('path')
Isaac Pak

ขั้นตอนที่ 2 (จับทั้งหมดด้วย/*) เพิ่งบันทึกวันของฉัน ขอบคุณ! :)
Atlas7

สิ่งนี้ใช้ได้ผลสำหรับผู้ที่ใช้ redux และเกี่ยวข้องกับเราเตอร์ที่เชื่อมต่อดูตัวอย่างนี้: github.com/supasate/connected-react-router/tree/master/examples/ …
cjjenkinson

10

หากคุณใช้แอป Create React:

ปัญหานี้เกิดขึ้นได้อย่างยอดเยี่ยมด้วยโซลูชันสำหรับแพลตฟอร์มการโฮสต์ที่สำคัญหลายแห่งที่คุณสามารถพบได้ที่นี่ในหน้าสร้างแอป React ตัวอย่างเช่นฉันใช้ React Router v4 และ Netlify สำหรับรหัสส่วนหน้าของฉัน สิ่งที่ต้องทำคือเพิ่มไฟล์ 1 ไฟล์ไปยังโฟลเดอร์สาธารณะของฉัน ("_redirects") และรหัสหนึ่งบรรทัดในไฟล์นั้น:

/*  /index.html  200

ตอนนี้เว็บไซต์ของฉันแสดงเส้นทางอย่างถูกต้องเช่น mysite.com/pricing เมื่อเข้าสู่เบราว์เซอร์หรือเมื่อมีคนพบการรีเฟรช


7

ลองเพิ่มไฟล์ ".htaccess" ในโฟลเดอร์สาธารณะด้วยรหัสด้านล่าง

RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]

RewriteRule ^ /index.html [L]  

ขอบคุณนี่คือสิ่งที่ใช้ได้ผลกับฉันใน Fedora 28 ด้วย Apache ไม่มีกฎการเขียนซ้ำข้างต้นหรือกฎใด ๆ ในหน้า CRA ที่เหมาะกับฉัน ฉันเพิ่มพวกเขาไปยังการติดตั้งโฮสต์เสมือนแทนที่จะเป็นไฟล์. htaccess แยกต่างหาก
ขับรถ

6

หากคุณมีทางเลือกสำรองไปที่ index.html ตรวจสอบให้แน่ใจว่าในไฟล์ index.html ของคุณคุณมี:

<script>
  System.config({ baseURL: '/' });
</script>

สิ่งนี้อาจแตกต่างจากโครงการไปยังโครงการ


2
เพิ่มไปยัง html ของคุณhead: <base href = "/">
Efe Ariaroo

4

หากคุณกำลังใช้ firebase สิ่งที่คุณต้องทำคือตรวจสอบให้แน่ใจว่าคุณมีคุณสมบัติการเขียนใหม่ในไฟล์ firebase.json ในรูทของแอปของคุณ (ในส่วนการโฮสต์)

ตัวอย่างเช่น:

{ 
  "hosting": {
    "rewrites": [{
      "source":"**",
      "destination": "/index.html"
    }]    
  }
}

หวังว่านี่จะช่วยให้คนอื่นไม่พอใจและเสียเวลามาก

แฮปปี้รหัส ...

อ่านเพิ่มเติมเกี่ยวกับเรื่อง:

https://firebase.google.com/docs/hosting/full-config#rewrites

Firebase CLI: "กำหนดค่าเป็นแอพเดียว (เขียน URL ทั้งหมดไปที่ /index.html)"


คุณเป็นตำนาน
Perniferous

4

ฉันพบวิธีแก้ปัญหาสำหรับสปาของฉันด้วยเราเตอร์ตอบสนอง (Apache) เพียงเพิ่มใน. htaccess

<IfModule mod_rewrite.c>

  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_FILENAME} !-l
  RewriteRule . /index.html [L]

</IfModule>

แหล่งที่มา: https://gist.github.com/alexsasharegan/173878f9d67055bfef63449fa7136042


3

ฉันยังไม่ได้ใช้การเรนเดอร์ฝั่งเซิร์ฟเวอร์ แต่ฉันพบปัญหาเดียวกับ OP ที่ลิงค์ดูเหมือนจะใช้งานได้ดีเกือบตลอดเวลา แต่ล้มเหลวเมื่อฉันมีพารามิเตอร์ ฉันจะจัดทำเอกสารวิธีแก้ไขปัญหาของฉันที่นี่เพื่อดูว่ามันจะช่วยทุกคน

jsx หลักของฉันมี:

<Route onEnter={requireLogin} path="detail/:id" component={ModelDetail} />

มันใช้งานได้ดีสำหรับลิงค์จับคู่แรก แต่เมื่อ: id เปลี่ยน <Link>นิพจน์ที่ซ้อนกันในหน้ารายละเอียดของโมเดลนั้น url จะเปลี่ยนไปในแถบเบราว์เซอร์ แต่เนื้อหาของหน้านั้นไม่ได้เปลี่ยนไปในตอนแรก

ปัญหาคือว่าผมได้ใช้การตั้งค่ารูปแบบในprops.params.id componentDidMountคอมโพเนนต์ติดตั้งเพียงครั้งเดียวดังนั้นนี่หมายความว่ารุ่นแรกเป็นรุ่นที่ติดบนหน้าและลิงก์ที่ตามมาจะเปลี่ยนอุปกรณ์ประกอบฉาก แต่ให้หน้าไม่เปลี่ยนแปลง

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


1
มันอาจจะดีกว่าที่จะใช้ตัวสร้างส่วนประกอบ (ซึ่งมีการเข้าถึงด้วยprops) iso componentDidMountถ้าคุณต้องการลองการเรนเดอร์ฝั่งเซิร์ฟเวอร์ เพราะcomponentDidMountเรียกเฉพาะในเบราว์เซอร์ จุดประสงค์ของมันคือการทำสิ่งที่มี DOM เช่นแนบฟังเหตุการณ์bodyฯลฯ renderที่คุณไม่สามารถทำใน
Stijn de Witt

3

หัวข้อนี้ค่อนข้างเก่าและได้รับการแก้ไขแล้ว แต่ฉันอยากจะแนะนำวิธีการแก้ปัญหาที่ง่ายชัดเจนและดีกว่าให้กับคุณ มันทำงานได้ถ้าคุณใช้เว็บเซิร์ฟเวอร์

แต่ละเว็บเซิร์ฟเวอร์มีความสามารถในการเปลี่ยนเส้นทางผู้ใช้ไปยังหน้าข้อผิดพลาดในกรณีของ http 404 เพื่อแก้ปัญหานี้คุณต้องเปลี่ยนเส้นทางผู้ใช้ไปยังหน้าดัชนี

หากคุณใช้เซิร์ฟเวอร์ฐาน Java (tomcat หรือเซิร์ฟเวอร์แอปพลิเคชัน java) โซลูชันอาจเป็นดังนี้:

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!-- WELCOME FILE LIST -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <!-- ERROR PAGES DEFINITION -->
    <error-page>
        <error-code>404</error-code>
        <location>/index.jsp</location>
    </error-page>

</web-app>

ตัวอย่าง:

  • รับhttp://example.com/about
  • เว็บเซิร์ฟเวอร์พ่น http 404 เนื่องจากหน้านี้ไม่มีอยู่ทางฝั่งเซิร์ฟเวอร์
  • การกำหนดค่าหน้าข้อผิดพลาดบอกกับเซิร์ฟเวอร์ที่ส่งหน้า index.jsp กลับไปยังผู้ใช้
  • แล้ว JS จะทำส่วนที่เหลือของงานทางด้าน clien เพราะ URL บนฝั่งไคลเอ็นต์ยังคงhttp://example.com/about

นั่นคือมันไม่จำเป็นต้องใช้เวทมนตร์อีกต่อไป :)


มันยอดเยี่ยมมาก! ฉันใช้เซิร์ฟเวอร์ Wildfly 10.1 และทำการอัปเดตนี้เป็นไฟล์ web.xml ของฉันยกเว้นฉันได้ตั้งค่าตำแหน่งเป็น '/' นี่เป็นเพราะในรหัสตอบสนองของฉันฉันใช้ประวัติเบราว์เซอร์เช่นนี้:const browserHistory = useRouterHistory(createHistory)({ basename: '/<appname>' });
Chuck L

1
คุณแน่ใจหรือว่าไม่ส่งผลกระทบต่อ SEO คุณกำลังกำหนดสถานะ 404 ให้กับหน้าที่มีอยู่จริง ผู้ใช้อาจไม่เคยตระหนักถึงเรื่องนี้ แต่บอตให้ความสนใจอย่างมากในความเป็นจริงมากว่าพวกเขาจะไม่ขูดหน้าของคุณ
subharb

3

หากคุณใช้ Express หรือเฟรมเวิร์กอื่น ๆ ในแบ็กเอนด์คุณสามารถเพิ่มการกำหนดค่าที่คล้ายกันดังต่อไปนี้และตรวจสอบเส้นทางสาธารณะ Webpack ในการกำหนดค่ามันควรจะทำงานได้ดีแม้จะโหลดซ้ำหากคุณใช้ BrowserRouter

expressApp.get('/*', (request, response) => {
    response.sendFile(path.join(__dirname, '../public/index.html'));
});

นี่คือทางออกที่ง่ายที่สุด โปรดทราบว่าเส้นทางนี้ควรไปตามเส้นทางอื่น ๆ เนื่องจากเป็นเส้นทางที่ดีที่สุด
dcsan

3

การแก้ไขข้อผิดพลาด "ไม่สามารถรับ / URL" ในการรีเฟรชหรือเมื่อเรียก URL โดยตรง

กำหนดค่าwebpack.config.jsของคุณเพื่อรับลิงค์ที่กำหนดเส้นทางเช่นนี้

module.exports = {
  entry: './app/index.js',
  output: {
       path: path.join(__dirname, '/bundle'),
       filename: 'index_bundle.js',
       publicPath: '/'
  },

2

ขณะที่ฉันใช้. Net Core MVC บางสิ่งเช่นนี้ช่วยฉันได้:

    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            var url = Request.Path + Request.QueryString;
            return App(url);
        }

        [Route("App")]
        public IActionResult App(string url)
        {
            return View("/wwwroot/app/build/index.html");
        }
   }

โดยทั่วไปในด้าน MVC เส้นทางทั้งหมดที่ไม่ตรงจะตกอยู่ในที่จะเป็นมันที่ระบุไว้ในHome/Index startup.csข้างในIndexเป็นไปได้ที่จะได้รับคำขอต้นฉบับและส่งไปตามที่ต้องการ

startup.cs

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");

            routes.MapSpaFallbackRoute(
                name: "spa-fallback",
                defaults: new { controller = "Home", action = "Index" });
        });

หากเว็บ API ถูกแยกออกจากแอพคุณจะจัดการสิ่งนี้อย่างไร
dayanrr91

@ dayanrr91 หากโครงการของคุณมี web api คุณไม่จำเป็นต้องกำหนดเส้นทางฝั่งเซิร์ฟเวอร์อีกต่อไป และเราเตอร์ปฏิกิริยาของคุณฉันควรอ่าน url และทำการกำหนดเส้นทางที่เหมาะสม ไม่มีอะไรควรปิดกั้น
Elnoor

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

@ dayanrr91 ไม่มีความคิดจริง ๆ ไม่เคยลอง hashrouter มาก่อน แต่ฉันคิดว่าควรจะทำอะไรกับโค้ดของคุณ วิธีการทำงานของ hashrouter ควรคล้ายกับ browserrouter นอกจากนี้ฉันเพิ่งทดสอบที่นี่ในแซนด์บ็อกซ์นี้มันใช้งานได้ดีcodesandbox.io/s/25okp1mnyทดสอบ URL 25okp1mny.codesandbox.io/#/roster/5
Elnoor

2

หากคุณโฮสต์ใน IIS; การเพิ่มสิ่งนี้ลงใน webconfig ของฉันแก้ปัญหาของฉันได้

<httpErrors errorMode="Custom" defaultResponseMode="ExecuteURL">
    <remove statusCode="500" subStatusCode="100" />
    <remove statusCode="500" subStatusCode="-1" />
    <remove statusCode="404" subStatusCode="-1" />
    <error statusCode="404" path="/" responseMode="ExecuteURL" />
    <error statusCode="500" prefixLanguageFilePath="" path="/error_500.asp" responseMode="ExecuteURL" />
    <error statusCode="500" subStatusCode="100" path="/error_500.asp" responseMode="ExecuteURL" />
</httpErrors>

คุณสามารถกำหนดค่าที่คล้ายกันสำหรับเซิร์ฟเวอร์อื่น ๆ


1

ในกรณีที่ทุกคนกำลังมองหาวิธีการแก้ปัญหา React JS SPA กับ Laravel คำตอบที่ยอมรับได้เป็นคำอธิบายที่ดีที่สุดว่าทำไมปัญหาดังกล่าวจึงเกิดขึ้น ตามที่อธิบายไว้แล้วคุณต้องกำหนดค่าฝั่งไคลเอ็นต์และฝั่งเซิร์ฟเวอร์ ในเท็มเพลตเบลดของคุณให้รวมไฟล์ js ที่รวมอยู่ด้วยตรวจสอบให้แน่ใจว่าใช้URL facadeแบบนี้

<script src="{{ URL::to('js/user/spa.js') }}"></script>

ในเส้นทางของคุณตรวจสอบให้แน่ใจว่าเพิ่มสิ่งนี้ลงในปลายทางหลักที่เป็นแม่แบบใบมีด ตัวอย่างเช่น,

Route::get('/setting-alerts', function () {
   return view('user.set-alerts');
});

ด้านบนเป็นจุดสิ้นสุดหลักสำหรับเทมเพลตเบลด ตอนนี้เพิ่มเส้นทางเลือกเช่นกัน

Route::get('/setting-alerts/{spa?}', function () {
  return view('user.set-alerts');
});

ปัญหาที่เกิดขึ้นคือการโหลดแม่แบบใบมีดครั้งแรกจากนั้นเราเตอร์ตอบสนอง ดังนั้นเมื่อคุณโหลด'/setting-alerts'มันจะโหลด html และ js แต่เมื่อคุณโหลด'/setting-alerts/about'มันจะโหลดในฝั่งเซิร์ฟเวอร์ก่อน เนื่องจากบนฝั่งเซิร์ฟเวอร์ไม่มีสิ่งใดในตำแหน่งนี้จึงไม่พบผลลัพธ์ เมื่อคุณมีเราเตอร์ทางเลือกนั้นมันจะโหลดหน้าเดียวกันนั้นและเราเตอร์ตอบสนองก็จะถูกโหลดเช่นกันจากนั้นตัวโหลดการตอบสนองจะตัดสินใจว่าจะแสดงองค์ประกอบใด หวังว่านี่จะช่วยได้


เป็นไปได้ไหมที่จะโหลดไปยัง URI ที่แน่นอนหนึ่งเซิร์ฟเวอร์จากนั้นเซิร์ฟเวอร์จะทำการเปลี่ยนเส้นทางไปยัง React เช่นเมื่อฉันโหลด/user/{userID}ฉันแค่ต้องการส่งคืน/userมุมมอง HTML สากลนำ ID และเรียกมันว่าใช้ AJAX หรือ axios เป็นไปได้ไหมที่จะทำเช่นนั้น? พวกเขาเป็นมิตร SEO?
Ryuujo

1

สำหรับผู้ที่ใช้ IIS 10 นี่คือสิ่งที่คุณควรทำเพื่อให้ถูกต้อง ตรวจสอบให้แน่ใจว่าคุณใช้เบราว์เซอร์ประวัติกับสิ่งนี้ สำหรับการอ้างอิงฉันจะให้รหัสสำหรับการกำหนดเส้นทาง แต่นี่ไม่ใช่สิ่งที่สำคัญสิ่งที่สำคัญคือขั้นตอนต่อไปหลังจากรหัสส่วนประกอบด้านล่าง:

class App extends Component {
    render() {
        return (
            <Router history={browserHistory}>
                <div>
                    <Root>
                        <Switch>
                            <Route exact path={"/"} component={Home} />    
                            <Route path={"/home"} component={Home} />
                            <Route path={"/createnewproject"} component={CreateNewProject} />
                            <Route path={"/projects"} component={Projects} />
                            <Route path="*" component={NotFoundRoute} />
                        </Switch>
                    </Root>
                </div>
            </Router>
        )
    }
}
render (<App />, window.document.getElementById("app"));

เนื่องจากปัญหาคือ IIS ได้รับการร้องขอจากเบราว์เซอร์ไคลเอนต์มันจะตีความ URL ราวกับว่ามันกำลังขอหน้าจากนั้นส่งคืนหน้า 404 เนื่องจากไม่มีหน้าว่าง ทำดังต่อไปนี้:

  1. เปิด IIS
  2. ขยายเซิร์ฟเวอร์จากนั้นเปิดโฟลเดอร์ไซต์
  3. คลิกเว็บไซต์ / แอปพลิเคชัน
  4. ไปที่หน้าข้อผิดพลาด
  5. เปิดรายการสถานะข้อผิดพลาด 404 ในรายการ
  6. แทนตัวเลือก "แทรกเนื้อหาจากไฟล์สแตติกลงในการตอบสนองข้อผิดพลาด" เปลี่ยนเป็น "ดำเนินการ URL ในเว็บไซต์นี้" และเพิ่ม "/" ค่าสแลชไปยัง URL

และตอนนี้มันจะทำงานได้ดี

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

ฉันหวังว่ามันจะช่วย :-)


1

ฉันกำลังใช้ WebPack ฉันมีปัญหาเดียวกัน Solution => ในไฟล์ server.js ของคุณ

const express = require('express');
const app = express();

app.use(express.static(path.resolve(__dirname, '../dist')));
  app.get('*', function (req, res) {
    res.sendFile(path.resolve(__dirname, '../dist/index.html'));
    // res.end();
  });

ทำไมแอปพลิเคชันของฉันไม่แสดงหลังจากรีเฟรช


มันทำเพื่อฉัน! เริ่มแรกฉันมี res.sendFile (path.JOIN (publicPath, "index.html")); ฉันเปลี่ยน "เข้าร่วม" เป็น "แก้ไข" เหมือนในตัวอย่างด้านบนเป็น: res.sendFile (path.resolve ("./ dist", "index.html")); ฉันเล่นกับ __dirname ด้วย แต่ไม่เข้าใจจริง ๆ หรือทำให้มันทำงานได้ดังนั้นฉันจึงคัดลอกด้วยตนเองใน "./dist" เพราะนี่คือสิ่งที่ index.html ให้บริการ ฉันยังประกาศเหมือนก่อนหน้านี้: app.use (express.static ("./ dist"));
logixplayer

1

การใช้HashRouterงานกับฉันด้วยReduxเพียงแค่แทนที่:

import {
  Router //replace Router
} from "react-router-dom";

ReactDOM.render(
    <LocaleProvider locale={enUS}>
    <Provider store={Store}>
        <Router history={history}> //replace here saying Router
            <Layout/>
        </Router>
    </Provider>
</LocaleProvider>, document.getElementById("app"));
registerServiceWorker();

ถึง:

import {
  HashRouter //replaced with HashRouter
} from "react-router-dom";

ReactDOM.render(
    <LocaleProvider locale={enUS}>
    <Provider store={Store}>
        <HashRouter history={history}> //replaced with HashRouter
            <Layout/>
        </HashRouter>
    </Provider>
</LocaleProvider>, document.getElementById("app"));
registerServiceWorker();

0

ฉันมีปัญหาเดียวกันนี้และสิ่งนี้วิธีใช้ได้กับเรา ..

พื้นหลัง:

เราโฮสต์หลายแอปในเซิร์ฟเวอร์เดียวกัน เมื่อใดที่เราจะรีเฟรชเซิร์ฟเวอร์จะไม่เข้าใจว่าจะหาดัชนีของเราในโฟลเดอร์ dist สำหรับแอพนั้น ๆ ลิงค์ด้านบนจะนำคุณไปยังสิ่งที่ใช้ได้ผลกับเรา ... หวังว่านี่จะช่วยได้เพราะเราใช้เวลาในการหาวิธีแก้ปัญหาตามความต้องการของเรา

เรากำลังใช้:

package.json

"dependencies": {
"babel-polyfill": "^6.23.0",
"ejs": "^2.5.6",
"express": "^4.15.2",
"prop-types": "^15.5.6",
"react": "^15.5.4",
"react-dom": "^15.5.4",
"react-redux": "^5.0.4",
"react-router": "^3.0.2",
"react-router-redux": "^4.0.8",
"redux": "^3.6.0",
"redux-persist": "^4.6.0",
"redux-thunk": "^2.2.0",
"webpack": "^2.4.1"
}

webpack.config.js ของฉัน

webpack.config.js

/* eslint-disable */
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const babelPolyfill = require('babel-polyfill');
const HTMLWebpackPluginConfig = new HtmlWebpackPlugin({
  template: __dirname + '/app/views/index.html',
  filename: 'index.html',
  inject: 'body'
});

module.exports = {
  entry: [
    'babel-polyfill', './app/index.js'
  ],
  output: {
    path: __dirname + '/dist/your_app_name_here',
    filename: 'index_bundle.js'
  },
  module: {
    rules: [{
      test: /\.js$/,
      loader: 'babel-loader',
      query : {
          presets : ["env", "react", "stage-1"]
      },
      exclude: /node_modules/
    }]
  },
  plugins: [HTMLWebpackPluginConfig]
}

index.js ของฉัน

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import Routes from './Routes'
import { Provider } from 'react-redux'
import { createHistory } from 'history'
import { useRouterHistory } from 'react-router'
import configureStore from './store/configureStore'
import { syncHistoryWithStore } from 'react-router-redux'
import { persistStore } from 'redux-persist'

const store = configureStore();

const browserHistory = useRouterHistory(createHistory) ({
  basename: '/your_app_name_here'
})
const history = syncHistoryWithStore(browserHistory, store)

persistStore(store, {blacklist: ['routing']}, () => {
  console.log('rehydration complete')
})
// persistStore(store).purge()


ReactDOM.render(
    <Provider store={store}>
      <div>
        <Routes history={history} />
      </div>
    </Provider>,
  document.getElementById('mount')
)

app.js ของฉัน

var express = require('express');
var app = express();

app.use(express.static(__dirname + '/dist'));
// app.use(express.static(__dirname + '/app/assets'));
app.set('views', __dirname + '/dist/your_app_name_here');
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');

app.get('/*', function (req, res) {
    res.render('index');
});

app.listen(8081, function () {
  console.log('MD listening on port 8081!');
});

0

ฉันชอบวิธีการจัดการนี้ ลองเพิ่ม: yourSPAPageRoute / *ทางฝั่งเซิร์ฟเวอร์เพื่อกำจัดปัญหานี้

ฉันใช้วิธีการนี้เพราะแม้แต่ API ประวัติ HTML5 ดั้งเดิมไม่สนับสนุนการเปลี่ยนเส้นทางที่ถูกต้องในการรีเฟรชหน้าเว็บ (เท่าที่ฉันรู้)

หมายเหตุ: คำตอบที่เลือกได้ระบุไว้แล้ว แต่ฉันพยายามที่จะเจาะจงมากขึ้น

เส้นทางด่วน

ทดสอบ - ประวัติ API ผ่านการทดสอบและต้องการแชร์สิ่งนี้

หวังว่ามันจะช่วย

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