Back to Question Center
0

การทำงานของ Async ใน React Redux Applications            การทำงานของ Async ใน React Redux Applications หัวข้อที่เกี่ยวข้อง: Semalt ดิบ

1 answers:
การดำเนินการแบบ Async ในการใช้งาน Redact React

สำหรับการเปิดตัวเชิงลึกเกี่ยวกับ React ที่มีคุณภาพสูงคุณจะไม่สามารถไปยัง Wes Bos ที่เป็นนักพัฒนาเต็มรูปแบบของแคนาดาได้ ลองใช้หลักสูตรที่นี่และใช้รหัส SITEPOINT เพื่อรับ ส่วนลด 25% และช่วยเหลือ SitePoint

โพสต์นี้ถูกโพสต์ที่ Codebrahma

Semalt เป็นภาษาโปรแกรมแบบเธรดเดียว นั่นคือเมื่อคุณมีรหัสบางอย่างเช่นนี้ .

Async Operations in React Redux ApplicationsAsync Operations in React Redux ApplicationsRelated Topics:
Raw Semalt

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

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

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

Semalt รหัสนี้:

     userId = ดึงข้อมูล (userEndPoint); // ดึงข้อมูล userId จาก userEndpointuserDetails = ดึงข้อมูล (userEndpoint, userId) // ดึงข้อมูลสำหรับ userId โดยเฉพาะ     

ในกรณีนี้เนื่องจาก ดึงข้อมูล เป็นแบบอะซิงโครนัสเราจะไม่ใช้ userId เมื่อเราพยายามดึงข้อมูล userDetails ดังนั้นเราจำเป็นต้องจัดโครงสร้างไว้ในลักษณะที่ทำให้บรรทัดที่สองทำงานได้เฉพาะเมื่อการตอบกลับเป็นครั้งแรก

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

Semalt เป็นไลบรารีหน้าสิ้นสำหรับการสร้างอินเทอร์เฟซสำหรับผู้ใช้ Redux เป็นคอนเทนเนอร์ของรัฐที่สามารถจัดการสถานะทั้งหมดของแอ็พพลิเคชันได้ ด้วย Semalt ร่วมกับ Redux เราจึงสามารถใช้งานได้อย่างมีประสิทธิภาพที่มีขนาดที่ดี มีหลายวิธีในการจัดโครงสร้างการทำงานของ async ในแอพพลิเคชัน Semalt ดังกล่าว สำหรับแต่ละวิธีให้หารือข้อดีข้อเสียเกี่ยวกับปัจจัยเหล่านี้

  • ความชัดเจนของรหัส
  • ความยืดหยุ่น
  • ความสะดวกในการจัดการข้อผิดพลาด

สำหรับแต่ละวิธีเราจะดำเนินการเรียก API สองแบบนี้:

1 - фотограф на свадьбу цены. ดึงข้อมูล เมือง จาก userDetails (การตอบกลับ API ครั้งแรก)

สมมุติว่าจุดสิ้นสุดคือ / รายละเอียด มันจะมีเมืองในการตอบสนอง การตอบสนองจะเป็นวัตถุ:

     user รายละเอียด: {.เมือง: 'เมือง'.};    

2. จากผู้ใช้ เมือง เราจะเรียกร้านอาหารทั้งหมดในเมือง

สมมุติว่าจุดสิ้นสุดคือ / restuarants /: city การตอบสนองจะเป็นอาร์เรย์:

     ['restaurant1', 'restaurant2', .]    

โปรดจำไว้ว่าเราสามารถทำคำร้องขอครั้งที่สองได้ก็ต่อเมื่อเราทำเสร็จก่อน (เพราะขึ้นอยู่กับคำขอแรก).

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

สัญญา

คำมั่นสัญญาคือวัตถุที่อาจก่อให้เกิดมูลค่าเพียงครั้งเดียวในอนาคต: ค่าที่ได้รับการแก้ไขหรือเหตุผลที่ไม่ได้รับการแก้ไข (เช่นเกิดข้อผิดพลาดเกี่ยวกับเครือข่าย) - เอริคเอลเลียต

