NLP แบบเต็มสแต็กพร้อม React: Ionic vs Cordova vs React Native
เผยแพร่แล้ว: 2022-03-11ประมาณ 15 ปีนับตั้งแต่ Apple เปิดตัว iPhone เครื่องแรก แนวการพัฒนาซอฟต์แวร์ก็เปลี่ยนไปอย่างมาก เนื่องจากสมาร์ทโฟนได้รับการนำไปใช้อย่างกว้างขวางและเติบโตอย่างต่อเนื่องในความสามารถเฉพาะตัว ผู้ใช้จึงต้องการเข้าถึงบริการซอฟต์แวร์ผ่านอุปกรณ์พกพามากกว่าเดสก์ท็อปหรือแล็ปท็อป สมาร์ทโฟนมีคุณสมบัติต่างๆ เช่น การระบุตำแหน่งทางภูมิศาสตร์ การตรวจสอบไบโอเมตริกซ์ และการตรวจจับการเคลื่อนไหว ซึ่งหลาย ๆ แพลตฟอร์มเดสก์ท็อปเพิ่งเริ่มคัดลอก ในบางกลุ่มประชากร สมาร์ทโฟนหรืออุปกรณ์พกพาที่คล้ายคลึงกันเป็นวิธีการหลักในการใช้ซอฟต์แวร์ โดยข้ามผ่านคอมพิวเตอร์ไปโดยสิ้นเชิง
บริษัทต่างๆ ได้สังเกตเห็นการเปลี่ยนแปลงนี้และได้เสริมความแข็งแกร่งในลักษณะสำคัญๆ แอพมือถือไม่ต้องคิดมากอีกต่อไป แอพพลิเคชั่นต่างๆ ตั้งแต่ Robinhood บริษัทนายหน้าทางการเงิน ไปจนถึง Instagram บริษัทโซเชียลมีเดีย ไปจนถึง Uber บริษัทให้บริการเรียกรถ กำลังนำกลยุทธ์การพัฒนาที่เน้นอุปกรณ์พกพามาใช้ หากมีแอปพลิเคชันบนเดสก์ท็อป แอปพลิเคชันมักจะเสนอให้เป็นส่วนเสริมของแอปบนอุปกรณ์เคลื่อนที่ แทนที่จะเป็นจุดสนใจหลัก
สำหรับนักพัฒนาแบบฟูลสแตก การปรับให้เข้ากับแนวโน้มที่เปลี่ยนแปลงเหล่านี้เป็นสิ่งสำคัญ โชคดีที่มีเทคโนโลยีที่ครบถ้วนและได้รับการสนับสนุนอย่างดีมากมายที่พร้อมช่วยให้นักพัฒนาเว็บได้ใช้ทักษะของตนกับการพัฒนาอุปกรณ์เคลื่อนที่ วันนี้ เราจะมาสำรวจสามเทคโนโลยีดังกล่าว: Cordova, Ionic และ React Native เราจะใช้ React.js ซึ่งเป็นหนึ่งในเฟรมเวิร์กที่ได้รับความนิยมมากที่สุดสำหรับการพัฒนาเว็บส่วนหน้า เป็นเทคโนโลยีการพัฒนาหลักของเรา ในขณะที่เราจะมุ่งเน้นไปที่การพัฒนาแอปพลิเคชันของ iPhone เทคโนโลยีเหล่านี้เป็นเทคโนโลยีข้ามแพลตฟอร์มและสามารถคอมไพล์ข้ามแพลตฟอร์มไปยังแพลตฟอร์ม Android ได้
สิ่งที่เราจะสร้างวันนี้
เราจะสร้างแอปพลิเคชันที่ใช้การประมวลผลภาษาธรรมชาติ (NLP) เพื่อประมวลผลและดูแลฟีด Twitter แอปพลิเคชันจะอนุญาตให้ผู้ใช้เลือกชุดของแฮนเดิล Twitter ดึงการอัปเดตล่าสุดโดยใช้ Twitter API และจัดหมวดหมู่ทวีตตามความคิดเห็นและหัวข้อ ผู้ใช้จะสามารถดูทวีตตามความคิดเห็นหรือหัวข้อได้
แบ็กเอนด์
ก่อนที่เราจะสร้างส่วนหน้า เราจะสร้างส่วนหลังก่อน เราจะใช้ระบบแบ็คเอนด์ที่เรียบง่ายในตอนนี้ - เราจะใช้การวิเคราะห์ความรู้สึกขั้นพื้นฐานแบบไม่มีขายทั่วไป และการติดแท็กบางส่วนของคำพูด ร่วมกับการล้างข้อมูลเล็กน้อยเพื่อจัดการกับปัญหาเฉพาะชุดข้อมูล เราจะใช้ไลบรารี NLP โอเพ่นซอร์สที่เรียกว่า TextBlob และแสดงผลผ่าน Flask
การวิเคราะห์ความรู้สึก การติดแท็กบางส่วนของคำพูด และ NLP: A Quick Primer
หากคุณไม่เคยทำงานกับแอปพลิเคชันการวิเคราะห์ภาษาธรรมชาติมาก่อน ข้อกำหนดเหล่านี้อาจแตกต่างออกไปสำหรับคุณ NLP เป็นคำศัพท์เฉพาะสำหรับเทคโนโลยีที่วิเคราะห์และประมวลผลข้อมูลภาษามนุษย์ตามธรรมชาติ แม้ว่าเรื่องนี้จะเป็นหัวข้อกว้างๆ แต่ก็มีความท้าทายมากมายที่มักเกิดขึ้นกับเทคโนโลยีทั้งหมดที่จัดการกับพื้นที่นี้ ตัวอย่างเช่น ภาษามนุษย์ ซึ่งแตกต่างจากภาษาโปรแกรมหรือข้อมูลตัวเลข มีแนวโน้มที่จะมีโครงสร้างที่หลวมเนื่องจากลักษณะที่อนุญาตของไวยากรณ์ภาษามนุษย์ นอกจากนี้ ภาษามนุษย์มีแนวโน้มที่จะมีบริบทอย่างมาก และวลีที่พูดหรือเขียนในบริบทหนึ่งอาจไม่แปลเป็นบริบทอื่น ในที่สุด โครงสร้างและบริบทนอกเหนือจากภาษาแล้ว ภาษามีความซับซ้อนมาก คำที่อยู่ด้านล่างในย่อหน้าสามารถเปลี่ยนความหมายของประโยคที่จุดเริ่มต้นของย่อหน้าได้ คำศัพท์สามารถประดิษฐ์ กำหนดใหม่ หรือเปลี่ยนแปลงได้ ความซับซ้อนทั้งหมดนี้ทำให้เทคนิคการวิเคราะห์ข้อมูลยากต่อการประยุกต์ใช้ข้าม
การวิเคราะห์ความรู้สึกเป็นสาขาย่อยของ NLP ที่เน้นการทำความเข้าใจอารมณ์ของข้อความภาษาธรรมชาติ แม้ว่าอารมณ์ของมนุษย์จะเป็นเรื่องส่วนตัวโดยเนื้อแท้ และดังนั้นจึงยากที่จะปักหมุดทางเทคโนโลยี การวิเคราะห์ความรู้สึกเป็นสาขาย่อยที่มีคำมั่นสัญญาทางการค้าอันยิ่งใหญ่ การประยุกต์ใช้การวิเคราะห์ความรู้สึกบางส่วนรวมถึงการจัดประเภทบทวิจารณ์ผลิตภัณฑ์เพื่อระบุการประเมินคุณลักษณะต่างๆ ทั้งในด้านบวกและด้านลบ การตรวจจับอารมณ์ของอีเมลหรือคำพูด และการจัดกลุ่มเนื้อเพลงตามอารมณ์ หากคุณกำลังมองหาคำอธิบายเชิงลึกเกี่ยวกับการวิเคราะห์ความคิดเห็น คุณสามารถอ่านบทความของฉันเกี่ยวกับการสร้างแอปพลิเคชันที่ใช้การวิเคราะห์ความคิดเห็นได้ที่นี่
การติดแท็กบางส่วนของคำพูด หรือการแท็ก POS เป็นฟิลด์ย่อยที่แตกต่างกันมาก เป้าหมายของการติดแท็ก POS คือการระบุส่วนของคำพูดของคำที่กำหนดในประโยคโดยใช้ข้อมูลทางไวยากรณ์และตามบริบท การระบุความสัมพันธ์นี้เป็นงานที่ยากกว่าที่เห็นในตอนแรก คำๆ หนึ่งอาจมีส่วนต่างๆ ของคำพูดที่แตกต่างกันมากตามบริบทและโครงสร้างของประโยค และกฎเกณฑ์ก็ไม่ได้ชัดเจนแม้แต่กับมนุษย์เสมอไป โชคดีที่รุ่นนอกชั้นวางจำนวนมากในปัจจุบันมีโมเดลที่ทรงพลังและใช้งานได้หลากหลายซึ่งรวมเข้ากับภาษาการเขียนโปรแกรมหลักๆ ส่วนใหญ่ หากคุณต้องการเรียนรู้เพิ่มเติม คุณสามารถอ่านบทความของฉันเกี่ยวกับการติดแท็ก POS ที่นี่
Flask, TextBlob และ Tweepy
สำหรับแบ็กเอนด์ NLP เราจะใช้ Flask, TextBlob และ Tweepy เราจะใช้ Flask เพื่อสร้างเซิร์ฟเวอร์ขนาดเล็กน้ำหนักเบา TextBlob เพื่อเรียกใช้การประมวลผลภาษาธรรมชาติของเรา และใช้ Tweepy เพื่อรับทวีตจาก Twitter API ก่อนที่คุณจะเริ่มเขียนโค้ด คุณจะต้องได้รับคีย์สำหรับนักพัฒนาซอฟต์แวร์จาก Twitter เพื่อให้คุณสามารถดึงทวีตได้
เราสามารถเขียนแบ็กเอนด์ที่ซับซ้อนกว่านี้ได้มาก และใช้เทคโนโลยี NLP ที่ซับซ้อนมากขึ้น แต่สำหรับจุดประสงค์ของเราในวันนี้ เราจะดูแลแบ็คเอนด์ให้เรียบง่ายที่สุดเท่าที่จะทำได้
รหัสแบ็คเอนด์
ตอนนี้ เราพร้อมที่จะเริ่มเขียนโค้ดแล้ว เปิดโปรแกรมแก้ไขและเทอร์มินัล Python ที่คุณชื่นชอบแล้วมาเริ่มกันเลย!
ขั้นแรก เราจะต้องการติดตั้งแพ็คเกจที่จำเป็น
pip install flask flask-cors textblob tweepy python -m textblob.download_corpora
ตอนนี้ มาเขียนโค้ดสำหรับการทำงานของเรากัน
เปิดสคริปต์ Python ใหม่ เรียกมันว่า server.py และนำเข้าไลบรารีที่จำเป็น:
import tweepy from textblob import TextBlob from collections import defaultdict
มาเขียนฟังก์ชันตัวช่วยกัน:
# simple, average a list of numbers with a guard clause to avoid division by zero def mean(lst): return sum(lst)/len(lst) if len(lst) > 0 else 0 # call the textblob sentiment analysis API and noun phrases API and return it as a dict def get_sentiment_and_np(sentence): blob = TextBlob(sentence) return{ 'sentiment': mean([s.sentiment.polarity for s in blob.sentences if s.sentiment.polarity != 0.0]), 'noun_phrases': list(blob.noun_phrases) } # use the tweepy API to get the last 50 posts from a user's timeline # We will want to get the full text if the text is truncated, and we will also remove retweets since they're not tweets by that particular account. def get_tweets(handle): auth = tweepy.OAuthHandler('YOUR_DEVELOPER_KEY') auth.set_access_token('YOUR_DEVELOPER_SECRET_KEY') api = tweepy.API(auth) tl = api.user_timeline(handle, count=50) tweets = [] for tl_item in tl: if 'retweeted_status' in tl_item._json: Continue # this is a retweet if tl_item._json['truncated']: status = api.get_status(tl_item._json['id'], tweet_mode='extended') # get full text tweets.append(status._json['full_text']) else: tweets.append(tl_item._json['text']) return tweets # http and https are sometimes recognized as noun phrases, so we filter it out. # We also try to skip noun phrases with very short words to avoid certain false positives # If this were a commercial app, we would want a more sophisticated filtering strategy. def good_noun_phrase(noun_phrase): noun_phrase_list = noun_phrase.split(' ') for np in noun_phrase_list: if np in {'http', 'https'} or len(np) < 3: return False return True
ตอนนี้เราได้เขียนฟังก์ชันตัวช่วยแล้ว เราสามารถรวมทุกอย่างด้วยฟังก์ชันง่ายๆ สองสามอย่าง:
# reshapes the tagged tweets into dictionaries that can be easily consumed by the front-end app def group_tweets(processed_tweets): # Sort it by sentiment sentiment_sorted = sorted(processed_tweets, key=lambda x: x['data']['sentiment']) # collect tweets by noun phrases. One tweet can be present in the list of more than one noun phrase, obviously. tweets_by_np = defaultdict(list) for pt in processed_tweets: for np in pt['data']['noun_phrases']: tweets_by_np[np].append(pt) grouped_by_np = {np.title(): tweets for np, tweets in tweets_by_np.items() if len(tweets) > 1 and good_noun_phrase(np)} return sentiment_sorted, grouped_by_np # download, filter, and analyze the tweets def download_analyze_tweets(accounts): processed_tweets = [] for account in accounts: for tweet in get_tweets(account): processed_tweet = ' '.join([i for i in tweet.split(' ') if not i.startswith('@')]) res = get_sentiment_and_np(processed_tweet) processed_tweets.append({ 'account': account, 'tweet': tweet, 'data': res }) sentiment_sorted, grouped_by_np = group_tweets(processed_tweets) return processed_tweets, sentiment_sorted, grouped_by_np
ตอนนี้คุณสามารถเรียกใช้ฟังก์ชัน download_analyze_tweets
ในรายการแฮนเดิลที่คุณต้องการติดตาม และคุณควรเห็นผลลัพธ์
ฉันรันรหัสต่อไปนี้:
if __name__ == '__main__': accounts = ['@spacex', '@nasa'] processed_tweets, sentiment_sorted, grouped_by_np = download_analyze_tweets(accounts) print(processed_tweets) print(sentiment_sorted) print(grouped_by_np)
การดำเนินการนี้ทำให้เกิดผลลัพธ์ดังต่อไปนี้ ผลลัพธ์ขึ้นอยู่กับเวลาอย่างชัดเจน ดังนั้น หากคุณเห็นสิ่งที่คล้ายกัน แสดงว่าคุณมาถูกทางแล้ว
[{'account': '@spacex', 'tweet': 'Falcon 9… [{'account': '@nasa', 'tweet': 'Our Mars rove… {'Falcon': [{'account': '@spacex', 'tweet': 'Falc….
ตอนนี้เราสามารถสร้างเซิร์ฟเวอร์ Flask ซึ่งค่อนข้างง่าย สร้างไฟล์เปล่าชื่อ server.py และเขียนโค้ดต่อไปนี้:
from flask import Flask, request, jsonify from twitter import download_analyze_tweets from flask_cors import CORS app = Flask(__name__) CORS(app) @app.route('/get_tweets', methods=['POST']) def get_tweets(): accounts = request.json['accounts'] processed_tweets, sentiment_sorted, grouped_by_np = download_analyze_tweets(accounts) return jsonify({ 'processedTweets': processed_tweets, 'sentimentSorted': sentiment_sorted, 'groupedByNp': grouped_by_np }) if __name__ == '__main__': app.run(debug=True)
เรียกใช้เซิร์ฟเวอร์ และตอนนี้คุณควรจะส่งคำขอโพสต์ไปยังเซิร์ฟเวอร์โดยใช้ไคลเอนต์ HTTP ที่คุณเลือก ส่งผ่าน {accounts: [“@NASA”, “@SpaceX”]} เป็นอาร์กิวเมนต์ json และคุณควรเห็น API ส่งคืนบางสิ่งที่คล้ายกับที่ส่งคืนในโค้ดการวิเคราะห์ของ Twitter
ตอนนี้เรามีเซิร์ฟเวอร์แล้ว เราก็พร้อมที่จะโค้ดส่วนหน้าแล้ว เนื่องจากมีความแตกต่างกันเล็กน้อยในเครือข่ายผ่านโปรแกรมจำลองโทรศัพท์ เราขอแนะนำให้คุณปรับใช้ API ของคุณที่ใดที่หนึ่ง มิฉะนั้น คุณจะต้องตรวจสอบว่าแอปของคุณทำงานบนโปรแกรมจำลองหรือไม่ และส่งคำขอไปยัง <Your Computer IP>:5000
แทน localhost:5000
เมื่ออยู่ในโปรแกรมจำลอง หากคุณปรับใช้โค้ด คุณสามารถส่งคำขอไปยัง URL นั้นได้
มีตัวเลือกมากมายในการปรับใช้เซิร์ฟเวอร์ สำหรับเซิร์ฟเวอร์ดีบักฟรีที่ใช้งานง่ายและต้องมีการตั้งค่าขั้นต่ำ ฉันขอแนะนำบางอย่างเช่น PythonAnywhere ซึ่งควรจะสามารถเรียกใช้เซิร์ฟเวอร์นี้ได้ทันที
ตอนนี้เราได้เข้ารหัสเซิร์ฟเวอร์ส่วนหลังแล้ว มาดูส่วนหน้ากัน เราจะเริ่มต้นด้วยหนึ่งในตัวเลือกที่สะดวกที่สุดสำหรับนักพัฒนาเว็บ: Cordova
การใช้งาน Apache Cordova
คอร์โดวา ไพรเมอร์
Apache Cordova เป็นเทคโนโลยีซอฟต์แวร์ที่ช่วยให้นักพัฒนาเว็บกำหนดเป้าหมายแพลตฟอร์มมือถือ ด้วยการใช้ประโยชน์จากความสามารถของเว็บเบราว์เซอร์ที่ใช้งานบนแพลตฟอร์มสมาร์ทโฟน Cordova จะรวมรหัสเว็บแอปพลิเคชันลงในคอนเทนเนอร์แอปพลิเคชันดั้งเดิมเพื่อสร้างแอปพลิเคชัน อย่างไรก็ตาม Cordova ไม่ได้เป็นเพียงเว็บเบราว์เซอร์แฟนซีเท่านั้น นักพัฒนาเว็บสามารถเข้าถึงคุณลักษณะเฉพาะของสมาร์ทโฟนได้ผ่าน Cordova API เช่น การสนับสนุนออฟไลน์ บริการระบุตำแหน่ง และกล้องในอุปกรณ์
สำหรับแอปพลิเคชันของเรา เราจะเขียนแอปพลิเคชันโดยใช้ React.js เป็นเฟรมเวิร์ก JS และ React-Bootstrap เป็นเฟรมเวิร์ก CSS เนื่องจาก Bootstrap เป็นเฟรมเวิร์ก CSS ที่ตอบสนอง จึงมีการรองรับการทำงานบนหน้าจอขนาดเล็กแล้ว เมื่อเขียนแอปพลิเคชันแล้ว เราจะรวบรวมแอปพลิเคชันบนเว็บโดยใช้ Cordova
การกำหนดค่าแอพ
เราจะเริ่มต้นด้วยการทำบางสิ่งที่ไม่เหมือนใครเพื่อตั้งค่าแอพ Cordova React ในบทความระดับ กลาง นักพัฒนา Shubham Patil อธิบายว่าเรากำลังทำอะไรอยู่ โดยพื้นฐานแล้ว เรากำลังตั้งค่าสภาพแวดล้อมการพัฒนา React โดยใช้ React CLI จากนั้นจึงสร้างสภาพแวดล้อมการพัฒนา Cordova โดยใช้ Cordova CLI ก่อนที่จะรวมทั้งสองเข้าด้วยกันในที่สุด
ในการเริ่มต้น ให้รันคำสั่งสองคำสั่งต่อไปนี้ในโฟลเดอร์โค้ดของคุณ:
cordova create TwitterCurationCordova create-react-app twittercurationreact
เมื่อตั้งค่าเสร็จแล้ว เราจะต้องการย้ายเนื้อหาของโฟลเดอร์สาธารณะและ src ของแอป React ไปยังแอป Cordova จากนั้นใน package.json ให้คัดลอกสคริปต์ รายการเบราว์เซอร์ และการอ้างอิงจากโปรเจ็กต์ React เพิ่ม "homepage": "./"
ที่รูทของ package.json เพื่อเปิดใช้งานความเข้ากันได้กับ Cordova
เมื่อรวม package.json แล้ว เราจะต้องการเปลี่ยนแปลงไฟล์ public/index.html เพื่อทำงานกับ Cordova เปิดไฟล์และคัดลอกเมตาแท็กจาก www/index.html รวมถึงสคริปต์ที่ส่วนท้ายของแท็กเนื้อหาเมื่อโหลด Cordova.js
ถัดไป เปลี่ยนไฟล์ src/index.js เพื่อตรวจสอบว่าทำงานบน Cordova หรือไม่ หากทำงานบน Cordova เราจะต้องเรียกใช้โค้ดการเรนเดอร์ภายในตัวจัดการเหตุการณ์พร้อมอุปกรณ์ หากใช้งานในเบราว์เซอร์ปกติ ให้แสดงผลทันที
const renderReactDom = () => { ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById('root') ); } if (window.cordova) { document.addEventListener('deviceready', () => { renderReactDom(); }, false); } else { renderReactDom(); }
สุดท้าย เราต้องตั้งค่าไปป์ไลน์การปรับใช้ของเรา เพิ่มคำจำกัดความด้านล่างลงในไฟล์ config.xml:
<hook type="before_prepare" src="hooks/prebuild.js" />
และใส่สคริปต์ต่อไปนี้ใน prebuild.js:
const path = require('path'); const { exec } = require('child_process'); const fs = require('fs'); const rimraf = require('rimraf'); function renameOutputFolder(buildFolderPath, outputFolderPath) { return new Promise((resolve, reject) => { fs.rename(buildFolderPath, outputFolderPath, (err) => { if (err) { reject(err); } else { resolve('Successfully built!'); } }); }); } function execPostReactBuild(buildFolderPath, outputFolderPath) { return new Promise((resolve, reject) => { if (fs.existsSync(buildFolderPath)) { if (fs.existsSync(outputFolderPath)) { rimraf(outputFolderPath, (err) => { if (err) { reject(err); return; } renameOutputFolder(buildFolderPath, outputFolderPath) .then(val => resolve(val)) .catch(e => reject(e)); }); } else { renameOutputFolder(buildFolderPath, outputFolderPath) .then(val => resolve(val)) .catch(e => reject(e)); } } else { reject(new Error('build folder does not exist')); } }); } module.exports = () => { const projectPath = path.resolve(process.cwd(), './node_modules/.bin/react-scripts'); return new Promise((resolve, reject) => { exec(`${projectPath} build`, (error) => { if (error) { console.error(error); reject(error); return; } execPostReactBuild(path.resolve(__dirname, '../build/'), path.join(__dirname, '../www/')) .then((s) => { console.log(s); resolve(s); }) .catch((e) => { console.error(e); reject(e); }); }); }); };
การดำเนินการนี้รัน React build และวางโฟลเดอร์ build ไว้ในตำแหน่งที่เหมาะสมก่อนที่ Cordova build จะเริ่มทำงาน ซึ่งจะทำให้กระบวนการปรับใช้เป็นไปโดยอัตโนมัติ
ตอนนี้ เราสามารถลองใช้แอพของเราได้แล้ว รันสิ่งต่อไปนี้ในบรรทัดคำสั่ง:
npm install rimraf npm install npm run start
คุณควรเห็นแอป React ตั้งค่าและทำงานในเบราว์เซอร์ เพิ่มคอร์โดวาตอนนี้:
cordova platform add iOS cordova run iOS
และคุณควรเห็นแอป React ทำงานในโปรแกรมจำลอง
การตั้งค่าเราเตอร์และแพ็คเกจ
ในการตั้งค่าโครงสร้างพื้นฐานบางอย่างที่เราจำเป็นต้องสร้างแอป ให้เริ่มต้นด้วยการติดตั้งแพ็คเกจที่จำเป็น:
npm install react-bootstrap react-router react-router-dom
ตอนนี้เราจะตั้งค่าการกำหนดเส้นทาง และในขณะทำเช่นนั้น เราจะตั้งค่าวัตถุสถานะส่วนกลางอย่างง่ายที่จะแชร์โดยส่วนประกอบทั้งหมด ในแอปพลิเคชันที่ใช้งานจริง เราจะต้องการใช้ระบบการจัดการสถานะเช่น Redux หรือ MobX แต่เราจะทำให้มันเรียบง่ายในตอนนี้ ไปที่ App.js และกำหนดค่าเส้นทางดังนี้:
import { BrowserRouter as Router, Redirect, Route, } from "react-router-dom"; function App() { const [curatedTweets, setCuratedTweets] = useState(); return <Router> <Route path="/" exact render={() => <Input setCuratedTweets={setCuratedTweets} />} /> <Route path="/display" render={() => <Display curatedTweets={curatedTweets} />} /> <Route path="*" exact render={() => <Redirect to="/" />} /> </Router> }
ด้วยการกำหนดเส้นทางนี้ เราได้แนะนำสองเส้นทางที่เราจะต้องนำไปใช้: อินพุตและการแสดงผล โปรดสังเกตว่าตัวแปร curatedTweets
กำลังถูกส่งผ่านไปยัง Display และตัวแปร setCuratedTweets
กำลังถูกส่งไปยัง Input ซึ่งหมายความว่าองค์ประกอบอินพุตจะสามารถเรียกใช้ฟังก์ชันเพื่อตั้งค่าตัวแปร curatedTweets
และ Display จะได้รับตัวแปรที่จะแสดง
ในการเริ่มเขียนโค้ดส่วนประกอบ ให้สร้างโฟลเดอร์ภายใต้ /src ชื่อ /src/components ภายใต้ /src/components ให้สร้างโฟลเดอร์อื่นชื่อ /src/components/input และอีกสองไฟล์ด้านล่าง: input.js และ input.css ทำเช่นเดียวกันกับองค์ประกอบ Display - create /src/components/display และข้างใต้: display.js และ display.css
ภายใต้สิ่งเหล่านี้ มาสร้างองค์ประกอบต้นขั้ว เช่น:
import React from 'react'; import 'input.css' const Input = () => <div>Input</div>; export default Input
และเช่นเดียวกันสำหรับจอแสดงผล:
import React from 'react'; import display.css' const Display = () => <div>Display</div>; export default Display
ด้วยเหตุนี้ การวางโครงลวดของเราจึงเสร็จสมบูรณ์และแอปควรทำงาน ให้เราเขียนโค้ดหน้า Input
ป้อนข้อมูลหน้า
แผนภาพใหญ่
ก่อนที่เราจะเขียนโค้ด ลองคิดดูว่าเราต้องการให้หน้า Input ของเราทำอะไร เห็นได้ชัดว่าเราต้องการวิธีที่ให้ผู้ใช้ป้อนและแก้ไขแฮนเดิล Twitter ที่พวกเขาต้องการดึงออกมา นอกจากนี้เรายังต้องการให้ผู้ใช้สามารถระบุว่าพวกเขาทำเสร็จแล้ว เมื่อผู้ใช้ระบุว่าเสร็จสิ้น เราจะต้องการดึงทวีตที่ดูแลจัดการจาก Python curation API ของเรา และสุดท้ายไปที่องค์ประกอบการแสดงผล
ตอนนี้เรารู้แล้วว่าต้องการให้ส่วนประกอบของเราทำอะไร เราก็พร้อมที่จะเขียนโค้ดแล้ว
การตั้งค่าไฟล์
เริ่มต้นด้วยการนำเข้าไลบรารี React Router ของ withRouter
เพื่อเข้าถึงฟังก์ชันการนำทาง React Bootstrap Components ที่เราต้องการ เช่น:
import React, {useState} from 'react'; import {withRouter} from 'react-router-dom'; import {ListGroup, Button, Form, Container, Row, Col} from 'react-bootstrap'; import './input.css';
ตอนนี้ มากำหนดฟังก์ชัน stub สำหรับ Input กัน เรารู้ว่าอินพุตได้รับฟังก์ชัน setCuratedTweets
และเราต้องการให้ความสามารถในการนำทางไปยังเส้นทางการแสดงผลหลังจากที่ตั้งค่าทวีตที่ดูแลจัดการจาก Python API ของเรา ดังนั้นเราจะต้องการนำอุปกรณ์ประกอบฉาก setCuratedTweets
และประวัติ (สำหรับการนำทาง)
const Input = ({setCuratedTweets, history}) => { return <div>Input</div> }
เพื่อให้มีการเข้าถึง API ประวัติ เราจะห่อด้วย withRouter
ในคำสั่งส่งออกที่ท้ายไฟล์:
export default withRouter(Input);
คอนเทนเนอร์ข้อมูล
มาตั้งค่าที่เก็บข้อมูลโดยใช้ React Hooks เรานำเข้าเบ็ด useState
แล้ว เพื่อให้เราสามารถเพิ่มโค้ดต่อไปนี้ลงในเนื้อหาขององค์ประกอบอินพุต:
const [handles, setHandles] = useState([]); const [handleText, setHandleText] = useState('');
สิ่งนี้จะสร้างคอนเทนเนอร์และตัวดัดแปลงสำหรับแฮนเดิล ซึ่งจะเก็บรายการของแฮนเดิลที่ผู้ใช้ต้องการดึงออกมา และ handleText
ซึ่งจะเก็บเนื้อหาของกล่องข้อความที่ผู้ใช้ใช้เพื่อป้อนแฮนเดิล
ตอนนี้ มาเขียนโค้ดส่วนประกอบ UI กัน
ส่วนประกอบ UI
ส่วนประกอบ UI จะค่อนข้างง่าย เราจะมีแถว Bootstrap หนึ่งแถวที่มีกล่องข้อความอินพุตพร้อมด้วยปุ่มสองปุ่ม หนึ่งแถวสำหรับเพิ่มเนื้อหาของกล่องอินพุตปัจจุบันไปยังรายการของแฮนเดิล และอีกปุ่มหนึ่งสำหรับดึงจาก API เราจะมีแถว Bootstrap อีกแถวที่แสดงรายการของตัวจัดการที่ผู้ใช้ต้องการดึงโดยใช้กลุ่มรายการ Bootstrap ในรหัสดูเหมือนว่า:
return ( <Container className="tl-container"> <Row> <Col> <Form.Control type="text" value={handleText} onChange={changeHandler} placeholder="Enter handle to pull" /> </Col> </Row> <Row className='input-row'> <Col> <Button variant="primary" onClick={getPull}>Pull</Button> {' '} <Button variant="success" onClick={onAddClicked}>Add</Button> </Col> </Row> <Row> <Col> <ListGroup className="handles-lg"> {handles.map((x, i) => <ListGroup.Item key={i}> {x} <span onClick={groupItemClickedBuilder(i)} className="delete-btn-span"> <Button variant="danger" size="sm"> delete </Button> </span> </ListGroup.Item>)} </ListGroup> </Col> </Row> </Container> );
นอกจากองค์ประกอบ UI แล้ว เรายังต้องการใช้ตัวจัดการเหตุการณ์ UI สามตัวที่จัดการการเปลี่ยนแปลงข้อมูล ตัวจัดการเหตุการณ์ getPull
ซึ่งเรียกใช้ API จะถูกนำไปใช้ในส่วนถัดไป
// set the handleText to current event value const textChangeHandler = (e) => { e.preventDefault(); setHandleText(e.target.value); } // Add handleText to handles, and then empty the handleText const onAddClicked = (e) => { e.preventDefault(); const newHandles = [...handles, handleText]; setHandles(newHandles); setHandleText(''); } // Remove the clicked handle from the list const groupItemClickedBuilder = (idx) => (e) => { e.preventDefault(); const newHandles = [...handles]; newHandles.splice(idx, 1); setHandles(newHandles); }
ตอนนี้ เราพร้อมที่จะใช้งานการเรียก API แล้ว
เรียก API
สำหรับการเรียก API เราต้องการแฮนเดิลที่เราต้องการดึง ส่งข้อมูลนั้นไปยัง Python API ในคำขอ POST และใส่ผลลัพธ์ JSON ที่เป็นผลลัพธ์ลงในตัวแปร curatedTweets
จากนั้น หากทุกอย่างเป็นไปด้วยดี เราต้องการนำทางไปยังเส้นทาง /display โดยทางโปรแกรม มิฉะนั้น เราจะบันทึกข้อผิดพลาดไปที่คอนโซลเพื่อให้เราสามารถดีบักได้ง่ายขึ้น
ในโหมดโค้ดจะมีลักษณะดังนี้:
const pullAPI = (e) => { e.preventDefault(); fetch('http://prismatic.pythonanywhere.com/get_tweets', { method: 'POST', mode: 'cors', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ accounts: handles }) }).then(r=>r.json()).then(j => { setCuratedTweets(j); history.push('/display'); }) .catch(e => { console.log(e); }) }
และด้วยเหตุนี้เราจึงควรไปได้ดี อย่าลังเลที่จะเรียกใช้แอป เพิ่มแฮนเดิลสองสามตัว และส่งคำขอไปยัง API
ตอนนี้ เราพร้อมที่จะเขียนโค้ดหน้าความเชื่อมั่นแล้ว
โหมดเรียงลำดับความรู้สึก
เนื่องจาก Python API ได้จัดเรียงทวีตตามความรู้สึกแล้ว เมื่อเราได้ผลจาก Python API แล้ว หน้าความรู้สึกก็ไม่ยากเกินไป
แผนภาพใหญ่
เราจะต้องการให้อินเทอร์เฟซรายการแสดงทวีต นอกจากนี้ เราต้องการองค์ประกอบการนำทางสองสามอย่างเพื่อสลับไปยังโหมดการจัดกลุ่มหัวข้อและกลับไปที่หน้าป้อนข้อมูล
ในการเริ่มต้น ให้กำหนดองค์ประกอบย่อยของโหมด SentimentDisplay ในไฟล์ display.js
SentimentDisplay Component
SentimentDisplay จะนำออบเจ็กต์ curatedTweets
และแสดงทวีตที่จัดเรียงตามความคิดเห็นในรายการ ด้วยความช่วยเหลือของ React-Bootstrap ส่วนประกอบนั้นค่อนข้างง่าย:
const SentimentDisplay = ({curatedTweets}) => { return <ListGroup> {curatedTweets.sentimentSorted.map((x, i) => <ListGroup.Item key={i}> <div className="account-div">{x.account}:</div> <span className="tweet-span">{x.tweet}</span> <span className="sentiments-span">({x.data.sentiment.toPrecision(2)})</span> </ListGroup.Item> )} </ListGroup> }
ระหว่างที่กำลังทำ เรามาเพิ่มสไตล์กัน ใส่สิ่งต่อไปนี้ใน display.css และนำเข้า:
.account-div { font-size: 12px; font-weight: 600; } .tweet-span { font-size: 11px; font-weight: 500; } .sentiments-span { font-size: 10px; } .tl-container { margin-top: 10px; } .disp-row { margin-top: 5px; }
ตอนนี้เราสามารถแสดงองค์ประกอบ SentimentDisplay ได้แล้ว เปลี่ยนฟังก์ชันการ Display
ดังนี้:
const Display = ({curatedTweets}) => { return <SentimentDisplay curatedTweets={curatedTweets} /> };
เรายังใช้โอกาสนี้ในการเขียนโค้ดส่วนประกอบการนำทางด้วย เราจะต้องการสองปุ่ม - ปุ่ม "กลับไปแก้ไข" และโหมดกลุ่มหัวข้อ
เราสามารถใช้ปุ่มเหล่านี้ในแถว Bootstrap แยกกันเหนือองค์ประกอบ SentimentDisplay เช่น:
Return <Container className="tl-container"> <Row> <Col> <Link to="/"><Button variant='primary'>Back</Button></Link> {' '} <Button variant='success'>View by Topic</Button> </Col> </Row> <Row className="disp-row"> <Col> <SentimentDisplay curatedTweets={curatedTweets} /> </Col> </Row> </Container>
เรียกใช้แอพและดึงทวีตจากที่จับสองสามอัน ดูเก๋ไก๋มาก!
โหมดการจัดกลุ่มหัวข้อ
ตอนนี้ เราต้องการใช้โหมดการจัดกลุ่มหัวข้อ มันซับซ้อนกว่า SentimentDisplay เล็กน้อย แต่ส่วนประกอบ Bootstrap ที่มีประโยชน์บางอย่างช่วยเราได้อย่างมาก
แผนภาพใหญ่
เราจะได้รับคำนามทั้งหมดและแสดงเป็นรายการหีบเพลง จากนั้นเราจะแสดงทวีตที่มีคำนามเมื่อมีการขยายรายการหีบเพลง
การนำการสลับไปใช้โหมดการจัดกลุ่มหัวข้อ
ขั้นแรก ให้ใช้ตรรกะเพื่อเปลี่ยนจากโหมดความรู้สึกเป็นโหมดการจัดกลุ่มหัวข้อ เริ่มต้นด้วยการสร้างองค์ประกอบต้นขั้วก่อน:
const TopicDisplay = () => { return <div>Topic Display</div> }
และตั้งค่าตรรกะบางอย่างเพื่อสร้างโหมดเพื่อแสดง ในองค์ประกอบการแสดงผลหลัก ให้เพิ่มบรรทัดต่อไปนี้เพื่อสร้างตรรกะสำหรับส่วนประกอบที่แสดง
// controls the display mode. Remember to import {useState} from 'react' const [displayType, setDisplayType] = useState('Sentiment'); // Switch the Display Mode const toggleDisplayType = () => { setDisplayType(displayType === 'Sentiment' ? 'Topic': 'Sentiment'); } // determines the text on the mode switch button const switchStr = displayType === 'Sentiment'? 'View by Topic': 'View by Sentiment'
และเปลี่ยน JSX ดังต่อไปนี้เพื่อเพิ่มตรรกะ:
Return <Container className="tl-container"> <Row> <Col> <Link to="/"><Button variant='primary'>Back</Button></Link> {' '} <Button variant='success' onClick={toggleDisplayType}>{switchStr}</Button> </Col> </Row> <Row className="disp-row"> <Col> { displayType === 'Sentiment'? <SentimentDisplay curatedTweets={curatedTweets} />: <TopicDisplay curatedTweets={curatedTweets} /> } </Col> </Row> </Container>
ตอนนี้ คุณควรเห็นต้นขั้วแสดงหัวข้อเมื่อคุณสลับ
ส่วนประกอบการแสดงหัวข้อ
ตอนนี้ เราพร้อมที่จะโค้ดคอมโพเนนต์ TopicDisplay
แล้ว ตามที่กล่าวไว้ก่อนหน้านี้ จะใช้ประโยชน์จาก Bootstrap Accordion List การนำไปใช้จริงค่อนข้างง่าย:
const TopicDisplay = ({curatedTweets}) => { return <Accordion> {Object.keys(curatedTweets.groupedByNp).map((x, i) => <Card key={i}> <Card.Header> <Accordion.Toggle as={Button} variant="link" eventKey={i}> {x} ({curatedTweets.groupedByNp[x].length}) </Accordion.Toggle> </Card.Header> <Accordion.Collapse eventKey={i}> <Card.Body> <ListGroup> {curatedTweets.groupedByNp[x].map((y, i2) => <ListGroup.Item key={i2}> <div className="account-div">{y.account}:</div> <span className="tweet-span">{y.tweet}</span> <span className="sentiments-span">({y.data.sentiment.toPrecision(2)})</span> </ListGroup.Item> )} </ListGroup> </Card.Body> </Accordion.Collapse> </Card> )} </Accordion> }
เรียกใช้แอป และคุณจะเห็นการแสดงหัวข้อ
ตอนนี้แอพเสร็จแล้ว และเราพร้อมที่จะสร้างแอพสำหรับอีมูเลเตอร์
เรียกใช้แอพในโปรแกรมจำลอง
Cordova ทำให้ง่ายต่อการเรียกใช้แอพในโปรแกรมจำลอง เพียงแค่เรียกใช้:
cordova platform add ios # if you haven't done so already cordova run ios
และคุณควรเห็นแอปในโปรแกรมจำลอง เนื่องจาก Bootstrap เป็นเว็บแอปที่ตอบสนอง เว็บแอปจึงปรับความกว้างของ iPhone และทุกอย่างดูดีมาก
เมื่อแอป Cordova เสร็จสิ้น มาดูการใช้งาน Ionic กัน
การดำเนินการอิออน-ปฏิกิริยา
อิออนไพรเมอร์
Ionic คือไลบรารีเว็บคอมโพเนนต์และชุดเครื่องมือ CLI ที่ทำให้การสร้างแอปพลิเคชันไฮบริดง่ายขึ้น เดิมที Ionic ถูกสร้างขึ้นบน AngularJS และ Cordova แต่พวกเขาได้เปิดตัวส่วนประกอบใน React.js และเริ่มสนับสนุน Capacitor ซึ่งเป็นแพลตฟอร์มที่คล้ายกับ Cordova สิ่งที่ทำให้ Ionic แตกต่างไปจากเดิมก็คือ แม้ว่าคุณจะใช้คอมโพเนนต์ของเว็บ แต่ส่วนประกอบต่างๆ ก็รู้สึกเหมือนกับอินเทอร์เฟซมือถือแบบเนทีฟมาก นอกจากนี้ รูปลักษณ์และความรู้สึกของส่วนประกอบ Ionic จะปรับให้เข้ากับระบบปฏิบัติการที่ทำงานอยู่โดยอัตโนมัติ ซึ่งช่วยให้แอปพลิเคชันดูและให้ความรู้สึกเป็นธรรมชาติและเป็นธรรมชาติมากขึ้น สุดท้ายนี้ แม้ว่าสิ่งนี้จะอยู่นอกขอบเขตของบทความของเรา Ionic ยังมีเครื่องมือสร้างหลายอย่างที่ทำให้การปรับใช้งานแอปพลิเคชันของคุณง่ายขึ้นอีกเล็กน้อย
สำหรับแอปพลิเคชันของเรา เราจะใช้ส่วนประกอบ React ของ Ionic เพื่อสร้าง UI ในขณะที่ใช้ประโยชน์จากตรรกะ JavaScript บางส่วนที่เราสร้างขึ้นระหว่างส่วน Cordova
การกำหนดค่าแอพ
ขั้นแรก เราจะต้องการติดตั้งเครื่องมืออิออน ลองทำสิ่งต่อไปนี้:
npm install -g @Ionic/cli native-run cordova-res
และหลังจากติดตั้งเสร็จแล้ว ให้ไปที่โฟลเดอร์โปรเจ็กต์กัน ตอนนี้ เราใช้ Ionic CLI เพื่อสร้างโฟลเดอร์โครงการใหม่ของเรา:
ionic start twitter-curation-Ionic blank --type=react --capacitor
ดูความมหัศจรรย์ที่เกิดขึ้น และตอนนี้เข้าไปในโฟลเดอร์ด้วย:
cd twitter-curation-Ionic
และเรียกใช้แอพเปล่าด้วย:
ionic serve
แอพของเราได้รับการตั้งค่าและพร้อมใช้งาน มากำหนดเส้นทางกัน
ก่อนที่เราจะดำเนินการต่อ คุณจะสังเกตเห็นว่า Ionic เริ่มต้นโครงการโดยใช้ TypeScript แม้ว่าฉันจะไม่ได้ใช้ TypeScript แต่ก็มีคุณสมบัติที่ดีมาก และเราจะใช้มันสำหรับการใช้งานนี้
การตั้งค่าเราเตอร์
สำหรับการใช้งานนี้ เราจะใช้สามเส้นทาง - input, sentimentDisplay
และ topicDisplay
เราทำเช่นนี้เพราะเราต้องการใช้ประโยชน์จากคุณลักษณะการเปลี่ยนภาพและการนำทางที่จัดเตรียมโดย Ionic และเนื่องจากเรากำลังใช้ส่วนประกอบ Ionic และรายการหีบเพลงไม่ได้มาพร้อมกับ Ionic ที่บรรจุไว้ล่วงหน้า แน่นอนว่าเราสามารถปรับใช้ของเราเองได้ แต่สำหรับบทช่วยสอนนี้ เราจะใช้ส่วนประกอบ Ionic ที่ให้มา
หากคุณไปที่ App.tsx คุณจะเห็นเส้นทางพื้นฐานที่กำหนดไว้แล้ว
ป้อนข้อมูลหน้า
แผนภาพใหญ่
เราจะใช้ตรรกะและโค้ดที่คล้ายคลึงกันในการนำ Bootstrap ไปใช้งาน โดยมีข้อแตกต่างที่สำคัญบางประการ อันดับแรก เราจะใช้ TypeScript ซึ่งหมายความว่าเราจะมีคำอธิบายประกอบสำหรับโค้ดของเรา ซึ่งคุณจะเห็นในส่วนถัดไป ประการที่สอง เราจะใช้ส่วนประกอบ Ionic ซึ่งมีลักษณะคล้ายกับ Bootstrap มาก แต่จะมีความไวต่อระบบปฏิบัติการในการจัดรูปแบบ สุดท้ายนี้ เราจะนำทางแบบไดนามิกโดยใช้ API ประวัติเหมือนในเวอร์ชัน Bootstrap แต่การเข้าถึงประวัติแตกต่างกันเล็กน้อยเนื่องจากการใช้งาน Ionic Router
การตั้งค่า
เริ่มต้นด้วยการตั้งค่าองค์ประกอบอินพุตด้วยองค์ประกอบต้นขั้ว สร้างโฟลเดอร์ภายใต้เพจชื่อ input และสร้างภายใต้ไฟล์ชื่อ Input.tsx ภายในไฟล์นั้น ให้ใส่โค้ดต่อไปนี้เพื่อสร้างส่วนประกอบ React สังเกตว่าเนื่องจากเราใช้ TypeScript มันจึงแตกต่างออกไปเล็กน้อย
import React, {useState} from 'react'; const Input : React.FC = () => { return <div>Input</div>; } export default Input;
และเปลี่ยนส่วนประกอบใน App.tsx เป็น:
const App: React.FC = () => ( <IonApp> <IonReactRouter> <IonRouterOutlet> <Route path="/input" component={Input} exact={true} /> <Route exact path="/" render={() => <Redirect to="/input" />} /> </IonRouterOutlet> </IonReactRouter> </IonApp> );
ตอนนี้ เมื่อคุณรีเฟรชแอป คุณควรเห็นองค์ประกอบอินพุต stub
คอนเทนเนอร์ข้อมูล
มาสร้างที่เก็บข้อมูลกันเถอะ เราต้องการคอนเทนเนอร์สำหรับตัวจัดการ Twitter ที่ป้อนเข้ามา เช่นเดียวกับเนื้อหาปัจจุบันของช่องป้อนข้อมูล เนื่องจากเราใช้ TypeScript เราจึงต้องเพิ่มคำอธิบายประกอบประเภทในการเรียก useState
ของเราในฟังก์ชันส่วนประกอบ:
const Input : React.FC = () => { const [text, setText] = useState<string>(''); const [accounts, setAccounts] = useState<Array<string>>([]); return <div>Input</div>; }
นอกจากนี้เรายังต้องการให้ที่เก็บข้อมูลเพื่อเก็บค่าส่งคืนจาก API เนื่องจากเนื้อหานั้นจำเป็นต้องแชร์กับเส้นทางอื่น เราจึงกำหนดไว้ที่ระดับ App.tsx นำเข้า useState
จาก React ในไฟล์ App.tsx และเปลี่ยนฟังก์ชันคอนเทนเนอร์ของแอปเป็นด้านล่าง:
const App: React.FC = () => { const [curatedTweets, setCuratedTweets] = useState<CuratedTweets>({} as CuratedTweets); return ( <IonApp> <IonReactRouter> <IonRouterOutlet> <Route path="/input" component={Input} exact={true} /> <Route exact path="/" render={() => <Redirect to="/input" />} /> </IonRouterOutlet> </IonReactRouter> </IonApp> ); }
ณ จุดนี้ หากคุณกำลังใช้ตัวแก้ไขที่มีการเน้นไวยากรณ์เช่น Visual Studio Code คุณจะเห็น CuratedTweets สว่างขึ้น เนื่องจากไฟล์ไม่ทราบว่าอินเทอร์เฟซ CuratedTweets เป็นอย่างไร มากำหนดกันตอนนี้ สร้างโฟลเดอร์ภายใต้ src ที่เรียกว่าอินเตอร์เฟส และสร้างไฟล์ภายในชื่อ CuratedTweets.tsx ในไฟล์ กำหนดอินเตอร์เฟส CuratedTweets ดังนี้:
interface TweetRecordData { noun_phrases: Array<string>, sentiment: number } export interface TweetRecord { account: string, data: TweetRecordData, tweet: string } export default interface CuratedTweets { groupedByNp: Record<string, Array<TweetRecord>>, processedTweets: Array<TweetRecord>, sentimentSorted: Array<TweetRecord> }
ตอนนี้แอปรู้เกี่ยวกับโครงสร้างของข้อมูลส่งคืน API แล้ว นำเข้าอินเทอร์เฟซ CuratedTweets ใน App.tsx คุณควรเห็น App.tsx คอมไพล์โดยไม่มีปัญหาในตอนนี้
เราต้องทำอีกสองสามสิ่งที่นี่ เราจำเป็นต้องส่งฟังก์ชัน setCuratedTweets
ไปยังองค์ประกอบ Input และทำให้ส่วนประกอบ Input รับรู้ถึงฟังก์ชันนี้
ใน App.tsx ให้แก้ไขเส้นทางอินพุตดังนี้:
<Route path="/input" render={() => <Input setCuratedTweets={setCuratedTweets}/>} exact={true} />
ตอนนี้ คุณควรเห็นตัวแก้ไขตั้งค่าสถานะเป็นอย่างอื่น - อินพุตไม่ทราบเกี่ยวกับพร็อพใหม่ที่กำลังถูกส่งผ่าน ดังนั้นเราจะต้องการกำหนดใน Input.tsx
First, import the CuratedTweets interface, then define the ContainerProps interface like so:
interface ContainerProps { setCuratedTweets: React.Dispatch<React.SetStateAction<CuratedTweets>> }
And finally, change the Input component definition like so:
const Input : React.FC<ContainerProps> = ({setCuratedTweets}) => { const [text, setText] = useState<string>(''); const [accounts, setAccounts] = useState<Array<string>>([]); return <div>Input</div>; }
We are done defining the data containers, and now, onto building the UI components.
UI Components
For the UI component, we will want to build an input component and a list display component. Ionic provides some simple containers for these.

Let's start by importing the library components we'll be using:
import { IonInput, IonItem, IonList, IonButton, IonGrid, IonRow, IonCol } from '@Ionic/react';
Now, we can replace the stub component with the IonInput
, wrapped in an IonGrid:
return <IonGrid> <IonRow> <IonCol> <IonInput value={text} placeholder="Enter accounts to pull from" onIonChange={e => setText(e.detail.value!)} /> </IonCol> </IonRow> </IonGrid>
Notice that the event listener is onIonChange
instead of onChange
. Otherwise, it should look very familiar.
When you open the app in your browser, it may not look like the Bootstrap app. However, if you set your browser to emulator mode, the UI will make more sense. It will look even better once you deploy it on mobile, so look forward to it.
Now, let's add some buttons. We will want an “Add to list” button and a “Pull API” button. For that, we can use IonButton. Change the size of the input's IonCol to 8 and add the following two buttons with columns:
<IonCol size="8"> <IonInput value={text} placeholder="Enter accounts to pull from" onIonChange={e => setText(e.detail.value!)} /> </IonCol> <IonCol size="2"> <IonButton style={{float: 'right'}} color="primary" size="small" onClick={onAddClicked}>Add</IonButton> </IonCol> <IonCol size="2"> <IonButton style={{float: 'right'}} color="success" size="small" onClick={onPullClicked}>Pull</IonButton> </IonCol>
Since we're writing the buttons, let's write the event handlers as well.
The handler to add a Twitter handle to the list is simple:
const onAddClicked = () => { if (text === undefined || text.length === 0) { return; } const newAccounts: Array<string> = [...accounts, text]; setAccounts(newAccounts); setText(''); }
We will implement the API call in the next section, so let's just put a stub function for onPullClicked
:
const onPullClicked = () => {}
Now, we need to write the component for displaying the list of handles that has been inputted by the user. For that, we will use IonList, put into a new IonRow:
<IonRow> <IonCol> <IonList> {accounts.map((x:string, i:number) => <IonItem key={i}> <IonGrid> <IonRow> <IonCol size="8" style={{paddingTop: '12px'}}>{x}</IonCol> <IonCol><IonButton style={{float: 'right'}} color="danger" size="small" onClick={deleteClickedBuilder(i)}>Delete</IonButton></IonCol> </IonRow> </IonGrid> </IonItem>)} </IonList> </IonCol> </IonRow>
Each list item is displaying the handle and a delete button in its very own IonGrid. For this code to compile, we will want to implement the deleteClickedHandler
as well. It should be very familiar from the previous section but with TypeScript annotations.
const deleteClickedBuilder = (idx: number) => () => { const newAccounts: Array<string> = [...accounts]; newAccounts.splice(idx, 1); setAccounts(newAccounts); }
Save this, and you should see the Input page with all the UI components implemented. We can add handles, delete handles, and click the button to invoke the API.
As a final exercise, let's move the in-line styles to CSS. Create a file in the input folder called input.css and import it in the Input.tsx file. Then, add the following styles:
.input-button { float: right; } .handle-display { padding-top: 12px; }
Now, add className="input-button”
on all of the IonButtons and className=”handle-display”
on the handle list item IonCol that is displaying the intended Twitter handle. Save the file, and you should see everything looking quite good.
API Call
The code to pull the API is very familiar from the previous section, with one exception - we have to get access to the history component to be able to dynamically change routes. We will do this using the withHistory
hook.
We first import the hook:
import { useHistory } from 'react-router';
And then implement the handler in the input component:
const history = useHistory(); const switchToDisplay = () => { history.push('/display'); } const onPullClicked = () => { fetch('http://prismatic.pythonanywhere.com/get_tweets', { method: 'POST', mode: 'cors', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ accounts }) }).then(r=>r.json()).then(j => { setCuratedTweets(j); switchToDisplay(); }) .catch(e => { console.log(e); }) }
การเพิ่มส่วนหัว
Our Input page looks quite nice, but it looks a little bare due to Ionic's mobile-centric styling. To make the UI look more natural, Ionic provides a header feature that lets us provide a more natural user experience. When running on mobile, the header will also simulate the native OS's mobile platform, which makes the user experience even more natural.
Change your component import to:
import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent, IonInput, IonItem, IonList, IonButton, IonGrid, IonRow, IonCol } from '@Ionic/react';
And now wrap the UI in an Ionic page with a header, like so:
return <IonPage> <IonHeader> <IonToolbar> <IonTitle>Twitter Curation App</IonTitle> </IonToolbar> </IonHeader> <IonContent> <IonHeader collapse="condense"> <IonToolbar> <IonTitle size="large">Twitter Curation App</IonTitle> </IonToolbar> </IonHeader> <IonGrid> <IonRow> <IonCol size="8"> <IonInput value={text} placeholder="Enter accounts to pull from" onIonChange={e => setText(e.detail.value!)} /> </IonCol> <IonCol size="2"> <IonButton className="input-button" color="primary" size="small" onClick={onAddClicked}>Add</IonButton> </IonCol> <IonCol size="2"> <IonButton className="input-button" color="success" size="small" onClick={onPullClicked}>Pull</IonButton> </IonCol> </IonRow> <IonRow> <IonCol> <IonList> {accounts.map((x:string, i:number) => <IonItem key={i}> <IonGrid> <IonRow> <IonCol size="8" className="handle-display">{x}</IonCol> <IonCol><IonButton className="input-button" color="danger" size="small" onClick={deleteClickedBuilder(i)}>Delete</IonButton></IonCol> </IonRow> </IonGrid> </IonItem>)} </IonList> </IonCol> </IonRow> </IonGrid> </IonContent> </IonPage>
ตอนนี้ดูดี!
เพจเรียงลำดับความรู้สึก
แผนภาพใหญ่
หน้าที่จัดเรียงความคิดเห็นส่วนใหญ่จะคล้ายกับหน้าที่จัดเรียงความคิดเห็นจากหน้า Bootstrap แต่ใช้ TypeScript และ Ionic นอกจากนี้ เรายังจะใช้การแสดงหัวข้อเป็นเส้นทางแยกต่างหากเพื่อใช้ประโยชน์จากคุณลักษณะการนำทางของ Ionic ในขณะที่ทำงานบนมือถือ ดังนั้น เราจะต้องให้หน้านี้มีความสามารถในการนำทางไปยังเส้นทางการแสดงหัวข้อด้วย
ตั้งค่าเส้นทาง
เริ่มต้นด้วยการสร้างโฟลเดอร์ใหม่ชื่อ Sentimentsorted และไฟล์ชื่อ SentimentSorted.tsx ด้านล่าง ส่งออกองค์ประกอบต้นขั้วดังนี้:
import React from 'react'; const SentimentSorted: React.FC = () => { return <div>Sentiment Sorted</div> } export default SentimentSorted;
และใน App.tsx ให้นำเข้าส่วนประกอบ:
import SentimentSorted from './pages/sentimentsorted/SentimentSorted';
และเพิ่มเส้นทาง:
<Route path="/display" render={() => <SentimentSorted curatedTweets={curatedTweets} />} />
คุณจะได้รับข้อผิดพลาดของ TypeScript โดยแจ้งว่า SentimentSorted
ไม่ได้คาดหวังพร็อพ curatedTweets ดังนั้นเรามาดูแลกันในส่วนถัดไป
ส่วนประกอบ UI
เริ่มต้นด้วยการกำหนดอุปกรณ์ประกอบฉากของคอนเทนเนอร์ เหมือนกับองค์ประกอบอินพุต:
import CuratedTweets from '../../interfaces/CuratedTweets'; interface ContainerProps { curatedTweets: CuratedTweets }
และตอนนี้ เปลี่ยนการแสดงผลต้นขั้ว:
const SentimentSorted: React.FC<ContainerProps> = ({curatedTweets}) => { return <div>Sentiment Sorted</div> }
และทุกอย่างควรรวบรวม
การแสดงผลนั้นง่ายมาก เป็นเพียง IonList ที่มีส่วนประกอบการแสดงผล:
return <IonGrid> <IonRow> <IonCol> <IonList> {(curatedTweets.sentimentSorted).map((x, i) => <IonItem key={i}> <IonLabel className="ion-text-wrap"> <h2>{x.account}:</h2> <h3>{x.tweet}</h3> <p>({x.data.sentiment.toPrecision(2)})</p> </IonLabel> </IonItem>)} </IonList> </IonCol> </IonRow> </IonGrid>
หากคุณบันทึกและดึงทวีตบางส่วนโดยใช้องค์ประกอบอินพุต คุณจะเห็นทวีตที่แสดงในรายการ
ตอนนี้ มาเพิ่มปุ่มนำทางกัน เพิ่มใน IonGrid:
<IonRow> <IonCol> <IonButton color='primary' onClick={switchToInput}>Back</IonButton> <IonButton color="success" onClick={toggleDisplayType}>{switchStr}</IonButton> </IonCol> </IonRow>
switchToInput
นั้นใช้งานง่ายมากกับ API ประวัติ:
const switchToInput = () => { history.goBack(); }
และ ToggleDisplayType
ก็น่าจะคุ้นเคยเช่นกัน:
const toggleDisplayType = () => { setDisplayType(displayType === 'Sentiment' ? 'Topic': 'Sentiment'); } const switchStr = displayType === 'Sentiment'? 'View by Topic': 'View by Sentiment'
ตอนนี้เราได้นำองค์ประกอบ SentimentDisplay
ไปใช้แล้ว ก่อนที่เราจะใช้งานหน้าแสดงหัวข้อ เราจำเป็นต้องติดตั้งใช้งานองค์ประกอบที่แสดงหัวข้อทั้งหมด เราจะทำอย่างนั้นในหัวข้อถัดไป
หัวข้อกลุ่มส่วนประกอบ
มาเพิ่มตัวเลือกการแสดงรายการหัวข้อและแสดงตามเงื่อนไข ในการทำเช่นนั้น เราต้องแยกรายการแสดงความเห็นอกเห็นใจ เปลี่ยนชื่อ SentimentDisplay เป็น Display และมาแยกรายการแสดงความรู้สึกกัน:
interface SentimentDisplayProps { sentimentSorted: Array<TweetRecord> } const SentimentDisplay: React.FC<SentimentDisplayProps> = ({sentimentSorted}) => { return <IonList> {(sentimentSorted || []).map((x, i) => <IonItem key={i}> <IonLabel className="ion-text-wrap"> <h2>{x.account}:</h2> <h3>{x.tweet}</h3> <p>({x.data.sentiment.toPrecision(2)})</p> </IonLabel> </IonItem>)} </IonList> }
สังเกตว่าเราใช้หนึ่งในคำจำกัดความของคลาสในอินเทอร์เฟซ CuratedTweets อย่างไร นั่นเป็นเพราะว่าส่วนประกอบเหล่านี้ไม่ต้องการออบเจ็กต์ CuratedTweets ทั้งหมด แต่มีเพียงชุดย่อยเท่านั้น รายการหัวข้อคล้ายกันมาก:
interface TopicDisplayProps { groupedByNP: Record<string, Array<TweetRecord>> } const TopicDisplay: React.FC<TopicDisplayProps> = ({groupedByNP}) => { return <IonList> {Object.keys(groupedByNP || {}).map((x, i) => <IonItem key={i} routerLink={`/topicDisplay/${encodeURIComponent(x)}`}> <IonLabel className="ion-text-wrap"> <h2>{x} ({groupedByNP[x].length})</h2> </IonLabel> </IonItem>)} </IonList> }
และตอนนี้ การแสดงตามเงื่อนไขก็ตั้งค่าได้ง่ายในส่วนประกอบการแสดงผล:
return ( <IonGrid> <IonRow> <IonCol> <IonButton color='primary' onClick={switchToInput}>Back</IonButton> <IonButton color="success" onClick={toggleDisplayType}>{switchStr}</IonButton> </IonCol> </IonRow> { displayType === 'Sentiment'? <SentimentDisplay sentimentSorted={curatedTweets.sentimentSorted} /> : <TopicDisplay groupedByNP={curatedTweets.groupedByNp} /> } </IonGrid> );
อย่าลืมเปลี่ยนการส่งออกเริ่มต้น และตอนนี้เราก็พร้อมที่จะใช้งานหน้าแสดงหัวข้อแล้ว
หน้าแสดงหัวข้อ
แผนภาพใหญ่
หน้าแสดงหัวข้อเป็นการแสดงรายการคล้ายกับการแสดงความรู้สึก แต่เราจะค้นหาหัวข้อที่เป็นปัญหาจากพารามิเตอร์เส้นทาง
ตั้งค่าเส้นทาง
ถ้าคุณมาไกลขนาดนี้ คุณควรรู้ว่าต้องทำอย่างไร มาสร้างโฟลเดอร์เพจชื่อ topicdisplay และ TopicDisplay.tsx เขียนองค์ประกอบ stub และนำเข้าไปยังหน้า App.tsx ตอนนี้ มาตั้งค่าเส้นทาง:
<Route path="/topicDisplay/:topic" render={() => <TopicDisplay curatedTweets={curatedTweets} /> } />
ตอนนี้เราพร้อมที่จะใช้งานองค์ประกอบ UI แล้ว
ส่วนประกอบ UI
ขั้นแรก ให้สร้างคำจำกัดความของ ContainerProps
:
interface ContainerProps { curatedTweets: CuratedTweets } const TopicDisplay: React.FC<ContainerProps> = ({curatedTweets}) => { Return <div>Topic Display</div> }
ตอนนี้ เราจะต้องเรียกหัวข้อจากชื่อเส้นทาง URL ในการทำเช่นนั้น เราจะใช้ API ประวัติ มานำเข้า useHistory
สร้างตัวอย่าง API ประวัติ และดึงหัวข้อจากชื่อพาธ ในขณะที่เรากำลังดำเนินการอยู่ เรามาลองใช้ฟังก์ชันสลับกลับกัน:
const TopicDisplay: React.FC<ContainerProps> = ({curatedTweets}) => { const history = useHistory(); const switchToDisplay = () => { history.goBack(); } const topic = history.location.pathname.split('/')[2]; const tweets = (curatedTweets.groupedByNp || {})[topic];
ตอนนี้เรามีทวีตในหัวข้อนั้นแล้ว การแสดงก็ค่อนข้างง่าย:
return ( <IonGrid> <IonRow> <IonCol> <IonButton color='primary' onClick={switchToDisplay}>Back</IonButton> </IonCol> </IonRow> <IonRow> <IonCol> <IonList> {(tweets || []).map((x, i) => <IonItem key={i}> <IonLabel className="ion-text-wrap"> <h2>{x.account}:</h2> <h3>{x.tweet}</h3> <p>({x.data.sentiment.toPrecision(2)})</p> </IonLabel> </IonItem>)} </IonList> </IonCol> </IonRow> </IonGrid> );
บันทึกและเรียกใช้และสิ่งต่าง ๆ ควรดูดี
เรียกใช้แอพในโปรแกรมจำลอง
ในการรันแอพในอีมูเลเตอร์ เราเพียงแค่รันคำสั่ง Ionic สองสามคำสั่งเพื่อเพิ่มแพลตฟอร์มมือถือและคัดลอกโค้ด เหมือนกับที่เราตั้งค่าด้วย Cordova
ionic build # builds the app ionic cap add ios # adds iOS as one of the platforms, only have to run once ionic cap copy # copy the build over ionic cap sync # only need to run this if you added native plugins ionic cap open ios # run the iOS emulator
และคุณควรเห็นแอปปรากฏขึ้น
ตอบสนองการใช้งานแบบเนทีฟ
React Native Primer
React Native ใช้แนวทางที่แตกต่างจากแนวทางบนเว็บของส่วนก่อนหน้าอย่างมาก React Native แสดงโค้ด React ของคุณเป็นส่วนประกอบดั้งเดิม นี้มาพร้อมกับข้อดีหลายประการ ประการแรก การผสานรวมกับระบบปฏิบัติการพื้นฐานนั้นลึกกว่ามาก ซึ่งช่วยให้นักพัฒนาใช้ประโยชน์จากคุณลักษณะใหม่ของสมาร์ทโฟนและคุณลักษณะเฉพาะของระบบปฏิบัติการที่อาจไม่มีให้บริการผ่าน Cordova/Capacitor ประการที่สอง เนื่องจากไม่มีเอ็นจิ้นการเรนเดอร์บนเว็บอยู่ตรงกลาง โดยทั่วไปแอป React Native จึงเร็วกว่าแอปที่เขียนโดยใช้ Cordova สุดท้าย เนื่องจาก React Native อนุญาตให้รวมส่วนประกอบดั้งเดิม นักพัฒนาจึงสามารถควบคุมแอปพลิเคชันของตนได้อย่างละเอียดยิ่งขึ้น
สำหรับแอปพลิเคชันของเรา เราจะใช้ตรรกะจากส่วนก่อนหน้านี้ และใช้ไลบรารีส่วนประกอบ React Native ที่เรียกว่า NativeBase เพื่อเขียนโค้ด UI ของเรา
การกำหนดค่าแอพ
ขั้นแรก คุณจะต้องติดตั้งส่วนประกอบที่จำเป็นทั้งหมดของ React Native โดยทำตามคำแนะนำที่นี่
เมื่อติดตั้ง React Native แล้ว มาเริ่มโครงการกันเลย:
react-native init TwitterCurationRN
ปล่อยให้สคริปต์การตั้งค่าทำงาน และสุดท้าย โฟลเดอร์ควรถูกสร้างขึ้น ซีดีลงในโฟลเดอร์และเรียกใช้ react-native run-ios และคุณควรเห็นโปรแกรมจำลองปรากฏขึ้นพร้อมกับแอปตัวอย่าง
เราจะต้องการติดตั้ง NativeBase ด้วยเนื่องจากเป็นไลบรารีส่วนประกอบของเรา ในการทำเช่นนั้น เราเรียกใช้:
npm install --save native-base react-native link
เราต้องการติดตั้ง React Native stack navigator ด้วย มาเริ่มกันเลย:
npm install --save @react-navigation/stack @react-navigation/native
และ
react-native link cd ios pod-install cd
เพื่อทำการเชื่อมโยงและติดตั้งปลั๊กอินดั้งเดิมให้สมบูรณ์
การตั้งค่าเราเตอร์
สำหรับการกำหนดเส้นทาง เราจะใช้สแตกเนวิเกเตอร์ที่เราติดตั้งในขั้นตอนข้างต้น
นำเข้าส่วนประกอบเราเตอร์:
import { NavigationContainer } from '@react-navigation/native'; import {createStackNavigator} from '@react-navigation/stack';
และตอนนี้ เราสร้างตัวนำทางสแต็ก:
const Stack = createStackNavigator();
เปลี่ยนเนื้อหาของคอมโพเนนต์ App เพื่อใช้ stack navigator:
const App = () => { return ( <> <StatusBar bar /> <NavigationContainer> <Stack.Navigator initialRouteName="Entry"> <Stack.Screen name="Entry" component={Entry} /> </Stack.Navigator> </NavigationContainer> </> ); };
ณ จุดนี้ คุณจะได้รับข้อผิดพลาดเนื่องจากยังไม่ได้กำหนดรายการ ลองกำหนดองค์ประกอบต้นขั้วเพื่อให้มีความสุข
สร้างโฟลเดอร์ส่วนประกอบในโครงการของคุณ สร้างไฟล์ชื่อ Entry.jsx และเพิ่มองค์ประกอบต้นขั้วดังนี้:
import React, {useState} from 'react'; import { Text } from 'native-base'; export default Entry = ({navigation}) => { return <Text>Entry</Text>; // All text must be wrapped in a <Text /> component or <TextView /> if you're not using NativeBase. }
นำเข้าองค์ประกอบรายการในแอปของคุณและควรสร้าง
ตอนนี้ เราพร้อมที่จะเขียนโค้ดหน้าอินพุตแล้ว
ป้อนข้อมูลหน้า
แผนภาพใหญ่
เราจะใช้เพจที่คล้ายกับเพจด้านบนแต่ใช้คอมโพเนนต์ NativeBase JavaScript และ React API ส่วนใหญ่ที่เราใช้ เช่น hooks and fetch ทั้งหมดยังคงใช้งานได้
ข้อแตกต่างประการหนึ่งคือวิธีที่เราทำงานกับ API การนำทาง ซึ่งคุณจะเห็นในภายหลัง
ส่วนประกอบ UI
ส่วนประกอบ NativeBase เพิ่มเติมที่เราจะใช้ ได้แก่ คอนเทนเนอร์ เนื้อหา อินพุต รายการ รายการ รายการ และปุ่ม สิ่งเหล่านี้ล้วนมีแอนะล็อกใน Ionic และ Bootstrap React และผู้สร้าง NativeBase ได้ทำให้มันใช้งานง่ายมากสำหรับผู้ที่คุ้นเคยกับไลบรารีเหล่านี้ เพียงนำเข้าเช่น:
import { Container, Content, Input, Item, Button, List, ListItem, Text } from 'native-base';
และองค์ประกอบคือ:
return <Container> <Content> <Item regular> <Input placeholder='Input Handles Here' onChangeText={inputChange} value={input} /> <Button primary onPress={onAddClicked}><Text> Add </Text></Button> <Text> </Text> <Button success onPress={onPullClicked}><Text> Pull </Text></Button> </Item> <Item> <List style={{width: '100%'}}> {handles.map((item) => <ListItem key={item.key}> <Text>{item.key}</Text> </ListItem>)} </List> </Item> </Content> </Container>
และตอนนี้ เรามาปรับใช้ตัวจัดการสถานะและเหตุการณ์:
const [input, changeInput] = useState(''); const [handles, changeHandles] = useState([]); const inputChange = (text) => { changeInput(text); } const onAddClicked = () => { const newHandles = [...handles, {key: input}]; changeHandles(newHandles); changeInput(''); }
และสุดท้าย การเรียก API:
const onPullClicked = () => { fetch('http://prismatic.pythonanywhere.com/get_tweets', { method: 'POST', mode: 'cors', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ accounts: handles.map(x => x.key) }) }).then(r=>r.json()).then(data => { navigation.navigate('SentimentDisplay', { data }); }) .catch(e => { console.log(e); }) }
ขอให้สังเกตว่าการใช้งานนี้เหมือนกับในการใช้งาน NativeBase ยกเว้นว่าเรากำลังนำทางไปในทางที่ต่างออกไป สแต็กเนวิเกเตอร์ส่งผ่านอุปกรณ์ที่เรียกว่า "การนำทาง" ไปยังส่วนประกอบต่างๆ และสามารถใช้เพื่อนำทางระหว่างเส้นทางด้วย . .navigate
นอกจากการนำทางอย่างง่าย ๆ คุณยังสามารถส่งข้อมูลไปยังองค์ประกอบเป้าหมายได้อีกด้วย เราจะใช้กลไกนี้ในการส่งข้อมูล
ในการคอมไพล์แอพ เรายังจำเป็นต้องทำให้ Entry
รู้ถึงองค์ประกอบการนำทาง ในการทำเช่นนั้น เราจะต้องเปลี่ยนการประกาศฟังก์ชันส่วนประกอบ:
export default Entry = ({navigation}) => {
ตอนนี้บันทึกและคุณควรเห็นหน้า
เพจเรียงลำดับความรู้สึก
แผนภาพใหญ่
เราจะใช้หน้าความเชื่อมั่นเหมือนกับส่วนก่อนหน้านี้ แต่เราจะจัดรูปแบบหน้าเว็บให้แตกต่างออกไปเล็กน้อย และเราจะใช้ไลบรารีการนำทางต่างกันเพื่อรับค่าส่งคืนการเรียก API
เนื่องจาก React Native ไม่มี CSS เราจึงจำเป็นต้องกำหนดวัตถุ StyleSheet หรือเพียงแค่โค้ดสไตล์ในบรรทัด เนื่องจากเราจะแชร์สไตล์บางส่วนในองค์ประกอบต่างๆ มาสร้างสไตล์ชีตส่วนกลางกัน เราจะทำอย่างนั้นหลังจากตั้งค่าเส้นทางแล้ว
นอกจากนี้ เนื่องจาก StackNavigator
มีปุ่มย้อนกลับในตัว เราจึงไม่จำเป็นต้องใช้ปุ่มย้อนกลับของเราเอง
ตั้งค่าเส้นทาง
การกำหนดเส้นทางใน StackNavigator
นั้นง่ายมาก เราเพียงแค่สร้างหน้าจอใหม่ที่ชื่อว่า Stack Screen และให้ส่วนประกอบเหมือนกับเราเตอร์ React
<NavigationContainer> <Stack.Navigator initialRouteName="Entry"> <Stack.Screen name="Entry" component={Entry} /> <Stack.Screen name="SentimentDisplay" component={SentimentDisplay} /> </Stack.Navigator> </NavigationContainer>
ในการทำงานนี้ แน่นอนว่าเราต้องสร้างองค์ประกอบ stub ใน components/SentimentDisplay.js:
import React from 'react'; import {Text} from 'native-base'; const SentimentDisplay = () => { return <Text>Sentiment Display</Text>; } export default SentimentDisplay;
และนำเข้า:
import SentimentDisplay from './components/SentimentDisplay';
ตอนนี้ เราพร้อมที่จะสร้างสไตล์ชีตส่วนกลางแล้ว
Global StyleSheet
ขั้นแรก สร้างไฟล์ชื่อ globalStyles.js จากนั้นนำเข้าองค์ประกอบ StyleSheet จาก React Native และกำหนดสไตล์:
import {StyleSheet} from 'react-native'; export default StyleSheet.create({ tweet: {paddingTop: 5}, accountName: {fontWeight: '600'}, })
และเราพร้อมที่จะเขียนโค้ด UI แล้ว
ส่วนประกอบ UI
คอมโพเนนต์ UI ค่อนข้างคุ้นเคย ยกเว้นวิธีที่เราทำงานกับเส้นทาง เราจะต้องการใช้การนำทางและอุปกรณ์ประกอบฉากพิเศษของ StackNavigator
เพื่อรับสถานะแอปพลิเคชันปัจจุบันและเพื่อนำทางไปยังการแสดงหัวข้อหากผู้ใช้ต้องการดูหน้านั้น
เปลี่ยนข้อกำหนดส่วนประกอบเพื่อเข้าถึงอุปกรณ์นำทาง:
const SentimentDisplay = ({route, navigation}) => {
และตอนนี้ เราใช้การอ่านสถานะแอปพลิเคชันและฟังก์ชันการนำทาง:
const {params: {data}} = route; const viewByTopicClicked = () => { navigation.navigate('TopicDisplay', { data }); }
นำเข้าสไตล์สากล:
import globalStyles from './globalStyles';
และส่วนประกอบ:
import { View } from 'react-native'; import {List, Item, Content, ListItem, Container, Text, Button} from 'native-base';
และสุดท้าย ส่วนประกอบ:
return <Container> <Content> <Item> <Button primary onPress={viewByTopicClicked}><Text>View By Topic</Text></Button> </Item> <Item> <List style={{width: '100%'}}> {data.sentimentSorted.map((item, i) => <ListItem key={i}> <View style={globalStyles.listItem}> <Text style={globalStyles.accountName}>{item.account}:</Text> <Text style={globalStyles.tweet}>{item.tweet} ({item.data.sentiment.toPrecision(2)})</Text> </View> </ListItem>)} </List> </Item> </Content> </Container>;
บันทึกและลองดึงทวีตแล้วคุณจะเห็นการแสดงความรู้สึก ตอนนี้เข้าสู่หน้าการจัดกลุ่มหัวข้อ
หน้าการจัดกลุ่มหัวข้อ
แผนภาพใหญ่
การแสดงหัวข้อมีความคล้ายคลึงกันมากอีกครั้ง เราจะใช้ตัวสร้างตัวจัดการเพื่อสร้างฟังก์ชันการนำทางเพื่อนำทางไปยังหน้าที่แสดงสำหรับรายการหัวข้อเฉพาะ และเราจะกำหนดสไตล์ชีตเฉพาะสำหรับหน้านี้ด้วย
สิ่งหนึ่งที่เราจะทำใหม่ก็คือการนำ TouchableOpacity มาใช้ ซึ่งเป็นส่วนประกอบเฉพาะของ React Native ซึ่งทำงานเหมือนกับปุ่ม
ตั้งค่าเส้นทาง
การกำหนดเส้นทางเหมือนกับเมื่อก่อน:
<Stack.Navigator initialRouteName="Entry"> <Stack.Screen name="Entry" component={Entry} /> <Stack.Screen name="SentimentDisplay" component={SentimentDisplay} /> <Stack.Screen name="TopicDisplay" component={TopicDisplay} /> </Stack.Navigator>
ส่วนประกอบต้นขั้วส่วนประกอบ/TopicDisplay.js:
import React from 'react'; import {Text} from 'native-base'; const TopicDisplay = () => { return <Text>Topic Display</Text>; } export default TopicDisplay;
และนำเข้า:
import TopicDisplay from './components/TopicDisplay;
ส่วนประกอบ UI
หลายอย่างนี้จะดูคุ้นเคยมาก นำเข้าฟังก์ชั่นห้องสมุด:
import { View, TouchableOpacity, StyleSheet } from 'react-native'; import {List, Item, Content, ListItem, Container, Text} from 'native-base';
นำเข้าสไตล์สากล:
import globalStyles from './globalStyles';
กำหนดรูปแบบที่กำหนดเอง:
const styles = StyleSheet.create({ topicName: {fontWeight: '600'}, })
กำหนดอุปกรณ์นำทาง:
export default TopicDisplay = ({route, navigation}) => {
กำหนดข้อมูลและตัวจัดการการดำเนินการ สังเกตว่าเรากำลังใช้ตัวสร้างตัวจัดการ ซึ่งเป็นฟังก์ชันที่ส่งคืนฟังก์ชัน:
const {params: {data}} = route; const specificItemPressedHandlerBuilder = (topic) => () => { navigation.navigate('TopicDisplayItem', { data, topic }); }
และตอนนี้ส่วนประกอบ สังเกตว่าเรากำลังใช้ TouchableOpacity ซึ่งสามารถมีตัวจัดการ onPress
ได้ เราสามารถใช้ TouchableTransparency ได้เช่นกัน แต่แอนิเมชั่นการคลิกค้างไว้ของ TouchableOpacity เหมาะกับแอปพลิเคชันของเรามากกว่า
return <Container> <Content> <Item> <List style={{width: '100%'}}> {Object.keys(data.groupedByNp).map((topic, i) => <ListItem key={i}> <View style={globalStyles.listItem}> <TouchableOpacity onPress={specificItemPressedHandlerBuilder(topic)}> <Text style={styles.topicName}>{topic}</Text> </TouchableOpacity> </View> </ListItem>)} </List> </Item> </Content> </Container>;
และสิ่งนี้ควรทำ บันทึกและลองใช้แอปพลิเคชัน!
ตอนนี้ เข้าสู่หน้ารายการแสดงหัวข้อ
หน้ารายการแสดงหัวข้อ
แผนภาพใหญ่
หน้ารายการแสดงหัวข้อจะคล้ายกันมาก และส่วนอื่นๆ ที่มีลักษณะแปลกประหลาดทั้งหมดได้รับการดูแล ดังนั้นจึงควรดำเนินไปอย่างราบรื่นจากที่นี่
ตั้งค่าเส้นทาง
เราจะเพิ่มการกำหนดเส้นทาง:
<Stack.Screen name="TopicDisplayItem" component={TopicDisplayItem} />
เพิ่มการนำเข้า:
import TopicDisplayItem from './components/TopicDisplayItem';
และสร้างองค์ประกอบต้นขั้ว แทนที่จะเป็นเพียงส่วนประกอบเปล่า เรามานำเข้าส่วนประกอบ NativeBase ที่เราจะใช้และกำหนดอุปกรณ์ประกอบฉากเส้นทาง:
import React from 'react'; import {View} from 'react-native'; import {List, Item, Content, ListItem, Container, Text} from 'native-base'; import globalStyles from './globalStyles'; const TopicDisplayItem = ({route}) => { const {params: {data, topic}} = route; return <Text>Topic Display Item</Text>; } export default TopicDisplayItem;
ส่วนประกอบ UI
UI Component ค่อนข้างเรียบง่าย เราเคยเห็นมาก่อนและไม่ได้นำตรรกะที่กำหนดเองไปใช้จริง งั้นไปกันเลยดีกว่า! หายใจลึก ๆ…
return <Container> <Content> <Item> <List style={{width: '100%'}}> {data.groupedByNp[topic].map((item, i) => <ListItem key={i}> <View style={globalStyles.listItem}> <Text style={globalStyles.accountName}>{item.account}:</Text> <Text style={globalStyles.tweet}>{item.tweet} ({item.data.sentiment.toPrecision(2)})</Text> </View> </ListItem>)} </List> </Item> </Content> </Container>;
บันทึกและเราควรจะดีไป! ตอนนี้เราพร้อมที่จะเรียกใช้แอปในโปรแกรมจำลองแล้ว... เดี๋ยวก่อนใช่ไหม
เรียกใช้แอพ
เนื่องจากคุณกำลังทำงานกับ React Native คุณได้เรียกใช้แอพในโปรแกรมจำลอง ดังนั้นส่วนนี้จึงได้รับการดูแลแล้ว นี่เป็นหนึ่งในสิ่งที่ยอดเยี่ยมเกี่ยวกับสภาพแวดล้อมการพัฒนาของ React Native
ว้าว! ด้วยเหตุนี้ เราจึงเสร็จสิ้นส่วนการเข้ารหัสของบทความนี้ มาดูกันว่าเราได้เรียนรู้อะไรเกี่ยวกับเทคโนโลยีบ้าง
เปรียบเทียบเทคโนโลยี
Cordova: ข้อดีและข้อเสีย
สิ่งที่ดีที่สุดเกี่ยวกับ Cordova คือความเร็วที่มากซึ่งนักพัฒนาเว็บที่มีทักษะสามารถเขียนโค้ดบางอย่างที่ใช้งานได้จริงและเรียบร้อยพอสมควร ทักษะการพัฒนาเว็บและการถ่ายโอนประสบการณ์ได้อย่างง่ายดายเพราะว่าคุณกำลังเขียนโค้ดเว็บแอป กระบวนการพัฒนานั้นรวดเร็วและง่ายดาย และการเข้าถึง Cordova API นั้นง่ายและใช้งานง่ายเช่นกัน
ข้อเสียของการใช้ Cordova โดยตรงส่วนใหญ่มาจากการพึ่งพาส่วนประกอบเว็บมากเกินไป ผู้ใช้คาดหวังถึงประสบการณ์การใช้งานที่เฉพาะเจาะจงและการออกแบบอินเทอร์เฟซเมื่อใช้แอปบนอุปกรณ์เคลื่อนที่ และเมื่อแอปพลิเคชันรู้สึกเหมือนเป็นไซต์บนอุปกรณ์เคลื่อนที่ ประสบการณ์อาจดูน่าตกใจเล็กน้อย นอกจากนี้ ฟีเจอร์ส่วนใหญ่ในแอพ เช่น แอนิเมชั่นในช่วงเปลี่ยนผ่านและยูทิลิตี้การนำทาง จะต้องดำเนินการด้วยตนเอง
อิออน: ข้อดีข้อเสีย
ส่วนที่ดีที่สุดของ Ionic คือจำนวนฟีเจอร์ที่เน้นอุปกรณ์พกพาที่ฉันได้รับ "ฟรี" ด้วยการเข้ารหัสแบบเดียวกับที่ฉันเขียนโค้ดเว็บแอปพลิเคชัน ฉันจึงสามารถสร้างแอปที่ดูเหมาะกับอุปกรณ์เคลื่อนที่มากกว่าเพียงแค่ใช้ Cordova และ React-Bootstrap มีแอนิเมชั่นการนำทาง ปุ่มที่มีสไตล์ที่ดูเป็นธรรมชาติ และตัวเลือกอินเทอร์เฟซผู้ใช้มากมายที่ทำให้ประสบการณ์ผู้ใช้ราบรื่นมาก
ข้อเสียของการใช้ Ionic มีสาเหตุมาจากจุดแข็งบางส่วน ประการแรก บางครั้งมันก็ยากที่จะจินตนาการว่าแอปจะทำงานอย่างไรในสภาพแวดล้อมต่างๆ เพียงเพราะแอปมีรูปลักษณ์เดียวไม่ได้หมายความว่าตำแหน่ง UI เดียวกันจะมีลักษณะเหมือนกันในสภาพแวดล้อมอื่น ประการที่สอง Ionic อยู่เหนือเทคโนโลยีพื้นฐานหลายชิ้น และการเข้าถึงส่วนประกอบบางอย่างนั้นยาก สุดท้ายนี้ เป็นข้อมูลเฉพาะสำหรับ Ionic-React แต่เนื่องจาก Ionic ถูกสร้างขึ้นครั้งแรกสำหรับ Angular ฟีเจอร์ Ionic-React จำนวนมากจึงดูเหมือนเอกสารและการสนับสนุนน้อยกว่า อย่างไรก็ตาม ดูเหมือนว่าทีม Ionic จะเอาใจใส่ความต้องการของนักพัฒนา React และนำเสนอฟีเจอร์ใหม่อย่างรวดเร็ว
React Native: ข้อดีข้อเสีย
React Native มีประสบการณ์ผู้ใช้ที่ราบรื่นมากในการพัฒนาบนมือถือ ด้วยการเชื่อมต่อกับอีมูเลเตอร์โดยตรง จึงไม่แปลกที่แอพพลิเคชั่นจะมีหน้าตาเป็นอย่างไร อินเทอร์เฟซดีบักเกอร์บนเว็บมีประโยชน์อย่างมากในการใช้เทคนิคการดีบักข้ามจากเว็บแอปพลิเคชันโลก และระบบนิเวศก็ค่อนข้างแข็งแกร่ง
ข้อเสียของ React Native มาจากความใกล้ชิดกับอินเทอร์เฟซดั้งเดิม ไลบรารีที่ใช้ DOM จำนวนมากไม่สามารถใช้งานได้ ซึ่งหมายความว่าต้องเรียนรู้ไลบรารีใหม่และแนวทางปฏิบัติที่ดีที่สุด หากปราศจากประโยชน์ของ CSS การจัดรูปแบบแอปพลิเคชันก็ค่อนข้างใช้งานง่าย สุดท้าย ด้วยองค์ประกอบใหม่มากมายให้เรียนรู้ (เช่น ดูแทน div องค์ประกอบข้อความตัดทุกอย่าง ปุ่ม vs. TouchableOpacity vs. TouchableTransparency เป็นต้น) จะมีช่วงการเรียนรู้เล็กน้อยในตอนเริ่มต้น ถ้ามีคนเข้ามา ตอบโต้ Native world โดยมีความรู้เกี่ยวกับกลไกมาก่อนเล็กน้อย
เมื่อใดควรใช้แต่ละเทคโนโลยี
เนื่องจาก Cordova, Ionic และ React Native ล้วนมีข้อดีและข้อเสียอย่างมาก แต่ละเทคโนโลยีจึงมีบริบทที่จะเพลิดเพลินไปกับประสิทธิภาพและประสิทธิภาพที่ดีที่สุด
หากคุณมีแอปพลิเคชันที่เป็นเว็บแรกอยู่แล้วโดยมีเอกลักษณ์ของแบรนด์ที่แข็งแกร่งรอบ ๆ การออกแบบ UI และรูปลักษณ์ทั่วไป ตัวเลือกที่ดีที่สุดของคุณคือ Cordova ซึ่งช่วยให้คุณเข้าถึงคุณสมบัติดั้งเดิมของสมาร์ทโฟนในขณะที่ให้คุณนำส่วนใหญ่ของคุณกลับมาใช้ใหม่ได้ ส่วนประกอบของเว็บและรักษาเอกลักษณ์แบรนด์ของคุณในกระบวนการ สำหรับแอปพลิเคชันที่ค่อนข้างง่ายโดยใช้เฟรมเวิร์กแบบตอบสนอง คุณอาจสร้างแอปบนอุปกรณ์เคลื่อนที่ได้โดยต้องมีการเปลี่ยนแปลงเพียงเล็กน้อย อย่างไรก็ตาม แอปพลิเคชันของคุณจะดูเหมือนแอปน้อยลงและดูเหมือนหน้าเว็บมากขึ้น และองค์ประกอบบางอย่างที่ผู้คนคาดหวังจากแอปบนอุปกรณ์เคลื่อนที่จะถูกเข้ารหัสแยกต่างหาก ดังนั้น ฉันขอแนะนำ Cordova ในกรณีที่คุณอยู่ในโปรเจ็กต์แรกบนเว็บที่ย้ายแอปพลิเคชันไปยังมือถือ
หากคุณกำลังเริ่มเขียนโค้ดแอปพลิเคชันใหม่ด้วยปรัชญาที่ให้ความสำคัญกับแอปเป็นอันดับแรก แต่ชุดทักษะของทีมของคุณมีพื้นฐานมาจากการพัฒนาเว็บ ฉันขอแนะนำ Ionic ไลบรารีของ Ionic ช่วยให้คุณเขียนโค้ดที่มีลักษณะและความรู้สึกใกล้เคียงกับองค์ประกอบดั้งเดิมได้อย่างรวดเร็ว ในขณะเดียวกันก็ให้คุณใช้ทักษะและสัญชาตญาณของคุณในฐานะนักพัฒนาเว็บ ฉันพบว่าแนวทางปฏิบัติที่ดีที่สุดสำหรับการพัฒนาเว็บสามารถนำไปใช้ร่วมกับการพัฒนาด้วย Ionic และการจัดรูปแบบด้วย CSS ได้อย่างลงตัว นอกจากนี้ ไซต์เวอร์ชันมือถือยังดูเป็นธรรมชาติมากกว่าเว็บไซต์ที่เข้ารหัสโดยใช้เฟรมเวิร์ก CSS ที่ตอบสนอง อย่างไรก็ตาม ในบางขั้นตอนระหว่างทาง ฉันพบว่าการรวม React-Ionic-Native API จำเป็นต้องมีการปรับด้วยตนเอง ซึ่งอาจใช้เวลานาน ดังนั้น ฉันขอแนะนำ Ionic ในกรณีที่แอปพลิเคชันของคุณมีการพัฒนาตั้งแต่ต้น และคุณต้องการแชร์โค้ดจำนวนมากระหว่างแอปพลิเคชันเว็บที่ใช้งานบนมือถือได้และแอปพลิเคชันบนมือถือ
หากคุณกำลังเขียนโค้ดแอปพลิเคชันใหม่โดยใช้โค้ดเบสแบบเนทีฟ คุณอาจต้องการลองใช้ React Native แม้ว่าคุณจะไม่ได้ใช้โค้ดเนทีฟ แต่ก็อาจเป็นตัวเลือกที่ดีที่สุดในกรณีที่คุณคุ้นเคยกับ React Native อยู่แล้ว หรือเมื่อข้อกังวลหลักของคุณคือแอปพลิเคชันบนมือถือมากกว่าแอปพลิเคชันแบบไฮบริด เมื่อได้เน้นความพยายามในการพัฒนาส่วนหน้าเป็นส่วนใหญ่ในการพัฒนาเว็บไซต์ ตอนแรกฉันพบว่าการเริ่มต้นใช้งาน React Native มีเส้นโค้งการเรียนรู้มากกว่า Ionic หรือ Cordova เนื่องจากความแตกต่างในการจัดองค์ประกอบและแบบแผนการเข้ารหัส อย่างไรก็ตาม เมื่อเรียนรู้ความแตกต่างเหล่านี้แล้ว ประสบการณ์การเขียนโค้ดก็ค่อนข้างราบรื่น โดยเฉพาะอย่างยิ่งด้วยความช่วยเหลือของไลบรารีส่วนประกอบอย่าง NativeBase ด้วยคุณภาพของสภาพแวดล้อมการพัฒนาและการควบคุมแอปพลิเคชัน หากส่วนหน้าของโครงการของคุณเป็นแอปพลิเคชันบนมือถือเป็นหลัก ฉันขอแนะนำ React Native เป็นเครื่องมือที่คุณเลือก
หัวข้อในอนาคต
หัวข้อหนึ่งที่ฉันไม่มีเวลาสำรวจคือความง่ายในการเข้าถึง API ดั้งเดิม เช่น กล้อง ตำแหน่งทางภูมิศาสตร์ หรือการตรวจสอบไบโอเมตริกซ์ ข้อดีอย่างหนึ่งของการพัฒนาอุปกรณ์เคลื่อนที่คือความสามารถในการเข้าถึงระบบนิเวศ API ที่สมบูรณ์ซึ่งโดยทั่วไปไม่สามารถเข้าถึงได้บนเบราว์เซอร์
ในบทความต่อๆ ไป ฉันต้องการสำรวจความง่ายในการพัฒนาแอปพลิเคชันที่เปิดใช้งาน API ดั้งเดิมเหล่านี้ โดยใช้เทคโนโลยีข้ามแพลตฟอร์มต่างๆ
บทสรุป
วันนี้ เราได้ใช้แอพ Twitter ที่ดูแลจัดการโดยใช้เทคโนโลยีการพัฒนามือถือข้ามแพลตฟอร์มที่แตกต่างกันสามแบบ ฉันหวังว่าสิ่งนี้จะทำให้คุณมีความรู้สึกที่ดีว่าแต่ละเทคโนโลยีเป็นอย่างไรและเป็นแรงบันดาลใจให้คุณพัฒนาแอปพลิเคชันที่ใช้ React ของคุณเอง
ขอบคุณสำหรับการอ่าน!