ตอบสนององค์ประกอบการทำงานที่ไร้สัญชาติ, PureComponent, Component; อะไรคือความแตกต่างและเมื่อใดที่เราควรใช้อะไร


189

มารู้ว่าจากReact v15.3.0เรามีคลาสฐานใหม่ที่เรียกว่าPureComponentเพื่อขยายด้วยPureRenderMixinในตัว shouldComponentUpdateสิ่งที่ผมเข้าใจคือว่าภายใต้ฝากระโปรงนี้มีพนักงานเปรียบเทียบตื้นของประกอบฉากภายใน

ตอนนี้เรามี 3 วิธีในการกำหนดส่วนประกอบ React:

  1. องค์ประกอบไร้สัญชาติที่ใช้งานได้ซึ่งไม่ขยายชั้นเรียนใด ๆ
  2. ส่วนประกอบที่ขยายPureComponentชั้นเรียน
  3. องค์ประกอบปกติที่ขยายComponentชั้นเรียน

เมื่อก่อนเราเคยเรียกองค์ประกอบไร้สัญชาติว่าเป็น Pure Components หรือแม้กระทั่ง Dumb Components ดูเหมือนว่าความหมายทั้งหมดของคำว่า "บริสุทธิ์" ได้เปลี่ยนไปแล้วใน React

ถึงแม้ว่าผมจะเข้าใจความแตกต่างขั้นพื้นฐานระหว่างสามเหล่านี้ผมยังไม่แน่ใจว่าเมื่อมีการเลือกสิ่งที่ นอกจากนี้สิ่งที่ส่งผลกระทบต่อประสิทธิภาพและการค้าของแต่ละ?


อัปเดต :

นี่คือคำถามที่ฉันคาดว่าจะได้รับการชี้แจง:

  • ฉันควรเลือกที่จะกำหนดองค์ประกอบที่เรียบง่ายของฉันเป็นฟังก์ชั่น (เพื่อประโยชน์ของความเรียบง่าย) หรือขยายPureComponentชั้นเรียน (เพื่อประโยชน์ในการปฏิบัติงาน)?
  • การเพิ่มประสิทธิภาพนั้นทำให้ฉันได้แลกเปลี่ยนกับความเรียบง่ายที่ฉันเสียไปจริงหรือเปล่า?
  • ฉันจะต้องขยายComponentชั้นเรียนปกติหรือไม่เมื่อฉันสามารถใช้PureComponentเพื่อประสิทธิภาพที่ดีขึ้นได้หรือไม่?

คำตอบ:


315

คุณจะตัดสินใจอย่างไรคุณเลือกระหว่างสามสิ่งนี้อย่างไรโดยขึ้นอยู่กับวัตถุประสงค์ / ขนาด / อุปกรณ์ประกอบฉาก / พฤติกรรมของส่วนประกอบของเรา

การขยายจากReact.PureComponentหรือจากReact.Componentด้วยshouldComponentUpdateวิธีการที่กำหนดเองจะมีผลกระทบด้านประสิทธิภาพ การใช้ส่วนประกอบการทำงานแบบไร้รัฐเป็นตัวเลือก "สถาปัตยกรรม" และไม่ได้รับประโยชน์ด้านประสิทธิภาพใด ๆ นอกกรอบ (ยัง)

  • สำหรับส่วนประกอบที่เรียบง่าย แต่เป็นการนำเสนอที่จำเป็นต้องนำกลับมาใช้ใหม่ได้ง่ายให้เลือกใช้ส่วนประกอบที่ใช้งานได้แบบไร้รัฐ วิธีนี้คุณมั่นใจได้ว่าพวกเขาจะแยกจากตรรกะแอปจริงที่พวกเขาเป็นเรื่องง่ายที่จะทดสอบและพวกเขาไม่ได้มีผลข้างเคียงที่ไม่คาดคิด ข้อยกเว้นคือถ้าด้วยเหตุผลบางอย่างคุณมีจำนวนมากหรือถ้าคุณต้องการวิธีการเรนเดอร์ที่ดีที่สุด (เนื่องจากคุณไม่สามารถกำหนดshouldComponentUpdateองค์ประกอบการทำงานแบบไร้รัฐได้)

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

  • ขยายComponentและใช้งานของคุณเองshouldComponentUpdateหากคุณต้องการประสิทธิภาพที่เพิ่มขึ้นโดยการเปรียบเทียบตรรกะที่กำหนดเองระหว่างอุปกรณ์ประกอบฉากถัดไป / ปัจจุบันและรัฐ ตัวอย่างเช่นคุณสามารถทำการเปรียบเทียบแบบลึกได้อย่างรวดเร็วโดยใช้ lodash # isEqual:

    class MyComponent extends Component {
        shouldComponentUpdate (nextProps, nextState) {
            return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
        }
    }

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