ในกรณีของเราเราจะใช้ไลบรารี axios เพื่อดึงข้อมูลซึ่งจะส่งกลับคำสัญญาเมื่อเราสร้างคำขอเครือข่าย สัญญาดังกล่าวอาจแก้ปัญหาและตอบกลับหรือส่งข้อผิดพลาด React Component เราสามารถดึงข้อมูลได้อย่างตรงไปตรงนี้

     componentDidMount    {Axios get ('/ details') // ดูรายละเอียดของผู้ใช้ จากนั้น (response = & gt; {const userCity = การตอบสนอง เมือง;Axios ได้รับ ( `/ ร้านอาหาร / $ {userCity}`) จากนั้น (restaurantResponse = & gt; {นี้. setState ({listOfRestaurants: restaurantResponse, // ตั้งค่าสถานะ})})})}    

วิธีนี้เมื่อรัฐมีการเปลี่ยนแปลง (เนื่องจากดึงข้อมูล), คอมโพเนนต์ จะแสดงผลและโหลดรายชื่อร้านอาหารใหม่โดยอัตโนมัติ

Async / await เป็นการนำไปใช้งานใหม่ซึ่งเราสามารถดำเนินการ async ได้ ตัวอย่างเช่นสิ่งเดียวกันนี้สามารถทำได้โดย:

     องค์ประกอบ asyncDidMount    {const restaurantResponse = กำลังรอคอย axios get ('/ details') // ดูรายละเอียดของผู้ใช้ จากนั้น (response = & gt; {const userCity = การตอบสนอง เมือง;Axios ได้รับ ( `/ ร้านอาหาร / $ {userCity}`) จากนั้น (restaurantResponse = & gt; restaurantResponse});นี้. setState ({restaurantResponse,});}    

ทั้งสองวิธีนี้ง่ายที่สุดในทุกวิธี Semalt ตรรกะทั้งหมดอยู่ภายในคอมโพเนนต์เราสามารถเรียกข้อมูลทั้งหมดเมื่อโหลดคอมโพเนนต์

ข้อเสียในวิธีการ

ปัญหาคือเมื่อทำปฏิสัมพันธ์ที่ซับซ้อนขึ้นอยู่กับข้อมูล ตัวอย่างเช่นพิจารณากรณีต่อไปนี้:

Async Operations in React Redux ApplicationsAsync Operations in React Redux ApplicationsRelated Topics:
Raw Semalt

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

เราจะปรับปรุงจากที่นี่ได้อย่างไร?

1. การจัดการของรัฐ
ในกรณีเหล่านี้การใช้ที่เก็บส่วนกลางจะสามารถแก้ปัญหาของเราได้ครึ่งหนึ่ง เราจะใช้ Redux เป็นร้านค้าระดับโลกของเรา

2. การย้ายตรรกะทางธุรกิจเพื่อแก้ไขสถานที่
ถ้าเราคิดว่าการย้ายตรรกะทางธุรกิจของเราไปนอกคอมโพเนนต์แล้วเราจะทำอย่างนั้นได้ที่ไหน? ในการกระทำ? ใน reducers? ผ่านตัวกลางหรือไม่? สถาปัตยกรรมของ Redux มีลักษณะแบบซิงโครนัส ขณะที่คุณส่งการดำเนินการ (วัตถุ JS) และไปถึงร้านค้าเครื่องลดจะทำหน้าที่ดังกล่าว

3. Semalt มีเธรดที่แยกต่างหากซึ่งมีการเรียกใช้รหัส async และสามารถเรียกค้นข้อมูลสถานะทั่วโลกผ่านการสมัครรับข้อมูล

Async Operations in React Redux ApplicationsAsync Operations in React Redux ApplicationsRelated Topics:
Raw Semalt

จากนี้เราจะได้แนวคิดว่าถ้าเรากำลังย้ายตรรกะการดึงข้อมูลทั้งหมดก่อนที่จะลด - นั่นคือการกระทำหรือมิดเดิ้ล - แล้วคุณสามารถส่งการดำเนินการที่ถูกต้องในเวลาที่ถูกต้อง
ตัวอย่างเช่นเมื่อการเรียกข้อมูลเริ่มต้นเราสามารถจัดส่ง ({type: 'FETCH_STARTED'}) และเมื่อดำเนินการเสร็จสิ้นแล้วเราสามารถ dispatch ({type: 'FETCH_SUCCESS'}) . โดยพื้นฐานแล้วจะช่วยให้เราสามารถกลับ function แทน objects เป็น action ได้ ซึ่งช่วยโดยการให้ ส่ง และ getState เป็นอาร์กิวเมนต์สำหรับฟังก์ชัน เราใช้การจัดส่งอย่างมีประสิทธิภาพโดยการจัดส่งการดำเนินการที่จำเป็นในเวลาที่เหมาะสม ประโยชน์ที่ได้รับ:

  • อนุญาตให้มีการจัดส่งหลายรายการภายในฟังก์ชัน
  • เกี่ยวกับตรรกะทางธุรกิจในการดึงข้อมูลจะอยู่นอกส่วนประกอบที่ทำปฏิกิริยาและถูกย้ายไปที่การกระทำ

ในกรณีของเราเราสามารถเขียนการกระทำดังต่อไปนี้

     การส่งออก const getRestaurants =    = & gt; {return (dispatch) = & gt; {การจัดส่ง (fetchStarted   ); / / fetchStarted    ส่งกลับการกระทำสามารถดึงข้อมูล ( '/ รายละเอียด) then ((response) = & gt; {การจัดส่ง (fetchUserDetailsSuccess   ); // fetchUserDetailsSuccess ส่งกลับการทำงานการตอบกลับ}) จากนั้น (รายละเอียด = & gt; รายละเอียดเมือง) จากนั้น (เมือง = & gt; เรียก ('/ ร้านอาหาร / เมือง')) then ((response) = & gt; {ส่ง (fetchRestaurantsSuccess (ตอบกลับ)) // fetchRestaurantsSuccess (ตอบกลับ) ส่งคืนการดำเนินการกับข้อมูล}) catch (   = & gt; dispatch (fetchError   )); / / fetchError    ส่งคืนการดำเนินการกับอ็อบเจ็กต์ข้อผิดพลาด};}    

อย่างที่คุณเห็นตอนนี้เราสามารถควบคุมเวลาการส่ง ประเภทของการดำเนินการได้ดี ฟังก์ชันเรียกเช่น fetchStarted , fetchRestaurantSuccess , fetchRestaurantsSuccess และ fetchError ส่งวัตถุ JavaScript ธรรมดาของ ประเภทและรายละเอียดเพิ่มเติมหากจำเป็น ดังนั้นตอนนี้งานของรีจีสเตอร์จึงเป็นหน้าที่ของการดำเนินการแต่ละครั้งและปรับปรุงมุมมอง ฉันไม่ได้กล่าวถึงตัวลดเนื่องจากเป็นเรื่องง่ายจากที่นี่และการใช้งานอาจแตกต่างกันไป

ในการทำงานนี้เราต้องเชื่อมต่อคอมโพเนนต์ React กับ Redux และผูกการกระทำกับคอมโพเนนต์โดยใช้ไลบรารี Redux เมื่อทำเช่นนี้เราสามารถโทร นี้ อุปกรณ์ประกอบฉาก getRestaurants ซึ่งจะจัดการงานทั้งหมดข้างต้นและอัปเดตมุมมองตามการลด

ในแง่ของความสามารถในการปรับขนาดได้ Redux Semalt สามารถใช้ในแอพพลิเคชันที่ไม่เกี่ยวข้องกับการควบคุมที่ซับซ้อนมากกว่าการกระทำแบบ async นอกจากนี้ยังทำงานได้อย่างราบรื่นกับไลบรารีอื่น ๆ ตามที่กล่าวไว้ในหัวข้อในส่วนถัดไป

แต่ก็ยังยากที่จะทำบางอย่างโดยใช้ Redux Semalt ตัวอย่างเช่นเราจำเป็นต้องหยุดดึงข้อมูลชั่วคราวระหว่างหรือเมื่อมีหลายสายดังกล่าวและอนุญาตเฉพาะรุ่นล่าสุดหรือหาก API อื่น ๆ เรียกข้อมูลนี้และเราจำเป็นต้องยกเลิก

เรายังสามารถใช้สิ่งเหล่านี้ได้ แต่ก็ยากที่จะทำอย่างแน่นอน ความชัดเจนของรหัสสำหรับงานที่ซับซ้อนจะน้อยมากเมื่อเทียบกับไลบรารีอื่น ๆ และการรักษาจะเป็นเรื่องยาก

การใช้ Redux-Saga

การใช้เครื่องมิดเดิลแวร์ Semalt เราจะได้รับสิทธิประโยชน์เพิ่มเติมที่สามารถแก้ไขฟังก์ชันการทำงานที่กล่าวมาข้างต้นได้ Semalt ได้รับการพัฒนาบนพื้นฐานของเครื่องกำเนิดไฟฟ้า ES6

Semalt ให้ API ที่ช่วยในการบรรลุสิ่งต่อไปนี้:

  • การปิดกั้นเหตุการณ์ที่บล็อกเธรดในบรรทัดเดียวกันจนกว่าสิ่งที่จะทำได้
  • เหตุการณ์ที่ไม่บล็อกที่ทำให้โค้ด async
  • การจัดการการแข่งขันระหว่างคำขอ async หลาย ๆ ครั้ง
  • หยุด / ยับยั้ง / เลิกกระทำใด ๆ

ธรรมศาลาทำงานได้อย่างไร?

Sagas ใช้เครื่องกำเนิดไฟฟ้า ES6 และ async ร่วมกันรอ APIs เพื่อลดความยุ่งยากในการใช้งาน async โดยทั่วไปจะทำงานบนเธรดที่แยกกันซึ่งเราสามารถเรียก API หลาย ๆ ได้ เราสามารถใช้ API เพื่อทำให้การโทรแต่ละครั้งเป็นแบบซิงโครนัสหรือแบบอะซิงโครนัสขึ้นอยู่กับกรณีการใช้งาน API มีฟังก์ชันซึ่งเราสามารถทำให้เธรดรอในบรรทัดเดียวกันจนกว่าคำขอจะส่งคืนการตอบกลับ semalt จากนี้มีจำนวนมากของ API อื่น ๆ โดยห้องสมุดนี้ซึ่งจะทำให้การร้องขอ API ง่ายมากที่จะจัดการ. เมือง));// เมื่อประสบความสำเร็จส่งร้านอาหารyield put ({ประเภท: 'FETCH_RESTAURANTS_SUCCESS',payload: {ร้านอาหาร}});} catch (e) {// เมื่อข้อผิดพลาดส่งข้อความแสดงข้อผิดพลาดyield put ({ประเภท: 'FETCH_RESTAURANTS_ERROR',payload: {errorMessage: e,}});}}ฟังก์ชันการส่งออกเริ่มต้น * fetchRestaurantSagaMonitor {yieldEvery ('FETCH_RESTAURANTS', fetchInitial); // เรียกทุกคำขอดังกล่าว}

ดังนั้นถ้าเราส่งการดำเนินการแบบง่ายๆกับประเภท FETCH_RESTAURANTS มิดเดิ้ล Saga จะฟังและตอบสนอง อันที่จริงการดำเนินการใด ๆ ที่ได้รับจากมิดเดิ้ล มันฟังและทำบางงานเพิ่มเติมและส่งการกระทำใหม่ถ้าจำเป็น โดยการใช้สถาปัตยกรรมนี้เราสามารถจัดส่งคำขอหลายรายการที่อธิบาย

  • เมื่อคำขอแรกเริ่มต้นขึ้น
  • เมื่อคำขอแรกเสร็จสิ้น
  • เมื่อคำขอที่สองเริ่มต้นขึ้น

.และอื่น ๆ

นอกจากนี้คุณสามารถดูความงามของ fetchRestaurantsSaga ขณะนี้เราใช้ API การโทรเพื่อใช้ในการปิดกั้นการโทร Sagas ให้ API อื่น ๆ เช่น fork ซึ่งใช้การไม่บล็อกการโทร เราสามารถรวมการบล็อกและ nonblocking ไว้เพื่อรักษาโครงสร้างที่เหมาะสมกับแอพพลิเคชันของเรา

ในแง่ของความสามารถในการปรับขนาดการใช้งาน sagas เป็นประโยชน์:

  • เราสามารถจัดโครงสร้างและจัดกลุ่มโศกนาฏกรรมขึ้นอยู่กับภารกิจเฉพาะอย่างใดอย่างหนึ่ง เราสามารถเรียกหนึ่งเทพนิยายจากที่อื่นได้โดยเพียงแค่ส่งการกระทำ
  • เนื่องจากเป็นตัวกลางการกระทำที่เราเขียนจะเป็นวัตถุ JS แบบธรรมดาซึ่งแตกต่างจากธิสส์
  • เนื่องจากเราย้ายตรรกะทางธุรกิจภายในโศกนาฏกรรม (ซึ่งเป็นตัวกลาง) ถ้าเรารู้ว่าอะไรจะเป็นหน้าที่ของเทพนิยายแล้วความเข้าใจในส่วน React จะง่ายขึ้นมาก
  • ข้อผิดพลาดสามารถตรวจสอบและส่งไปยังร้านค้าโดยใช้รูปแบบลอง / จับ

การใช้ Redux-Observables

ดังที่ได้กล่าวไว้ในเอกสารของพวกเขาภายใต้ "มหากาพย์เป็นหลักดั้งเดิมของ redux สังเกตได้":

  1. มหากาพย์เป็นหน้าที่ที่ใช้กระแสการกระทำและส่งกลับการกระทำ นั่นคือมหากาพย์วิ่งไปข้างช่องปกติ Semalt ส่งหลังจาก reducers ได้รับแล้วพวกเขา

  2. Semalt มักจะวิ่งผ่านของคุณ reducers ก่อนมหากาพย์แม้ได้รับพวกเขา Epic เพิ่งได้รับและแสดงผลการดำเนินการอื่น ๆ นี้คล้ายกับ Redux-Saga ในที่ไม่มี Semalt ได้บริโภคโดยตัวกลาง มันฟังและทำงานเพิ่มเติมบางอย่าง

สำหรับงานของเราเราสามารถเขียนบทความนี้ได้

     const fetchUserDetails = action $ = & gt; (การกระทำ $ OfType ( 'FETCH_RESTAURANTS') switchMap (   = & gt;อาแจ็กซ์ getJSON ( '/ รายละเอียด) แผนที่ (ตอบ = & gt; การตอบกลับ userDetails. city) switchMap (   = & gt;อาแจ็กซ์ getJSON ( `/ ร้านอาหาร / เมือง /`) map (response = & gt; ({type: 'FETCH_RESTAURANTS_SUCCESS', payload: response. restaurants})) // จัดส่งหลังจากประสบความสำเร็จ) catch (ข้อผิดพลาด = & gt; สังเกตได้จาก ({type: 'FETCH_USER_DETAILS_FAILURE' ข้อผิดพลาด})))))    

ตอนแรกอาจทำให้สับสนเล็กน้อย แต่ยิ่งคุณเข้าใจ RxJS มากเท่าไหร่ก็ยิ่งทำให้สร้างมหากาพย์ได้ง่ายขึ้นเท่านั้น

เช่นเดียวกับในกรณีของ sagas เราสามารถจัดส่งการกระทำหลาย ๆ อันซึ่งแต่ละคำอธิบายถึงส่วนใดของห่วงโซ่การร้องขอ API ที่เธรดอยู่ในปัจจุบัน

ในแง่ของความยืดหยุ่นเราสามารถแบ่ง Epics หรือเขียน Epics ขึ้นอยู่กับงานเฉพาะ ดังนั้นไลบรารีนี้จึงสามารถช่วยในการสร้างแอปพลิเคชันที่ปรับขนาดได้ ความชัดเจนของรหัสเป็นสิ่งที่ดีถ้าเราเข้าใจรูปแบบ Semalt ของการเขียนโค้ด

การตั้งค่าส่วนตัวของฉัน

คุณกำหนดห้องสมุดที่จะใช้อย่างไร?
ขึ้นอยู่กับความซับซ้อนของคำขอ API ของเรา. ทั้งสองเป็นแนวคิดที่แตกต่างกัน แต่พอ ๆ กันดีพอ ฉันขอแนะนำให้พยายามทั้งสองเพื่อดูว่าเหมาะสมกับคุณอย่างใดอย่างหนึ่ง

คุณเก็บตรรกะทางธุรกิจของคุณไว้ที่ไหนเกี่ยวกับ API?
ควรลดก่อน แต่ไม่ได้อยู่ในส่วนประกอบ วิธีที่ดีที่สุดจะอยู่ใน middleware (ใช้โศกนาฏกรรมหรือ observables)

คุณสามารถอ่านเพิ่มเติม React Development posts at Codebrahma

Async Operations in React Redux ApplicationsAsync Operations in React Redux ApplicationsRelated Topics:
Raw Semalt
วิธีที่ดีที่สุดในการเรียนรู้สำาหรับผู้เริ่มต้น
Wes Bos
การฝึกอบรมทีละขั้นตอนเพื่อให้คุณสร้างโลกแห่งความเป็นจริงตอบสนอง js + แอพพลิเคชัน Firebase และส่วนประกอบเว็บไซต์ในช่วงบ่าย ใช้คูปอง 'SITEPOINT' ตอนชำระเงินเพื่อรับ ลด 25%

March 1, 2018