
إن مهمة مقارنة الأطر هي مهمة لا شكر لها ، وتفضيلات المطورين مختلفة ، وتتغير التقنيات بسرعة كبيرة. سريع جدا. ستصبح هذه المقالة قديمة حتى قبل أن أنقر فوق زر النشر.
كانت هناك محاولات للمقارنة ، على سبيل المثال ، منذ حوالي خمس سنوات ، ألهم الرجال (كولين إيبرهاردت وكريس برايس) عددًا من المطورين لتقديم طلب للبحث عن العقارات وفقًا للمعارف التقليدية التي تم تجميعها بوضوح. الفكرة رائعة ، حتى أننا شاركنا وقدمنا نسخة من هذا التطبيق على
DevExtreme . ولكن من حيث الدعم ، فإن مثل هذا المشروع هو جحيم ، ويمثل مشروع
بروبيرتي كروس الآن طبقة تاريخية معينة تسبب الحنين إلى الماضي ومشاعر دافئة ، ولكن من الصعب استخدامها عمليًا.
إذا أخذنا فقط js world ، فهذا هو مشروع
todomvc مفعم بالحيوية يقارن فقط جزء js ، بدون تغليف في هاتف محمول أو سطح مكتب أو أي تطبيق. المشروع حي ومدعوم. على الأرجح ، لا تزال هناك أمثلة رائعة جدًا لم نلاحظها في Google عندما كنا نعد المقالة ، لكننا لن نشعر بالضيق حيال ذلك.
تجربتنا أقل طموحًا وتعرض فقط القسم الحالي من التقنيات ، حتى جزء صغير منها. هنا رابط للمقالات
الأولى والثانية من التجربة.
مزيد من القراءة هي المقالة الثالثة حول كيفية تقديم طلب على React Native وفقًا للمعارف التقليدية. إنه مشابه جدًا للمئات ، وربما الآلاف ، من إعادة سرد الوثائق الأخرى حول كيفية تقديم طلب على React Native. عزيزي القارئ ، حذرتك ، ضميري واضح.
تذكر المعارف التقليدية
بشكل عام ، يمكن عرضه بالكامل في المقالة الأولى. لكني سأضيف صورة لما يجب أن ينتج في النهاية. هناك القليل من الصور.

قليل من المعدات. ما هو رد فعل السكان الأصليين؟
React Native هو إطار عمل لإنشاء تطبيقات الجوال عبر الأنظمة الأساسية من Facebook. كما هو الحال في رد الفعل "العادي" للويب ، يتم تجميع تطبيق واجهة المستخدم من الطوب - المكونات التي تستجيب للتغيرات في حالتها (حالتها) والخصائص التي تم تمريرها إليها (الدعائم) ، ولكن ، على عكس الويب ، يتم تقديمها إلى عناصر تحكم أصلية.
من الناحية المثالية ، يتم استخدام مبادئ الحصانة والوظائف النقية ، مما يضمن بساطة وعزل الاختبار. وهنا تجدر الإشارة إلى أن React نفسها بسيطة للغاية ، وتذهب هذه البساطة إلى الجزء المتحرك.
تعمل الإضافات الإضافية في الشفرة الأصلية ورمز JS على تسوية الفرق بين الأنظمة الأساسية عند الإمكان. في الواقع ، يوفر React Native بعض توحيد خصائص المكون في كل نظام تشغيل.
على سبيل المثال ، تعد ScrollView و HorizontalScrollView مكونين مختلفين في Android. وفي iOS UIScrollView ، الذي يدعم التمرير الأفقي والرأسي. وفي React Native ، سنستخدم الكود التالي عبر الأنظمة الأساسية:
<ScrollView horizontal={true}/>
باتباع نهج كفء ، نحصل على تطبيق أصلي "صادق" يعمل على نظامي iOS و Android.
في عالم مثالي ، عند التطوير في React Native ، لا يتعين عليك الكتابة بلغة Java أو Objective-C. ولكن هناك مثل هذه الفرصة عندما تحتاج إلى تنفيذ مكون يتجاوز قدرات React Native.
لعب المطورون من Airbnb كثيرًا مع هذا ، ويمكننا أن نرى العديد من التطبيقات الجديرة بالاهتمام في رد فعل المجتمع التي كانت في السابق في مستودعهم. على سبيل المثال ،
Lottie هي مكتبة لاستيراد الرسوم المتحركة من Adobe After Effects أو
الخرائط عبر الأنظمة الأساسية .
يتم تنفيذ كود JS في التطبيق على محرك
JavaScriptCore . يتم الاتصال بين الشفرة الأصلية و JS باستخدام جسر غير متزامن (جسر) ، والذي يسمح لك بنقل الخصائص (الدعائم) ، وأحداث الزناد (الأحداث) واسترجاعات.

