البرمجة اللغوية العصبية الكاملة مع React: Ionic vs Cordova مقابل React Native

نشرت: 2022-03-11

في ما يقرب من 15 عامًا منذ أن أصدرت Apple أول iPhone ، تغير مشهد تطوير البرمجيات بشكل كبير. نظرًا لاعتماد الهواتف الذكية على نطاق واسع واستمرار نموها في القدرات الفريدة ، يفضل المستخدمون بشكل متزايد الوصول إلى خدمات البرامج من خلال الأجهزة المحمولة بدلاً من أجهزة الكمبيوتر المكتبية أو أجهزة الكمبيوتر المحمولة. تقدم الهواتف الذكية ميزات مثل تحديد الموقع الجغرافي ، والمصادقة البيومترية ، واستشعار الحركة ، والعديد منها بدأت الآن فقط في نسخ منصات سطح المكتب. في بعض الديموغرافيات ، يعتبر الهاتف الذكي أو جهاز محمول مشابه هو الوسيلة الأساسية لاستهلاك البرامج ، متجاوزًا أجهزة الكمبيوتر تمامًا.

لاحظت الشركات هذا التحول وعززته بطرق رئيسية. لم تعد تطبيقات الأجهزة المحمولة فكرة متأخرة. تعتمد التطبيقات التي تتراوح من Robinhood ، وهي شركة وساطة مالية ، إلى Instagram ، وهي شركة وسائط اجتماعية ، إلى Uber ، وهي شركة لنقل الركاب ، استراتيجية تطوير تعتمد على الأجهزة المحمولة أولاً. إذا كان هناك تطبيق سطح مكتب ، فغالبًا ما يتم تقديمه كمكمل لتطبيق الهاتف المحمول ، بدلاً من التركيز الأساسي.

بالنسبة للمطورين المتكاملين ، يعد التكيف مع هذه الاتجاهات المتغيرة أمرًا بالغ الأهمية. لحسن الحظ ، هناك العديد من التقنيات الناضجة والمدعومة جيدًا لمساعدة مطوري الويب على تطبيق مهاراتهم على تطوير الأجهزة المحمولة. اليوم ، سوف نستكشف ثلاث تقنيات من هذا القبيل: كوردوفا ، وأيوني ، وتفاعل أصلية. سنستخدم React.js ، أحد أكثر الأطر شيوعًا لتطوير الويب الأمامي ، كتقنية التطوير الأساسية لدينا. بينما سنركز على تطوير تطبيق iPhone ، فهذه تقنيات مشتركة بين الأنظمة الأساسية ويمكن تجميعها عبر نظام Android الأساسي.

ما سنبنيه اليوم

سننشئ تطبيقًا يستخدم معالجة اللغة الطبيعية (NLP) لمعالجة موجزات Twitter ورعايتها. سيسمح التطبيق للمستخدم بتحديد مجموعة من مقابض Twitter ، وسحب آخر التحديثات باستخدام Twitter API ، وتصنيف التغريدات على أساس المشاعر والموضوع. سيتمكن المستخدم بعد ذلك من مشاهدة التغريدات بناءً على المشاعر أو الموضوع.

النهاية الخلفية

قبل أن نبني الواجهة الأمامية ، سنرغب في بناء النهاية الخلفية. سنبقي النهاية الخلفية بسيطة في الوقت الحالي - سنستخدم تحليل المشاعر الأساسي الجاهز وعلامات جزء من الكلام ، إلى جانب القليل من تنظيف البيانات للتعامل مع المشكلات الخاصة بمجموعة البيانات. سوف نستخدم مكتبة NLP مفتوحة المصدر تسمى TextBlob ونقدم النتيجة عبر Flask.

تحليل المشاعر ، وضع علامات على جزء من الكلام ، ومعالجة اللغات الطبيعية: كتاب تمهيدي سريع

إذا لم تكن قد عملت مع تطبيقات تحليل اللغة الطبيعية من قبل ، فقد تكون هذه المصطلحات غريبة جدًا عليك. البرمجة اللغوية العصبية هو مصطلح شامل للتقنيات التي تحلل بيانات لغة الإنسان الطبيعية وتعالجها. في حين أن هذا موضوع واسع ، إلا أن هناك العديد من التحديات المشتركة بين جميع التقنيات التي تتناول هذا المجال. على سبيل المثال ، تميل اللغة البشرية ، على عكس لغة البرمجة أو البيانات الرقمية ، إلى أن تكون غير محكمة التنظيم بسبب الطبيعة المتساهلة لقواعد اللغة البشرية. بالإضافة إلى ذلك ، تميل اللغة البشرية إلى أن تكون سياقية للغاية ، وقد لا تُترجم العبارة التي يتم نطقها أو كتابتها في سياق ما إلى سياق آخر. أخيرًا ، بغض النظر عن الهيكل والسياق ، فإن اللغة معقدة للغاية. يمكن للكلمات الموجودة أسفل الفقرة تغيير معنى الجملة في بداية الفقرة. يمكن اختراع المفردات أو إعادة تعريفها أو تغييرها. كل هذه التعقيدات تجعل تقنيات تحليل البيانات صعبة التطبيق.

