การนำทางในระบบนิเวศ React.js

เผยแพร่แล้ว: 2022-03-11

การนำทางระบบนิเวศ react.js

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

เมื่อ React.js เปิดตัวเมื่อสองปีที่แล้ว ฉันเพิ่งเรียนรู้ Angular และเลิกใช้ React อย่างรวดเร็วเนื่องจากเป็นห้องสมุดเทมเพลตที่คลุมเครือ ในช่วงสองปีที่ผ่านมา Angular ได้รับความสนใจจากนักพัฒนา JavaScript และเกือบจะมีความหมายเหมือนกันกับการพัฒนา JS สมัยใหม่ ฉันยังเริ่มเห็น Angular ในสภาพแวดล้อมองค์กรที่อนุรักษ์นิยมมากๆ และฉันก็ยอมรับอนาคตที่สดใส

แต่ทันใดนั้น สิ่งแปลกประหลาดก็เกิดขึ้น ดูเหมือนว่า Angular จะตกเป็นเหยื่อของปรากฏการณ์ Osborne หรือ "ความตายโดยการประกาศล่วงหน้า" ทีมงานประกาศว่า อย่างแรก Angular 2 จะแตกต่างไปจากเดิมอย่างสิ้นเชิง โดยไม่มีเส้นทางการโยกย้ายที่ชัดเจนจาก Angular 1 และอย่างที่สอง Angular 2 จะไม่สามารถใช้ได้อีกหนึ่งปีหรือประมาณนั้น สิ่งนั้นบอกอะไรแก่ผู้ที่ต้องการเริ่มต้นโครงการเว็บใหม่ คุณต้องการเขียนโปรเจ็กต์ใหม่ของคุณในเฟรมเวิร์กที่เวอร์ชันใหม่จะล้าสมัยหรือไม่?

ความกังวลในหมู่นักพัฒนานี้อยู่ในมือของ React ซึ่งกำลังพยายามสร้างตัวเองในชุมชน แต่ React มักจะวางตลาดตัวเองเป็น "V" ใน "MVC" สิ่งนี้ทำให้เกิดความคับข้องใจในหมู่นักพัฒนาเว็บที่คุ้นเคยกับการทำงานกับเฟรมเวิร์กที่สมบูรณ์ ฉันจะเติมเต็มส่วนที่ขาดหายไปได้อย่างไร ฉันควรเขียนของตัวเองหรือไม่? ฉันควรใช้ห้องสมุดที่มีอยู่หรือไม่ ถ้าเป็นเช่นนั้นอันไหน?

แน่นอนว่า Facebook (ผู้สร้าง React.js) มีเอซอีกหนึ่งตัวอยู่ในหลุม: เวิร์กโฟลว์ Flux ซึ่งสัญญาว่าจะเติมเต็มฟังก์ชัน "M" และ "C" ที่ขาดหายไป เพื่อให้สิ่งต่าง ๆ น่าสนใจยิ่งขึ้น Facebook ระบุว่า Flux เป็น "รูปแบบ" ไม่ใช่เฟรมเวิร์ก และการใช้งาน Flux เป็นเพียงตัวอย่างหนึ่งของรูปแบบ ตามคำพูดของพวกเขา การใช้งานนั้นเรียบง่ายจริงๆ และเกี่ยวข้องกับการเขียนต้นแบบที่ละเอียดและซ้ำซากจำนวนมากเพื่อให้สิ่งต่างๆ ดำเนินไป

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

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

นั่นเป็นเหตุผลที่สิ่งใหม่ ๆ รอบ ๆ React น่าสนใจมาก ส่วนใหญ่สามารถนำกลับมาใช้ใหม่ได้อย่างง่ายดายในสภาพแวดล้อม JavaScript อื่นๆ แม้ว่าคุณจะไม่ได้วางแผนที่จะใช้ React การดูระบบนิเวศของมันก็เป็นแรงบันดาลใจ คุณอาจต้องการทำให้ระบบบิลด์ของคุณง่ายขึ้นโดยใช้การกำหนดค่าโมดูล Bundler Webpack ที่ทรงพลังแต่ค่อนข้างง่าย หรือเริ่มเขียน ECMAScript 6 และแม้แต่ ECMAScript 7 ในปัจจุบันด้วยคอมไพเลอร์ Babel

ในบทความนี้ ผมจะพูดถึงคุณลักษณะและไลบรารีที่น่าสนใจบางส่วนที่มีให้ใช้งาน มาสำรวจระบบนิเวศของ React กันเถอะ!

React Explorer ชาย

สร้างระบบ

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

  • การจัดการการพึ่งพาภายนอกและภายใน
  • การรันคอมไพเลอร์และตัวประมวลผลล่วงหน้า
  • การเพิ่มประสิทธิภาพสินทรัพย์เพื่อการผลิต
  • การรันเว็บเซิร์ฟเวอร์การพัฒนา โปรแกรมดูไฟล์ และตัวโหลดเบราว์เซอร์ใหม่

ในช่วงไม่กี่ปีที่ผ่านมา เวิร์กโฟลว์ของ Yeoman กับ Bower และ Grunt ถูกนำเสนอในฐานะทรินิตี้ศักดิ์สิทธิ์ของการพัฒนา frontend สมัยใหม่ การแก้ปัญหาของการสร้างต้นแบบ การจัดการแพ็คเกจ และงานทั่วไปที่ดำเนินไปตามลำดับ ด้วยการเปลี่ยนจาก Grunt เป็น Gulp ที่ก้าวหน้ามากขึ้นเมื่อเร็วๆ นี้