صورة مأخوذة من توثيق ممتاز
سهل التفاعل . (نوصي بشدة القراءة.)
أثناء عملية البناء ، يتم استخدام babel من الطراز الجديد لتحويل كود JS ، وهذا يسمح لك باستخدام بناء جملة ES6 الجديد ، بالإضافة إلى بعض ميزات ES8 (على سبيل المثال ، انتظار متزامن). إذا كنت ، عزيزي القارئ ، مطورًا لـ js ، فأنت تفهم مدى جودة ذلك عندما يكون هناك عامل انتشار ومدى سوءه عندما لا يكون كذلك.
بالنسبة لتخطيط الصفحة ، يتم استخدام تقنية Flexbox ، ويتم تنفيذها بواسطة محرك اليوجا عبر الأنظمة الأساسية. لديها اختلافات عن flexbox للمتصفح ، لكنها غير مهمة وترتبط بشكل أساسي بالافتراضيات. بالطبع ، هناك فروق دقيقة ، لكنك ستكون محظوظًا ، وكل شيء سيكون فقط وفقًا للتوثيق.
تحضير المكدس ونشره. محطة الأنبوب
للعمل مع RN ، نحتاج إلى
Node.js ومدير حزم npm ، الذي يأتي مجمعًا. ليس من الضروري ، ولكن من المستحسن جدًا تثبيت تطبيق
Expo على جهازك. سيسمح لك بتشغيل مشروعنا على هاتفك ، بالإضافة إلى إنشاء تطبيق iOS وتشغيله عندما لا يكون لديك macOS في متناول اليد.
لنقم بإنشاء تطبيق جديد. للقيام بذلك ، استخدم حزمة إنشاء
التطبيق-رد الفعل-التطبيق الأصلي .
في المحطة ، نفّذ ما يلي:
npm install -g create-react-native-app create-react-native-app notes cd notes npm run start
امسح رمز QR ضوئيًا باستخدام Expo أو أدخل رابطًا من الوحدة الطرفية ، أو حتى أرسل الرابط إلى هاتفك مباشرة من الوحدة الطرفية.
لدي عمومًا شك في أن مطوري cli للرد الأصلي لديهم رجل عجوز ذو شعر رمادي وجد ألعابًا روغيليك بدون واجهة مستخدم عندما لا يوجد سوى محطة طرفية ، وبدلاً من بطاقة فيديو متطورة ، فقط خيالك.