รายละเอียดเพิ่มเติม

ฟังก์ชั่นไร้สัญชาติ:

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

ข้อดี:

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

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

จุดด้อย:

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

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

  • ไม่รองรับการอ้างอิง: https://github.com/facebook/react/issues/4936

คอมโพเนนต์ที่ขยายคลาส PureComponent VS คอมโพเนนต์ปกติที่ขยายคลาสคอมโพเนนต์:

ตอบสนองเคยมีPureRenderMixinคุณสามารถแนบกับคลาสที่กำหนดโดยใช้React.createClassไวยากรณ์ มิกซ์อินจะshouldComponentUpdateทำการเปรียบเทียบตื้น ๆ ระหว่างอุปกรณ์ประกอบฉากถัดไปกับรัฐถัดไปเพื่อตรวจสอบว่ามีอะไรเปลี่ยนแปลงหรือไม่ หากไม่มีอะไรเปลี่ยนแปลงก็ไม่จำเป็นต้องทำการเรนเดอร์ซ้ำอีกครั้ง

หากคุณต้องการใช้ไวยากรณ์ ES6 คุณไม่สามารถใช้มิกซ์อินได้ ดังนั้นเพื่อความสะดวก React แนะนำระดับที่คุณสามารถรับมรดกจากแทนการใช้PureComponent เพียงแค่การดำเนินการในลักษณะเดียวกันของ มันเป็นสิ่งอำนวยความสะดวกส่วนใหญ่คุณจึงไม่ต้องใช้มันด้วยตัวเองเนื่องจากการเปรียบเทียบระหว่างสถานะปัจจุบัน / ถัดไปที่ตื้นและอุปกรณ์ประกอบฉากน่าจะเป็นสถานการณ์ที่พบบ่อยที่สุดที่สามารถให้ประสิทธิภาพที่รวดเร็วแก่คุณได้ComponentPureComponentshouldComponentUpdatePureRendererMixin

ตัวอย่าง:

class UserAvatar extends Component {
    render() {
       return <div><img src={this.props.imageUrl} /> {{ this.props.username }} </div>
    }
} 

ขณะที่คุณสามารถเห็นผลลัพธ์ขึ้นอยู่กับและprops.imageUrl props.usernameหากอยู่ในองค์ประกอบหลักที่คุณแสดง<UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" />ด้วยอุปกรณ์ประกอบฉากเดียวกัน React จะเรียกrenderทุกครั้งแม้ว่าเอาต์พุตจะเหมือนกันทุกประการ โปรดจำไว้ว่าแม้ว่า React จะสร้างความแตกต่างดังนั้น DOM จะไม่ได้รับการอัปเดตจริง ๆ กระนั้นการแสดงความแตกต่างของโดมอาจมีราคาแพงดังนั้นในสถานการณ์นี้มันจะเป็นการสิ้นเปลือง

หากUserAvatarองค์ประกอบขยายออกไปPureComponentจะทำการเปรียบเทียบแบบตื้น และเนื่องจากอุปกรณ์ประกอบฉากและอุปกรณ์ต่อไปเหมือนกันrenderจะไม่ถูกเรียกเลย

หมายเหตุเกี่ยวกับคำจำกัดความของ "บริสุทธิ์" ใน React:

โดยทั่วไป "ฟังก์ชั่นบริสุทธิ์" เป็นฟังก์ชั่นที่ประเมินผลเสมอเพื่อให้ได้ผลลัพธ์เดียวกันที่ได้รับการป้อนข้อมูลเดียวกัน ผลลัพธ์ (สำหรับ React นั่นคือสิ่งที่ถูกส่งคืนโดยrenderวิธีการ) ไม่ได้ขึ้นอยู่กับประวัติ / รัฐใด ๆ และไม่มีผลข้างเคียงใด ๆ (การดำเนินการที่เปลี่ยน "โลก" นอกฟังก์ชั่น)