تحليل المشاعر هو حقل فرعي من البرمجة اللغوية العصبية (NLP) يركز على فهم الانفعالية لممر اللغة الطبيعية. في حين أن المشاعر الإنسانية ذاتية بطبيعتها ، وبالتالي يصعب تحديدها تقنيًا ، فإن تحليل المشاعر هو حقل فرعي له وعود تجارية هائلة. تتضمن بعض تطبيقات تحليل المشاعر تصنيف مراجعات المنتج لتحديد التقييم الإيجابي والسلبي للميزات المختلفة ، واكتشاف الحالة المزاجية للبريد الإلكتروني أو الخطاب وتجميع كلمات الأغاني حسب الحالة المزاجية. إذا كنت تبحث عن شرح أكثر تعمقًا لتحليل المشاعر ، فيمكنك قراءة مقالتي حول إنشاء تطبيق قائم على تحليل المشاعر هنا.

يعد وضع علامات على جزء من الكلام ، أو وضع علامات POS ، حقلاً فرعيًا مختلفًا تمامًا. الهدف من علامات نقاط البيع هو تحديد جزء الكلام من كلمة معينة في جملة باستخدام المعلومات النحوية والسياقية. يعد تحديد هذه العلاقة مهمة أكثر صعوبة مما تراه العين في البداية - يمكن أن تحتوي الكلمة على أجزاء مختلفة جدًا من الكلام بناءً على سياق الجملة وهيكلها ، والقواعد ليست واضحة دائمًا حتى للبشر. لحسن الحظ ، توفر العديد من النماذج الجاهزة اليوم نماذج قوية ومتعددة الاستخدامات تتكامل مع معظم لغات البرمجة الرئيسية. إذا كنت ترغب في معرفة المزيد ، يمكنك قراءة مقالتي حول علامات نقاط البيع هنا.

Flask و TextBlob و Tweepy

بالنسبة للنهاية الخلفية الخاصة بنا في البرمجة اللغوية العصبية ، سنستخدم Flask و TextBlob و Tweepy. سنستخدم Flask لإنشاء خادم صغير وخفيف الوزن ، TextBlob لتشغيل معالجة اللغة الطبيعية لدينا ، و Tweepy للحصول على تغريدات من Twitter API. قبل أن تبدأ في البرمجة ، ستحتاج أيضًا إلى الحصول على مفتاح مطور من Twitter حتى تتمكن من استرداد التغريدات.

يمكننا كتابة خلفية أكثر تعقيدًا واستخدام تقنيات البرمجة اللغوية العصبية الأكثر تعقيدًا ، ولكن لأغراضنا اليوم ، سنبقي النهاية الخلفية بسيطة قدر الإمكان.

كود النهاية

الآن ، نحن جاهزون لبدء البرمجة. أطلق محرر 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 ، وسترى واجهة برمجة التطبيقات تقدم شيئًا مشابهًا لما تم إرجاعه في كود تحليل Twitter.

الآن بعد أن أصبح لدينا خادمًا ، أصبحنا جاهزين لتشفير الواجهة الأمامية. نظرًا لوجود فارق بسيط في الشبكات عبر محاكي الهاتف ، أوصي بنشر API في مكان ما. بخلاف ذلك ، سترغب في اكتشاف ما إذا كان تطبيقك يعمل على محاكي وإرسال الطلبات إلى <Your Computer IP>:5000 بدلاً من localhost:5000 عندما يكون في محاكي. إذا قمت بنشر الرمز ، يمكنك ببساطة إصدار الطلب إلى عنوان URL هذا.

هناك العديد من الخيارات لنشر الخادم. للحصول على خادم تصحيح أخطاء مجاني وبسيط مع الحد الأدنى من الإعداد المطلوب ، أوصي بشيء مثل PythonAnywhere ، والذي يجب أن يكون قادرًا على تشغيل هذا الخادم خارج الصندوق.

الآن بعد أن قمنا بترميز الخادم الخلفي ، فلنلقِ نظرة على الواجهة الأمامية. سنبدأ بأحد أكثر الخيارات ملاءمة لمطور الويب: كوردوفا.

تنفيذ اباتشي كوردوفا

كوردوفا برايمر

Apache Cordova هي تقنية برمجية لمساعدة مطوري الويب على استهداف منصات الأجهزة المحمولة. من خلال الاستفادة من إمكانات مستعرض الويب المطبقة على الأنظمة الأساسية للهواتف الذكية ، تقوم كوردوفا بلف كود تطبيق الويب في حاوية تطبيق أصلية لإنشاء تطبيق. ومع ذلك ، فإن كوردوفا ليس مجرد متصفح ويب فاخر. من خلال واجهة برمجة تطبيقات كوردوفا ، يمكن لمطوري الويب الوصول إلى العديد من الميزات الخاصة بالهواتف الذكية مثل الدعم في وضع عدم الاتصال وخدمات الموقع والكاميرا الموجودة على الجهاز.