ในสภาพแวดล้อม React คุณสามารถลืมสิ่งเหล่านี้ได้อย่างปลอดภัย ไม่ใช่ว่าคุณใช้งานไม่ได้ แต่มีโอกาสที่คุณจะใช้ Webpack และ NPM แบบเก่าที่ดีได้ เป็นไปได้อย่างไร? Webpack เป็นโมดูลบันเดิล ซึ่งใช้ไวยากรณ์โมดูล CommonJS ซึ่งพบได้ทั่วไปในโลก Node.js ในเบราว์เซอร์เช่นกัน มันทำให้สิ่งต่าง ๆ ง่ายขึ้นจริง ๆ เนื่องจากคุณไม่จำเป็นต้องเรียนรู้ตัวจัดการแพ็คเกจอื่นสำหรับส่วนหน้า คุณเพียงแค่ใช้ NPM และแชร์การพึ่งพาระหว่างเซิร์ฟเวอร์และส่วนหน้า คุณไม่จำเป็นต้องจัดการกับปัญหาในการโหลดไฟล์ JS ในลำดับที่ถูกต้อง เพราะมันอนุมานจากการนำเข้าการขึ้นต่อกันที่ระบุในแต่ละไฟล์ และกลุ่มทั้งหมดถูกต่อเข้ากับสคริปต์ที่โหลดได้ไฟล์เดียวอย่างถูกต้อง

โลโก้ Webpack
เว็บแพ็ค

เพื่อให้น่าสนใจยิ่งขึ้น Webpack ไม่เหมือนกับ Browserify ลูกพี่ลูกน้องที่เก่ากว่า ซึ่งสามารถจัดการสินทรัพย์ประเภทอื่นๆ ได้เช่นกัน ตัวอย่างเช่น ด้วยตัวโหลด คุณสามารถแปลงไฟล์แอสเซทเป็นฟังก์ชัน JavaScript ที่อินไลน์หรือโหลดไฟล์อ้างอิง ดังนั้น ลืมการประมวลผลล่วงหน้าด้วยตนเองและการอ้างอิงเนื้อหาจาก HTML เพียงแค่ require ไฟล์ CSS/SASS/LESS จาก JavaScript แล้ว Webpack จะดูแลส่วนที่เหลือด้วยไฟล์กำหนดค่าอย่างง่าย Webpack ยังรวมถึงเว็บเซิร์ฟเวอร์การพัฒนาและโปรแกรมดูไฟล์ นอกจากนี้ คุณสามารถใช้คีย์ "scripts" ใน package.json เพื่อกำหนด shell one-liners:

 { "name": "react-example-filmdb", "version": "0.0.1", "description": "Isomorphic React + Flux film database example", "main": "server/index.js", "scripts": { "build": "./node_modules/.bin/webpack --progress --stats --config ./webpack/prod.config.js", "dev": "node --harmony ./webpack/dev-server.js", "prod": "NODE_ENV=production node server/index.js", "test": "./node_modules/.bin/karma start --single-run", "postinstall": "npm run build" } ... }

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

ที่เกี่ยวข้อง: ส่วนประกอบ React ทำให้การทดสอบ UI เป็นเรื่องง่าย

ECMAScript ของวันพรุ่งนี้ วันนี้

ความเร็วของการพัฒนาภาษา JavaScript เพิ่มขึ้นอย่างมากในช่วงไม่กี่ปีที่ผ่านมา และหลังจากช่วงเวลาแห่งการขจัดสิ่งแปลกปลอมและทำให้ภาษามีเสถียรภาพ ตอนนี้เราพบว่ามีคุณลักษณะใหม่ที่มีประสิทธิภาพเข้ามา ร่างข้อกำหนด ECMAScript 6 (ES6) ได้รับการสรุปแล้ว และถึงแม้ ยังไม่ได้รับการประกาศอย่างเป็นทางการ แต่กำลังพบการยอมรับอย่างกว้างขวาง กำลังดำเนินการเกี่ยวกับ ECMAScript 7 (ES7) แต่คุณลักษณะหลายอย่างได้ถูกนำมาใช้โดยไลบรารีที่ทันสมัยกว่าอยู่แล้ว

โลโก้ ECMAScript 7
ECMAScript7

เป็นไปได้อย่างไร? บางทีคุณอาจคิดว่าคุณไม่สามารถใช้ประโยชน์จากคุณลักษณะ JavaScript ใหม่เหล่านี้ได้จนกว่าจะได้รับการสนับสนุนใน Internet Explorer แต่ลองคิดดูอีกครั้ง ทรานสพิล ES แพร่หลายมากจนเราสามารถทำได้โดยไม่ได้รับการสนับสนุนเบราว์เซอร์ที่เหมาะสม ทรานสปิล ES ที่ดีที่สุดที่มีในตอนนี้คือ Babel: จะใช้โค้ด ES6+ ใหม่ล่าสุดของคุณและแปลงเป็น vanilla ES5 ดังนั้น คุณสามารถใช้ฟีเจอร์ ES ใหม่ใดๆ ได้ทันทีที่มีการคิดค้น (และใช้งานใน Babel ซึ่งมักจะเกิดขึ้นค่อนข้างมาก อย่างรวดเร็ว).

โลโก้ Babel
Babel

คุณลักษณะ JavaScript ใหม่ล่าสุดมีประโยชน์ในเฟรมเวิร์กส่วนหน้าทั้งหมด และ React เพิ่งได้รับการอัปเดตเพื่อให้ทำงานได้ดีกับข้อกำหนด ES6 และ ES7 คุณสมบัติใหม่เหล่านี้ควรขจัดปัญหาปวดหัวมากมายเมื่อพัฒนาด้วย React มาดูส่วนเสริมที่มีประโยชน์ที่สุดและประโยชน์ต่อโปรเจ็กต์ React กันอย่างไร ต่อมา เราจะมาดูวิธีใช้เครื่องมือและไลบรารีที่มีประโยชน์กับ React ในขณะที่ใช้ไวยากรณ์ที่ได้รับการปรับปรุงนี้

คลาส ES6