ในการตอบสนอง, ส่วนประกอบไร้สัญชาติไม่จำเป็นต้องมีส่วนประกอบที่บริสุทธิ์ตามนิยามข้างต้นถ้าคุณเรียกว่า "ไร้สัญชาติ" องค์ประกอบที่ไม่เคยเรียกร้องที่และที่ไม่ได้ใช้this.setStatethis.state

ในความเป็นจริงใน a PureComponentคุณยังคงสามารถทำผลข้างเคียงได้ในระหว่างวงจรชีวิต ตัวอย่างเช่นคุณสามารถส่งคำขอภายในอาแจ็กซ์componentDidMountหรือคุณอาจจะทำการคำนวณ DOM บางแบบไดนามิกปรับความสูงของ div renderภายใน

คำจำกัดความ "องค์ประกอบโง่" มีความหมาย "ปฏิบัติ" มากกว่า (อย่างน้อยในความเข้าใจของฉัน): องค์ประกอบโง่ "ได้รับการบอก" สิ่งที่ต้องทำโดยองค์ประกอบแม่ผ่านอุปกรณ์ประกอบฉากและไม่รู้วิธีการทำสิ่งต่าง ๆ แต่ใช้อุปกรณ์ประกอบฉาก โทรกลับแทน

ตัวอย่างของ "สมาร์ท" AvatarComponent:

class AvatarComponent extends Component {
    expandAvatar () {
        this.setState({ loading: true });
        sendAjaxRequest(...).then(() => {
            this.setState({ loading: false });
        });
    }        

    render () {
        <div onClick={this.expandAvatar}>
            <img src={this.props.username} />
        </div>
    }
}

ตัวอย่างของ "ใบ้" AvatarComponent:

class AvatarComponent extends Component {
    render () {
        <div onClick={this.props.onExpandAvatar}>
            {this.props.loading && <div className="spinner" />}
            <img src={this.props.username} />
        </div>
    }
}

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


1
ฉันขอขอบคุณคำตอบและความรู้ที่คุณแบ่งปัน แต่คำถามจริงของฉันคือเมื่อเราควรเลือกอะไร . สำหรับตัวอย่างเดียวกันที่คุณพูดถึงในคำตอบของคุณฉันจะกำหนดมันได้อย่างไร มันควรจะเป็นองค์ประกอบไร้สัญชาติทำงาน (ถ้าเป็นเพราะเหตุใด) หรือขยาย PureComponent (ทำไม?) หรือขยายชั้นองค์ประกอบ (อีกครั้งทำไม?) คุณจะตัดสินใจอย่างไรคุณเลือกระหว่างสามสิ่งนี้อย่างไรโดยขึ้นอยู่กับวัตถุประสงค์ / ขนาด / อุปกรณ์ประกอบฉาก / พฤติกรรมของส่วนประกอบของเรา
Yadhu Kiran

1
ไม่มีปัญหา. สำหรับองค์ประกอบไร้สายการทำงานมีรายการข้อดี / ข้อเสียที่ฉันคุณสามารถพิจารณาในการตัดสินใจว่าจะเป็นแบบที่ดี นั่นคือคำตอบที่คุณจุดแรก? ฉันจะพยายามตอบคำถามทางเลือกให้มากขึ้น
fabio.sussetto

2
ส่วนประกอบที่ใช้งานได้จะถูกแสดงผลซ้ำเมื่อองค์ประกอบหลักได้รับการอัปเดตแม้ว่าจะไม่ได้ใช้propsเลย ตัวอย่าง
AlexM

1
นี่เป็นหนึ่งในคำตอบที่ครอบคลุมที่สุดที่ฉันได้อ่านในบางครั้ง การทำงานที่ดี. หนึ่งความคิดเห็นเกี่ยวกับประโยคแรกมาก: เมื่อขยายคุณไม่ควรดำเนินการPureComponent shouldComponentUpdate()คุณควรเห็นคำเตือนถ้าคุณทำเช่นนี้จริง
jjramos