ولكننا ، في غضون ذلك ، أنشأنا للتو وأطلقنا تطبيق "Hello World".
وكل "مرحبا العالم" -أ لا يكفي. تحليل المعارف التقليدية
وفقا لبيان العمل ، سيكون هيكل البيانات للتطبيق
Note: { userName: string, avatar: string, editTime: string, text: string } Project: { name: string, notes: Array<Note> } Projects: Array<Project>
للعمل مع هذه البيانات ، أود أن أغتنم بعض الحلول العصرية للغاية المستندة إلى CQRS. سيسمح هذا بالحفاظ على تكامل البيانات ، ويوفر سرعة قراءة عالية مع القدرة على إعادة ترتيب الإسقاطات ، بالإضافة إلى النشر السريع على السحابة باستخدام أمر واحد. مثل
Resolve ، الذي تم تطويره من قبل زملائنا.
لكنني لن آخذها ، فلدينا تجربة بسيطة ، بدون خلفية. ومن أجل البساطة ،
سأستخدم بنية
التدفق ، ولا سيما تطبيقه -
redux . تأتي البيانات من حالة التطبيق إلى المكونات كدعائم. يمكن للمكونات استدعاء الإجراءات لتحديث البيانات.
سيحتوي التطبيق على 3 شاشات ، كل ذلك وفقًا لشروط الاستخدام:
- قائمة المشاريع - المشاريع ،
- صفحة المشروع التفصيلية مع قائمة الملاحظات - المشروع ،
- صفحة ملاحظة تفصيلية - ملاحظة
للتنقل بين الشاشات
، سأستخدم مكتبة
التنقل التفاعلية. توضح الأرقام القريبة من الرسم البياني في صفحة المكتبة عدد مرات تنزيله في الأسبوع. الآن هناك حوالي 100 ألف في الأسبوع. من الجيد أنني لم أختر مكتبة واحدة من هذا القبيل للملاحة. ونعم ، يمكنك رؤية أرقام حزم npm الأخرى التي أشرت إليها في هذه المقالة لفهم عدد مستخدمي هذه التقنية تقريبًا في وقت معين.
قم بإنشاء تطبيق
بالنسبة إلى React Native ، فإن مكون التطبيق من ملف App.js هو نقطة الدخول إلى التطبيق.
export default class App extends Component { render() { return ( <Provider store={store}> <Navigator /> </Provider> ) } }
يتم توصيل التخزين بالبيانات وحالة التطبيق بواسطة مكون الموفر من مكتبة رد الفعل. يوفر ذلك إعادة توجيه البيانات للمكونات المتداخلة.
إنشاء متصفح للانتقالات بين الشاشات في التطبيق. يعكس بوضوح هيكل التطبيق ، المعلن عنه في التجربة ، ويرسم التحولات المتحركة بين الشاشات لكل منصة.
const Navigator = createStackNavigator({ Projects: { screen: Projects }, Project: { screen: Project }, Note: { screen: Note } })
شاشات المستكشف هي مكونات - حاويات. يحصلون على البيانات من حالة التطبيق.
قائمة المشاريع - المشاريع
على الشاشة مع قائمة المشاريع سيكون هناك قائمة وزر لإضافة مشروع - في رأس النافذة على اليمين. سنقوم بإنشاء مشروع جديد على شاشة المشروع.
للملاحة ، نستخدم كائن التنقل ، الذي انتقل إلى الدعائم المكون الرئيسي - المستكشف.
export class Projects extends PureComponent { static navigationOptions = ({ navigation }) => ({ headerRight: ( <AddButton onPress={() => navigation.navigate('Project')} /> ) }) navigateProject = project => { this.props.navigation.navigate('Project', { projectId: project.id, name: project.name }) } render() { return ( <ProjectList projects={this.props.projects} onPressProject={this.navigateProject} /> ) } }
لعرض قائمة المشاريع ، سنستخدم FlatList - قائمة عبر الأنظمة الأساسية مع المحاكاة الافتراضية:
export class ProjectList extends PureComponent { static propTypes = { projects: ProjectsType, onPressProject: PropTypes.func } renderItem = ({ item }) => ( <ProjectListItem project={item} onPressProject={this.props.onPressProject} /> ) render() { return ( <FlatList data={this.props.projects} keyExtractor={item => item.id} renderItem={this.renderItem} /> ) } }
لكل عنصر نقوم بتعيين مفتاح فريد - هنا لدينا معرف العنصر. يعد ذلك ضروريًا حتى يتمكن التفاعل من التمييز بين العناصر الموجودة في القائمة وتحديث العناصر التي تغيرت فقط.
إضافة مكون إلى عنصر القائمة.
export class ProjectListItem extends PureComponent { static propTypes = { project: ProjectType, onPressProject: PropTypes.func } onPressProject = () => { const { project, onPressProject } = this.props onPressProject(project) } render() { return ( <TouchableOpacity onPress={this.onPressProject}> <View style={styles.project}> <Text style={styles.name}>{this.props.project.name}</Text> </View> </TouchableOpacity> ) } }
TouchableOpactity - غلاف يستجيب للنقرات. عند النقر ، يصبح المكون المتداخل أكثر شفافية.
عرض - تناظري div للويب - المكون الأساسي للترميز.
نص - حاوية للنص.
إضافة أنماط:
const styles = StyleSheet.create({ project: { paddingVertical: 30, paddingHorizontal: 15, backgroundColor: 'white', borderBottomWidth: StyleSheet.hairlineWidth, borderColor: 'gray' }, name: { fontSize: 16 } })
تشبه بنية النمط css ، والفرق الرئيسي هو أنه يمكنك تصميم المكون نفسه فقط (على سبيل المثال ، لا يمكنك تعيين حجم الخط للتطبيق بأكمله ، فقط لمكون نص معين)