การเขียนโปรแกรมเชิงวัตถุเป็นกระบวนทัศน์ที่ทรงพลังและเป็นที่ยอมรับอย่างกว้างขวาง แต่จาวาสคริปต์นั้นค่อนข้างแปลกใหม่ เฟรมเวิร์กส่วนหน้าส่วนใหญ่ ไม่ว่าจะเป็น Backbone, Ember, Angular หรือ React ได้นำวิธีการกำหนดคลาสและสร้างวัตถุที่เป็นกรรมสิทธิ์ของตนเองมาใช้ แต่สำหรับ ES6 ตอนนี้ เรามีคลาสดั้งเดิมใน JavaScript และมันสมเหตุสมผลแล้วที่จะใช้คลาสเหล่านี้แทนที่จะเขียน Implementation ของเราเอง ดังนั้น แทนที่จะ:

 React.createClass({ displayName: 'HelloMessage', render() { return <div>Hello {this.props.name}</div>; } })

เราสามารถเขียน:

 class HelloMessage extends React.Component { render() { return <div>Hello {this.props.name}</div>; } }

สำหรับตัวอย่างที่ละเอียดยิ่งขึ้น ให้พิจารณาโค้ดนี้ โดยใช้ไวยากรณ์เก่า:

 React.createClass({ displayName: 'Counter', getDefaultProps: function(){ return {initialCount: 0}; }, getInitialState: function() { return {count: this.props.initialCount} }, propTypes: {initialCount: React.PropTypes.number}, tick() { this.setState({count: this.state.count + 1}); }, render() { return ( <div onClick={this.tick}> Clicks: {this.state.count} </div> ); } });

และเปรียบเทียบกับรุ่น ES6:

 class Counter extends React.Component { static propTypes = {initialCount: React.PropTypes.number}; static defaultProps = {initialCount: 0}; constructor(props) { super(props); this.state = {count: props.initialCount}; } state = {count: this.props.initialCount}; tick() { this.setState({count: this.state.count + 1}); } render() { return ( <div onClick={this.tick.bind(this)}> Clicks: {this.state.count} </div> ); } }

ที่นี่ เมธอด React lifecycle getDefaultProps และ getInitialState ไม่จำเป็นอีกต่อไป getDefaultProps กลายเป็นตัวแปรคลาสสแตติก defaultProps และสถานะเริ่มต้นถูกกำหนดไว้ในคอนสตรัคเตอร์ ข้อเสียเปรียบเพียงอย่างเดียวคือเมธอดจะไม่ถูกผูกไว้โดยอัตโนมัติอีกต่อไป ดังนั้นคุณต้องใช้การ bind เมื่อเรียกตัวจัดการจาก JSX

มัณฑนากร

มัณฑนากรเป็นคุณลักษณะที่มีประโยชน์จาก ES7 สิ่งเหล่านี้ช่วยให้คุณเพิ่มพฤติกรรมของฟังก์ชันหรือคลาสโดยรวมไว้ในฟังก์ชันอื่น ตัวอย่างเช่น สมมติว่าคุณต้องการมีตัวจัดการการเปลี่ยนแปลงที่เหมือนกันกับส่วนประกอบบางส่วนของคุณ แต่คุณไม่ต้องการส่งไปยัง antipattern การสืบทอด คุณสามารถใช้มัณฑนากรคลาสแทนได้ มากำหนดมัณฑนากรดังนี้:

 addChangeHandler: function(target) { target.prototype.changeHandler = function(key, attr, event) { var state = {}; state[key] = this.state[key] || {}; state[key][attr] = event.currentTarget.value; this.setState(state); }; return target; }

สิ่งสำคัญที่นี่คือฟังก์ชัน addChangeHandler เพิ่มฟังก์ชัน changeHandler ให้กับต้นแบบของคลาสเป้าหมาย

ในการใช้มัณฑนากร เราสามารถเขียน:

 MyClass = changeHandler(MyClass)

หรือมากกว่าอย่างหรูหราด้วยไวยากรณ์ ES7:

 @addChangeHandler class MyClass { ... }

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

 @addChangeHandler class LoginInput extends React.Component { constructor(props) { super(props); this.state = { login: {} }; } render() { return ( <input type='text' value={this.state.login.username} onChange={this.changeHandler.bind(this, 'login', 'username')} /> <input type='password' value={this.state.login.password} onChange={this.changeHandler.bind(this, 'login', 'password')} /> ) } }

เมื่อผู้ใช้เปลี่ยนฟิลด์ชื่อผู้ใช้ ค่าจะถูกบันทึกลงใน this.state.login.username โดยไม่ต้องกำหนดตัวจัดการแบบกำหนดเองเพิ่มเติม

ฟังก์ชั่นลูกศร

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

 class DirectorsStore { onFetch(directors) { var _this = this; this.directorsHash = {}; directors.forEach(function(x){ _this.directorsHash[x._id] = x; }) } }