بالنسبة لتطبيقنا ، سنكتب تطبيقًا باستخدام React.js كإطار عمل JS و React-Bootstrap كإطار عمل CSS. نظرًا لأن Bootstrap هو إطار عمل CSS سريع الاستجابة ، فإنه يدعم بالفعل التشغيل على الشاشات الأصغر. بمجرد كتابة التطبيق ، سنقوم بتجميعه في تطبيق ويب باستخدام كوردوفا.

تكوين التطبيق

سنبدأ بعمل شيء فريد لإعداد تطبيق Cordova React. في مقال على موقع Medium ، يشرح المطور Shubham Patil ما نقوم به. بشكل أساسي ، نقوم بإعداد بيئة تطوير React باستخدام React CLI ، ثم بيئة تطوير كوردوفا باستخدام Cordova CLI ، قبل دمج الاثنين في النهاية.

للبدء ، قم بتشغيل الأمرين التاليين في مجلد التعليمات البرمجية الخاص بك:

 cordova create TwitterCurationCordova create-react-app twittercurationreact

بمجرد الانتهاء من الإعداد ، سنرغب في نقل محتويات المجلد العام ومجلد src لتطبيق React إلى تطبيق Cordova. بعد ذلك ، في package.json ، انسخ النصوص وقائمة المستعرض والتبعيات من مشروع React. أضف أيضًا "homepage": "./" إلى جذر package.json لتمكين التوافق مع كوردوفا.

بمجرد دمج package.json ، سنرغب في تغيير ملف public / index.html ليعمل مع كوردوفا. افتح الملف وانسخ العلامات الوصفية من www / index.html وكذلك النص في نهاية علامة النص عند تحميل Cordova.js.

بعد ذلك ، قم بتغيير ملف src / index.js لاكتشاف ما إذا كان يعمل على Cordova. إذا كان يعمل على كوردوفا ، فسنرغب في تشغيل كود العرض داخل معالج الحدث deviceready. إذا كان يعمل في متصفح عادي ، فقط قم بالعرض على الفور.

 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 ويضع مجلد الإنشاء في المكان المناسب قبل بدء بناء كوردوفا ، وبالتالي أتمتة عملية النشر.

الآن ، يمكننا محاولة تشغيل تطبيقنا. قم بتشغيل ما يلي في سطر الأوامر:

 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 ، وسيحصل العرض على المتغير ليتم عرضه.

لبدء ترميز المكونات ، دعنا ننشئ مجلدًا تحت / src يسمى / src / element. ضمن / src / element ، أنشئ مجلدًا آخر باسم / src / component / input وملفين أسفله: input.js و input.css. افعل الشيء نفسه لمكون العرض - إنشاء / src / المكونات / العرض وأسفل: 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

مع ذلك ، اكتمل الإطار الشبكي الخاص بنا ويجب تشغيل التطبيق. لنقم الآن بكود صفحة الإدخال.

صفحة الإدخال

خطة الصورة الكبيرة

قبل أن نكتب الكود ، دعنا نفكر فيما نريد أن تفعله صفحة الإدخال. من الواضح أننا نريد طريقة للمستخدمين لإدخال وتعديل مقابض Twitter التي يريدون السحب منها. نريد أيضًا أن يكون المستخدمون قادرين على الإشارة إلى أنهم انتهوا. عندما يشير المستخدمون إلى الانتهاء ، سنرغب في سحب التغريدات المنسقة من واجهة برمجة تطبيقات تنظيم Python الخاصة بنا والانتقال أخيرًا إلى مكون العرض.

الآن بعد أن عرفنا ما نريد أن يفعله المكون الخاص بنا ، أصبحنا جاهزين للتشفير.

إعداد الملف

لنبدأ باستيراد مكتبة withRouter Router مع جهاز التوجيه للوصول إلى وظائف التنقل ، ومكونات React Bootstrap التي نحتاجها ، مثل:

 import React, {useState} from 'react'; import {withRouter} from 'react-router-dom'; import {ListGroup, Button, Form, Container, Row, Col} from 'react-bootstrap'; import './input.css';

الآن ، دعنا نحدد دالة كعب الروتين للإدخال. نحن نعلم أن Input يحصل على وظيفة setCuratedTweets ، ونريد أيضًا أن نمنحه القدرة على الانتقال إلى مسار العرض بعد أن يقوم بتعيين التغريدات المنسقة من Python API. لذلك ، سنريد أن نأخذ من الدعائم setCuratedTweets والتاريخ (للملاحة).

 const Input = ({setCuratedTweets, history}) => { return <div>Input</div> }