1
เพื่อให้ได้ประสิทธิภาพที่แท้จริงคุณควรลองใช้PureComponentส่วนประกอบที่มีคุณสมบัติออบเจ็กต์ / อาร์เรย์ที่ซ้อนกัน แน่นอนคุณต้องระวังสิ่งที่เกิดขึ้น หากฉันเข้าใจถูกต้องหากคุณไม่ได้ทำการเปลี่ยนอุปกรณ์ประกอบฉาก / รัฐโดยตรง (ซึ่ง React พยายามป้องกันไม่ให้คุณทำตามคำเตือน) หรือผ่านห้องสมุดภายนอกคุณควรใช้งานPureComponentแทนการใช้ที่อื่นแทนComponent... ของส่วนประกอบที่ง่ายมากที่จริง ๆ แล้วมันจะเร็วกว่าที่จะไม่ใช้มัน - ดูnews.ycombinator.com/item?id=14418576
Matt Browne

28

ฉันไม่ใช่อัจฉริยะเกินปฏิกิริยา แต่จากความเข้าใจของฉันเราสามารถใช้แต่ละองค์ประกอบในสถานการณ์ต่อไปนี้

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

  2. องค์ประกอบที่บริสุทธิ์ -นี่คือรายการที่มีวงจรชีวิตและพวกเขาจะกลับมาผลลัพธ์เดียวกันเสมอเมื่อได้รับชุดอุปกรณ์ประกอบฉากที่เฉพาะเจาะจง ส่วนประกอบเหล่านั้นสามารถใช้งานได้เมื่อแสดงรายการผลลัพธ์หรือข้อมูลวัตถุเฉพาะซึ่งไม่มีองค์ประกอบย่อยที่ซับซ้อนและใช้เพื่อดำเนินการที่มีผลกระทบต่อตัวเองเท่านั้น รายการแสดงบัตรผู้ใช้หรือรายการบัตรผลิตภัณฑ์ (ข้อมูลผลิตภัณฑ์พื้นฐาน) และการกระทำที่ผู้ใช้สามารถทำได้คือคลิกเพื่อดูหน้ารายละเอียดหรือสั่งซื้อ

  3. คอมโพเนนต์ปกติหรือคอมเพล็กซ์คอมเพล็กซ์ -ฉันใช้คำว่าคอมเพล็กซ์คอมเพล็กซ์เพราะโดยปกติแล้วจะเป็นองค์ประกอบระดับหน้าเว็บและประกอบด้วยองค์ประกอบย่อยมากมายและเนื่องจากเด็กแต่ละคนสามารถทำงานในแบบที่เป็นเอกลักษณ์ของตัวเองเพื่อให้คุณไม่มั่นใจ 100% ทำให้ผลลัพธ์เดียวกันกับสถานะที่กำหนด ดังที่ฉันพูดไปแล้วโดยปกติสิ่งเหล่านี้ควรใช้เป็นส่วนประกอบของคอนเทนเนอร์


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

5
  • React.Componentเป็นองค์ประกอบ "ปกติ" เริ่มต้น คุณประกาศโดยใช้คำหลักและclass extends React.Componentคิดว่าพวกเขาเป็นชั้นเรียนด้วยวิธีวงจรชีวิตตัวจัดการเหตุการณ์และวิธีการใด ๆ

  • React.PureComponentเป็นReact.Componentที่ดำเนินการshouldComponentUpdate()กับฟังก์ชั่นที่ไม่เปรียบเทียบตื้นของตนและprops stateคุณต้องใช้forceUpdate()ถ้าคุณรู้ว่าองค์ประกอบนั้นมีอุปกรณ์ประกอบฉากหรือระบุข้อมูลซ้อนที่เปลี่ยนแปลงและคุณต้องการแสดงผลอีกครั้ง ดังนั้นจึงไม่ดีถ้าคุณต้องการให้ส่วนประกอบทำการเรนเดอร์ใหม่เมื่ออาร์เรย์หรือวัตถุที่คุณส่งผ่านเป็นอุปกรณ์ประกอบฉากหรือการเปลี่ยนแปลงสถานะของคุณ

  • องค์ประกอบการทำงานเป็นองค์ประกอบที่ไม่มีฟังก์ชั่นวงจรชีวิต พวกมันไร้สัญชาติ แต่พวกมันก็ดีและสะอาดจนตอนนี้เรามีตะขอ (ตั้งแต่ตอบโต้ 16.8) ดังนั้นคุณจึงยังคงมีสถานะได้ ดังนั้นฉันเดาว่ามันเป็นแค่ "องค์ประกอบที่สะอาด"

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