ด้วยไวยากรณ์ ES6 ใหม่ function(x){ สามารถเขียนใหม่เป็น (x) => { คำจำกัดความเมธอด "ลูกศร" นี้ไม่เพียงแต่ผูก this กับขอบเขตภายนอกอย่างถูกต้อง แต่ยังสั้นกว่ามากอีกด้วย ซึ่งนับได้อย่างแน่นอนเมื่อเขียนโค้ดอะซิงโครนัสจำนวนมาก

 onFetch(directors) { this.directorsHash = {}; directors.forEach((x) => { this.directorsHash[x._id] = x; }) }

การมอบหมายงานทำลายล้าง

การกำหนดโครงสร้างแบบทำลายล้างที่แนะนำใน ES6 ช่วยให้คุณมีออบเจ็กต์แบบผสมที่ด้านซ้ายของงาน:

 var o = {p: 42, q: true}; var {p, q} = o; console.log(p); // 42 console.log(q); // true

นี่เป็นสิ่งที่ดี แต่มันช่วยเราใน React ได้อย่างไร? พิจารณาตัวอย่างต่อไปนี้:

 function makeRequest(url, method, params) { var config = { url: url, method: method, params: params }; ... }

ด้วยการทำลายโครงสร้าง คุณสามารถบันทึกการกดแป้นได้ไม่กี่ครั้ง คีย์ตามตัวอักษร {url, method, params} จะได้รับการกำหนดค่าโดยอัตโนมัติจากขอบเขตที่มีชื่อเดียวกันกับคีย์ สำนวนนี้ใช้ค่อนข้างบ่อย และการกำจัดการทำซ้ำจะทำให้โค้ดเกิดข้อผิดพลาดน้อยลง

 function makeRequest(url, method, params) { var config = {url, method, params}; ... }

การทำลายโครงสร้างยังช่วยให้คุณโหลดเพียงส่วนย่อยของโมดูล:

 const {clone, assign} = require('lodash'); function output(data, optional) { var payload = clone(data); assign(payload, optional); }

อาร์กิวเมนต์: ค่าเริ่มต้น ส่วนที่เหลือ และการแพร่กระจาย

อาร์กิวเมนต์ของฟังก์ชันมีประสิทธิภาพมากกว่าใน ES6 สุดท้าย คุณสามารถตั้งค่าอาร์กิวเมนต์ เริ่มต้น ได้:

 function http(endpoint, method='GET') { console.log(method) ... } http('/api') // GET

เบื่อกับการยุ่งกับวัตถุ arguments เทอะทะ? ด้วยข้อกำหนดใหม่ คุณสามารถรับอาร์กิวเมนต์ ที่เหลือ เป็นอาร์เรย์ได้:

 function networkAction(context, method, ...rest) { // rest is an array return method.apply(context, rest); }

และถ้าคุณไม่ชอบการเรียก apply() คุณสามารถ กระจาย อาร์เรย์ไปยังอาร์กิวเมนต์ของฟังก์ชันได้:

 myArguments = ['foo', 'bar', 123]; myFunction(...myArguments);

เครื่องกำเนิดและฟังก์ชัน Async

ES6 แนะนำตัวสร้าง JavaScript ตัวสร้างนั้นเป็นฟังก์ชัน JavaScript ที่สามารถหยุดการทำงานชั่วคราวและกลับมาทำงานต่อได้ในภายหลัง โดยจดจำสถานะของมัน ทุกครั้งที่พบคีย์เวิร์ด yield การดำเนินการจะหยุดชั่วคราว และอาร์กิวเมนต์ yield จะถูกส่งกลับไปยังอ็อบเจ็กต์ที่เรียก:

 function* sequence(from, to) { console.log('Ready!'); while(from <= to) { yield from++; } }

ต่อไปนี้คือตัวอย่างการทำงานของตัวสร้างนี้:

 > var cursor = sequence(1,3) Ready! > cursor.next() { value: 1, done: false } > cursor.next() { value: 2, done: false } > cursor.next() { value: 3, done: false } > cursor.next() { value: undefined, done: true }

เมื่อเราเรียกใช้ฟังก์ชันตัวสร้าง มันจะทำงานจนถึง yield แรก จากนั้นหยุด หลังจากที่เราเรียก next() มันจะคืนค่าแรก และดำเนินการดำเนินการต่อไป yield แต่ละรายการจะส่งกลับค่าอื่น แต่หลังจากการเรียกครั้งที่สาม ฟังก์ชันตัวสร้างจะสิ้นสุดลง และการเรียกใช้ next() แต่ละครั้งจะส่งกลับ { value: undefined, done: true }

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

เพื่อแสดงแนวคิดนี้ ก่อนอื่นเราต้องมีฟังก์ชันอะซิงโครนัส โดยปกติ เราจะมีการดำเนินการ I/O อยู่บ้าง แต่เพื่อความง่าย ลองใช้ setTimeout และคืนค่าสัญญา (โปรดทราบว่า ES6 ยังแนะนำคำสัญญาดั้งเดิมกับ JavaScript)

 function asyncDouble(x) { var deferred = Promise.defer(); setTimeout(function(){ deferred.resolve(x*2); }, 1000); return deferred.promise; }

ต่อไป เราต้องการฟังก์ชันของผู้บริโภค:

 function consumer(generator){ var cursor = generator(); var value; function loop() { var data = cursor.next(value); if (data.done) { return; } else { data.value.then(x => { value = x; loop(); }) } } loop(); }

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

ค่าที่ส่งคืนได้รับการแก้ไขในตัวจัดการ then() และส่งผ่านไปยัง value ซึ่งกำหนดไว้ในขอบเขตภายนอกและจะถูกส่งต่อไปยังการเรียก next(value) การเรียกนี้ทำให้ค่าเป็นผลมาจากการแสดงออกของผลตอบแทนที่สอดคล้องกัน ซึ่งหมายความว่าขณะนี้เราสามารถเขียนแบบอะซิงโครนัสโดยไม่ต้องโทรกลับเลย:

 function* myGenerator(){ const data1 = yield asyncDouble(1); console.log(`Double 1 = ${data1}`); const data2 = yield asyncDouble(2); console.log(`Double 2 = ${data2}`); const data3 = yield asyncDouble(3); console.log(`Double 3 = ${data3}`); } consumer(myGenerator);

เครื่องกำเนิด myGenerator จะหยุดชั่วคราวในแต่ละ yield โดยรอให้ผู้บริโภคส่งมอบสัญญาที่ได้รับการแก้ไข และแน่นอน เราจะเห็นตัวเลขที่คำนวณได้ปรากฏขึ้นในคอนโซลในช่วงเวลาหนึ่งวินาที

 Double 1 = 2 Double 2 = 4 Double 3 = 6

นี่แสดงให้เห็นถึงแนวคิดพื้นฐาน อย่างไรก็ตาม ฉันไม่แนะนำให้คุณใช้รหัสนี้ในการผลิต ให้เลือกห้องสมุดที่ได้รับการทดสอบอย่างดี เช่น co. วิธีนี้จะช่วยให้คุณเขียนโค้ด async พร้อมผลตอบแทน ได้อย่างง่ายดาย รวมถึงการจัดการข้อผิดพลาด:

 co(function *(){ var a = yield Promise.resolve(1); console.log(a); var b = yield Promise.resolve(2); console.log(b); var c = yield Promise.resolve(3); console.log(c); }).catch(function(err){ console.error(err.stack); });

ตัวอย่างนี้แสดงวิธีการเขียนโค้ดแบบอะซิงโครนัสโดยไม่ต้องโทรกลับโดยใช้ตัวสร้าง ES6 ES7 นำแนวทางนี้ไปอีกขั้นโดยแนะนำ async และ await คำสำคัญ และขจัดความจำเป็นในไลบรารีตัวสร้างทั้งหมด ด้วยความสามารถนี้ ตัวอย่างก่อนหน้านี้จะมีลักษณะดังนี้:

 async function (){ try { var a = await Promise.resolve(1); console.log(a); var b = await Promise.resolve(2); console.log(b); var c = await Promise.resolve(3); console.log(c); } catch (err) { console.error(err.stack); } };

ในความคิดของฉัน การทำงานกับโค้ดแบบอะซิงโครนัสใน JavaScript นั้นไม่ใช่เรื่องยากอีกต่อไป ไม่ใช่แค่ใน React เท่านั้น แต่ในที่อื่นๆ ด้วย

ตัวสร้างไม่เพียงแต่กระชับและตรงไปตรงมาเท่านั้น แต่ยังช่วยให้เราใช้เทคนิคที่ยากมากที่จะนำไปใช้กับการเรียกกลับ ตัวอย่างที่เด่นชัดของความดีของเครื่องกำเนิดไฟฟ้าคือไลบรารีมิดเดิลแวร์ koa สำหรับ Node.js มันมีจุดมุ่งหมายเพื่อแทนที่ Express และเพื่อให้บรรลุเป้าหมายนั้น มันมาพร้อมกับคุณสมบัตินักฆ่า: ห่วงโซ่มิดเดิลแวร์ไม่เพียงโฟล ว์ดาวน์สตรี ม (ตามคำขอของไคลเอ็นต์) แต่ยังรวมถึง อัป สตรีม ซึ่งช่วยให้แก้ไขเพิ่มเติมในการตอบสนองของเซิร์ฟเวอร์ พิจารณาตัวอย่างเซิร์ฟเวอร์ koa ต่อไปนี้:

 // Response time logger middleware app.use(function *(next){ // Downstream var start = new Date; yield next; // Upstream this.body += ' World'; var ms = new Date - start; console.log('%s %s - %s', this.method, this.url, ms); }); // Response handler app.use(function *(){ this.body = 'Hello'; }); app.listen(3000); 

โลโก้ Koa
โคอา

มิดเดิลแวร์การตอบสนองจะปล่อยดาวน์สตรีมไปยังตัวจัดการการตอบสนอง ซึ่งตั้งค่าเนื้อหาการตอบสนอง และในโฟลว์อัปสตรีม (หลังจากนิพจน์ yield yield อนุญาตให้แก้ไข this.body ได้ เช่นเดียวกับฟังก์ชันอื่นๆ เช่น การบันทึกเวลา ซึ่งเป็นไปได้ เพราะต้นน้ำและปลายน้ำใช้บริบทของฟังก์ชันเดียวกัน สิ่งนี้มีประสิทธิภาพมากกว่า Express มาก ซึ่งการพยายามทำสิ่งเดียวกันให้สำเร็จจะจบลงดังนี้:

 var start; // Downstream middleware app.use(function(req, res, next) { start = new Date; next(); // Already returned, cannot continue here }); // Response app.use(function (req, res, next){ res.send('Hello World') next(); }); // Upstream middleware app.use(function(req, res, next) { // res already sent, cannot modify var ms = new Date - start; console.log('%s %s - %s', this.method, this.url, ms); next(); }); app.listen(3000);

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

นอกจากนี้ เมื่อใช้ koa คุณจะได้รับเวิร์กโฟลว์ตัวสร้างแบบอะซิงโครนัสฟรี:

 app.use(function *(){ try { const part1 = yield fs.readFile(this.request.query.file1, 'utf8'); const part2 = yield fs.readFile(this.request.query.file2, 'utf8'); this.body = part1 + part2; } catch (err) { this.status = 404 this.body = err; } }); app.listen(3000);

คุณสามารถจินตนาการถึงคำสัญญาและการเรียกกลับที่เกี่ยวข้องกับการสร้างตัวอย่างเล็กๆ นี้ขึ้นมาใหม่ใน Express

โลโก้ Node.js

Node.js ทั้งหมดนี้เกี่ยวข้องกับ React อย่างไร? โหนดเป็นตัวเลือกแรกเมื่อพิจารณาแบ็กเอนด์ที่เหมาะสมสำหรับ React เนื่องจาก Node เขียนด้วย JavaScript เช่นกัน จึงรองรับการแชร์โค้ดระหว่างส่วนหลังและส่วนหน้า ทำให้เราสร้างเว็บแอปพลิเคชัน isomorphic React ได้ แต่เพิ่มเติมเกี่ยวกับเรื่องนี้ในภายหลัง

ห้องสมุดฟลักซ์

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

โลโก้ฟลักซ์
ฟลักซ์

สิ่งที่ยังไม่ได้ตกลงกันในระดับสากลคือการใช้งาน Flux แบบใดให้เลือก Facebook Flux จะเป็นตัวเลือกที่ชัดเจน แต่สำหรับคนส่วนใหญ่ จะใช้รายละเอียดมากเกินไป การใช้งานทางเลือกส่วนใหญ่มุ่งเน้นไปที่การลดจำนวนต้นแบบที่ต้องใช้ด้วยวิธีการกำหนดค่าคอนฟิกแบบมีอนุสัญญา และยังมีฟังก์ชันอำนวยความสะดวกบางอย่างสำหรับส่วนประกอบที่มีลำดับสูงกว่า การเรนเดอร์ฝั่งเซิร์ฟเวอร์ และอื่นๆ ผู้เข้าแข่งขันอันดับต้นๆ ที่มีตัวชี้วัดความนิยมต่างๆ สามารถดูได้ที่นี่ ฉันได้ตรวจสอบ Alt, Reflux, Flummox, Fluxxor และ Marty.js แล้ว

วิธีของฉันในการเลือกห้องสมุดที่เหมาะสมนั้นไม่ได้มีวัตถุประสงค์แต่อย่างใด แต่ก็อาจช่วยได้อยู่ดี Fluxxor เป็นหนึ่งในห้องสมุดแรกๆ ที่ฉันเช็คเอาท์ แต่ตอนนี้มันดูเก่าไปหน่อย Marty.js น่าสนใจและมีคุณสมบัติมากมาย แต่ก็ยังเกี่ยวข้องกับต้นแบบจำนวนมาก และฟังก์ชันบางอย่างก็ดูเหมือนไม่จำเป็น กรดไหลย้อนดูดีและมีแรงฉุด แต่รู้สึกยากสำหรับผู้เริ่มต้นและยังขาดเอกสารที่เหมาะสม Flummox และ Alt มีความคล้ายคลึงกันมาก แต่ดูเหมือนว่า Alt จะมีต้นแบบน้อยกว่า การพัฒนาที่กระตือรือร้นมาก เอกสารที่เป็นปัจจุบัน และชุมชน Slack ที่เป็นประโยชน์ ดังนั้นฉันจึงเลือก Alt

Alt Flux

โลโก้ Alt Flux
Alt Flux

ด้วย Alt เวิร์กโฟลว์ Flux จะง่ายขึ้นมากโดยไม่สูญเสียพลัง เอกสาร Flux ของ Facebook ได้กล่าวไว้มากมายเกี่ยวกับโปรแกรมเลือกจ่ายงาน แต่เราไม่ต้องสนใจเรื่องนั้นเพราะใน Alt โปรแกรมเลือกจ่ายงานจะเชื่อมโยงกับการดำเนินการตามแบบแผนโดยปริยาย และมักจะไม่ต้องการรหัสที่กำหนดเองใดๆ ซึ่งทำให้เรามี ร้านค้า การดำเนินการ และ ส่วนประกอบ เท่านั้น เลเยอร์ทั้งสามนี้อาจใช้ในลักษณะที่แมปเข้ากับโมเดลความคิดของ MVC ได้อย่างสวยงาม: Stores คือ Models การดำเนินการคือ Controllers และส่วนประกอบคือ Views ความแตกต่างหลักคือกระแสข้อมูลทิศทางเดียวที่เป็นศูนย์กลางของรูปแบบ Flux ซึ่งหมายความว่าตัวควบคุม (การดำเนินการ) ไม่สามารถแก้ไขมุมมอง (ส่วนประกอบ) ได้โดยตรง แต่สามารถทริกเกอร์ได้เฉพาะการปรับเปลี่ยนแบบจำลอง (จัดเก็บ) ซึ่งมุมมองจะถูกผูกไว้แบบพาสซีฟ นี่เป็นแนวทางปฏิบัติที่ดีที่สุดสำหรับนักพัฒนา Angular ที่รู้แจ้งบางคนแล้ว

เวิร์กโฟลว์ Flux กับ Alt

เวิร์กโฟลว์มีดังนี้:

  1. ส่วนประกอบเริ่มต้นการดำเนินการ
  2. ร้านค้ารับฟังการดำเนินการและอัปเดตข้อมูล
  3. ส่วนประกอบถูกผูกไว้กับร้านค้า และแสดงผลใหม่เมื่อมีการอัพเดตข้อมูล

การกระทำ

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

ดังนั้นการเรียก REST API จึงเป็นของการกระทำ เวิร์กโฟลว์ที่สมบูรณ์มีดังนี้:

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

สำหรับคำขอ AJAX เราสามารถใช้ไลบรารี axios ซึ่งจัดการกับข้อมูล JSON และส่วนหัวได้อย่างราบรื่น แทนที่จะใช้คำสัญญาหรือการโทรกลับ เราสามารถใช้รูปแบบ async / await ของ ES7 ได้ หากสถานะการตอบสนอง POST ไม่ใช่ 2XX แสดงว่าเกิดข้อผิดพลาดและเราส่งข้อมูลที่ส่งคืนหรือได้รับข้อผิดพลาด

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

 class LoginActions { constructor() { // Automatic action this.generateActions('logout'); } // Manual action async login(data) { try { const response = await axios.post('/auth/login', data); this.dispatch({ok: true, user: response.data}); } catch (err) { console.error(err); this.dispatch({ok: false, error: err.data}); } } } module.exports = (alt.createActions(LoginActions));

ร้านค้า

ที่เก็บ Flux มีจุดประสงค์สองประการ: มีตัวจัดการการดำเนินการ และดำเนินการสถานะ มาดูตัวอย่างหน้าเข้าสู่ระบบของเราต่อเพื่อดูว่ามันทำงานอย่างไร

มาสร้าง LoginStore ด้วยแอตทริบิวต์สถานะสองสถานะ: user สำหรับผู้ใช้ที่เข้าสู่ระบบในปัจจุบัน และ error สำหรับข้อผิดพลาดที่เกี่ยวข้องกับการเข้าสู่ระบบปัจจุบัน ด้วยจิตวิญญาณของการลดต้นแบบ Alt ช่วยให้เราสามารถผูกมัดกับการกระทำทั้งหมดจากคลาสเดียวด้วยฟังก์ชันเดียว bindActions

 class LoginStore { constructor() { this.bindActions(LoginActions); this.user = null; this.error = null; } ...

ชื่อตัวจัดการถูกกำหนดตามแบบแผน on นำหน้าชื่อการดำเนินการที่เกี่ยวข้อง ดังนั้นการดำเนินการ login จึงได้รับการจัดการโดย onLogin เป็นต้น โปรดทราบว่าอักษรตัวแรกของชื่อการดำเนินการจะเป็นตัวพิมพ์ใหญ่ในสไตล์ camelCase ใน LoginStore ของเรา เรามีตัวจัดการต่อไปนี้ ซึ่งถูกเรียกโดยการกระทำที่เกี่ยวข้อง:

 ... onLogin(data) { if (data.ok) { this.user = data.user; this.error = null; router.transitionTo('home'); } else { this.user = null; this.error = data.error } } onLogout() { this.user = null; this.error = null; } }

ส่วนประกอบ

วิธีปกติในการผูกร้านค้ากับส่วนประกอบคือการใช้ React mixin บางประเภท แต่เนื่องจากมิกซ์อินกำลังตกยุค จึงต้องมีวิธีอื่น วิธีใหม่วิธีหนึ่งคือการใช้ส่วนประกอบที่มีลำดับสูงกว่า เรานำส่วนประกอบของเรามาใส่ไว้ในส่วนประกอบ wrapper ซึ่งจะดูแลการฟังจากร้านค้าและเรียกการเรนเดอร์ซ้ำ ส่วนประกอบของเราจะได้รับสถานะของร้านค้าใน props แนวทางนี้ยังมีประโยชน์ในการจัดระเบียบโค้ดของเราให้เป็นส่วนประกอบที่ฉลาดและโง่เขลา ซึ่งกลายเป็นเทรนด์ล่าสุดเมื่อเร็วๆ นี้ สำหรับ Alt ตัวตัดส่วนประกอบนั้นถูกใช้งานโดย AltContainer :

 export default class Login extends React.Component { render() { return ( <AltContainer stores={{LoginStore: LoginStore}}> <LoginPage/> </AltContainer> )} }

องค์ประกอบ LoginPage ของเรายังใช้ตัวตกแต่ง changeHandler ที่เปิดตัวก่อนหน้านี้ ข้อมูลจาก LoginStore ใช้เพื่อแสดงข้อผิดพลาดในกรณีที่เข้าสู่ระบบไม่สำเร็จ และ AltContainer จะดูแลการแสดงผลซ้ำ การคลิกที่ปุ่มเข้าสู่ระบบจะดำเนินการการ login โดยทำให้เวิร์กโฟลว์ Alt ฟลักซ์สมบูรณ์:

 @changeHandler export default class LoginPage extends React.Component { constructor(props) { super(props); this.state = { loginForm: {} }; } login() { LoginActions.login(this.state.loginForm) } render() { return ( <Alert>{{this.props.LoginStore.error}}</Alert> <Input label='Username' type='text' value={this.state.login.username} onChange={this.changeHandler.bind(this, 'loginForm', 'username')} /> <Input label='Password' type='password' value={this.state.login.password} onChange={this.changeHandler.bind(this, 'loginForm', 'password')} /> <Button onClick={this.login.bind(this)}>Login</Button> )} }

การแสดงผลแบบไอโซมอร์ฟิค

เว็บแอปพลิเคชัน Isomorphic เป็นหัวข้อยอดนิยมในปัจจุบัน เพราะพวกเขาแก้ปัญหางานบ้านที่ใหญ่ที่สุดของแอปพลิเคชันหน้าเดียวแบบเดิมๆ ในแอปพลิเคชันเหล่านั้น มาร์กอัปจะถูกสร้างขึ้นแบบไดนามิกโดย JavaScript ในเบราว์เซอร์ ผลลัพธ์คือเนื้อหาไม่พร้อมใช้งานสำหรับไคลเอ็นต์ที่ปิด JavaScript โดยเฉพาะอย่างยิ่ง โปรแกรมรวบรวมข้อมูลเว็บของเครื่องมือค้นหา ซึ่งหมายความว่าหน้าเว็บของคุณไม่ได้รับการจัดทำดัชนีและไม่ปรากฏในผลการค้นหา มีวิธีแก้ไขปัญหานี้ แต่ก็ยังห่างไกลจากความเหมาะสม แนวทาง isomorphic พยายามแก้ไขปัญหานี้โดยการแสดง URL ที่ร้องขอล่วงหน้าของแอปพลิเคชันหน้าเดียวบนเซิร์ฟเวอร์ ด้วย Node.js คุณมี JavaScript บนเซิร์ฟเวอร์ ซึ่งหมายความว่า React ยังสามารถเรียกใช้ฝั่งเซิร์ฟเวอร์ได้ ไม่น่าจะยากเกินไปใช่ไหม

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

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

ขั้นแรก เราเริ่มต้น Flux บนเซิร์ฟเวอร์โดยใช้ alt.bootstrap เป็นไปได้ที่จะเติม Flux ที่จัดเก็บไว้ล่วงหน้าด้วยข้อมูลสำหรับการแสดงผล นอกจากนี้ยังจำเป็นต้องตัดสินใจเลือกส่วนประกอบที่จะแสดงผลสำหรับ URL ใด ซึ่งเป็นฟังก์ชันของ Router ฝั่งไคลเอ็นต์ เรากำลังใช้ alt เวอร์ชันซิงเกิล ดังนั้นหลังจากการเรนเดอร์แต่ละครั้ง เราจำเป็นต้อง alt.flush() ร้านค้าเพื่อให้พวกเขาทำความสะอาดสำหรับคำขออื่น การใช้ iso add-on จะทำให้สถานะของ Flux เป็นอนุกรมกับมาร์กอัป HTML เพื่อให้ลูกค้าทราบว่าจะรับที่ใด:

 // We use react-router to run the URL that is provided in routes.jsx var getHandler = function(routes, url) { var deferred = Promise.defer(); Router.run(routes, url, function (Handler) { deferred.resolve(Handler); }); return deferred.promise; }; app.use(function *(next) { yield next; // We seed our stores with data alt.bootstrap(JSON.stringify(this.locals.data || {})); var iso = new Iso(); const handler = yield getHandler(reactRoutes, this.request.url); const node = React.renderToString(React.createElement(handler)); iso.add(node, alt.flush()); this.render('layout', {html: iso.render()}); });

ในฝั่งไคลเอ็นต์ เรารับสถานะเซิร์ฟเวอร์ และบูตสแตรป alt กับข้อมูล จากนั้นเราเรียกใช้ Router และ React.render บนคอนเทนเนอร์เป้าหมาย ซึ่งจะอัปเดตมาร์กอัปที่สร้างโดยเซิร์ฟเวอร์ตามความจำเป็น

 Iso.bootstrap(function (state, _, container) { // Bootstrap the state from the server alt.bootstrap(state) Router.run(routes, Router.HistoryLocation, function (Handler, req) { let node = React.createElement(Handler) React.render(node, container) }) })

น่ารัก!

Front-End Libraries ที่มีประโยชน์

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

React-Bootstrap

โลโก้ React-Bootstrap

Twitter's Bootstrap framework has become commonplace since it is of immense help to every web developer who does not want to spend a ton of time working in CSS. In the prototyping phase especially, Bootstrap is indispensable. To leverage the power of bootstrap in a React application, it's good to use it with React-Bootstrap, which comes with nice React syntax and re-implements Bootstrap's jQuery plugins using native React components. The resulting code is terse and easy to understand:

 <Navbar brand='React-Bootstrap'> <Nav> <NavItem eventKey={1} href='#'>Link</NavItem> <NavItem eventKey={2} href='#'>Link</NavItem> <DropdownButton eventKey={3} title='Dropdown'> <MenuItem eventKey='1'>Action</MenuItem> <MenuItem eventKey='2'>Another action</MenuItem> <MenuItem eventKey='3'>Something else here</MenuItem> <MenuItem divider /> <MenuItem eventKey='4'>Separated link</MenuItem> </DropdownButton> </Nav> </Navbar>

Personally, I cannot escape the feeling that this is what HTML should always have been like.

If you want to use Bootstrap source with Webpack, consider using less-loader or bootstrap-sass-loader, depending on the preprocessor you prefer. It will allow you to easily customize which Bootstrap components to include, and also allow the usage of LESS or SASS global variables in your CSS code.

ตอบสนองเราเตอร์

React Router Logo

React Router has become the de-facto standard for routing in React. It allows nested routing, support for redirections, plays nicely with isomorphic rendering, and has a simple JSX-based syntax:

 <Router history={new BrowserHistory}> <Route path="/" component={App}> <Route path="about" name="about" component={About}/> <Route path="users" name="users" component={Users} indexComponent={RecentUsers}> <Route path="/user/:userId" name="user" component={User}/> </Route> <Route path="*" component={NoMatch}/> </Route> </Router>

React Router also provides a Link component that you can use for navigation in your application, specifying only the route name:

 <nav> <Link to="about">About</Link> <Link to="users">Users</Link> </nav>

There is even a library for integration with React-Bootstrap, so if you are using Bootstrap's components and don't feel like setting the active class on them manually all the time, you can use react-router-bootstrap and write code like this:

 <Nav> <NavItemLink to="about">About</NavItemLink> <NavItemLink to="users">Users</NavItemLink> </Nav>

No additional setup is necessary. Active links will take care of themselves.

Formsy-React

Working with forms can be tedious, so let's get some help from the formsy-react library, which will help us manage validations and data models. The Formsy-React library, strangely enough, does not include the actual form inputs because users are encouraged to write their own (which is great). But if you are content with the common ones, just use formsy-react-components. Bootstrap classes are included:

 import Formsy from 'formsy-react'; import {Input} from 'formsy-react-components'; export default class FormsyForm extends React.Component { enableButton() { this.setState({canSubmit: true}); } disableButton() { this.setState({canSubmit: true}); } submit(model) { FormActions.saveEmail(model.email); } render() { return ( <Formsy.Form onValidSubmit={this.submit} onValid={this.enableButton} onInvalid={this.disableButton}> <Input name="email" validations="isEmail" validationError="This is not a valid email" required/> <button type="submit" disabled={!this.state.canSubmit}>Submit</button> </Formsy.Form> )} }

Calendar and Typeahead

Calendar and typeahead are icing on the cake of every UI toolkit. Sadly, these two components were removed from Bootstrap 3, probably because they are too specialized for a general purpose CSS framework. Luckily, I've been able to find worthy replacements in react-pikaday and react-select. I've tested more than 10 libraries, and these two came out as the best. They are dead easy to use, as well:

 import Pikaday from 'react-pikaday'; import Select from 'react-select'; export default class CalendarAndTypeahead extends React.Component { constructor(props){ super(props); this.options = [ { value: 'one', label: 'One' }, { value: 'two', label: 'Two' } ]; } dateChange(date) { this.setState({date: date}); }, selectChange(selected) { this.setState({selected: selected}); }, render() { return ( <Pikaday value={this.state.date} onChange={this.dateChange} /> <Select name="form-field-name" value={this.state.selected} options={this.options} onChange={selectChange} /> )} }
When it comes to React, it's a jungle out there! Here's a map to help you find your way.
ทวีต

Conclusion - React.JS

In this article I've presented libraries and techniques that I consider some of the most productive in current web development. Some of them are React-specific, but due to React's open nature, many of them are usable in other environments as well. Technological progress is sometimes hindered by fear of the newest stuff, so I hope this article will help to dissipate doubts concerning React, Flux and the newest features in ECMAScript.

React Ecosystem

If you are interested, you can take a look at my example application built with these technologies. The source code is available on GitHub.

ขอบคุณที่อ่าน!

Related: React.js Best Practices and Tips by Toptal Developers