لمنحها إمكانية الوصول إلى واجهة برمجة التطبيقات للتاريخ ، سنقوم بلفها بـ withRouter في بيان التصدير في نهاية الملف:

 export default withRouter(Input);

حاويات البيانات

لنقم بإعداد حاويات البيانات باستخدام خطافات React. لقد استوردنا بالفعل الخطاف useState حتى نتمكن من إضافة الكود التالي إلى جسم مكون الإدخال:

 const [handles, setHandles] = useState([]); const [handleText, setHandleText] = useState('');

يؤدي هذا إلى إنشاء الحاوية والمعدلات للمقابض ، والتي ستحتفظ بقائمة المقابض التي يرغب المستخدم في السحب منها ، و handleText الذي سيحتفظ بمحتوى مربع النص الذي يستخدمه المستخدم لإدخال المقبض.

الآن ، لنقم بترميز مكونات واجهة المستخدم.

مكونات واجهة المستخدم

ستكون مكونات واجهة المستخدم بسيطة إلى حد ما. سيكون لدينا صف Bootstrap واحد يحتوي على مربع نص الإدخال إلى جانب زرين ، أحدهما لإضافة محتوى مربع الإدخال الحالي إلى قائمة المقابض ، والآخر لسحبه من واجهة برمجة التطبيقات. سيكون لدينا صف 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> );

بالإضافة إلى مكون واجهة المستخدم ، سنرغب في تنفيذ معالجات أحداث واجهة المستخدم الثلاثة التي تتعامل مع تغييرات البيانات. سيتم تنفيذ معالج حدث 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 . ثم ، إذا سارت الأمور على ما يرام ، فنحن نريد الانتقال برمجيًا إلى مسار العرض /. خلافًا لذلك ، سنقوم بتسجيل الخطأ في وحدة التحكم حتى نتمكن من تصحيح الأخطاء بسهولة أكبر.

في وضع الشفرة ، يبدو الأمر كما يلي:

 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); }) }

ومع ذلك ، يجب أن نكون على ما يرام. لا تتردد في تشغيل التطبيق وإضافة مقابض وإرسال طلب إلى واجهة برمجة التطبيقات.

الآن ، نحن جاهزون لترميز صفحة المشاعر.

وضع فرز المشاعر

نظرًا لأن Python API تقوم بالفعل بفرز التغريدات حسب المشاعر ، فبمجرد حصولنا على النتيجة من Python API ، فإن صفحة المشاعر ليست صعبة للغاية في الواقع.

خطة الصورة الكبيرة

سنريد واجهة قائمة لإظهار التغريدات. سنحتاج أيضًا إلى مكونين للتنقل للتبديل إلى وضع تجميع الموضوعات والعودة إلى صفحة الإدخال.

للبدء ، دعنا نحدد المكون الفرعي لوضع SentimentDisplay في ملف display.js.

عرض مكون

سيأخذ 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 المفيدة جدًا بشكل كبير.

خطة الصورة الكبيرة

سنحصل على جميع العبارات الاسمية ونعرضها كقائمة أكورديون. سنقوم بعد ذلك بعرض التغريدات التي تحتوي على عبارات الاسم بمجرد توسيع قائمة الأكورديون.

تنفيذ التبديل إلى وضع تجميع المواضيع

أولاً ، دعنا نطبق المنطق للتبديل من وضع المشاعر إلى وضع تجميع المواضيع. لنبدأ بإنشاء مكون stub أولاً:

 const TopicDisplay = () => { return <div>Topic Display</div> }

وضبط بعض المنطق لإنشاء وضع لعرضه. في Display Component الرئيسي ، أضف الأسطر التالية لإنشاء المنطق الذي يتم عرض المكون من أجله.

 // 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

الآن ، نحن جاهزون لتشفير مكون TopicDisplay . كما تمت مناقشته من قبل ، فإنه سيعزز قائمة Bootstrap Accordion. التنفيذ بسيط إلى حد ما:

 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 platform add ios # if you haven't done so already cordova run ios

ويجب أن ترى التطبيق في المحاكي. نظرًا لأن Bootstrap هو تطبيق ويب سريع الاستجابة ، فإن تطبيق الويب يتكيف مع عرض جهاز iPhone ، ويبدو كل شيء جيدًا.

بعد الانتهاء من تطبيق كوردوفا ، لنلقِ نظرة الآن على التطبيق الأيوني.

تنفيذ التفاعل الأيوني

أيوني التمهيدي

Ionic هي مكتبة مكونة على شبكة الإنترنت ومجموعة أدوات CLI التي تجعل إنشاء التطبيقات المختلطة أسهل. في الأصل ، تم بناء Ionic على قمة AngularJS و Cordova ، لكنهم أطلقوا مكوناتهم منذ ذلك الحين في React.js وبدأوا في دعم Capacitor ، وهو نظام أساسي شبيه بـ Cordova. ما يميز Ionic هو أنه على الرغم من أنك تستخدم مكونات الويب ، فإن المكونات تبدو مشابهة جدًا لواجهة الهاتف المحمول الأصلية. بالإضافة إلى ذلك ، يتكيف شكل ومضمون المكونات الأيونية تلقائيًا مع نظام التشغيل الذي يعمل عليه ، مما يساعد مرة أخرى على جعل التطبيق يبدو أكثر طبيعية وطبيعية. أخيرًا ، في حين أن هذا خارج نطاق مقالتنا ، توفر Ionic أيضًا العديد من أدوات البناء التي تجعل نشر تطبيقك أسهل قليلاً.