صفحة المشروع التفصيلية مع قائمة الملاحظات - المشروع
وبالمثل ، قم بإنشاء صفحة مفصلة. الاختلافات فقط في وجود عنوان في المستكشف ومدخلات إضافية. في المستكشف ، قم بتعيين العنوان - اسم المشروع. إذا لم يتم تحديد معرف المشروع ، فسنعرض عليك إدخال اسم المشروع وإنشاء اسم جديد.
export class Project extends PureComponent { static navigationOptions = ({ navigation }) => { const projectId = navigation.getParam('projectId') return { title: navigation.getParam('name', ''), headerRight: ( <AddButton onPress={() => navigation.navigate('Note', { projectId })} /> ) } } removeNote = noteId => { const { projectId, removeNote } = this.props removeNote(projectId, noteId) } navigateNote = noteId => { const { projectId, navigation } = this.props navigation.navigate('Note', { noteId, projectId }) } createProject = name => { const newProjectId = shortid.generate() this.props.navigation.setParams({ projectId: newProjectId, name }) this.props.addProject(newProjectId, name) } render() { const { projectId, project } = this.props if (!projectId) { return ( <ProjectNameInput onSubmitEditing={this.createProject} /> ) } return ( <NoteList notes={project.notes} onNavigateNote={this.navigateNote} onRemoveNote={this.removeNote} /> ) } }
صفحة المشروع هي قائمة بالملاحظات. وفقًا لـ ToR لكل ملاحظة هناك قائمة سياق مع التحرير والحذف. يمكنك أيضًا حذف الملاحظة بتمرير سريع. هناك قائمة منفصلة في React Native ، مع إمكانية التمرير السريع - SwipeableFlatList.
<SwipeableFlatList data={this.props.notes} bounceFirstRowOnMount={false} keyExtractor={item => item.id} maxSwipeDistance={MAX_SWIPE_DISTANCE} renderQuickActions={this.renderQuickActions} renderItem={this.renderItem} />
عند حذف ملاحظة ، سنطلب تأكيدًا ، لذلك سنطلق عليه تنبيه النظام القياسي
onRemoveNote = noteId => { Alert.alert( 'Remove Note', 'Do you want to remove note ?', [ { text: 'Cancel', onPress: () => {}}, { text: 'Remove', onPress: () => this.props.onRemoveNote(noteId) } ] ) }

هناك نقطة مثيرة للاهتمام لقائمة السياق. على عكس التنبيه ، يختلف تنفيذه في RN لأجهزة Android و iOS.
بالنسبة لنظام Android ، استخدم القائمة المنبثقة
showPopupMenu = () => { const button = findNodeHandle(this._buttonRef) UIManager.showPopupMenu( button, [ 'Edit', 'Delete' ], e => console.error(e), (e, i) => this.onPressMenu(i) ) }
لنظام iOS - ورقة العمل
showActionSheet = () => { ActionSheetIOS.showActionSheetWithOptions({ options: [ 'Edit', 'Delete', 'Cancel' ], destructiveButtonIndex: 1, cancelButtonIndex: 2 }, this.onPressMenu ) }
هناك عدة طرق لتقسيم الشفرة الخاصة بالنظام الأساسي. سنستخدم كائن النظام الأساسي.
onOpenMenu = Platform.select({ android: this.showPopupMenu, ios: this.showActionSheet })

