Project Templates
Social Network
วิธีสร้างเครือข่ายสังคมด้วย React
114 นาที
บทนำ ในบทเรียนนี้ คุณจะสร้าง back4gram แพลตฟอร์มโซเชียลเน็ตเวิร์กที่มีฟีเจอร์ครบถ้วนคล้ายกับ instagram back4gram อนุญาตให้ผู้ใช้สร้างบัญชี แชร์โพสต์พร้อมภาพ โต้ตอบผ่านการกดถูกใจและความคิดเห็น ค้นหาสิ่งต่าง ๆ และสื่อสารผ่านการส่งข้อความแบบเรียลไทม์ โครงการนี้แสดงให้เห็นถึงวิธีการรวมความสามารถด้านหน้าอันทรงพลังของ react กับบริการด้านหลังที่แข็งแกร่งของ back4app เพื่อสร้างแอปพลิเคชันโซเชียลที่ทันสมัยและมีฟีเจอร์มากมาย react เป็นตัวเลือกที่สมบูรณ์แบบสำหรับด้านหน้าโซเชียลเน็ตเวิร์กเนื่องจากสถาปัตยกรรมที่ใช้ส่วนประกอบ ซึ่งช่วยให้สามารถใช้ ui elements ซ้ำได้และการเรนเดอร์ที่มีประสิทธิภาพ ในขณะเดียวกัน back4app ก็ให้บริการ parse server ที่จัดการซึ่งดูแลการตรวจสอบสิทธิ์ผู้ใช้ การจัดเก็บข้อมูล การอัปโหลดไฟล์ และฟีเจอร์เรียลไทม์โดยไม่ต้องสร้างโครงสร้างเซิร์ฟเวอร์ที่ซับซ้อนจากศูนย์ โดยการทำตามบทเรียนนี้ คุณจะสร้างโซเชียลเน็ตเวิร์กที่สมบูรณ์ด้วย การตรวจสอบผู้ใช้ (ลงทะเบียน, เข้าสู่ระบบ, รีเซ็ตรหัสผ่าน) การจัดการโปรไฟล์ การสร้างโพสต์พร้อมการอัปโหลดภาพ การมีปฏิสัมพันธ์ทางสังคม (ถูกใจ, ความคิดเห็น) การส่งข้อความแบบเรียลไทม์พร้อมตัวบ่งชี้การพิมพ์ ฟังก์ชันการค้นหาข้อมูล การตั้งค่าและความชอบของผู้ใช้ ระหว่างทาง คุณจะได้รับทักษะที่มีค่าใน การพัฒนา react ด้วย hooks และ context การพัฒนา ui ด้วย chakra ui การรวม parse server ผ่าน back4app การจัดการข้อมูลแบบเรียลไทม์ด้วย livequery กระบวนการตรวจสอบผู้ใช้ การจัดการการอัปโหลดไฟล์ การนำไปใช้การออกแบบที่ตอบสนอง ไม่ว่าคุณจะต้องการเปิดแพลตฟอร์มโซเชียลของตัวเองหรือเพียงแค่ต้องการเข้าใจว่าเครือข่ายโซเชียลสมัยใหม่ถูกสร้างขึ้นอย่างไร บทเรียนนี้จะมอบความรู้และประสบการณ์จริงให้กับคุณเพื่อบรรลุเป้าหมายของคุณ ในทุกช่วงเวลาคุณสามารถเข้าถึงโค้ดทั้งหมดได้ที่ github ข้อกำหนดเบื้องต้น ในการทำให้บทแนะนำนี้เสร็จสมบูรณ์ คุณจะต้องมี บัญชี back4app ลงทะเบียนสำหรับบัญชีฟรีที่ back4app com https //www back4app com/ คุณจะใช้สิ่งนี้เพื่อสร้างและจัดการบริการด้านหลังของคุณ ติดตั้ง node js และ npm บนเครื่องของคุณ ติดตั้ง node js (เวอร์ชัน 14 x หรือใหม่กว่า) และ npm จาก nodejs org https //nodejs org/ ตรวจสอบการติดตั้งของคุณโดยการรัน node v และ npm v ในเทอร์มินัลของคุณ ความเข้าใจพื้นฐานเกี่ยวกับ react ความคุ้นเคยกับคอมโพเนนต์ react, hooks, และ jsx หากคุณต้องการทบทวน react โปรดดูที่ เอกสารประกอบการใช้งาน react อย่างเป็นทางการ https //reactjs org/docs/getting started html โปรแกรมแก้ไขโค้ด โปรแกรมแก้ไขโค้ดสมัยใหม่ใด ๆ เช่น visual studio code, sublime text หรือ atom git (ไม่บังคับ) สำหรับการควบคุมเวอร์ชันและติดตามไปพร้อมกับที่เก็บข้อมูล ทรัพยากรเสริม เอกสาร back4app https //www back4app com/docs/get started/welcome คู่มือ parse javascript https //docs parseplatform org/js/guide/ เอกสาร chakra ui https //chakra ui com/docs/getting started เอกสาร react router https //reactrouter com/en/main ขั้นตอนที่ 1 — การตั้งค่า backend ของ back4app ในขั้นตอนนี้ คุณจะสร้างโปรเจกต์ back4app ใหม่และกำหนดค่าโครงสร้างฐานข้อมูลที่จำเป็นสำหรับแอปพลิเคชันเครือข่ายสังคมของคุณ back4app มี parse server ที่จัดการซึ่งจะจัดการการตรวจสอบสิทธิ์ผู้ใช้ การจัดเก็บข้อมูล และฟีเจอร์เรียลไทม์ การสร้างโปรเจกต์ back4app ใหม่ เข้าสู่ระบบบัญชี back4app ของคุณและไปที่แดชบอร์ด คลิกที่ปุ่ม "สร้างแอปใหม่" ป้อน "back4gram" เป็นชื่อแอปของคุณ เลือกภูมิภาคเซิร์ฟเวอร์ที่ใกล้ที่สุด และคลิก "สร้าง" เมื่อแอปของคุณถูกสร้างขึ้น คุณจะถูกเปลี่ยนเส้นทางไปยังแดชบอร์ดแอป การทำความเข้าใจโครงสร้างฐานข้อมูล ก่อนที่จะสร้างคลาสในฐานข้อมูลของคุณ มาทำความเข้าใจโมเดลข้อมูลที่จำเป็นสำหรับเครือข่ายสังคมของเรากันเถอะ ตามความต้องการของแอปพลิเคชันของเรา เราจะต้องการคลาสต่อไปนี้ ผู้ใช้ (มีอยู่แล้วตามค่าเริ่มต้นใน parse) คลาสนี้จัดการการตรวจสอบสิทธิ์ผู้ใช้และข้อมูลโปรไฟล์ เราจะขยายมันด้วยฟิลด์เพิ่มเติมเช่นประวัติและอวตาร โพสต์ จัดเก็บโพสต์ของผู้ใช้รวมถึงเนื้อหาข้อความและภาพ ฟิลด์ เนื้อหา (string), ผู้เขียน (pointer to user), รูปภาพ (file), ไลค์ (number), ความคิดเห็น (array), สร้างเมื่อ (date) ความคิดเห็น เก็บความคิดเห็นเกี่ยวกับโพสต์ ฟิลด์ เนื้อหา (string), ผู้เขียน (pointer to user), โพสต์ (pointer to post), สร้างเมื่อ (date) การสนทนา แสดงถึงการสนทนาในแชทระหว่างผู้ใช้ ฟิลด์ ผู้เข้าร่วม (อาร์เรย์ของพอยเตอร์ไปยังผู้ใช้), ข้อความล่าสุด (สตริง), อัปเดตเมื่อ (วันที่) ข้อความ ข้อความแต่ละข้อความภายในการสนทนา ฟิลด์ ข้อความ (string), ผู้ส่ง (pointer to user), การสนทนา (pointer to conversation), สร้างเมื่อ (date) สถานะการพิมพ์ (สำหรับตัวบ่งชี้การพิมพ์แบบเรียลไทม์) ติดตามเมื่อผู้ใช้กำลังพิมพ์ในบทสนทนา ฟิลด์ ผู้ใช้ (ชี้ไปที่ผู้ใช้), การสนทนา (ชี้ไปที่การสนทนา), กำลังพิมพ์ (บูลีน) การสร้างคลาสฐานข้อมูล ตอนนี้เรามาสร้างคลาสเหล่านี้ในฐานข้อมูล back4app ของคุณกันเถอะ ไปที่ส่วน "ฐานข้อมูล" ในแดชบอร์ด back4app ของคุณ ขยายคลาสผู้ใช้ คลิกที่คลาส "ผู้ใช้" ที่มีอยู่แล้ว เพิ่มคอลัมน์ต่อไปนี้ ชีวประวัติ (ประเภท สตริง) อวตาร (ประเภท ไฟล์) ผู้ติดตาม (ประเภท หมายเลข, ค่าเริ่มต้น 0) ตาม (ประเภท หมายเลข, ค่าเริ่มต้น 0) การสร้างคลาสโพสต์ คลิก "สร้างชั้นเรียน" ป้อน "post" เป็นชื่อคลาสและเลือก "สร้างคลาสว่าง" เพิ่มคอลัมน์ต่อไปนี้ เนื้อหา (ประเภท สตริง) ผู้เขียน (ประเภท ชี้ไปที่ ผู้ใช้) ภาพ (ประเภท ไฟล์) ชอบ (ประเภท หมายเลข, ค่าเริ่มต้น 0) ความคิดเห็น (ประเภท อาร์เรย์) สร้างเมื่อ (ประเภท วันที่, เพิ่มโดยอัตโนมัติ) การสร้างคลาสความคิดเห็น คลิก "สร้างชั้นเรียน" ป้อน "comment" เป็นชื่อคลาสและเลือก "สร้างคลาสว่าง" เพิ่มคอลัมน์ต่อไปนี้ เนื้อหา (ประเภท สตริง) ผู้เขียน (ประเภท ชี้ไปที่ ผู้ใช้) โพสต์ (ประเภท ชี้ไปที่โพสต์) สร้างเมื่อ (ประเภท วันที่, เพิ่มโดยอัตโนมัติ) การสร้างคลาสการสนทนา คลิก "สร้างชั้นเรียน" ป้อน "conversation" เป็นชื่อคลาสและเลือก "สร้างคลาสว่าง" เพิ่มคอลัมน์ต่อไปนี้ ผู้เข้าร่วม (ประเภท อาร์เรย์) ข้อความสุดท้าย (ประเภท สตริง) อัปเดตเมื่อ (ประเภท วันที่, เพิ่มโดยอัตโนมัติ) การสร้างคลาสข้อความ คลิก "สร้างชั้นเรียน" ป้อน "message" เป็นชื่อคลาสและเลือก "สร้างคลาสว่าง" เพิ่มคอลัมน์ต่อไปนี้ ข้อความ (ประเภท สตริง) ผู้ส่ง (ประเภท ชี้ไปที่ ผู้ใช้) การสนทนา (ประเภท ชี้ไปที่การสนทนา) สร้างเมื่อ (ประเภท วันที่, เพิ่มโดยอัตโนมัติ) การสร้างคลาส typingstatus คลิก "สร้างชั้นเรียน" ป้อน "typingstatus" เป็นชื่อคลาสและเลือก "สร้างคลาสว่าง" เพิ่มคอลัมน์ต่อไปนี้ ผู้ใช้ (ประเภท ตัวชี้ไปยัง ผู้ใช้) การสนทนา (ประเภท ชี้ไปที่การสนทนา) กำลังพิมพ์ (ประเภท บูลีน) การตั้งค่าการอนุญาตของคลาส (ไม่บังคับ) เพื่อให้แน่ใจในความปลอดภัยของข้อมูล เราจำเป็นต้องกำหนดรายการควบคุมการเข้าถึง (acls) ที่เหมาะสมสำหรับแต่ละคลาส ไปที่ส่วน "ความปลอดภัย & คีย์" ในแดชบอร์ด back4app ของคุณ ภายใต้ "ความปลอดภัยระดับชั้นเรียน" ให้กำหนดสิทธิ์ดังต่อไปนี้ ผู้ใช้ การเข้าถึงแบบอ่านสาธารณะ เปิดใช้งาน (เพื่อให้ผู้ใช้สามารถดูโปรไฟล์ของผู้ใช้อื่นได้) การเข้าถึงการเขียนสาธารณะ ปิดใช้งาน (ผู้ใช้สามารถแก้ไขโปรไฟล์ของตนเองได้เท่านั้น) โพสต์คลาส การเข้าถึงการอ่านสาธารณะ เปิดใช้งาน (ทุกคนสามารถเห็นโพสต์) การเข้าถึงการเขียนสาธารณะ เปิดใช้งาน (ผู้ใช้ที่ผ่านการตรวจสอบสามารถสร้างโพสต์ได้) เพิ่ม clp สำหรับการอัปเดต/ลบเพื่อจำกัดเฉพาะผู้เขียนเท่านั้น ความคิดเห็นชั้น การเข้าถึงการอ่านสาธารณะ เปิดใช้งาน (ทุกคนสามารถเห็นความคิดเห็น) การเข้าถึงการเขียนสาธารณะ เปิดใช้งาน (ผู้ใช้ที่ผ่านการตรวจสอบสามารถสร้างความคิดเห็นได้) เพิ่ม clp สำหรับการอัปเดต/ลบเพื่อจำกัดเฉพาะผู้เขียนเท่านั้น ชั้นเรียนสนทนา การเข้าถึงอ่านสาธารณะ ปิดใช้งาน (การสนทนาเป็นส่วนตัว) การเข้าถึงการเขียนสาธารณะ เปิดใช้งาน (ผู้ใช้ที่ผ่านการตรวจสอบสามารถสร้างการสนทนาได้) เพิ่ม clp เพื่อจำกัดการเข้าถึงการอ่าน/เขียนสำหรับผู้เข้าร่วมการสนทนา ข้อความคลาส การเข้าถึงอ่านสาธารณะ ปิดใช้งาน (ข้อความเป็นส่วนตัว) การเข้าถึงการเขียนสาธารณะ เปิดใช้งาน (ผู้ใช้ที่ผ่านการตรวจสอบสามารถส่งข้อความได้) เพิ่ม clp เพื่อจำกัดการเข้าถึงการอ่าน/เขียนสำหรับผู้เข้าร่วมการสนทนา คลาส typingstatus การเข้าถึงอ่านสาธารณะ ปิดใช้งาน (สถานะการพิมพ์เป็นส่วนตัว) การเข้าถึงการเขียนสาธารณะ เปิดใช้งาน (ผู้ใช้ที่ผ่านการตรวจสอบสามารถอัปเดตสถานะการพิมพ์ได้) เพิ่ม clp เพื่อจำกัดการเข้าถึงการอ่าน/เขียนสำหรับผู้เข้าร่วมการสนทนา การตั้งค่า livequery สำหรับฟีเจอร์เรียลไทม์ เพื่อเปิดใช้งานฟีเจอร์เรียลไทม์ เช่น การส่งข้อความและตัวบ่งชี้การพิมพ์ เราจำเป็นต้องกำหนดค่า livequery ไปที่ส่วน "การตั้งค่าเซิร์ฟเวอร์" ในแดชบอร์ด back4app ของคุณ ภายใต้ "parse server" ให้ค้นหาส่วน "livequery" และเปิดใช้งานมัน เพิ่มคลาสต่อไปนี้เพื่อให้ livequery ตรวจสอบ ข้อความ สถานะการพิมพ์ โพสต์ (สำหรับการอัปเดตเรียลไทม์เกี่ยวกับไลค์และความคิดเห็น) บันทึกการเปลี่ยนแปลงของคุณ การรับคีย์แอปพลิเคชันของคุณ คุณจะต้องใช้คีย์แอปพลิเคชัน back4app ของคุณเพื่อเชื่อมต่อส่วนหน้า react ของคุณกับส่วนหลัง ไปที่ "การตั้งค่าแอป" > "ความปลอดภัย & คีย์" จดคีย์ต่อไปนี้ รหัสแอปพลิเคชัน คีย์ javascript url เซิร์ฟเวอร์ url เซิร์ฟเวอร์ livequery (การกำหนดค่า subdomain สำหรับฟีเจอร์เรียลไทม์) คุณจะใช้คีย์เหล่านี้ในแอปพลิเคชัน react ของคุณเพื่อเริ่มต้น parse ขั้นตอนที่ 2 — การสร้างโปรเจกต์ frontend react ในขั้นตอนนี้ คุณจะตั้งค่าโปรเจกต์ react ใหม่และกำหนดค่าให้ทำงานกับ back4app backend ของคุณ คุณจะติดตั้งการพึ่งพาที่จำเป็น สร้างโครงสร้างโปรเจกต์ และเชื่อมต่อกับ parse server ของคุณ การตั้งค่าโปรเจกต์ react ใหม่ เริ่มต้นด้วยการสร้างแอปพลิเคชัน react ใหม่โดยใช้ create react app ซึ่งให้การตั้งค่าการสร้างที่ทันสมัยโดยไม่ต้องมีการกำหนดค่า เปิดเทอร์มินัลของคุณและไปที่ไดเรกทอรีที่คุณต้องการสร้างโปรเจกต์ของคุณ รันคำสั่งต่อไปนี้เพื่อสร้างแอปพลิเคชัน react ใหม่ npx create react app back4gram เมื่อโปรเจกต์ถูกสร้างขึ้นแล้ว ให้ไปที่ไดเรกทอรีโปรเจกต์ cd back4gram เริ่มเซิร์ฟเวอร์พัฒนาเพื่อให้แน่ใจว่าทุกอย่างทำงานได้ npm start นี่จะเปิดแอปพลิเคชัน react ใหม่ของคุณในเบราว์เซอร์ที่ http //localhost 3000 http //localhost 3000 การติดตั้งการพึ่งพาที่จำเป็น ตอนนี้เรามาติดตั้งแพ็คเกจที่เราจะต้องใช้สำหรับแอปพลิเคชันเครือข่ายสังคมของเรา หยุดเซิร์ฟเวอร์พัฒนา (กด ctrl+c ในเทอร์มินัลของคุณ) ติดตั้ง parse sdk เพื่อเชื่อมต่อกับ back4app npm install parse ติดตั้ง react router สำหรับการนำทาง npm install react router dom ติดตั้ง chakra ui สำหรับส่วนประกอบของอินเทอร์เฟซผู้ใช้ของเรา npm install @chakra ui/react @emotion/react @emotion/styled framer motion ติดตั้งยูทิลิตี้ ui เพิ่มเติมและไลบรารีไอคอน npm install react icons คำอธิบายโครงสร้างโปรเจกต์ มาจัดระเบียบโปรเจกต์ของเราด้วยโครงสร้างที่ชัดเจน สร้างไดเรกทอรีต่อไปนี้ใน src โฟลเดอร์ mkdir p src/components/ui src/pages src/contexts src/utils นี่คือสิ่งที่แต่ละไดเรกทอรีใช้สำหรับ components คอมโพเนนต์ ui ที่นำกลับมาใช้ใหม่ได้ ui คอมโพเนนต์ ui พื้นฐาน เช่น ปุ่ม ฟอร์ม โมดัล โฟลเดอร์คอมโพเนนต์อื่น ๆ สำหรับฟีเจอร์เฉพาะ (เช่น โพสต์ ความคิดเห็น) pages คอมโพเนนต์หน้าทั้งหมดที่ตรงกับเส้นทาง contexts ผู้ให้บริการบริบท react สำหรับการจัดการสถานะ utils ฟังก์ชันและตัวช่วยยูทิลิตี้ การสร้างตัวแปรสภาพแวดล้อม เพื่อเก็บข้อมูลรับรอง back4app ของคุณอย่างปลอดภัย ให้สร้าง env ไฟล์ในรากของโปรเจกต์ของคุณ สร้างไฟล์ใหม่ชื่อ env local ในรากของโปรเจกต์ touch env local เปิดไฟล์และเพิ่มข้อมูลรับรอง back4app ของคุณ react app parse app id=your application id react app parse js key=your javascript key react app parse server url=https //parseapi back4app com react app parse live query url=wss\ //your app id back4app io แทนที่ค่าตัวอย่างด้วยข้อมูลรับรอง back4app ที่แท้จริงของคุณจากขั้นตอนที่ 1 ตรวจสอบให้แน่ใจว่าได้เพิ่ม env local ลงใน gitignore ไฟล์เพื่อป้องกันการคอมมิตข้อมูลที่ละเอียดอ่อน การกำหนดค่า parse sdk ด้วยข้อมูลประจำตัว back4app ตอนนี้เรามาตั้งค่า parse sdk เพื่อเชื่อมต่อกับแบ็กเอนด์ back4app ของคุณ สร้างไฟล์ใหม่ src/utils/parseconfig js // src/utils/parseconfig js import parse from 'parse/dist/parse min js'; // เริ่มต้น parse parse initialize( process env react app parse app id, process env react app parse js key ); parse serverurl = process env react app parse server url; // เริ่มต้น live queries if (process env react app parse live query url) { parse livequeryserverurl = process env react app parse live query url; } export default parse; อัปเดตไฟล์ของคุณ src/index js เพื่อนำเข้าการกำหนดค่า parse import react from 'react'; import reactdom from 'react dom/client'; import ' /index css'; import app from ' /app'; import reportwebvitals from ' /reportwebvitals'; import ' /utils/parseconfig'; // นำเข้าการกำหนดค่า parse const root = reactdom createroot(document getelementbyid('root')); root render( \<react strictmode> \<app /> \</react strictmode> ); reportwebvitals(); การตั้งค่าองค์ประกอบแอปด้วยการนำทาง เรามาอัปเดตองค์ประกอบแอปหลักเพื่อรวมการนำทางและผู้ให้บริการ chakra ui อัปเดต src/app js import react from 'react'; import { browserrouter as router, routes, route } from 'react router dom'; import { chakraprovider, extendtheme } from '@chakra ui/react'; // นำเข้าหน้า (เราจะสร้างสิ่งเหล่านี้ถัดไป) import landingpage from ' /pages/landingpage'; import loginpage from ' /pages/loginpage'; import signuppage from ' /pages/signuppage'; import resetpasswordpage from ' /pages/resetpasswordpage'; import feedpage from ' /pages/feedpage'; import profilepage from ' /pages/profilepage'; import postdetailspage from ' /pages/postdetailspage'; import messagespage from ' /pages/messagespage'; import searchpage from ' /pages/searchpage'; import settingspage from ' /pages/settingspage'; // สร้างธีมที่กำหนดเอง const theme = extendtheme({ config { initialcolormode 'dark', usesystemcolormode false, }, colors { brand { 50 '#e5f4ff', 100 '#b8dcff', 200 '#8ac5ff', 300 '#5cadff', 400 '#2e96ff', 500 '#147dff', 600 '#0061cc', 700 '#004799', 800 '#002d66', 900 '#001433', }, }, }); function app() { return ( \<chakraprovider theme={theme}> \<router> \<routes> \<route path="/" element={\<landingpage />} /> \<route path="/login" element={\<loginpage />} /> \<route path="/signup" element={\<signuppage />} /> \<route path="/reset password" element={\<resetpasswordpage />} /> \<route path="/feed" element={\<feedpage />} /> \<route path="/profile" element={\<profilepage />} /> \<route path="/post/\ id" element={\<postdetailspage />} /> \<route path="/messages" element={\<messagespage />} /> \<route path="/search" element={\<searchpage />} /> \<route path="/settings" element={\<settingspage />} /> \</routes> \</router> \</chakraprovider> ); } export default app; การสร้างคอมโพเนนต์เส้นทางที่ปลอดภัย เพื่อรักษาความปลอดภัยของเส้นทางที่ต้องการการตรวจสอบสิทธิ์ มาสร้างคอมโพเนนต์ protectedroute กันเถอะ ก่อนอื่น สร้าง authcontext เพื่อจัดการสถานะการตรวจสอบสิทธิ์ของผู้ใช้ // src/contexts/authcontext js import react, { createcontext, usestate, usecontext, useeffect } from 'react'; import parse from 'parse/dist/parse min js'; const authcontext = createcontext(); export function useauth() { return usecontext(authcontext); } export function authprovider({ children }) { const \[currentuser, setcurrentuser] = usestate(null); const \[isloading, setisloading] = usestate(true); useeffect(() => { // check if user is already logged in const checkuser = async () => { try { const user = await parse user current(); setcurrentuser(user); } catch (error) { console error('error checking current user ', error); } finally { setisloading(false); } }; checkuser(); }, \[]); // login function const login = async (username, password) => { try { const user = await parse user login(username, password); setcurrentuser(user); return user; } catch (error) { throw error; } }; // signup function const signup = async (username, email, password) => { try { const user = new parse user(); user set('username', username); user set('email', email); user set('password', password); const result = await user signup(); setcurrentuser(result); return result; } catch (error) { throw error; } }; // logout function const logout = async () => { try { await parse user logout(); setcurrentuser(null); } catch (error) { throw error; } }; // reset password function const resetpassword = async (email) => { try { await parse user requestpasswordreset(email); } catch (error) { throw error; } }; const value = { currentuser, isloading, login, signup, logout, resetpassword, }; return \<authcontext provider value={value}>{children}\</authcontext provider>; } ตอนนี้ สร้างคอมโพเนนต์ protectedroute // src/components/protectedroute js import react from 'react'; import { navigate } from 'react router dom'; import { useauth } from ' /contexts/authcontext'; import { flex, spinner } from '@chakra ui/react'; function protectedroute({ children }) { const { currentuser, isloading } = useauth(); if (isloading) { return ( \<flex justify="center" align="center" height="100vh"> \<spinner size="xl" /> \</flex> ); } if (!currentuser) { return \<navigate to="/login" />; } return children; } export default protectedroute; อัปเดตคอมโพเนนต์ app เพื่อใช้ authprovider และ protectedroute // src/app js (updated) import react from 'react'; import { browserrouter as router, routes, route } from 'react router dom'; import { chakraprovider, extendtheme } from '@chakra ui/react'; import { authprovider } from ' /contexts/authcontext'; import protectedroute from ' /components/protectedroute'; // import pages import landingpage from ' /pages/landingpage'; import loginpage from ' /pages/loginpage'; import signuppage from ' /pages/signuppage'; import resetpasswordpage from ' /pages/resetpasswordpage'; import feedpage from ' /pages/feedpage'; import profilepage from ' /pages/profilepage'; import postdetailspage from ' /pages/postdetailspage'; import messagespage from ' /pages/messagespage'; import searchpage from ' /pages/searchpage'; import settingspage from ' /pages/settingspage'; // theme configuration (same as before) const theme = extendtheme({ // theme configuration }); function app() { return ( \<chakraprovider theme={theme}> \<authprovider> \<router> \<routes> \<route path="/" element={\<landingpage />} /> \<route path="/login" element={\<loginpage />} /> \<route path="/signup" element={\<signuppage />} /> \<route path="/reset password" element={\<resetpasswordpage />} /> \<route path="/feed" element={ \<protectedroute> \<feedpage /> \</protectedroute> } /> \<route path="/profile" element={ \<protectedroute> \<profilepage /> \</protectedroute> } /> \<route path="/post/\ id" element={ \<protectedroute> \<postdetailspage /> \</protectedroute> } /> \<route path="/messages" element={ \<protectedroute> \<messagespage /> \</protectedroute> } /> \<route path="/search" element={ \<protectedroute> \<searchpage /> \</protectedroute> } /> \<route path="/settings" element={ \<protectedroute> \<settingspage /> \</protectedroute> } /> \</routes> \</router> \</authprovider> \</chakraprovider> ); } export default app; การสร้างหน้า landing page พื้นฐาน มาสร้างหน้า landing page ที่เรียบง่ายเพื่อทดสอบการตั้งค่าของเรากันเถอะ // src/pages/landingpage js import react from 'react'; import { box, heading, text, button, vstack, flex } from '@chakra ui/react'; import { link as routerlink } from 'react router dom'; function landingpage() { return ( \<box bg="gray 900" minh="100vh" color="white"> \<flex direction="column" align="center" justify="center" textalign="center" py={20} \> \<heading size="2xl" mb={4}> back4gram \</heading> \<text fontsize="lg" maxw="600px" mb={8}> join a vibrant community where your voice matters share stories, ideas, and moments with friends and the world \</text> \<vstack spacing={4} maxw="md" mx="auto"> \<button as={routerlink} to="/signup" colorscheme="brand" size="lg" w="full" \> create account \</button> \<button as={routerlink} to="/login" variant="outline" size="lg" w="full" \> log in \</button> \</vstack> \</flex> \</box> ); } export default landingpage; ทดสอบการตั้งค่าของคุณ ตอนนี้ที่คุณได้ตั้งค่าโครงสร้างพื้นฐานของแอปพลิเคชัน react ของคุณและเชื่อมต่อกับ back4app แล้ว มาทดสอบกันเถอะ เริ่มเซิร์ฟเวอร์พัฒนา npm start เปิดเบราว์เซอร์ของคุณและไปที่ http //localhost 3000 http //localhost 3000 คุณควรเห็นหน้าแรกพร้อมปุ่มสำหรับลงทะเบียนหรือเข้าสู่ระบบ ตรวจสอบคอนโซลเบราว์เซอร์ของคุณเพื่อให้แน่ใจว่าไม่มีข้อผิดพลาดที่เกี่ยวข้องกับการเริ่มต้น parse ขั้นตอนที่ 3 — การนำฟีเจอร์การตรวจสอบสิทธิ์ไปใช้ ในขั้นตอนนี้ เราจะนำฟีเจอร์การตรวจสอบสิทธิ์ผู้ใช้ไปใช้สำหรับแอปพลิเคชันเครือข่ายสังคมของเราด้วย parse server ของ back4app เราจะตรวจสอบว่าระบบการตรวจสอบสิทธิ์ของ parse ทำงานอย่างไรและนำฟังก์ชันการเข้าสู่ระบบ การลงทะเบียน และการรีเซ็ตรหัสผ่านไปใช้ การทำความเข้าใจระบบการตรวจสอบสิทธิ์ผู้ใช้ของ parse parse server ของ back4app ให้ระบบการจัดการผู้ใช้ที่ครอบคลุมผ่าน parse user คลาส มาทำความเข้าใจว่าการตรวจสอบสิทธิ์ของ parse ทำงานอย่างไรในแอปพลิเคชันของเรา คลาส parse user คลาส parse user เป็นซับคลาสพิเศษของ parse object ที่ออกแบบมาโดยเฉพาะสำหรับการจัดการผู้ใช้ ในแอปพลิเคชัน back4gram ของเรา เราใช้มันเพื่อ เก็บข้อมูลประจำตัวของผู้ใช้ (ชื่อผู้ใช้, อีเมล, รหัสผ่าน) จัดการสถานะการตรวจสอบสิทธิ์ จัดการโทเค็นเซสชันโดยอัตโนมัติ เมื่อดูที่การใช้งานของเรา เราสามารถเห็นได้ว่าเรามีปฏิสัมพันธ์กับคลาส parse user อย่างไร // from signuppage js const user = new parse user(); user set('username', username); user set('email', email); user set('password', password); await user signup(); โค้ดนี้สร้างอ็อบเจ็กต์ parse user ใหม่ ตั้งค่าฟิลด์ที่จำเป็น และเรียกใช้เมธอด signup() เพื่อลงทะเบียนผู้ใช้ใน back4app การตรวจสอบสิทธิ์ใน parse มาดูว่าการตรวจสอบสิทธิ์ทำงานอย่างไรในแอปพลิเคชันของเรา กระบวนการลงทะเบียน ใน signuppage js ของเรา เรารวบรวมชื่อผู้ใช้ อีเมล และรหัสผ่าน เราตรวจสอบข้อมูลนำเข้า (ตรวจสอบฟิลด์ที่ว่าง, รูปแบบอีเมลที่ถูกต้อง, ความยาวรหัสผ่าน) เราสร้างวัตถุ parse user ใหม่และตั้งค่าข้อมูลประจำตัว เราจะเรียก signup() ซึ่งส่งข้อมูลไปยัง back4app แปลงแฮชของรหัสผ่านก่อนที่จะเก็บมัน เมื่อสำเร็จ ผู้ใช้จะเข้าสู่ระบบโดยอัตโนมัติด้วยโทเค็นเซสชัน กระบวนการเข้าสู่ระบบ ใน loginpage js ของเรา เรารวบรวมชื่อผู้ใช้และรหัสผ่าน เราจะเรียกใช้ parse user login() ด้วยข้อมูลประจำตัวเหล่านี้ parse ตรวจสอบข้อมูลประจำตัวกับข้อมูลที่เก็บไว้ หากถูกต้อง, parse จะสร้างโทเค็นเซสชัน โทเค็นเซสชันจะถูกเก็บไว้ในที่เก็บของเบราว์เซอร์โดยอัตโนมัติ การจัดการเซสชัน การวิเคราะห์จะรวมโทเค็นเซสชันโดยอัตโนมัติในคำขอ api ทั้งหมด เราใช้ parse user current() เพื่อดึงข้อมูลผู้ใช้ที่เข้าสู่ระบบอยู่ในขณะนี้ เซสชันยังคงอยู่แม้จะรีเฟรชหน้า การดำเนินการลงทะเบียนผู้ใช้ มาดูส่วนประกอบ signuppage ของเรากันเพื่อทำความเข้าใจว่าการลงทะเบียนผู้ใช้ถูกดำเนินการอย่างไร การตรวจสอบฟอร์ม ก่อนที่จะส่งข้อมูลไปยัง back4app เราจะตรวจสอบข้อมูลที่ผู้ใช้ป้อน // from signuppage js const validateform = () => { const newerrors = {}; if (!username trim()) { newerrors username = 'username is required'; } if (!email trim()) { newerrors email = 'email is required'; } else if (!/\s+@\s+\\ \s+/ test(email)) { newerrors email = 'email is invalid'; } if (!password) { newerrors password = 'password is required'; } else if (password length < 6) { newerrors password = 'password must be at least 6 characters'; } if (password !== confirmpassword) { newerrors confirmpassword = 'passwords do not match'; } seterrors(newerrors); return object keys(newerrors) length === 0; }; การตรวจสอบนี้ทำให้มั่นใจว่า ชื่อผู้ใช้ไม่ว่างเปล่า อีเมลถูกต้อง รหัสผ่านมีอย่างน้อย 6 ตัวอักษร รหัสผ่านและการยืนยันตรงกัน การจัดการข้อผิดพลาดในการลงทะเบียน ตัวจัดการการลงทะเบียนของเรารวมถึงการจัดการข้อผิดพลาดสำหรับข้อผิดพลาดเฉพาะของ parse // from signuppage js try { // create a new user const user = new parse user(); user set('username', username); user set('email', email); user set('password', password); await user signup(); toaster create({ title 'success', description 'account created successfully!', type 'success', }); navigate('/feed'); } catch (error) { toaster create({ title 'signup failed', description error message, type 'error', }); // handle specific parse errors if (error code === 202) { seterrors({ errors, username 'username already taken'}); } else if (error code === 203) { seterrors({ errors, email 'email already in use'}); } } back4app คืนรหัสข้อผิดพลาดเฉพาะที่เราสามารถใช้เพื่อให้ข้อเสนอแนะแก่ผู้ใช้ได้ รหัส 202 ชื่อผู้ใช้ถูกใช้งานแล้ว รหัส 203 อีเมลถูกใช้งานแล้ว โค้ดทั้งหมดสำหรับการลงทะเบียนผู้ใช้/การสมัครสมาชิกสามารถดูได้ที่นี่ การดำเนินการเข้าสู่ระบบผู้ใช้ คอมโพเนนต์ loginpage ของเราจัดการการตรวจสอบสิทธิ์ผู้ใช้โดยใช้ parse user login() แบบฟอร์มเข้าสู่ระบบ แบบฟอร์มเข้าสู่ระบบจะเก็บชื่อผู้ใช้และรหัสผ่าน // from loginpage js \<form onsubmit={handlelogin}> \<vstack spacing={4}> \<field label="username"> \<input type="text" value={username} onchange={(e) => setusername(e target value)} placeholder="your username" required /> \</field> \<field label="password" errortext={error} \> \<input type="password" value={password} onchange={(e) => setpassword(e target value)} placeholder="your password" required /> \</field> \<link as={routerlink} to="/reset password" alignself="flex end" fontsize="sm"> forgot password? \</link> \<button colorscheme="blue" width="full" type="submit" loading={isloading} \> log in \</button> \</vstack> \</form> การตรวจสอบเซสชัน ตามที่แสดงไว้ก่อนหน้านี้ เราจะตรวจสอบเซสชันที่มีอยู่เมื่อหน้าเข้าสู่ระบบโหลด // from loginpage js useeffect(() => { const checkcurrentuser = async () => { try { const user = await parse user current(); if (user) { setcurrentuser(user); navigate('/feed'); } } catch (error) { console error('error checking current user ', error); } }; checkcurrentuser(); }, \[navigate]); นี่คือฟีเจอร์หลักของ parse มันจัดการโทเค็นเซสชันในที่เก็บของเบราว์เซอร์โดยอัตโนมัติ ทำให้เราสามารถตรวจสอบได้ง่ายว่าผู้ใช้ได้เข้าสู่ระบบแล้วหรือไม่ การดำเนินการรีเซ็ตรหัสผ่าน back4app มีฟลows การรีเซ็ตรหัสผ่านที่สร้างไว้แล้ว ในแอปพลิเคชันของเรา เราเชื่อมโยงไปยังหน้าการรีเซ็ตรหัสผ่านจากแบบฟอร์มเข้าสู่ระบบ // from loginpage js \<link as={routerlink} to="/reset password" alignself="flex end" fontsize="sm"> forgot password? \</link> กระบวนการรีเซ็ตรหัสผ่านใน back4app ทำงานดังนี้ ผู้ใช้ขอรีเซ็ตรหัสผ่านด้วยอีเมลของตน parse ส่งลิงก์รีเซ็ตพิเศษไปยังอีเมลของผู้ใช้ ผู้ใช้คลิกลิงก์และตั้งรหัสผ่านใหม่ parse อัปเดตแฮชรหัสผ่านในฐานข้อมูล ในการนำสิ่งนี้ไปใช้ในแอปพลิเคชันของเรา เราจะใช้ // example password reset implementation try { await parse user requestpasswordreset(email); // show success message } catch (error) { // handle error } การรักษาความปลอดภัยเส้นทางสำหรับผู้ใช้ที่ได้รับการตรวจสอบสิทธิ์ เพื่อปกป้องเส้นทางบางอย่างในแอปพลิเคชันของเรา เราใช้คอมโพเนนต์ protectedroute ที่ตรวจสอบว่าผู้ใช้ได้รับการตรวจสอบสิทธิ์หรือไม่ // from protectedroute js function protectedroute({ children }) { const { currentuser, isloading } = useauth(); if (isloading) { return ( \<flex justify="center" align="center" height="100vh"> \<spinner size="xl" /> \</flex> ); } if (!currentuser) { return \<navigate to="/login" />; } return children; } คอมโพเนนต์นี้ ใช้ authcontext ของเราเพื่อตรวจสอบว่าผู้ใช้ล็อกอินอยู่หรือไม่ แสดงสปินเนอร์ขณะตรวจสอบ เปลี่ยนเส้นทางไปยังหน้าล็อกอินหากไม่พบผู้ใช้ แสดงเนื้อหาที่ถูกป้องกันหากผู้ใช้ได้รับการตรวจสอบแล้ว เราใช้คอมโพเนนต์นี้ในการตั้งค่าเส้นทางของเรา // from app js \<route path="/feed" element={ \<protectedroute> \<feedpage /> \</protectedroute> } /> การกำหนดค่าการตรวจสอบสิทธิ์ back4app back4app มีตัวเลือกการกำหนดค่าหลายอย่างสำหรับการตรวจสอบสิทธิ์ในแดชบอร์ด การตรวจสอบอีเมล คุณสามารถกำหนดให้มีการตรวจสอบอีเมลก่อนที่ผู้ใช้จะสามารถเข้าสู่ระบบได้ กำหนดค่านี้ใน "การตั้งค่าเซิร์ฟเวอร์" > "parse server" > "การตรวจสอบสิทธิ์ผู้ใช้" นโยบายรหัสผ่าน กำหนดความยาวและความซับซ้อนขั้นต่ำของรหัสผ่าน กำหนดค่านี้ใน "การตั้งค่าเซิร์ฟเวอร์" > "parse server" > "การตรวจสอบสิทธิ์ผู้ใช้" ระยะเวลาการใช้งานเซสชัน ควบคุมระยะเวลาที่เซสชันของผู้ใช้ยังคงมีผล กำหนดค่านี้ใน "การตั้งค่าเซิร์ฟเวอร์" > "parse server" > "การกำหนดค่าเซสชัน" แม่แบบอีเมล ปรับแต่งอีเมลการตรวจสอบและการรีเซ็ตรหัสผ่าน กำหนดค่านี้ใน "การตั้งค่าแอป" > "แม่แบบอีเมล" การทดสอบการใช้งานการพิสูจน์ตัวตนของคุณ เพื่อให้แน่ใจว่าระบบการพิสูจน์ตัวตนของคุณทำงานได้อย่างถูกต้อง ทดสอบการลงทะเบียนผู้ใช้ ลองลงทะเบียนด้วยข้อมูลที่ถูกต้อง ลองลงทะเบียนด้วยชื่อผู้ใช้ที่มีอยู่แล้ว (ควรแสดงข้อผิดพลาด) ตรวจสอบว่าผู้ใช้ปรากฏในแดชบอร์ด back4app ของคุณภายใต้คลาส " user" ทดสอบการเข้าสู่ระบบผู้ใช้ ลองเข้าสู่ระบบด้วยข้อมูลที่ถูกต้อง (ควรเปลี่ยนเส้นทางไปยังฟีด) ลองเข้าสู่ระบบด้วยข้อมูลที่ไม่ถูกต้อง (ควรแสดงข้อผิดพลาด) ทดสอบการเก็บรักษาเซสชัน เข้าสู่ระบบและรีเฟรชหน้า (ควรยังคงอยู่ในสถานะเข้าสู่ระบบ) ปิดและเปิดเบราว์เซอร์ใหม่ (ควรยังคงอยู่ในสถานะเข้าสู่ระบบหากเซสชันถูกต้อง) ทดสอบเส้นทางที่ป้องกัน ลองเข้าถึง /feed เมื่อออกจากระบบ (ควรเปลี่ยนเส้นทางไปยังหน้าล็อกอิน) ลองเข้าถึง /feed เมื่อเข้าสู่ระบบ (ควรแสดงหน้าฟีด) โค้ดสำหรับคอมโพเนนต์ login สามารถพบได้ที่นี่ ขั้นตอนที่ 4 — การพัฒนาฟังก์ชัน feed ในขั้นตอนนี้ คุณจะดำเนินการฟีเจอร์หลักของเครือข่ายสังคม ฟีด นี่คือที่ที่ผู้ใช้จะสร้างโพสต์ ดูเนื้อหาจากผู้อื่น และมีปฏิสัมพันธ์ผ่านการกดถูกใจและความคิดเห็น เราจะใช้ parse server ของ back4app เพื่อเก็บและเรียกโพสต์ จัดการการอัปโหลดไฟล์สำหรับภาพ และดำเนินการอัปเดตแบบเรียลไทม์ การเข้าใจโครงสร้างหน้า feed หน้า feed ในแอปพลิเคชันของเรามีสามส่วนหลัก แถบด้านข้างสำหรับการนำทาง พื้นที่ feed หลักสำหรับการสร้างโพสต์และการแสดงรายการโพสต์ ส่วนที่กำลังเป็นที่นิยม (บนหน้าจอขนาดใหญ่) มาดูว่ามันถูกนำไปใช้ใน feedpage js ของเราอย่างไร // from feedpage js main structure function feedpage() { // state and hooks return ( \<flex minh="100vh" bg="gray 800" color="white"> {/ left sidebar (navigation) /} \<box w={\['0px', '200px']} bg="gray 900" p={4} display={\['none', 'block']}> {/ navigation links /} \</box> {/ main content (feed) /} \<box flex="1" p={4} overflowy="auto"> {/ post creation form /} {/ posts list /} \</box> {/ right sidebar (trending) /} \<box w={\['0px', '250px']} bg="gray 700" p={4} display={\['none', 'block']}> {/ trending content /} \</box> \</flex> ); } เลย์เอาต์ที่ตอบสนองนี้ปรับให้เข้ากับขนาดหน้าจอที่แตกต่างกัน โดยซ่อนแถบด้านข้างในอุปกรณ์มือถือ การสร้างคลาสโพสต์ใน back4app ก่อนที่จะดำเนินการส่วนหน้า ให้เรามั่นใจว่าฐานข้อมูล back4app ของเราได้รับการตั้งค่าอย่างถูกต้องสำหรับโพสต์ คลาสโพสต์ควรมีฟิลด์ดังต่อไปนี้ เนื้อหา (string) เนื้อหาข้อความของโพสต์ ภาพ (file) การแนบภาพที่เลือกได้ ผู้เขียน (pointer to user) ผู้ใช้ที่สร้างโพสต์ ถูกใจ (number) จำนวนการถูกใจในโพสต์ ถูกใจโดย (array) อาร์เรย์ของรหัสผู้ใช้ที่ถูกใจโพสต์ สร้างเมื่อ (date) เพิ่มโดยอัตโนมัติโดย parse ตั้งค่าการอนุญาตที่เหมาะสมสำหรับคลาสโพสต์ การเข้าถึงอ่านสาธารณะ ทุกคนควรสามารถอ่านโพสต์ได้ การเข้าถึงเขียนสาธารณะ ผู้ใช้ที่ผ่านการตรวจสอบควรสามารถสร้างโพสต์ได้ การอนุญาตในการอัปเดต/ลบ เฉพาะผู้เขียนเท่านั้นที่สามารถแก้ไขโพสต์ของตนได้ การสร้างโพสต์ มาดูว่าการสร้างโพสต์ถูกนำไปใช้ในคอมโพเนนต์ feedpage ของเราอย่างไร // from feedpage js post creation state const \[postcontent, setpostcontent] = usestate(''); const \[postimage, setpostimage] = usestate(null); const \[imagepreview, setimagepreview] = usestate(''); const \[issubmitting, setissubmitting] = usestate(false); // image selection handler const handleimageselect = (e) => { if (e target files && e target files\[0]) { const file = e target files\[0]; setpostimage(file); // create preview url const previewurl = url createobjecturl(file); setimagepreview(previewurl); } }; // post submission handler const handlesubmitpost = async () => { if (!postcontent trim() && !postimage) { toaster create({ title 'error', description 'please add some text or an image to your post', type 'error', }); return; } setissubmitting(true); try { // create a new post object const post = parse object extend('post'); const newpost = new post(); // set post content newpost set('content', postcontent); newpost set('author', parse user current()); newpost set('likes', 0); newpost set('likedby', \[]); // if there's an image, upload it if (postimage) { const parsefile = new parse file(postimage name, postimage); await parsefile save(); newpost set('image', parsefile); } // save the post await newpost save(); // reset form setpostcontent(''); setpostimage(null); setimagepreview(''); // refresh posts fetchposts(); toaster create({ title 'success', description 'your post has been published!', type 'success', }); } catch (error) { toaster create({ title 'error', description error message, type 'error', }); } finally { setissubmitting(false); } }; จุดสำคัญเกี่ยวกับการสร้างโพสต์ การจัดการไฟล์ใน parse parse file ใช้สำหรับอัปโหลดภาพไปยังที่เก็บของ back4app ไฟล์จะถูกบันทึกก่อน แล้วจึงแนบไปยังวัตถุโพสต์ back4app จะจัดการการเก็บไฟล์โดยอัตโนมัติและสร้าง url การสร้างวัตถุ parse เราขยายคลาส 'โพสต์' ด้วย parse object extend('post') เราสร้างอินสแตนซ์ใหม่ด้วย new post() เราตั้งค่าคุณสมบัติด้วย set() วิธีการ เราบันทึกวัตถุไปยัง back4app ด้วย save() การเชื่อมโยงผู้ใช้ เราจะเชื่อมโยงโพสต์กับผู้ใช้ปัจจุบันโดยใช้ parse user current() นี่จะสร้างความสัมพันธ์แบบพอยเตอร์ในฐานข้อมูล แบบฟอร์ม ui สำหรับการสร้างโพสต์มีลักษณะดังนี้ {/ post creation form /} \<box mb={6} bg="gray 700" p={4} borderradius="md"> \<vstack align="stretch" spacing={4}> \<textarea placeholder="what's on your mind?" value={postcontent} onchange={(e) => setpostcontent(e target value)} minh="100px" /> {imagepreview && ( \<box position="relative"> \<image src={imagepreview} maxh="200px" borderradius="md" /> \<iconbutton icon={\<closeicon />} size="sm" position="absolute" top="2" right="2" onclick={() => { setpostimage(null); setimagepreview(''); }} /> \</box> )} \<hstack> \<button lefticon={\<attachmenticon />} onclick={() => document getelementbyid('image upload') click()} \> add image \</button> \<input id="image upload" type="file" accept="image/ " onchange={handleimageselect} display="none" /> \<button colorscheme="blue" ml="auto" isloading={issubmitting} onclick={handlesubmitpost} disabled={(!postcontent trim() && !postimage) || issubmitting} \> post \</button> \</hstack> \</vstack> \</box> การดึงและแสดงโพสต์ ตอนนี้เรามาดูวิธีการดึงและแสดงโพสต์จาก back4app กัน // from feedpage js fetching posts const \[posts, setposts] = usestate(\[]); const \[isloading, setisloading] = usestate(true); const \[page, setpage] = usestate(0); const \[hasmore, sethasmore] = usestate(true); const postsperpage = 10; const fetchposts = async (loadmore = false) => { try { const currentpage = loadmore ? page + 1 0; // create a query for the post class const post = parse object extend('post'); const query = new parse query(post); // include the author object (pointer) query include('author'); // sort by creation date, newest first query descending('createdat'); // pagination query limit(postsperpage); query skip(currentpage postsperpage); // execute the query const results = await query find(); // process the results const fetchedposts = results map(post => ({ id post id, content post get('content'), image post get('image') ? post get('image') url() null, likes post get('likes') || 0, likedby post get('likedby') || \[], createdat post get('createdat'), author { id post get('author') id, username post get('author') get('username'), avatar post get('author') get('avatar') ? post get('author') get('avatar') url() null } })); // update state if (loadmore) { setposts(prevposts => \[ prevposts, fetchedposts]); setpage(currentpage); } else { setposts(fetchedposts); setpage(0); } // check if there are more posts to load sethasmore(results length === postsperpage); } catch (error) { console error('error fetching posts ', error); toaster create({ title 'error', description 'failed to load posts please try again ', type 'error', }); } finally { setisloading(false); } }; // load posts when component mounts useeffect(() => { fetchposts(); }, \[]); จุดสำคัญเกี่ยวกับการดึงโพสต์ การสร้างคำค้น เราสร้างคำค้นด้วย new parse query(post) เรารวมวัตถุที่เกี่ยวข้องด้วย query include('author') เราจัดเรียงด้วย query descending('createdat') เราทำการแบ่งหน้าโดยใช้ query limit() และ query skip() เราดำเนินการคำค้นด้วย query find() การประมวลผลผลลัพธ์ วัตถุ parse มี get() เมธอดเพื่อเข้าถึงคุณสมบัติ สำหรับฟิลด์ไฟล์ เราใช้ file url() เพื่อดึง url เราจะแปลงวัตถุ parse เป็นวัตถุ javascript ธรรมดาสำหรับสถานะ react การแบ่งหน้า เรานำฟังก์ชัน "โหลดเพิ่มเติม" มาใช้พร้อมกับการติดตามหน้า เราตรวจสอบว่ามีโพสต์เพิ่มเติมให้โหลดก่อนที่จะทำการร้องขอเพิ่มเติม โพสต์จะแสดงในรูปแบบรายการ {/ posts list /} {isloading ? ( \<center py={10}> \<spinner size="xl" /> \</center> ) posts length > 0 ? ( \<vstack spacing={4} align="stretch"> {posts map(post => ( \<box key={post id} p={4} bg="gray 700" borderradius="md"> {/ post header with author info /} \<hstack mb={2}> \<avatar root size="sm"> \<avatar fallback name={post author username} /> \<avatar image src={post author avatar} /> \</avatar root> \<text fontweight="bold">{post author username}\</text> \<text fontsize="sm" color="gray 400">• {formatdate(post createdat)}\</text> \</hstack> {/ post content /} \<text mb={4}>{post content}\</text> {/ post image if any /} {post image && ( \<image src={post image} maxh="400px" borderradius="md" mb={4} /> )} {/ post actions /} \<hstack> \<button variant="ghost" lefticon={\<likeicon />} onclick={() => handlelikepost(post id, post likedby)} color={post likedby includes(currentuser id) ? "blue 400" "white"} \> {post likes} likes \</button> \<button variant="ghost" lefticon={\<commenticon />} as={routerlink} to={`/post/${post id}`} \> comments \</button> \</hstack> \</box> ))} {/ load more button /} {hasmore && ( \<button onclick={() => fetchposts(true)} isloading={isloadingmore}> load more \</button> )} \</vstack> ) ( \<center py={10}> \<text>no posts yet be the first to post!\</text> \</center> )} การนำฟังก์ชันถูกใจไปใช้ เรามาตรวจสอบว่าฟังก์ชันการถูกใจถูกนำไปใช้ได้อย่างไร // from feedpage js like functionality const handlelikepost = async (postid, likedby) => { try { const currentuserid = parse user current() id; const isliked = likedby includes(currentuserid); // get the post object const post = parse object extend('post'); const query = new parse query(post); const post = await query get(postid); // update likes count and likedby array if (isliked) { // unlike remove user from likedby and decrement likes post set('likedby', likedby filter(id => id !== currentuserid)); post set('likes', (post get('likes') || 1) 1); } else { // like add user to likedby and increment likes post set('likedby', \[ likedby, currentuserid]); post set('likes', (post get('likes') || 0) + 1); } // save the updated post await post save(); // update local state setposts(prevposts => prevposts map(p => p id === postid ? { p, likes isliked ? p likes 1 p likes + 1, likedby isliked ? p likedby filter(id => id !== currentuserid) \[ p likedby, currentuserid] } p ) ); } catch (error) { console error('error liking post ', error); toaster create({ title 'error', description 'failed to like post please try again ', type 'error', }); } }; จุดสำคัญเกี่ยวกับฟังก์ชันการถูกใจ การอัปเดตที่มองโลกในแง่ดี เราจะอัปเดต ui ทันที ก่อนที่เซิร์ฟเวอร์จะยืนยันการเปลี่ยนแปลง สิ่งนี้ทำให้แอปดูตอบสนองได้มากขึ้น การอัปเดตวัตถุที่วิเคราะห์ เราดึงโพสต์เฉพาะด้วย query get(postid) เราปรับเปลี่ยนคุณสมบัติของมันด้วย post set() เราบันทึกการเปลี่ยนแปลงด้วย post save() การติดตามการถูกใจ เรารักษาทั้งจำนวน ( การถูกใจ ) และรายชื่อผู้ใช้ ( likedby ) สิ่งนี้ช่วยให้เราสามารถแสดงจำนวนที่ถูกต้องและกำหนดว่าผู้ใช้ปัจจุบันได้ถูกใจโพสต์หรือไม่ การนำการอัปเดตแบบเรียลไทม์ไปใช้ด้วย livequery (ไม่บังคับ) เพื่อให้ฟีดอัปเดตแบบเรียลไทม์เมื่อมีการสร้างโพสต์ใหม่ เราสามารถใช้ parse livequery // from feedpage js livequery setup const livequerysubscription = useref(null); useeffect(() => { // set up livequery for real time updates const setuplivequery = async () => { try { const post = parse object extend('post'); const query = new parse query(post); // subscribe to new posts livequerysubscription current = await query subscribe(); // when a new post is created livequerysubscription current on('create', (post) => { // only add to feed if it's not already there setposts(prevposts => { if (prevposts some(p => p id === post id)) return prevposts; const newpost = { id post id, content post get('content'), image post get('image') ? post get('image') url() null, likes post get('likes') || 0, likedby post get('likedby') || \[], createdat post get('createdat'), author { id post get('author') id, username post get('author') get('username'), avatar post get('author') get('avatar') ? post get('author') get('avatar') url() null } }; return \[newpost, prevposts]; }); }); // when a post is updated (e g , liked) livequerysubscription current on('update', (post) => { setposts(prevposts => prevposts map(p => p id === post id ? { p, content post get('content'), image post get('image') ? post get('image') url() p image, likes post get('likes') || 0, likedby post get('likedby') || \[] } p ) ); }); } catch (error) { console error('error setting up livequery ', error); } }; setuplivequery(); // clean up subscription when component unmounts return () => { if (livequerysubscription current) { livequerysubscription current unsubscribe(); } }; }, \[]); จุดสำคัญเกี่ยวกับ livequery การตั้งค่าการสมัครสมาชิก เราสร้างคำค้นหาและสมัครสมาชิกกับมันด้วย query subscribe() นี่จะสร้างการเชื่อมต่อ websocket กับเซิร์ฟเวอร์ livequery ของ back4app การจัดการเหตุการณ์ เราฟังเหตุการณ์ 'สร้าง' เมื่อมีการสร้างโพสต์ใหม่ เราฟังเหตุการณ์ 'อัปเดต' เมื่อมีการแก้ไขโพสต์ เราปรับปรุงสถานะท้องถิ่นของเราให้สอดคล้องกัน การทำความสะอาด เรายกเลิกการสมัครเมื่อส่วนประกอบถูกยกเลิกเพื่อลดการรั่วไหลของหน่วยความจำ การเพิ่มประสิทธิภาพการโหลดโพสต์ด้วยการแบ่งหน้า เราได้ดำเนินการแบ่งหน้าเบื้องต้นด้วยปุ่ม "โหลดเพิ่มเติม" แล้ว มาปรับปรุงด้วยการเลื่อนแบบไม่สิ้นสุดกันเถอะ // from feedpage js infinite scrolling const \[isloadingmore, setisloadingmore] = usestate(false); const feedref = useref(null); // intersection observer for infinite scrolling useeffect(() => { if (!hasmore) return; const observer = new intersectionobserver( (entries) => { if (entries\[0] isintersecting && !isloading && !isloadingmore) { loadmoreposts(); } }, { threshold 0 5 } ); const loadmoretrigger = document getelementbyid('load more trigger'); if (loadmoretrigger) { observer observe(loadmoretrigger); } return () => { if (loadmoretrigger) { observer unobserve(loadmoretrigger); } }; }, \[hasmore, isloading, isloadingmore]); const loadmoreposts = async () => { if (!hasmore || isloadingmore) return; setisloadingmore(true); try { await fetchposts(true); } finally { setisloadingmore(false); } }; และเพิ่มสิ่งนี้ที่ท้ายรายการโพสต์ {/ infinite scroll trigger /} {hasmore && ( \<box id="load more trigger" h="20px" /> )} จุดสำคัญเกี่ยวกับการเลื่อนแบบไม่สิ้นสุด intersection observer เราใช้ intersection observer api เพื่อตรวจจับเมื่อผู้ใช้เลื่อนถึงด้านล่าง เมื่อองค์ประกอบที่กระตุ้นปรากฏขึ้น เราจะโหลดโพสต์เพิ่มเติม สถานะการโหลด เราติดตามสถานะการโหลดที่แยกต่างหากสำหรับการโหลดเริ่มต้นและ "โหลดเพิ่มเติม" นี่ช่วยป้องกันคำขอที่เกิดขึ้นพร้อมกันหลายคำขอ การพิจารณาด้านประสิทธิภาพ เราโหลดโพสต์จำนวนคงที่ในแต่ละครั้ง (การแบ่งหน้า) เราตรวจสอบว่ามีโพสต์เพิ่มเติมที่จะโหลดก่อนที่จะทำการร้องขอเพิ่มเติม การเพิ่มประสิทธิภาพ back4app เพื่อเพิ่มประสิทธิภาพเมื่อทำงานกับ back4app ใช้ดัชนี เพิ่มดัชนีไปยังฟิลด์ที่ถูกสอบถามบ่อยในแดชบอร์ด back4app ของคุณ สำหรับคลาสโพสต์ ให้เพิ่มดัชนีที่ 'createdat' และ 'author' การสอบถามที่เลือก ใช้ query select() เพื่อดึงเฉพาะฟิลด์ที่คุณต้องการ สิ่งนี้ช่วยลดการถ่ายโอนข้อมูลและปรับปรุงประสิทธิภาพ การเพิ่มประสิทธิภาพการนับ แทนที่จะดึงโพสต์ทั้งหมดเพื่อนับ ให้ใช้ query count() สิ่งนี้มีประสิทธิภาพมากขึ้นในการกำหนดจำนวนรวม ขั้นตอนที่ 6 — การเพิ่มการมีส่วนร่วมทางสังคม ในขั้นตอนนี้ เราจะเสริมสร้างเครือข่ายสังคมของเราโดยการนำฟีเจอร์การโต้ตอบทางสังคมที่สำคัญมาใช้ ความคิดเห็นบนโพสต์ โปรไฟล์ผู้ใช้ และการตั้งค่าผู้ใช้ เราจะมุ่งเน้นไปที่วิธีที่ฟีเจอร์เหล่านี้โต้ตอบกับ back4app backend และกลไกที่ทำให้มันทำงานได้ การนำความคิดเห็นมาใช้บนโพสต์ ความคิดเห็นเป็นฟีเจอร์การโต้ตอบทางสังคมที่สำคัญซึ่งต้องการการสร้างแบบจำลองข้อมูลที่เหมาะสมใน back4app มาดูกันว่าแอปพลิเคชันของเราโต้ตอบกับ parse server อย่างไรในการนำความคิดเห็นมาใช้ โมเดลข้อมูล back4app สำหรับความคิดเห็น ใน back4app ความคิดเห็นจะถูกนำมาใช้เป็นคลาสแยกที่มีความสัมพันธ์กับทั้งผู้ใช้และโพสต์ โครงสร้างคลาสความคิดเห็น เนื้อหา (string) เนื้อหาข้อความของความคิดเห็น ผู้เขียน (pointer to user) ชี้ไปยังผู้ใช้ที่สร้างความคิดเห็น โพสต์ (pointer to post) ชี้ไปยังโพสต์ที่ถูกแสดงความคิดเห็น สร้างเมื่อ (date) จัดการโดยอัตโนมัติจาก parse ประเภทความสัมพันธ์ ผู้ใช้ → ความคิดเห็น หนึ่งต่อหลาย (ผู้ใช้หนึ่งคนสามารถสร้างความคิดเห็นได้หลายความคิดเห็น) โพสต์ → ความคิดเห็น หนึ่งต่อหลาย (โพสต์หนึ่งโพสต์สามารถมีความคิดเห็นได้หลายความคิดเห็น) การดึงความคิดเห็นจาก back4app หน้า postdetailspage ของเราใช้การค้นหา parse เพื่อดึงความคิดเห็นสำหรับโพสต์เฉพาะ // from postdetailspage js comment fetching const fetchcomments = async () => { try { // create a query on the comment class const comment = parse object extend('comment'); const query = new parse query(comment); // find comments for this specific post using a pointer equality constraint query equalto('post', postobject); // include the author information query include('author'); // sort by creation date (newest first) query descending('createdat'); // execute the query const results = await query find(); // transform parse objects to plain objects for react state const commentslist = results map(comment => ({ id comment id, content comment get('content'), createdat comment get('createdat'), author { id comment get('author') id, username comment get('author') get('username'), avatar comment get('author') get('avatar') ? comment get('author') get('avatar') url() null } })); setcomments(commentslist); } catch (error) { console error('error fetching comments ', error); toaster create({ title 'error', description 'failed to load comments', type 'error', }); } }; กลไกหลักของ back4app parse object extend() สร้างการอ้างอิงไปยังคลาส comment ใน back4app query equalto() สร้างข้อจำกัดเพื่อค้นหาความคิดเห็นเฉพาะสำหรับโพสต์เฉพาะ query include() ดำเนินการเหมือนการเข้าร่วมเพื่อดึงวัตถุที่เกี่ยวข้องในคำค้นหาเดียว query descending() เรียงลำดับผลลัพธ์ตามฟิลด์เฉพาะ การสร้างความคิดเห็นใน back4app เมื่อผู้ใช้เพิ่มความคิดเห็น เราจะสร้างวัตถุ parse ใหม่และสร้างความสัมพันธ์ // from postdetailspage js adding a comment const handleaddcomment = async (e) => { e preventdefault(); if (!newcomment trim()) { return; } setiscommenting(true); try { // create a new comment object in back4app const comment = parse object extend('comment'); const comment = new comment(); // set comment data and relationships comment set('content', newcomment); comment set('author', parse user current()); // pointer to current user comment set('post', postobject); // pointer to current post // save the comment to back4app await comment save(); // clear the input setnewcomment(''); // refresh comments fetchcomments(); toaster create({ title 'success', description 'your comment has been added', type 'success', }); } catch (error) { toaster create({ title 'error', description error message, type 'error', }); } finally { setiscommenting(false); } }; กลไกหลักของ back4app new comment() สร้างอินสแตนซ์ใหม่ของคลาสความคิดเห็น comment set() ตั้งค่าคุณสมบัติบนวัตถุ parse รวมถึงชี้ไปที่วัตถุที่เกี่ยวข้อง comment save() ส่งวัตถุไปยัง back4app เพื่อจัดเก็บ parse user current() ดึงผู้ใช้ที่ได้รับการตรวจสอบสิทธิ์ในปัจจุบันเพื่อสร้างความสัมพันธ์ของผู้เขียน back4app ความปลอดภัยสำหรับความคิดเห็น เพื่อรักษาความปลอดภัยความคิดเห็นใน back4app อย่างถูกต้อง กำหนดการอนุญาตระดับคลาส (clps) อ่าน สาธารณะ (ทุกคนสามารถอ่านความคิดเห็นได้) เขียน ผู้ใช้ที่ได้รับการตรวจสอบเท่านั้น (เฉพาะผู้ใช้ที่ล็อกอินเท่านั้นที่สามารถแสดงความคิดเห็นได้) อัปเดต/ลบ ผู้สร้างเท่านั้น (เฉพาะผู้เขียนความคิดเห็นเท่านั้นที่สามารถแก้ไขหรือลบได้) ตั้งค่าการอนุญาตเหล่านี้ในแดชบอร์ด back4app ของคุณ { "find" { " " true }, "get" { " " true }, "create" { " " true }, "update" { "requiresauthentication" true }, "delete" { "requiresauthentication" true }, "addfield" { "requiresauthentication" true } } ขั้นตอนที่ 7 สร้างโปรไฟล์ผู้ใช้ด้วย back4app โปรไฟล์ผู้ใช้ในแอปพลิเคชันของเราใช้คลาสผู้ใช้ที่สร้างไว้ใน parse พร้อมกับฟิลด์ที่กำหนดเอง มาดูว่า profilepage js ทำงานร่วมกับ back4app อย่างไร การขยายคลาสผู้ใช้ back4app คลาสผู้ใช้ parse ได้รับการขยายด้วยฟิลด์เพิ่มเติมสำหรับเครือข่ายสังคมของเรา ฟิลด์ผู้ใช้ที่กำหนดเอง อวตาร (ไฟล์) รูปโปรไฟล์ที่เก็บในที่เก็บไฟล์ของ back4app ชีวประวัติ (สตริง) ชีวประวัติของผู้ใช้ เว็บไซต์ (สตริง) url เว็บไซต์ของผู้ใช้ ชื่อแสดง (สตริง) ชื่อแสดงของผู้ใช้ การดึงข้อมูลผู้ใช้และโพสต์ หน้าโปรไฟล์ของเราดึงข้อมูลผู้ใช้และโพสต์ของผู้ใช้ // from profilepage js profile data fetching const fetchuserdata = async () => { try { // get current user from parse session const currentuser = await parse user current(); if (!currentuser) { navigate('/login'); return; } setuser(currentuser); // create a query to find posts by this user const post = parse object extend('post'); const query = new parse query(post); query equalto('author', currentuser); query include('author'); query descending('createdat'); const results = await query find(); // transform parse objects to plain objects const postslist = results map(post => ({ id post id, content post get('content'), image post get('image') ? post get('image') url() null, likes post get('likes') || 0, createdat post get('createdat'), author { id post get('author') id, username post get('author') get('username'), avatar post get('author') get('avatar') ? post get('author') get('avatar') url() null } })); setposts(postslist); setstats(prevstats => ({ prevstats, posts postslist length })); } catch (error) { console error('error fetching user data ', error); toaster create({ title 'error', description 'failed to load profile data', type 'error', }); } finally { setisloading(false); } }; กลไกหลักของ back4app parse user current() ดึงผู้ใช้ที่ได้รับการตรวจสอบจากโทเค็นเซสชัน query equalto('author', currentuser) สร้างข้อจำกัดความเท่ากันของพอยเตอร์เพื่อค้นหาโพสต์โดยผู้ใช้ปัจจุบัน post get('image') url() เข้าถึง url ของวัตถุไฟล์ parse ที่เก็บไว้ใน back4app การตั้งค่าผู้ใช้ หน้า settingspage ช่วยให้ผู้ใช้สามารถอัปเดตข้อมูลโปรไฟล์และจัดการการตั้งค่าบัญชีของตนได้ มาดูกันว่ามันทำงานร่วมกับ back4app อย่างไร // from settingspage js user settings implementation function settingspage() { const \[privacysettings, setprivacysettings] = usestate({ profilevisibility 'public', postprivacy 'friends' }); const \[twofactorauth, settwofactorauth] = usestate(false); const \[isopen, setisopen] = usestate(false); const cancelref = useref(); // save user settings to back4app const savesettings = async (settingstype, settingsdata) => { try { const currentuser = await parse user current(); if (!currentuser) { toaster create({ title 'error', description 'you must be logged in to save settings', type 'error', }); return; } // update the appropriate settings based on type switch (settingstype) { case 'privacy' currentuser set('privacysettings', settingsdata); break; case 'security' currentuser set('securitysettings', settingsdata); break; case 'notifications' currentuser set('notificationsettings', settingsdata); break; default break; } // save the user object await currentuser save(); toaster create({ title 'success', description 'your settings have been saved', type 'success', }); } catch (error) { toaster create({ title 'error', description error message, type 'error', }); } }; return ( \<box maxw="800px" mx="auto" p={4}> \<heading mb={6}>account settings\</heading> \<tabs root defaultvalue="profile"> \<tabs list> \<tabs trigger value="profile">profile\</tabs trigger> \<tabs trigger value="privacy">privacy\</tabs trigger> \<tabs trigger value="security">security\</tabs trigger> \<tabs trigger value="notifications">notifications\</tabs trigger> \<tabs indicator /> \</tabs list> {/ settings tabs content /} {/ /} \</tabs root> {/ account deactivation dialog /} \<dialog root open={isopen} onopenchange={setisopen}> {/ /} \</dialog root> \</box> ); } กลไกหลักของ back4app parse user current() ดึงผู้ใช้ปัจจุบันเพื่ออัปเดตการตั้งค่า currentuser set() อัปเดตคุณสมบัติของผู้ใช้ในวัตถุผู้ใช้ parse currentuser save() บันทึกการเปลี่ยนแปลงไปยัง back4app สคีมาการตั้งค่าผู้ใช้ back4app ในการตั้งค่าใน back4app เพิ่มฟิลด์เหล่านี้ในคลาสผู้ใช้ privacysettings (object) อ็อบเจ็กต์ json ที่มีการตั้งค่าความเป็นส่วนตัว securitysettings (object) อ็อบเจ็กต์ json ที่มีการตั้งค่าความปลอดภัย notificationsettings (object) อ็อบเจ็กต์ json ที่มีการตั้งค่าการแจ้งเตือน ตัวอย่างสคีมาสำหรับอ็อบเจ็กต์เหล่านี้ // privacysettings { "profilevisibility" "public", // หรือ "friends" หรือ "private" "postprivacy" "friends", // หรือ "public" หรือ "private" "showactivity" true } // securitysettings { "twofactorauth" false, "loginalerts" true } // notificationsettings { "likes" true, "comments" true, "follows" true, "messages" true } ฟังก์ชัน cloud ของ back4app สำหรับการโต้ตอบทางสังคม สำหรับการโต้ตอบทางสังคมที่ซับซ้อนมากขึ้น คุณสามารถใช้ cloud functions ใน back4app ได้ ตัวอย่างเช่น การติดตามการแจ้งเตือนความคิดเห็น unhandled content type // example cloud function for comment notifications parse cloud aftersave("comment", async (request) => { // only run for new comments, not updates if (request original) return; const comment = request object; const post = comment get("post"); const commenter = request user; // skip if user is commenting on their own post const postquery = new parse query("post"); const fullpost = await postquery get(post id, { usemasterkey true }); const postauthor = fullpost get("author"); if (postauthor id === commenter id) return; // create a notification const notification = parse object extend("notification"); const notification = new notification(); notification set("type", "comment"); notification set("fromuser", commenter); notification set("touser", postauthor); notification set("post", post); notification set("read", false); await notification save(null, { usemasterkey true }); }); ในการดำเนินการนี้ ไปที่แดชบอร์ด back4app ของคุณ ไปที่ "cloud code" > "cloud functions" สร้างฟังก์ชันใหม่ด้วยโค้ดด้านบน ปรับใช้ฟังก์ชัน ขั้นตอนที่ 8 — การสร้างการส่งข้อความแบบเรียลไทม์ ในขั้นตอนนี้ เราจะดำเนินการฟังก์ชันการส่งข้อความแบบเรียลไทม์โดยใช้ฟีเจอร์ livequery ของ back4app ซึ่งจะช่วยให้ผู้ใช้สามารถแลกเปลี่ยนข้อความได้ทันทีโดยไม่ต้องรีเฟรชหน้าเว็บ สร้างประสบการณ์การแชทที่มีชีวิตชีวาเหมือนกับแพลตฟอร์มการส่งข้อความยอดนิยม การทำความเข้าใจ back4app livequery ก่อนที่จะดำดิ่งสู่การดำเนินการ มาทำความเข้าใจว่า livequery ของ back4app ทำงานอย่างไร livequery คืออะไร? livequery เป็นฟีเจอร์ของ parse server ที่อนุญาตให้ไคลเอนต์สมัครสมาชิกเพื่อการค้นหา เมื่อวัตถุที่ตรงกับคำค้นหาเหล่านี้เปลี่ยนแปลง เซิร์ฟเวอร์จะส่งการอัปเดตไปยังลูกค้าที่สมัครสมาชิกโดยอัตโนมัติ นี่สร้างฟังก์ชันการทำงานแบบเรียลไทม์โดยไม่ต้องจัดการ websocket ที่ซับซ้อนด้วยตัวเอง การทำงานของ livequery livequery สร้างการเชื่อมต่อ websocket ระหว่างไคลเอนต์และเซิร์ฟเวอร์ ลูกค้าสมัครสมาชิกเพื่อดูแลสอบถามเฉพาะที่ต้องการติดตาม เมื่อข้อมูลที่ตรงกับคำค้นหาเหล่านี้เปลี่ยนแปลง เซิร์ฟเวอร์จะส่งเหตุการณ์ผ่าน websocket ลูกค้าได้รับเหตุการณ์เหล่านี้และอัปเดต ui ตามนั้น เหตุการณ์ livequery สร้าง ถูกกระตุ้นเมื่อมีวัตถุใหม่ที่ตรงกับการค้นหาถูกสร้างขึ้น อัปเดต ถูกกระตุ้นเมื่อมีการอัปเดตวัตถุที่มีอยู่ซึ่งตรงกับการค้นหา เข้า ถูกกระตุ้นเมื่อวัตถุเริ่มตรงกับคำค้น ออก ถูกกระตุ้นเมื่อวัตถุไม่ตรงกับการค้นหาอีกต่อไป ลบ ถูกกระตุ้นเมื่อวัตถุที่ตรงกับการค้นหาถูกลบ การตั้งค่า livequery ใน back4app เพื่อเปิดใช้งาน livequery สำหรับแอปพลิเคชันของคุณ ให้ทำตามขั้นตอนเหล่านี้ เปิดใช้งาน subdomain ของ back4app เข้าสู่ระบบบัญชี back4app ของคุณ ไปที่ "การตั้งค่าแอป" > "การตั้งค่าเซิร์ฟเวอร์" ค้นหา "server url และ live query" บล็อกและคลิกที่ "settings" ตรวจสอบตัวเลือก "เปิดใช้งานซับโดเมน back4app ของคุณ" โดเมนย่อยนี้จะทำหน้าที่เป็นเซิร์ฟเวอร์ livequery ของคุณ เปิดใช้งาน livequery ในหน้า ตั้งค่า เดียวกัน ให้ตรวจสอบตัวเลือก "เปิดใช้งาน live query" เลือกชั้นเรียนที่คุณต้องการติดตามด้วย livequery ข้อความ (สำหรับข้อความแชท) สถานะการพิมพ์ (สำหรับตัวบ่งชี้การพิมพ์) การสนทนา (สำหรับการอัปเดตการสนทนา) บันทึกการเปลี่ยนแปลงของคุณ บันทึก url เซิร์ฟเวอร์ livequery ของคุณ url เซิร์ฟเวอร์ livequery ของคุณจะอยู่ในรูปแบบ wss\ //yourappname back4app io คุณจะต้องใช้ url นี้เพื่อเริ่มต้นไคลเอนต์ livequery ในแอปพลิเคชัน react ของคุณ การตั้งค่า livequery ในแอป react ของคุณ ในการใช้ livequery ในแอปพลิเคชัน react ของคุณ คุณต้องเริ่มต้นไคลเอนต์ livequery // from parseconfig js or app js parse initialize( process env react app parse app id, process env react app parse js key ); parse serverurl = process env react app parse server url; // initialize live queries with your subdomain parse livequeryserverurl = process env react app parse live query url; // e g , 'wss\ //yourappname back4app io' ในไฟล์ env local ให้แน่ใจว่าได้รวม react app parse live query url=wss\ //yourappname back4app io การสร้างโมเดลข้อมูลสำหรับการส่งข้อความ ระบบการส่งข้อความของเราต้องการสองคลาสหลักใน back4app ชั้นเรียนสนทนา ผู้เข้าร่วม (อาเรย์) อาเรย์ของพอยเตอร์ผู้ใช้สำหรับผู้ใช้ในบทสนทนา ข้อความล่าสุด (string) เนื้อหาของข้อความล่าสุด วันที่ข้อความล่าสุด (วันที่) เวลาที่ส่งข้อความล่าสุด อัปเดตเมื่อ (วันที่) จัดการโดยอัตโนมัติจาก parse ประเภทข้อความ การสนทนา (ชี้) ชี้ไปที่การสนทนาที่ข้อความนี้เป็นส่วนหนึ่ง ผู้ส่ง (ชี้ไปที่) ชี้ไปที่ผู้ใช้ที่ส่งข้อความ เนื้อหา (string) เนื้อความของข้อความ อ่าน (boolean) ว่าข้อความถูกอ่านแล้วหรือไม่ สร้างเมื่อ (วันที่) จัดการโดยอัตโนมัติด้วย parse คลาส typingstatus การสนทนา (ชี้) ชี้ไปที่การสนทนา ผู้ใช้ (ชี้) ชี้ไปที่ผู้ใช้ที่กำลังพิมพ์ กำลังพิมพ์ (boolean) ว่าผู้ใช้กำลังพิมพ์อยู่ในขณะนี้ การนำเสนออินเทอร์เฟซการส่งข้อความ มาดูกันว่า messagespage ของเรานั้นทำการส่งข้อความแบบเรียลไทม์อย่างไร unhandled content type // from messagespage js component structure function messagespage() { const \[conversations, setconversations] = usestate(\[]); const \[selectedconversation, setselectedconversation] = usestate(null); const \[messages, setmessages] = usestate(\[]); const \[newmessage, setnewmessage] = usestate(''); const \[isloading, setisloading] = usestate(true); const \[typingusers, settypingusers] = usestate(\[]); const messagesendref = useref(null); const messagesubscription = useref(null); const typingsubscription = useref(null); const conversationsubscription = useref(null); const typingtimeoutref = useref(null); // rest of the component } คอมโพเนนต์นี้รักษาสถานะหลายส่วนไว้ unhandled content type conversations รายการสนทนาของผู้ใช้ selectedconversation การสนทนาที่เลือกในขณะนี้ messages ข้อความในการสนทนาที่เลือก typingusers ผู้ใช้ที่กำลังพิมพ์ในสนทนา นอกจากนี้ยังใช้ refs เพื่อเก็บการสมัครสมาชิก livequery และจัดการตัวบ่งชี้การพิมพ์ด้วย การสมัครสมาชิก livequery สำหรับข้อความ กุญแจสู่การส่งข้อความแบบเรียลไทม์คือการสมัครสมาชิก livequery สำหรับข้อความในสนทนาปัจจุบัน // from messagespage js livequery subscription for messages const subscribetomessages = async (conversation) => { try { // unsubscribe from previous subscription if exists if (messagesubscription current) { messagesubscription current unsubscribe(); } // create a query for messages in this conversation const message = parse object extend('message'); const query = new parse query(message); // create a pointer to the conversation const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = conversation id; // find messages for this conversation query equalto('conversation', conversationpointer); // include the sender information query include('sender'); // subscribe to the query const subscription = await query subscribe(); messagesubscription current = subscription; // when a new message is created subscription on('create', (message) => { // add the new message to our state setmessages(prevmessages => { // check if message already exists in our list const exists = prevmessages some(m => m id === message id); if (exists) return prevmessages; // add the new message return \[ prevmessages, { id message id, content message get('content'), createdat message get('createdat'), sender { id message get('sender') id, username message get('sender') get('username'), avatar message get('sender') get('avatar') ? message get('sender') get('avatar') url() null }, read message get('read') }]; }); // scroll to bottom when new message arrives scrolltobottom(); // mark message as read if from other user if (message get('sender') id !== parse user current() id) { markmessageasread(message); } }); } catch (error) { console error('error subscribing to messages ', error); } }; กลไก livequery ที่สำคัญ การสร้างคำค้น เราสร้างคำค้นสำหรับข้อความในสนทนาปัจจุบัน การสมัครรับข้อมูลคำค้น เราเรียก query subscribe() เพื่อเริ่มฟังการเปลี่ยนแปลง การจัดการเหตุการณ์ เราใช้ subscription on('create', callback) เพื่อจัดการข้อความใหม่ การยกเลิกการสมัคร เราจัดเก็บการอ้างอิงการสมัครและยกเลิกเมื่อจำเป็น การนำเสนอการพิมพ์ด้วย livequery การแสดงสถานะการพิมพ์เป็นอีกหนึ่งฟีเจอร์เรียลไทม์ที่นำเสนอด้วย livequery // from messagespage js livequery for typing indicators const subscribetotypingstatus = async (conversation) => { try { // unsubscribe from previous subscription if exists if (typingsubscription current) { typingsubscription current unsubscribe(); } // create a query for typing status in this conversation const typingstatus = parse object extend('typingstatus'); const query = new parse query(typingstatus); // create a pointer to the conversation const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = conversation id; // find typing status for this conversation query equalto('conversation', conversationpointer); // include the user information query include('user'); // subscribe to the query const subscription = await query subscribe(); typingsubscription current = subscription; // when a typing status is updated subscription on('update', (typingstatus) => { const user = typingstatus get('user'); const istyping = typingstatus get('istyping'); // update typing users list settypingusers(prevtypingusers => { // if user is typing, add them to the list if (istyping) { // check if user is already in the list const exists = prevtypingusers some(u => u id === user id); if (exists) return prevtypingusers; // add user to typing list return \[ prevtypingusers, { id user id, username user get('username') }]; } else { // if user stopped typing, remove them from the list return prevtypingusers filter(u => u id !== user id); } }); }); // when a new typing status is created subscription on('create', (typingstatus) => { const user = typingstatus get('user'); const istyping = typingstatus get('istyping'); // only add to typing users if they are actually typing if (istyping && user id !== parse user current() id) { settypingusers(prevtypingusers => { // check if user is already in the list const exists = prevtypingusers some(u => u id === user id); if (exists) return prevtypingusers; // add user to typing list return \[ prevtypingusers, { id user id, username user get('username') }]; }); } }); } catch (error) { console error('error subscribing to typing status ', error); } }; การอัปเดตสถานะการพิมพ์ เมื่อผู้ใช้พิมพ์ เราจะอัปเดตสถานะการพิมพ์ของพวกเขา // from messagespage js updating typing status const updatetypingstatus = async (istyping) => { if (!selectedconversation) return; try { const currentuser = await parse user current(); // create a pointer to the conversation const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = selectedconversation id; // check if a typing status already exists const typingstatus = parse object extend('typingstatus'); const query = new parse query(typingstatus); query equalto('conversation', conversationpointer); query equalto('user', currentuser); let typingstatus = await query first(); if (typingstatus) { // update existing typing status typingstatus set('istyping', istyping); } else { // create new typing status typingstatus = new typingstatus(); typingstatus set('conversation', conversationpointer); typingstatus set('user', currentuser); typingstatus set('istyping', istyping); } await typingstatus save(); } catch (error) { console error('error updating typing status ', error); } }; // handle typing indicator with debounce const handletyping = () => { updatetypingstatus(true); // clear previous timeout if (typingtimeoutref current) { cleartimeout(typingtimeoutref current); } // set typing to false after 3 seconds of inactivity typingtimeoutref current = settimeout(() => { updatetypingstatus(false); }, 3000); }; การส่งข้อความ เมื่อส่งข้อความ เราจะสร้างอ็อบเจ็กต์ข้อความใหม่และให้ livequery จัดการการอัปเดต // from messagespage js sending messages const sendmessage = async (e) => { e preventdefault(); if (!newmessage trim() || !selectedconversation) return; try { const currentuser = await parse user current(); // create a pointer to the conversation const conversation = parse object extend('conversation'); const conversationpointer = new conversation(); conversationpointer id = selectedconversation id; // create a new message const message = parse object extend('message'); const message = new message(); message set('content', newmessage); message set('sender', currentuser); message set('conversation', conversationpointer); message set('read', false); await message save(); // update the conversation with the last message const conversation = await new parse query(conversation) get(selectedconversation id); conversation set('lastmessage', newmessage); conversation set('lastmessagedate', new date()); await conversation save(); // clear the input setnewmessage(''); // set typing status to false updatetypingstatus(false); } catch (error) { console error('error sending message ', error); toast({ title 'error', description 'failed to send message', status 'error', duration 3000, isclosable true, }); } }; การทำความสะอาดการสมัคร livequery การทำความสะอาดการสมัคร livequery เมื่อไม่จำเป็นอีกต่อไปเป็นสิ่งสำคัญ // from messagespage js cleanup useeffect(() => { // fetch initial conversations fetchconversations(); subscribetoconversations(); // cleanup subscriptions when component unmounts return () => { if (messagesubscription current) { messagesubscription current unsubscribe(); } if (typingsubscription current) { typingsubscription current unsubscribe(); } if (conversationsubscription current) { conversationsubscription current unsubscribe(); } if (typingtimeoutref current) { cleartimeout(typingtimeoutref current); } }; }, \[]); ข้อพิจารณาด้านประสิทธิภาพ livequery ของ back4app เมื่อดำเนินการ livequery ให้พิจารณาเคล็ดลับด้านประสิทธิภาพเหล่านี้ ระบุให้ชัดเจนกับคำถาม สมัครสมาชิกเฉพาะข้อมูลที่คุณต้องการ ใช้ข้อจำกัดเพื่อลดขอบเขตของการสมัครสมาชิก ตัวอย่างเช่น สมัครสมาชิกเฉพาะข้อความในสนทนาปัจจุบัน จัดการการสมัครสมาชิกอย่างระมัดระวัง ยกเลิกการสมัครเมื่อไม่ต้องการข้อมูลอีกต่อไป สร้างการสมัครสมาชิกใหม่เมื่อบริบทเปลี่ยนแปลง เก็บอ้างอิงการสมัครสมาชิกเพื่อลงทะเบียนยกเลิกในภายหลัง ใช้ acls เพื่อความปลอดภัย ตั้งค่า acls ที่เหมาะสมบนข้อความและวัตถุการสนทนา ตรวจสอบให้แน่ใจว่าผู้ใช้สามารถเข้าถึงการสนทนาที่พวกเขาเป็นส่วนหนึ่งเท่านั้น livequery เคารพ acls ดังนั้นผู้ใช้ที่ไม่ได้รับอนุญาตจะไม่ได้รับการอัปเดต ปรับแต่งเซิร์ฟเวอร์ livequery ในแดชบอร์ด back4app ให้กำหนดค่าชั้นเรียนที่ต้องการ livequery อย่าเปิดใช้งาน livequery สำหรับชั้นเรียนที่ไม่ต้องการการอัปเดตแบบเรียลไทม์ ขั้นตอนที่ 9 — การนำฟังก์ชันการค้นหามาใช้ ในขั้นตอนนี้ เราจะดำเนินการฟังก์ชันการค้นหาที่ครอบคลุมสำหรับเครือข่ายสังคมของเรา ผู้ใช้จะสามารถค้นหาผู้ใช้คนอื่น โพสต์ตามเนื้อหา และแฮชแท็ก ฟีเจอร์นี้จะทำให้ผู้ใช้ค้นพบเนื้อหาและเชื่อมต่อกับผู้อื่นบนแพลตฟอร์มได้ง่ายขึ้น การเข้าใจการค้นหาใน back4app ก่อนที่จะดำดิ่งสู่การดำเนินการ มาทำความเข้าใจว่าการค้นหาใน back4app ทำงานอย่างไร ระบบค้นหาคำสั่ง back4app ใช้ระบบค้นหาของ parse server สำหรับการค้นหา สามารถทำการค้นหาได้ในหลายคลาส คุณสามารถค้นหาตามการจับคู่ที่แน่นอน, มี, เริ่มต้นด้วย, ฯลฯ ตัวเลือกการค้นหาข้อความ เริ่มต้นด้วย ค้นหาสตริงที่เริ่มต้นด้วยสตริงเฉพาะ มี ค้นหาสตริงที่มีซับสตริงเฉพาะ ตรงกัน ใช้การแสดงออกปกติเพื่อการจับคู่รูปแบบที่ซับซ้อนมากขึ้น ค้นหาข้อความทั้งหมด (ฟีเจอร์สำหรับองค์กร) ให้ความสามารถในการค้นหาข้อความทั้งหมดที่ล้ำสมัย ข้อพิจารณาด้านประสิทธิภาพ การค้นหาข้อความอาจใช้ทรัพยากรมาก ควรสร้างดัชนีสำหรับฟิลด์ที่ค้นหาบ่อย ควรปรับแต่งคำค้นหาเพื่อลดจำนวนผลลัพธ์ การสร้างหน้าค้นหา คอมโพเนนต์ searchpage ของเราจะจัดการกับประเภทการค้นหาที่แตกต่างกันและแสดงผลลัพธ์ มาดูโครงสร้างของมันกัน // from searchpage js component structure function searchpage() { const \[searchquery, setsearchquery] = usestate(''); const \[searchtype, setsearchtype] = usestate('users'); // 'users', 'posts', 'hashtags' const \[searchresults, setsearchresults] = usestate(\[]); const \[isloading, setisloading] = usestate(false); const \[trendingtopics, settrendingtopics] = usestate(\[]); // rest of the component } คอมโพเนนต์รักษาสถานะสำหรับ คำค้นที่ผู้ใช้ป้อน ประเภทของการค้นหาที่กำลังดำเนินการ ผลการค้นหา สถานะการโหลด หัวข้อที่กำลังเป็นที่นิยม การค้นหาผู้ใช้ มาดูวิธีการค้นหาผู้ใช้ใน back4app กันเถอะ // from searchpage js user search implementation const searchusers = async (query) => { setisloading(true); try { // create a query on the user class const userquery = new parse query(parse user); // search for usernames that contain the query string (case insensitive) userquery matches('username', new regexp(query, 'i')); // limit results to improve performance userquery limit(20); const users = await userquery find(); // transform parse objects to plain objects const userresults = users map(user => ({ id user id, username user get('username'), avatar user get('avatar') ? user get('avatar') url() null, bio user get('bio') || '' })); setsearchresults(userresults); } catch (error) { console error('error searching users ', error); toaster create({ title 'error', description 'failed to search users', type 'error', }); } finally { setisloading(false); } }; กลไกหลักของ back4app new parse query(parse user) สร้างคำค้นในคลาสผู้ใช้ userquery matches('username', new regexp(query, 'i')) ทำการจับคู่ regex ที่ไม่สนใจตัวพิมพ์ใหญ่บนชื่อผู้ใช้ userquery limit(20) จำกัดผลลัพธ์เพื่อปรับปรุงประสิทธิภาพ userquery find() ดำเนินการค้นหาและส่งคืนผู้ใช้ที่ตรงกัน การค้นหาคอนเทนต์โพสต์ ตอนนี้มาดูวิธีการค้นหาโพสต์ตามเนื้อหากันเถอะ // from searchpage js post search implementation const searchposts = async (query) => { setisloading(true); try { // create a query on the post class const post = parse object extend('post'); const postquery = new parse query(post); // search for posts with content containing the query string postquery matches('content', new regexp(query, 'i')); // include the author information postquery include('author'); // sort by creation date (newest first) postquery descending('createdat'); // limit results postquery limit(20); const posts = await postquery find(); // transform parse objects to plain objects const postresults = posts map(post => ({ id post id, content post get('content'), image post get('image') ? post get('image') url() null, likes post get('likes') || 0, createdat post get('createdat'), author { id post get('author') id, username post get('author') get('username'), avatar post get('author') get('avatar') ? post get('author') get('avatar') url() null } })); setsearchresults(postresults); } catch (error) { console error('error searching posts ', error); toaster create({ title 'error', description 'failed to search posts', type 'error', }); } finally { setisloading(false); } }; กลไกหลักของ back4app parse object extend('post') อ้างอิงถึงคลาสโพสต์ postquery matches('content', new regexp(query, 'i')) ทำการจับคู่ regex แบบไม่สนใจตัวพิมพ์ใหญ่ในเนื้อหาโพสต์ postquery include('author') รวมข้อมูลผู้เขียนในคำค้นเดียว postquery descending('createdat') เรียงลำดับผลลัพธ์ตามวันที่สร้าง การค้นหาฮาชแท็ก การค้นหาฮาชแท็กต้องใช้วิธีการที่แตกต่างออกไป เราจะค้นหาบทความที่มีฮาชแท็กอยู่ในนั้น // from searchpage js hashtag search implementation const searchhashtags = async (query) => { setisloading(true); try { // remove # if present at the beginning const hashtagquery = query startswith('#') ? query substring(1) query; // create a query on the post class const post = parse object extend('post'); const postquery = new parse query(post); // search for posts with content containing the hashtag // we use word boundaries to find actual hashtags postquery matches('content', new regexp(`#${hashtagquery}\\\b`, 'i')); // include the author information postquery include('author'); // sort by creation date (newest first) postquery descending('createdat'); // limit results postquery limit(20); const posts = await postquery find(); // transform parse objects to plain objects const hashtagresults = posts map(post => ({ id post id, content post get('content'), image post get('image') ? post get('image') url() null, likes post get('likes') || 0, createdat post get('createdat'), author { id post get('author') id, username post get('author') get('username'), avatar post get('author') get('avatar') ? post get('author') get('avatar') url() null } })); setsearchresults(hashtagresults); } catch (error) { console error('error searching hashtags ', error); toaster create({ title 'error', description 'failed to search hashtags', type 'error', }); } finally { setisloading(false); } }; กลไกหลักของ back4app เราใช้ regex กับขอบเขตของคำ ( \\\b ) เพื่อค้นหาแฮชแท็กที่แท้จริง วิธีนี้จะค้นหาบทความที่มีเนื้อหาที่มีแฮชแท็กเฉพาะ การนำเสนอหัวข้อที่กำลังเป็นที่นิยม ในการนำเสนอหัวข้อที่กำลังเป็นที่นิยม เราจำเป็นต้องวิเคราะห์โพสต์ล่าสุดและนับจำนวนการเกิดขึ้นของแฮชแท็ก // from searchpage js fetching trending topics const fetchtrendingtopics = async () => { try { // create a query on the post class const post = parse object extend('post'); const query = new parse query(post); // get posts from the last 7 days const oneweekago = new date(); oneweekago setdate(oneweekago getdate() 7); query greaterthan('createdat', oneweekago); // limit to a reasonable number for analysis query limit(500); const posts = await query find(); // extract hashtags from post content const hashtagcounts = {}; posts foreach(post => { const content = post get('content') || ''; // find all hashtags in the content const hashtags = content match(/#(\w+)/g) || \[]; // count occurrences of each hashtag hashtags foreach(hashtag => { const tag = hashtag tolowercase(); hashtagcounts\[tag] = (hashtagcounts\[tag] || 0) + 1; }); }); // convert to array and sort by count const trendingarray = object entries(hashtagcounts) map((\[hashtag, count]) => ({ hashtag, count })) sort((a, b) => b count a count) slice(0, 10); // get top 10 settrendingtopics(trendingarray); } catch (error) { console error('error fetching trending topics ', error); } }; กลไกหลักของ back4app เราสอบถามโพสต์จาก 7 วันที่ผ่านมาโดยใช้ query greaterthan('createdat', oneweekago) เราวิเคราะห์เนื้อหาเพื่อนำออกและนับแฮชแท็ก เราจัดเรียงตามความถี่เพื่อหาหมายเลขแฮชแท็กที่ได้รับความนิยมมากที่สุด การจัดการการค้นหา ตอนนี้เรามาดูวิธีการจัดการการค้นหาตามประเภทการค้นหา // from searchpage js search execution const handlesearch = (e) => { e preventdefault(); if (!searchquery trim()) return; switch (searchtype) { case 'users' searchusers(searchquery); break; case 'posts' searchposts(searchquery); break; case 'hashtags' searchhashtags(searchquery); break; default searchusers(searchquery); } }; การเพิ่มประสิทธิภาพการค้นหาใน back4app เพื่อเพิ่มประสิทธิภาพการค้นหาใน back4app สร้างดัชนี ไปที่แดชบอร์ด back4app ของคุณ ไปที่ "database browser" > เลือกคลาส (เช่น user, post) คลิกที่แท็บ "indexes" สร้างดัชนีสำหรับฟิลด์ที่ค้นหาบ่อย สำหรับคลาส user สร้างดัชนีบน username สำหรับคลาส post สร้างดัชนีบน content ใช้ข้อจำกัดการค้นหา ใช้ limit() เพื่อจำกัดจำนวนผลลัพธ์ ใช้ select() เพื่อดึงเฉพาะฟิลด์ที่คุณต้องการ ใช้ skip() สำหรับการแบ่งหน้าเมื่อจัดการกับชุดผลลัพธ์ขนาดใหญ่ พิจารณาใช้ cloud functions สำหรับการค้นหาที่ซับซ้อน สำหรับตรรกะการค้นหาที่ซับซ้อนมากขึ้น ให้ใช้ cloud function สิ่งนี้ช่วยให้คุณสามารถดำเนินการประมวลผลฝั่งเซิร์ฟเวอร์และส่งคืนผลลัพธ์ที่ปรับให้เหมาะสม ตัวอย่างฟังก์ชันคลาวด์สำหรับการค้นหาขั้นสูง // example cloud function for advanced search parse cloud define("advancedsearch", async (request) => { const { query, type, limit = 20 } = request params; if (!query) { throw new error("search query is required"); } let results = \[]; switch (type) { case 'users' const userquery = new parse query(parse user); userquery matches('username', new regexp(query, 'i')); userquery limit(limit); results = await userquery find({ usemasterkey true }); break; case 'posts' const post = parse object extend('post'); const postquery = new parse query(post); postquery matches('content', new regexp(query, 'i')); postquery include('author'); postquery limit(limit); results = await postquery find({ usemasterkey true }); break; // add more search types as needed default throw new error("invalid search type"); } return results; }); ขั้นตอนที่ 10 — การทดสอบและการปรับใช้เครือข่ายสังคมของคุณ ในขั้นตอนสุดท้ายนี้ เราจะพูดถึงวิธีการทดสอบแอปพลิเคชันของคุณ เตรียมพร้อมสำหรับการผลิต นำไปใช้งานกับบริการโฮสติ้ง และติดตามและปรับขนาดแบ็กเอนด์ back4app ของคุณ ขั้นตอนเหล่านี้มีความสำคัญเพื่อให้เครือข่ายสังคมของคุณทำงานได้อย่างราบรื่นในสภาพแวดล้อมการผลิต การทดสอบแอปพลิเคชันของคุณ ก่อนที่จะนำไปใช้งาน สิ่งสำคัญคือต้องทดสอบแอปพลิเคชันของคุณอย่างละเอียดเพื่อตรวจจับข้อบกพร่องหรือปัญหาใด ๆ 1\ การทดสอบด้วยมือ สร้างแผนการทดสอบที่ครอบคลุมฟีเจอร์หลักทั้งหมดของแอปพลิเคชันของคุณ การตรวจสอบสิทธิ์ผู้ใช้ ทดสอบการลงทะเบียนด้วยข้อมูลที่ถูกต้องและไม่ถูกต้อง ทดสอบการเข้าสู่ระบบด้วยข้อมูลประจำตัวที่ถูกต้องและไม่ถูกต้อง ทดสอบฟังก์ชันการรีเซ็ตรหัสผ่าน ทดสอบการเก็บสถานะเซสชันและการออกจากระบบ ฟังก์ชันการโพสต์ ทดสอบการสร้างโพสต์ด้วยข้อความและภาพ ทดสอบการดูโพสต์ในฟีด ทดสอบการถูกใจและแสดงความคิดเห็นในโพสต์ ทดสอบการลบโพสต์ ปฏิสัมพันธ์ทางสังคม ทดสอบการดูโปรไฟล์ผู้ใช้ ทดสอบการแสดงความคิดเห็นในโพสต์ ทดสอบการส่งข้อความแบบเรียลไทม์ ฟังก์ชันการค้นหา ทดสอบการค้นหาผู้ใช้ ทดสอบการค้นหาสำหรับโพสต์ ทดสอบการค้นหาแฮชแท็ก การทดสอบข้ามเบราว์เซอร์ ทดสอบบน chrome, firefox, safari, และ edge ทดสอบบนเบราว์เซอร์มือถือ 2\ การทดสอบอัตโนมัติ เพื่อการทดสอบที่มีความแข็งแกร่งมากขึ้น ให้ดำเนินการทดสอบอัตโนมัติ // example jest test for the login component import react from 'react'; import { render, fireevent, waitfor } from '@testing library/react'; import loginpage from ' /src/pages/loginpage'; import parse from 'parse/dist/parse min js'; // mock parse jest mock('parse/dist/parse min js', () => ({ user { login jest fn() } })); test('login form submits with username and password', async () => { parse user login mockresolvedvalueonce({ id '123', get () => 'testuser' }); const { getbylabeltext, getbyrole } = render(\<loginpage />); // fill in the form fireevent change(getbylabeltext(/username/i), { target { value 'testuser' } }); fireevent change(getbylabeltext(/password/i), { target { value 'password123' } }); // submit the form fireevent click(getbyrole('button', { name /log in/i })); // check if parse user login was called with correct arguments await waitfor(() => { expect(parse user login) tohavebeencalledwith('testuser', 'password123'); }); }); 3\ การทดสอบ back4app ทดสอบการกำหนดค่าของ back4app ของคุณ ฟังก์ชันคลาวด์ ทดสอบฟังก์ชันคลาวด์ทั้งหมดด้วยข้อมูลนำเข้าที่หลากหลาย ความปลอดภัย ตรวจสอบว่าอนุญาตระดับคลาสของคุณทำงานได้อย่างถูกต้อง livequery ทดสอบฟังก์ชันการทำงานแบบเรียลไทม์กับลูกค้าหลายราย การเตรียมความพร้อมสำหรับการผลิต ก่อนที่จะนำไปใช้งาน ให้ปรับแต่งแอปพลิเคชันของคุณสำหรับการผลิต 1\ การกำหนดค่าของสภาพแวดล้อม สร้างไฟล์สภาพแวดล้อมแยกสำหรับการพัฒนาและการผลิต \# env development react app parse app id=your dev app id react app parse js key=your dev js key react app parse server url=https //parseapi back4app com react app parse live query url=wss\ //your dev app back4app io \# env production react app parse app id=your production app id react app parse js key=your production js key react app parse server url=https //parseapi back4app com react app parse live query url=wss\ //your prod app back4app io 2\ การปรับแต่งการสร้าง ปรับแต่งการสร้าง react ของคุณ // in your package json "scripts" { "analyze" "source map explorer 'build/static/js/ js'", "build" "generate sourcemap=false react scripts build" } ติดตั้ง source map explorer เพื่อวิเคราะห์ขนาดของบันเดิลของคุณ npm install save dev source map explorer 3\ การปรับแต่งประสิทธิภาพ ใช้การแบ่งโค้ดเพื่อลดเวลาโหลดเริ่มต้น // in app js, use react lazy for route components import react, { suspense, lazy } from 'react'; import { browserrouter as router, routes, route } from 'react router dom'; import { chakraprovider } from '@chakra ui/react'; import loadingspinner from ' /components/loadingspinner'; // lazy load pages const landingpage = lazy(() => import(' /pages/landingpage')); const loginpage = lazy(() => import(' /pages/loginpage')); const feedpage = lazy(() => import(' /pages/feedpage')); // other pages function app() { return ( \<chakraprovider> \<router> \<suspense fallback={\<loadingspinner />}> \<routes> \<route path="/" element={\<landingpage />} /> \<route path="/login" element={\<loginpage />} /> \<route path="/feed" element={\<feedpage />} /> {/ other routes /} \</routes> \</suspense> \</router> \</chakraprovider> ); } การปรับใช้ไปยังบริการโฮสติ้ง มีหลายตัวเลือกสำหรับการปรับใช้แอปพลิเคชัน react ของคุณ 1\ การปรับใช้ไปยัง vercel vercel เป็นตัวเลือกที่ยอดเยี่ยมสำหรับแอปพลิเคชัน react ติดตั้ง vercel cli npm install g vercel ปรับใช้แอปพลิเคชันของคุณ vercel สำหรับการปรับใช้ในผลิตภัณฑ์ vercel prod 2\ การปรับใช้ไปยัง netlify netlify เป็นอีกหนึ่งตัวเลือกที่ยอดเยี่ยม ติดตั้ง netlify cli npm install g netlify cli สร้างแอปพลิเคชันของคุณ npm run build ปรับใช้ไปยัง netlify netlify deploy สำหรับการปรับใช้ในผลิตภัณฑ์ netlify deploy prod 3\ การปรับใช้ไปยัง github pages สำหรับตัวเลือกการปรับใช้ที่ง่าย ติดตั้ง gh pages npm install save dev gh pages เพิ่มลงใน package json "homepage" "https //yourusername github io/your repo name", "scripts" { "predeploy" "npm run build", "deploy" "gh pages d build" } ปรับใช้ npm run deploy การตรวจสอบและปรับขนาด backend ของ back4app เมื่อเครือข่ายสังคมของคุณเติบโตขึ้น คุณจะต้องตรวจสอบและปรับขนาด backend ของ back4app 1\ การตรวจสอบประสิทธิภาพ back4app มีเครื่องมือหลายอย่างในการตรวจสอบแอปพลิเคชันของคุณ แดชบอร์ดวิเคราะห์ ตรวจสอบคำขอ api การใช้งานพื้นที่เก็บข้อมูล และการดำเนินการไฟล์ บันทึก ตรวจสอบบันทึกเซิร์ฟเวอร์สำหรับข้อผิดพลาดและปัญหาด้านประสิทธิภาพ เมตริกประสิทธิภาพ ติดตามเวลาตอบสนองและระบุจุดคอขวด ในการเข้าถึงเครื่องมือเหล่านี้ ไปที่แดชบอร์ด back4app ของคุณ ไปที่ "การวิเคราะห์" เพื่อดูสถิติการใช้งาน ตรวจสอบ "บันทึก" สำหรับบันทึกการดำเนินการโดยละเอียด 2\ การขยายแบ็กเอนด์ของคุณ เมื่อฐานผู้ใช้ของคุณเติบโต คุณอาจต้องขยายแบ็กเอนด์ back4app ของคุณ อัปเกรดแผนของคุณ ย้ายไปยังแผนระดับสูงกว่าที่มีทรัพยากรมากขึ้น ปรับแต่งการค้นหา ใช้ดัชนีและจำกัดการค้นหาเพื่อปรับปรุงประสิทธิภาพ ใช้การแคช ใช้การแคชฝั่งไคลเอ็นต์สำหรับข้อมูลที่เข้าถึงบ่อย 3\ การปรับแต่งฐานข้อมูล ปรับแต่งฐานข้อมูลของคุณเพื่อประสิทธิภาพที่ดีกว่า สร้างดัชนี เพิ่มดัชนีไปยังฟิลด์ที่ถูกสอบถามบ่อยๆ // ตัวอย่าง การสร้างดัชนีในฟิลด์ 'username' ในคลาส user const schema = new parse schema(' user'); schema addindex('username index', { username 1 }); schema update(); ใช้ aggregation pipeline สำหรับการดำเนินการข้อมูลที่ซับซ้อน // ตัวอย่าง นับโพสต์โดยผู้ใช้ const pipeline = \[ { group { objectid '$author', count { $sum 1 } } } ]; const results = await parse cloud aggregate('post', pipeline); 4\ การใช้งาน cdn สำหรับสื่อ เพื่อการส่งภาพและสื่อที่รวดเร็วขึ้น กำหนดค่า cdn เช่น cloudflare หรือ amazon cloudfront อัปเดตการตั้งค่าการจัดเก็บไฟล์ back4app ของคุณเพื่อใช้ cdn อัปเดต url ไฟล์ในแอปพลิเคชันของคุณเพื่อใช้โดเมน cdn 5\ การตั้งค่าการแจ้งเตือนการตรวจสอบ ตั้งค่าการแจ้งเตือนเพื่อให้ได้รับการแจ้งเตือนเกี่ยวกับปัญหา ไปที่แดชบอร์ด back4app ของคุณ ไปที่ "การตั้งค่าแอป" > "การแจ้งเตือน" กำหนดค่าการแจ้งเตือนสำหรับ การใช้งาน api สูง อัตราความผิดพลาดพุ่งสูงขึ้น ขนาดฐานข้อมูลที่จำกัด เวลาที่เซิร์ฟเวอร์หยุดทำงาน สรุปสิ่งที่ได้ทำสำเร็จ ตลอดทั้งบทแนะนำนี้ คุณได้ ตั้งค่าโครงสร้างพื้นฐานแบ็คเอนด์ที่แข็งแกร่ง สร้างบัญชี back4app และกำหนดค่าการใช้งานของคุณ ออกแบบสคีมาฐานข้อมูลสำหรับผู้ใช้ โพสต์ ความคิดเห็น และข้อความ การตั้งค่าความปลอดภัยและสิทธิ์ระดับคลาสที่กำหนด ตั้งค่า livequery สำหรับฟังก์ชันการทำงานแบบเรียลไทม์ สร้างส่วนหน้า react สมัยใหม่ สร้าง ui ที่ตอบสนองด้วยส่วนประกอบ chakra ui ได้ดำเนินการจัดการเส้นทางฝั่งไคลเอนต์ด้วย react router พัฒนาส่วนประกอบที่นำกลับมาใช้ใหม่สำหรับโพสต์ ความคิดเห็น และโปรไฟล์ผู้ใช้ เชื่อมต่อส่วนหน้า (frontend) ของคุณกับ back4app โดยใช้ parse javascript sdk นำฟีเจอร์หลักของเครือข่ายสังคมมาใช้ การตรวจสอบสิทธิ์ผู้ใช้ (การลงทะเบียน, การเข้าสู่ระบบ, การรีเซ็ตรหัสผ่าน) การสร้างโพสต์และการมีปฏิสัมพันธ์ (ถูกใจ, ความคิดเห็น) โปรไฟล์ผู้ใช้และการตั้งค่า การส่งข้อความแบบเรียลไทม์ระหว่างผู้ใช้ ฟังก์ชันการค้นหาสำหรับผู้ใช้ โพสต์ และแฮชแท็ก ปรับให้เหมาะสมสำหรับการผลิต ดำเนินการปรับปรุงประสิทธิภาพเช่นการแยกโค้ด ตั้งค่าการกำหนดค่าสภาพแวดล้อมสำหรับการพัฒนาและการผลิต เรียนรู้วิธีการนำแอปพลิเคชันของคุณไปยังบริการโฮสติ้ง สำรวจกลยุทธ์การตรวจสอบและการปรับขนาดสำหรับแบ็กเอนด์ back4app ของคุณ ตอนนี้คุณมีพื้นฐานที่มั่นคงสำหรับแอปพลิเคชันเครือข่ายสังคมที่สามารถขยายและปรับแต่งให้ตรงกับความต้องการเฉพาะของคุณได้แล้ว ขั้นตอนถัดไปในการขยายแอปพลิเคชัน นี่คือวิธีที่น่าตื่นเต้นในการพัฒนาแอปพลิเคชันเครือข่ายสังคมของคุณ ฟีเจอร์สื่อขั้นสูง เพิ่มการสนับสนุนสำหรับการอัปโหลดและเล่นวิดีโอ ดำเนินการกรองภาพและเครื่องมือแก้ไข สร้างเรื่องราวหรือฟีเจอร์เนื้อหาชั่วคราว เพิ่มการสนับสนุนสำหรับ gifs และสื่อที่มีความหลากหลายอื่นๆ การมีปฏิสัมพันธ์ทางสังคมที่ดีขึ้น สร้างเครื่องมือแนะนำสำหรับข้อเสนอเพื่อน เพิ่มฟังก์ชันการทำงานของกลุ่มหรือชุมชน สร้างกิจกรรมที่มีความสามารถในการตอบรับคำเชิญ พัฒนาระบบการแจ้งเตือนสำหรับการโต้ตอบของผู้ใช้ ตัวเลือกการสร้างรายได้ ดำเนินการฟีเจอร์สมาชิกพรีเมียม เพิ่มการซื้อในแอปสำหรับสินค้าดิจิทัล สร้างตลาดสำหรับการทำธุรกรรมระหว่างผู้ใช้ รวมเข้ากับผู้ประมวลผลการชำระเงินเช่น stripe ประสบการณ์มือถือ แปลงแอปพลิเคชันของคุณเป็นแอปพลิเคชันเว็บเชิงพัฒนา (pwa) พัฒนาแอปมือถือแบบเนทีฟโดยใช้ react native ดำเนินการแจ้งเตือนแบบพุชสำหรับอุปกรณ์เคลื่อนที่ ปรับแต่ง ui สำหรับขนาดหน้าจอและทิศทางที่แตกต่างกัน การวิเคราะห์และข้อมูลเชิงลึก รวมเครื่องมือวิเคราะห์เพื่อติดตามการมีส่วนร่วมของผู้ใช้ สร้างแดชบอร์ดสำหรับประสิทธิภาพของเนื้อหา ดำเนินการทดสอบ a/b สำหรับฟีเจอร์ใหม่ พัฒนาข้อมูลเชิงลึกเกี่ยวกับพฤติกรรมผู้ใช้เพื่อปรับปรุงแพลตฟอร์ม การควบคุมเนื้อหา ดำเนินการกรองเนื้อหาโดยอัตโนมัติ สร้างระบบรายงานสำหรับเนื้อหาที่ไม่เหมาะสม พัฒนาเครื่องมือผู้ดูแลระบบสำหรับการตรวจสอบเนื้อหา ใช้การเรียนรู้ของเครื่องสำหรับการวิเคราะห์เนื้อหาที่ชาญฉลาด แหล่งข้อมูลเพิ่มเติมสำหรับการเรียนรู้ เพื่อขยายความรู้และทักษะของคุณต่อไป นี่คือแหล่งข้อมูลที่มีค่าบางประการ เอกสารและบทเรียนของ back4app เอกสาร back4app https //www back4app com/docs/get started/welcome คู่มือ parse javascript https //docs parseplatform org/js/guide/ ช่อง youtube ของ back4app https //www youtube com/c/back4app รีแอคและจาวาสคริปต์สมัยใหม่ เอกสาร react https //reactjs org/docs/getting started html javascript info https //javascript info/ หลักสูตร react ของ egghead io https //egghead io/q/react การออกแบบ ui และ ux เอกสาร chakra ui https //chakra ui com/docs/getting started รูปแบบการออกแบบ ui https //ui patterns com/ การวิจัย ux ของ nielsen norman group https //www nngroup com/articles/ การเพิ่มประสิทธิภาพ ประสิทธิภาพ web dev https //web dev/performance scoring/ การเพิ่มประสิทธิภาพของ react https //reactjs org/docs/optimizing performance html google pagespeed insights https //developers google com/speed/pagespeed/insights/ ชุมชนและการสนับสนุน สแต็กโอเวอร์โฟลว์ https //stackoverflow\ com/questions/tagged/parse platform ฟอรัมชุมชน parse https //community parseplatform org/ ชุมชนพัฒนา react https //dev to/t/react จำไว้ว่าการสร้างเครือข่ายสังคมที่ประสบความสำเร็จเป็นกระบวนการที่ต้องทำซ้ำ เริ่มต้นด้วยพื้นฐานที่มั่นคง (ซึ่งคุณมีอยู่แล้ว) รวบรวมข้อเสนอแนะแบบผู้ใช้ และปรับปรุงแอปพลิเคชันของคุณอย่างต่อเนื่องตามรูปแบบการใช้งานในโลกจริง เราหวังว่าบทแนะนำนี้จะมอบความรู้และความมั่นใจให้คุณในการสร้างแอปพลิเคชันที่น่าทึ่งด้วย react และ back4app ขอให้สนุกกับการเขียนโค้ด