بالنسبة لتطبيقنا ، سنستخدم مكونات Ionic's React لبناء واجهة المستخدم مع الاستفادة من بعض منطق 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 ، إلا أنه يحتوي على بعض الميزات الرائعة للغاية ، وسنستخدمه في هذا التنفيذ.

إعداد جهاز التوجيه

من أجل هذا التنفيذ ، سوف نستخدم ثلاثة مسارات - المدخلات ، sentimentDisplay ، topicDisplay . نقوم بذلك لأننا نريد الاستفادة من ميزات الانتقال والتنقل التي توفرها Ionic ولأننا نستخدم مكونات Ionic ، ولا تأتي قوائم الأكورديون مُعبأة مسبقًا مع Ionic. يمكننا تنفيذ منطقتنا بالطبع ، ولكن في هذا البرنامج التعليمي ، سنبقى مع المكونات الأيونية المتوفرة.

إذا انتقلت إلى ملف App.tsx ، فمن المفترض أن ترى المسارات الأساسية المحددة بالفعل.

صفحة الإدخال

خطة الصورة الكبيرة

سنستخدم الكثير من المنطق والرمز المشابهين لتطبيق Bootstrap ، مع بعض الاختلافات الرئيسية. أولاً ، سنستخدم TypeScript ، مما يعني أنه سيكون لدينا كتابة التعليقات التوضيحية لشفرتنا ، والتي ستراها في القسم التالي. ثانيًا ، سنستخدم مكونات Ionic ، والتي تشبه إلى حد بعيد أسلوب Bootstrap ولكنها ستكون حساسة لنظام التشغيل في تصميمها. أخيرًا ، سننتقل ديناميكيًا باستخدام واجهة برمجة تطبيقات السجل كما هو الحال في إصدار Bootstrap ولكن الوصول إلى السجل بشكل مختلف قليلاً بسبب تنفيذ Ionic Router.

اعداد

لنبدأ بإعداد مكون الإدخال بمكوِّن كعب روتين. قم بإنشاء مجلد أسفل الصفحات المسماة الإدخال وقم بإنشاء ملف باسم 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> );

الآن ، عند تحديث التطبيق ، يجب أن ترى مكون كعب الإدخال.

حاويات البيانات

لنقم بإنشاء حاويات البيانات الآن. نريد حاويات مقابض Twitter المُدخلة بالإضافة إلى المحتويات الحالية لمربع الإدخال. نظرًا لأننا نستخدم TypeScript ، فسنحتاج إلى إضافة تعليقات النوع إلى استدعاء useState بنا في وظيفة المكون:

 const Input : React.FC = () => { const [text, setText] = useState<string>(''); const [accounts, setAccounts] = useState<Array<string>>([]); return <div>Input</div>; }

سنريد أيضًا أن تحتوي حاوية البيانات على قيم الإرجاع من واجهة برمجة التطبيقات. نظرًا لأن محتوى ذلك يجب مشاركته مع المسارات الأخرى ، فإننا نحددها على مستوى 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> }

الآن يعرف التطبيق بنية بيانات إرجاع واجهة برمجة التطبيقات. استيراد واجهة CuratedTweets في App.tsx. يجب أن تشاهد ترجمة App.tsx بدون أي مشكلة الآن.

نحن بحاجة إلى القيام ببعض الأشياء الأخرى هنا. نحتاج إلى تمرير الدالة setCuratedTweets إلى مكون الإدخال وجعل مكون الإدخال على دراية بهذه الوظيفة.

في 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 ، لذلك دعونا نعتني بذلك الآن في القسم التالي.

مكونات واجهة المستخدم

لنبدأ بتحديد دعامات الحاوية. يشبه إلى حد كبير مكون الإدخال:

 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 باستخدام واجهة برمجة التطبيقات للتاريخ:

 const switchToInput = () => { history.goBack(); }

ويجب أن يكون ToggleDisplayType مألوفًا أيضًا:

 const toggleDisplayType = () => { setDisplayType(displayType === 'Sentiment' ? 'Topic': 'Sentiment'); } const switchStr = displayType === 'Sentiment'? 'View by Topic': 'View by Sentiment'

الآن لدينا عنصر عرض SentimentDisplay الذي تم تنفيذه. الآن ، قبل أن ننفذ صفحة عرض الموضوع ، نحتاج إلى تنفيذ المكون الذي يعرض جميع الموضوعات. سنفعل ذلك في القسم التالي.