صفحة ملاحظة تفصيلية - ملاحظة
كما أن صفحة الملاحظات بدائية للغاية. ولكن ، على عكس سابقاتها ، نستخدم الحالة لتخزين نتائج إدخال المستخدم المتوسطة.
export class Note extends PureComponent { static navigationOptions = ({ navigation }) => ({ headerRight: ( <SaveButton onPress={navigation.getParam('onSaveNote')} /> ) }) state = { noteText: '' } componentDidMount() { this.props.navigation.setParams({ onSaveNote: this.onSaveNote }) } onSaveNote = () => { Keyboard.dismiss() const { projectId, noteId, note, navigation, addNote, editNote } = this.props const { noteText } = this.state if (!noteId) { const newNoteId = shortId.generate() navigation.setParams({ noteId: newNoteId }) addNote(projectId, newNoteId, noteText) } else if (noteText && noteText !== note.text) { editNote(projectId, noteId, noteText) } } onChangeNote = noteText => { this.setState({ noteText }) } render() { const initialTextValue = this.props.note ? this.props.note.text : '' const noteText = this.state.noteText || initialTextValue return ( <NoteDetail noteText={noteText} onChangeNoteText={this.onChangeNote} /> ) } }
الشاشة التفصيلية للملاحظة - المكون "السخيف" الكلاسيكي - تقدم تقريرًا لأعلى عن تغيير النص وتظهر النص الذي يمرره الوالد إليه
export class NoteDetail extends PureComponent { static propTypes = { noteText: PropTypes.string, onChangeNoteText: PropTypes.func } render() { const { noteText, onChangeNoteText } = this.props return ( <View style={styles.note}> <TextInput multiline style={styles.noteText} value={noteText} placeholder="Type note text here ..." underlineColorAndroid="transparent" onChangeText={onChangeNoteText} /> </View> ) } }

في المجموع ، تلقينا الطلب كما هو الحال في المعارف التقليدية. اكتملت التجربة. يمكن الاطلاع على رمز التطبيق في
المستودع العام
المجموع ، إيجابيات وسلبيات المواطن الأصلي
الإيجابيات:
React Native مألوف ومفهوم للمطورين المطلعين على React و Node.js وإطار npm. من الممكن استخدام جميع المناهج والمكتبات ، كما هو الحال بالنسبة للتفاعل المنتظم.
عدد ضخم من حزم js من npm. على الأرجح ، تم حل معظم المهام القياسية بالفعل وربما تحت ترخيص MIT.
مجتمع كبير. استخدم كل من المطورين الأفراد والشركات الكبيرة RN من أجل التطوير ، ويستمرون في استخدامه.
العديد من المجموعات الجاهزة لمكونات واجهة المستخدم ، مثل
NativeBase و
React Native Elements والمكتبات من الشركات الكبيرة مثل Facebook و Airbnb و Wix.com.
مجموعة أدوات واضحة لتطوير التطبيقات بسهولة من Hello World إلى
Instagram .
السلبيات:
يبدأ التطبيق بشكل أبطأ من التطبيق الأصلي وهناك بعض الصعوبات في تصحيح الأخطاء. يعمل كود JS في وبدون مصحح على محركات مختلفة. كتب Airbnb جيدًا حول هذه المسألة في
سلسلة من المقالات حول سبب تخليهم عن React Native في التنمية.
نظرًا لأن مجموعة الأدوات تتكون من العديد من الحزم التي يتم تطويرها بشكل منفصل ، فهناك احتمال وجود تعارض في الإصدار وكسر.
لا يمكن عمل كل شيء بدون كود أصلي. وعندما تجري تغييرات على الشفرة الأصلية ، ستفقد القدرة على استخدام Expo وتجبر نفسك على إنشاء التطبيق باستخدام أدوات التطوير الأصلية القياسية.
شكرا جزيلا ل
Mirimon و
HeaTTheatR لدعوتي للمشاركة في هذه التجربة. كان ذلك مثيرا. أخيرا ، أضف تصويت.