مكون مجموعات المواضيع

دعنا نضيف خيار عرض قائمة الموضوعات ونعرضه بشكل مشروط. للقيام بذلك ، نحتاج إلى كسر قائمة عرض المشاعر. أعد تسمية العرض إلى العرض ، ودعنا نكسر قائمة عرض المشاعر:

 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 ، ونكتب مكونًا أساسيًا ، ثم نستورده إلى صفحة App.tsx. الآن ، لنقم بإعداد المسارات:

 <Route path="/topicDisplay/:topic" render={() => <TopicDisplay curatedTweets={curatedTweets} /> } />

نحن الآن جاهزون لتنفيذ مكون واجهة المستخدم.

مكونات واجهة المستخدم

أولاً ، لنقم بإنشاء تعريف ContainerProps :

 interface ContainerProps { curatedTweets: CuratedTweets } const TopicDisplay: React.FC<ContainerProps> = ({curatedTweets}) => { Return <div>Topic Display</div> }

الآن ، سنحتاج إلى استرداد الموضوع من اسم مسار URL. للقيام بذلك ، سنستخدم واجهة برمجة التطبيقات للتاريخ. لذلك دعونا نستورد useHistory ، وننشئ مثيلاً لواجهة برمجة تطبيقات السجل ، وسحب الموضوع من اسم المسار. أثناء قيامنا بذلك ، دعنا أيضًا ننفذ وظيفة التبديل مرة أخرى:

 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 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 نهجًا مختلفًا تمامًا عن الأساليب المستندة إلى الويب في الأقسام السابقة. React Native يعرض كود React الخاص بك كمكونات أصلية. هذا يأتي مع العديد من المزايا. أولاً ، التكامل مع نظام التشغيل الأساسي أعمق بكثير ، مما يسمح للمطور بالاستفادة من ميزات الهاتف الذكي الجديدة والميزات الخاصة بنظام التشغيل التي قد لا تكون متاحة من خلال كوردوفا / مكثف. ثانيًا ، نظرًا لعدم وجود محرك عرض مستند إلى الويب في المنتصف ، فإن تطبيق React Native يكون بشكل عام أسرع من تطبيق مكتوب باستخدام Cordova. أخيرًا ، نظرًا لأن React Native يسمح بدمج المكونات الأصلية ، يمكن للمطورين ممارسة سيطرة أكثر دقة على تطبيقاتهم.

بالنسبة لتطبيقنا ، سنستخدم المنطق من الأقسام السابقة ونستخدم مكتبة مكون React Native تسمى NativeBase لترميز واجهة المستخدم الخاصة بنا.

تكوين التطبيق

أولاً ، ستحتاج إلى تثبيت جميع المكونات المطلوبة لـ React Native باتباع التعليمات الواردة هنا.

بمجرد تثبيت React Native ، لنبدأ المشروع:

 react-native init TwitterCurationRN

دع البرنامج النصي للإعداد يعمل ، وفي النهاية ، يجب إنشاء المجلد. قرص مضغوط في المجلد وتشغيل نظام التشغيل التفاعلي الأصلي ، ويجب أن ترى المحاكي منبثقًا مع التطبيق المثال.

سنرغب أيضًا في تثبيت 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();

قم بتغيير محتوى مكون التطبيق لاستخدام متصفح المكدس:

 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 التي استخدمناها ، مثل hooks و fetch ، متوفرة.

سيكون الاختلاف الوحيد هو الطريقة التي نعمل بها مع واجهة برمجة تطبيقات التنقل ، والتي ستراها لاحقًا.

مكونات واجهة المستخدم

مكونات 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}) => {

احفظ الآن ، وسترى الصفحة.

صفحة المشاعر مرتبة

خطة الصورة الكبيرة

سنقوم بتنفيذ صفحة المشاعر مثل الأقسام السابقة إلى حد كبير ، لكننا سنقوم بتصميم الصفحة بشكل مختلف قليلاً ، وسنستخدم أيضًا مكتبة التنقل بشكل مختلف للحصول على قيمة إرجاع استدعاء واجهة برمجة التطبيقات.

نظرًا لأن 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>

لإنجاز هذا العمل ، سنحتاج بالطبع إلى إنشاء مكون كعب روتين في المكونات / 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';

الآن ، نحن جاهزون لإنشاء ورقة الأنماط العامة.

StyleSheet العامة

أولاً ، قم بإنشاء ملف يسمى globalStyles.js. بعد ذلك ، استورد مكون StyleSheet من React Native وحدد الأنماط:

 import {StyleSheet} from 'react-native'; export default StyleSheet.create({ tweet: {paddingTop: 5}, accountName: {fontWeight: '600'}, })

ونحن جاهزون لتشفير واجهة المستخدم.

مكونات واجهة المستخدم

مكون واجهة المستخدم مألوف جدًا ، باستثناء كيفية تعاملنا مع المسارات. سنرغب في استخدام التنقل والمسار الخاصين 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;

مكونات واجهة المستخدم

سيبدو الكثير من هذا مألوفًا جدًا. استيراد وظائف المكتبة:

 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;

مكونات واجهة المستخدم

مكون واجهة المستخدم بسيط للغاية. لقد رأينا ذلك من قبل ونحن لا نطبق أي منطق مخصص حقًا. لذا ، دعنا نذهب لذلك! خذ نفس عميق…

 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.

يا للعجب! مع ذلك ، انتهينا من جزء الترميز في هذه المقالة. دعونا نرى ما تعلمناه عن التقنيات.

مقارنة التقنيات

كوردوفا: إيجابيات وسلبيات

أفضل شيء في كوردوفا هو السرعة المطلقة التي يستطيع بها مطور الويب الماهر ترميز شيء وظيفي ورائع بشكل معقول. يتم نقل مهارات تطوير الويب والخبرة بسهولة لأنك ، بعد كل شيء ، تقوم بترميز تطبيق ويب. عملية التطوير سريعة وبسيطة ، كما أن الوصول إلى واجهة برمجة تطبيقات كوردوفا أمر بسيط وبديهي أيضًا.

تأتي عيوب استخدام كوردوفا مباشرة في الغالب من الاعتماد المفرط على مكونات الويب. أصبح المستخدمون يتوقعون تجربة مستخدم محددة وتصميم واجهة عند استخدام تطبيقات الأجهزة المحمولة ، وعندما يبدو التطبيق وكأنه موقع للجوال ، يمكن أن تكون التجربة مزعجة بعض الشيء. بالإضافة إلى ذلك ، يجب تنفيذ معظم الميزات المضمنة في التطبيقات ، مثل الرسوم المتحركة الانتقالية وأدوات التنقل ، يدويًا.

أيوني: إيجابيات وسلبيات

كان أفضل جزء في Ionic هو عدد الميزات التي تتمحور حول الجوال والتي حصلت عليها مجانًا. من خلال ترميز يشبه إلى حد كبير ترميز تطبيق ويب ، تمكنت من إنشاء تطبيق يبدو أكثر ملاءمة للهاتف المحمول من مجرد استخدام Cordova و React-Bootstrap. كانت هناك رسوم متحركة للتنقل وأزرار ذات أنماط ذات مظهر أصلي والعديد من خيارات واجهة المستخدم التي جعلت تجربة المستخدم سلسة للغاية.

عيب استخدام Ionic كان سببه جزئيا قوتها. أولاً ، كان من الصعب أحيانًا تخيل كيف سيتصرف التطبيق في بيئات مختلفة. لمجرد أن التطبيق بدا بطريقة ما لا يعني أن موضع واجهة المستخدم نفسه سيبدو كما هو في بيئة أخرى. ثانيًا ، يوجد Ionic في مقدمة العديد من التقنيات الأساسية ، وقد ثبت صعوبة الوصول إلى بعض المكونات. أخيرًا ، هذا خاص بـ Ionic-React ، ولكن نظرًا لأن Ionic تم تصميمه لأول مرة لـ Angular ، بدا أن العديد من ميزات Ionic-React لديها توثيق ودعم أقل. ومع ذلك ، يبدو أن فريق Ionic مهتم جدًا باحتياجات مطوري React ويقدم ميزات جديدة بسرعة.

رد الفعل الأصلي: إيجابيات وسلبيات

تتمتع React Native بتجربة مستخدم سلسة للغاية أثناء تطويرها على الهاتف المحمول. من خلال الاتصال مباشرة بالمحاكي ، لم يكن لغزًا كيف سيبدو التطبيق. كانت واجهة مصحح الأخطاء المستندة إلى الويب مفيدة للغاية في تطبيق تقنيات تصحيح الأخطاء من عالم تطبيقات الويب ، كما أن النظام البيئي قوي للغاية.

يأتي عيب React Native من قربه من الواجهة الأصلية. لا يمكن استخدام العديد من المكتبات القائمة على DOM ، مما يعني الحاجة إلى تعلم مكتبات جديدة وأفضل الممارسات. بدون الاستفادة من CSS ، كان تصميم التطبيق أقل سهولة إلى حد ما. أخيرًا ، مع وجود العديد من المكونات الجديدة التي يجب تعلمها (على سبيل المثال ، عرض بدلاً من div ، مكون نص يلتف كل شيء ، الأزرار مقابل TouchableOpacity مقابل TouchableTransparency ، إلخ) ، هناك القليل من منحنى التعلم في البداية إذا كان شخص ما قد دخل إلى تفاعل مع العالم الأصلي بقليل من المعرفة المسبقة بالميكانيكا.

متى تستخدم كل تقنية

نظرًا لأن كل من Cordova و Ionic و React Native تتمتع جميعها بإيجابيات وسلبيات قوية جدًا ، فإن لكل تقنية سياق تتمتع فيه بأفضل إنتاجية وأداء.

إذا كان لديك بالفعل تطبيق موجود هو الويب أولاً بهوية علامة تجارية قوية تحيط بتصميم واجهة المستخدم والشكل العام والمظهر ، فإن أفضل خيار لك هو كوردوفا ، والذي يمنحك الوصول إلى الميزات الأصلية للهاتف الذكي مع السماح لك بإعادة استخدام معظم مكونات الويب والحفاظ على هوية علامتك التجارية في هذه العملية. بالنسبة للتطبيقات البسيطة نسبيًا التي تستخدم إطار عمل سريع الاستجابة ، قد تتمكن من إنشاء تطبيق جوال مع القليل من التغييرات المطلوبة. ومع ذلك ، سيبدو تطبيقك أقل شبهاً بالتطبيق وأكثر شبهاً بصفحة ويب ، وسيتم ترميز بعض المكونات التي يتوقعها الأشخاص من تطبيق الهاتف المحمول بشكل منفصل. لذلك ، أوصي باستخدام Cordova في الحالات التي تكون فيها في مشروع ويب أولاً تنقل التطبيق إلى الهاتف المحمول.

إذا كنت تبدأ في ترميز تطبيق جديد بفلسفة التطبيق أولاً ، ولكن مجموعة مهارات فريقك في المقام الأول في تطوير الويب ، فإنني أوصي بـ Ionic. تتيح لك مكتبة Ionic كتابة التعليمات البرمجية التي تبدو وتشعر بالقرب من المكونات الأصلية مع السماح لك بتطبيق مهاراتك وغرائزك كمطور ويب. لقد وجدت أن أفضل ممارسات تطوير الويب تنطبق بسهولة على التطوير باستخدام Ionic ، وأن التصميم باستخدام CSS يعمل بسلاسة. بالإضافة إلى ذلك ، يبدو إصدار الهاتف المحمول للموقع أصليًا أكثر بكثير من موقع الويب المشفر باستخدام إطار عمل CSS سريع الاستجابة. ومع ذلك ، في بعض الخطوات على طول الطريق ، وجدت أن تكامل React-Ionic-Native API يتطلب بعض الضبط اليدوي ، والذي قد يكون مضيعة للوقت. لذلك ، أوصي بـ Ionic في الحالات التي يتم فيها تطوير تطبيقك من الألف إلى الياء وتريد مشاركة قدر كبير من التعليمات البرمجية بين تطبيق ويب متوافق مع الأجهزة المحمولة وتطبيق الهاتف المحمول.

إذا كنت تقوم بترميز تطبيق جديد باستخدام بعض قواعد التعليمات البرمجية الأصلية ، فقد ترغب في تجربة React Native. حتى إذا كنت لا تستخدم رمزًا أصليًا ، فقد يكون أيضًا الخيار الأفضل في الحالات التي تكون فيها بالفعل على دراية بـ React Native ، أو عندما يكون اهتمامك الأساسي هو تطبيق الهاتف المحمول بدلاً من تطبيق مختلط. بعد أن ركزت معظم جهود تطوير الواجهة الأمامية على تطوير الويب ، وجدت في البداية أن البدء في React Native كان له منحنى تعليمي أكثر من Ionic أو Cordova بسبب الاختلافات في تنظيم المكون واتفاقيات الترميز. ومع ذلك ، بمجرد تعلم هذه الفروق الدقيقة ، تصبح تجربة التشفير سلسة للغاية ، خاصة بمساعدة مكتبة مكونة مثل NativeBase. نظرًا لجودة بيئة التطوير والتحكم في التطبيق ، إذا كانت الواجهة الأمامية لمشروعك عبارة عن تطبيق للهاتف المحمول بشكل أساسي ، فإنني أوصي بـ React Native كأداة من اختيارك.

موضوعات المستقبل

كان أحد الموضوعات التي لم يكن لدي وقت لاستكشافها هو سهولة الوصول إلى واجهات برمجة التطبيقات الأصلية مثل الكاميرا أو تحديد الموقع الجغرافي أو المصادقة البيومترية. تتمثل إحدى الفوائد العظيمة لتطوير الأجهزة المحمولة في إمكانية الوصول إلى نظام واجهة برمجة تطبيقات غني لا يمكن الوصول إليه بشكل عام من المتصفح.

في المقالات المستقبلية ، أود استكشاف سهولة تطوير هذه التطبيقات الأصلية التي تدعم واجهة برمجة التطبيقات باستخدام تقنيات متعددة الأنظمة الأساسية.

خاتمة

اليوم ، قمنا بتنفيذ تطبيق Twitter curation باستخدام ثلاث تقنيات مختلفة لتطوير الأجهزة المحمولة عبر الأنظمة الأساسية. آمل أن يكون هذا قد أعطاك فكرة جيدة عن شكل كل تقنية وألهمك لتطوير تطبيقك المستند إلى React.

شكرا لقرائتك!