Kivy. Xamarin React Native. Trois cadres - une expérience (partie 3)


La comparaison des frameworks est une tâche très ingrate, les préférences des développeurs sont différentes, les technologies changent très rapidement. Trop vite. Cet article deviendra obsolète avant même que je clique sur le bouton de publication.


Il y a eu des tentatives de comparaison, par exemple, il y a environ cinq ans, les gars (Colin Eberhardt et Chris Price) ont inspiré un certain nombre de développeurs à faire une demande de recherche de biens immobiliers selon des savoirs traditionnels clairement compilés. L'idée est cool, nous avons même participé et réalisé une version de cette application sur DevExtreme . Mais en termes de soutien, un tel projet est un enfer et maintenant le projet Property Cross représente une certaine couche historique qui provoque la nostalgie et des sentiments chaleureux, mais n'est guère utile.

Si vous ne prenez que le monde js, c'est-à-dire un projet todomvc assez vivant qui ne compare que la partie js, sans emballage dans un mobile, un ordinateur de bureau ou toute autre application. Le projet est vivant et soutenu. Très probablement, il y a encore des exemples très sympas que nous n'avions pas remarqués dans Google lorsque nous préparions l'article, mais nous n'en serons pas contrariés.

Notre expérience est moins ambitieuse et ne montre que la partie actuelle des technologies, même une petite partie. Voici un lien vers les premier et deuxième articles de l'expérience.

Une lecture plus approfondie est le troisième article sur la façon de faire une demande sur React Native selon TK. Il est très similaire à des centaines, voire des milliers, d'autres récits de documentation sur la façon de créer une application sur React Native. Cher lecteur, je vous ai prévenu, ma conscience est claire.

Rappelez-vous TK


En général, il peut être entièrement consulté dans le premier article. Mais j'ajouterai une image de ce qui devrait aboutir à la fin. Il y a peu de photos.
image

Une pincée d'équipement. Qu'est-ce que React Native?


React Native est un cadre pour créer des applications mobiles multiplateformes à partir de Facebook. Comme dans le React «normal» pour le Web, l'application d'interface utilisateur est assemblée à partir de briques - des composants qui répondent aux changements de leur état (état) et des propriétés qui leur sont transmises (accessoires), mais, contrairement au Web, sont rendus dans des contrôles natifs.

Idéalement, les principes d'immunité et de fonctions pures sont utilisés, ce qui garantit la simplicité et l'isolement des tests. Et ici, il convient de noter que React lui-même est très simple, et cette simplicité va dans la partie mobile.

Des modules complémentaires supplémentaires en code natif et JS atténuent la différence entre les plates-formes lorsque cela est possible. En fait, React Native fournit une certaine unification des propriétés du composant dans chaque système d'exploitation.

Par exemple, ScrollView et HorizontalScrollView sont 2 composants différents dans Android. Et dans iOS UIScrollView, qui prend en charge le défilement horizontal et vertical. Et dans React Native, nous utiliserons le code multiplateforme suivant:

<ScrollView horizontal={true}/> 

Avec une approche compétente, nous obtenons une application native «honnête» qui fonctionne sur iOS et Android.

Dans un monde idéal, lors du développement en React Native, vous n'avez pas besoin d'écrire en Java ou en Objective-C. Mais il existe une telle opportunité lorsque vous devez implémenter un composant qui va au-delà des capacités de React Native.

Les développeurs d'Airbnb ont beaucoup joué avec cela, et nous pouvons voir de nombreuses implémentations dignes dans la communauté réagir qui étaient auparavant dans leur référentiel. Par exemple, Lottie est une bibliothèque pour importer des animations à partir d'Adobe After Effects ou des cartes multiplateformes .

Le code JS dans l'application est exécuté sur le moteur JavaScriptCore . La communication entre le code natif et JS se fait à l'aide d'un pont asynchrone (pont), qui vous permet de transférer des propriétés (accessoires), de déclencher des événements (événements) et des rappels.

Image extraite d'une excellente documentation React Made Native Easy . (Je recommande vivement la lecture.)

Pendant le processus de construction, un babel à la mode est utilisé pour convertir le code JS, cela vous permet d'utiliser la nouvelle syntaxe ES6, ainsi que certaines fonctionnalités ES8 (par exemple, async-wait). Si vous, mon cher lecteur, êtes un développeur js, vous comprenez à quel point c'est bon quand il y a un opérateur de propagation et à quel point c'est mauvais quand il ne l'est pas.

Pour la mise en page, la technologie Flexbox est utilisée, implémentée par le moteur Yoga multiplateforme. Il présente des différences par rapport à la flexbox du navigateur, mais ils sont insignifiants et concernent principalement les valeurs par défaut. Bien sûr, il y a des nuances, mais vous aurez de la chance, et tout ne sera que selon la documentation.

Préparation et déploiement de la pile. Terminal de tube


Pour travailler avec RN, nous avons besoin de Node.js et du gestionnaire de packages npm, qui est fourni. Ce n'est pas nécessaire, mais il est très souhaitable d'installer l'application Expo sur votre appareil. Il vous permettra de lancer notre projet sur votre téléphone, ainsi que de créer et d'exécuter une application iOS lorsque vous n'avez pas macOS à portée de main.

Créons une nouvelle application. Pour ce faire, utilisez le package create-react-native-app .

Dans le terminal, exécutez:

 npm install -g create-react-native-app create-react-native-app notes cd notes npm run start 

Scannez le code QR à l'aide d'Expo ou entrez un lien depuis le terminal, ou même envoyez le lien vers votre téléphone, directement depuis le terminal.

J'ai généralement un soupçon que les développeurs de cli pour Reaper Native avaient un vieil homme aux cheveux gris qui a trouvé des jouets roguelike sans interface utilisateur quand il n'y a qu'un terminal, et au lieu d'une carte vidéo haut de gamme, seulement votre imagination.

Mais nous, quant à nous, venons de créer et de lancer l'application «Hello World».

Et l'ensemble «Hello World» -a ne suffit pas. Analyser les savoirs traditionnels


Selon l'énoncé des travaux, la structure des données de l'application sera

 Note: { userName: string, avatar: string, editTime: string, text: string } Project: { name: string, notes: Array<Note> } Projects: Array<Project> 

Pour travailler avec de telles données, je prendrais une solution très à la mode basée sur CQRS. Cela permettrait de préserver l'intégrité des données, offrant une vitesse de lecture élevée avec la possibilité de réorganiser les projections, ainsi qu'un déploiement rapide dans le cloud avec une seule commande. Comme Resolve , développé par nos collègues.

Mais je ne le prendrai pas, nous avons une expérience simple, sans backend. Et pour plus de simplicité, j'utiliserai l'architecture de flux , en particulier sa mise en œuvre - redux . Les données de l'état de l'application arrivent aux composants comme accessoires. Les composants peuvent appeler des actions pour mettre à jour les données.

L'application aura 3 écrans, tous selon les TdR:
  • liste des projets - Projets,
  • page de projet détaillée avec une liste de notes - Projet,
  • page de note détaillée - Remarque


Pour la navigation entre les écrans, j'utiliserai la bibliothèque standard de navigation de réaction . Les chiffres près du graphique sur la page de la bibliothèque montrent combien de fois il est téléchargé par semaine. Maintenant, il y en a environ 100 000 par semaine. C'est bien que je n'ai pas choisi une telle bibliothèque pour la navigation. Et oui, vous pouvez voir les chiffres des autres packages npm que j'ai indiqués dans cet article pour comprendre approximativement le nombre d'utilisateurs de cette technologie à un moment donné.

Créer une application


Pour React Native, le composant App du fichier App.js est le point d'entrée de l'application.

 export default class App extends Component { render() { return ( <Provider store={store}> <Navigator /> </Provider> ) } } 

Le magasin avec les données et l'état de l'application est connecté par le composant Provider de la bibliothèque react-redux. Cela permet le transfert de données pour les composants imbriqués.

Créez un navigateur pour les transitions entre les écrans de l'application. Il reflète clairement la structure de l'application, déclarée dans l'expérience, et dessine des transitions animées entre les écrans pour chaque plateforme.

 const Navigator = createStackNavigator({ Projects: { screen: Projects }, Project: { screen: Project }, Note: { screen: Note } }) 

Les écrans du navigateur sont des composants - conteneurs. Ils obtiennent des données de l'état de l'application.

Liste des projets - Projets


Sur l'écran avec une liste de projets, il y aura une liste et un bouton pour ajouter un projet - dans l'en-tête de la fenêtre à droite. Nous allons créer un nouveau projet sur l'écran Projet.

Pour la navigation, nous utilisons l'objet de navigation, qui a transmis aux accessoires le composant parent - le navigateur.

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

Pour afficher la liste des projets, nous utiliserons FlatList - une liste multiplateforme avec virtualisation:

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

Pour chaque élément, nous définissons une clé unique - nous avons ici l'id de l'élément. Ceci est nécessaire pour que la réaction puisse distinguer les éléments de la liste et mettre à jour uniquement ceux qui ont changé.

Ajoutez un composant à l'élément de liste.

 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 - un wrapper qui répond aux clics. Lorsque vous cliquez dessus, le composant imbriqué devient plus transparent.
Vue - analogue de div pour le web - composant de balisage de base.
Texte - un conteneur pour le texte.

Ajouter des styles:

 const styles = StyleSheet.create({ project: { paddingVertical: 30, paddingHorizontal: 15, backgroundColor: 'white', borderBottomWidth: StyleSheet.hairlineWidth, borderColor: 'gray' }, name: { fontSize: 16 } }) 

La syntaxe de style ressemble à css, la principale différence est que vous ne pouvez styliser que le composant lui-même (par exemple, vous ne pouvez pas définir la taille de police pour l'application entière, uniquement pour un composant Text spécifique)


Page de projet détaillée avec une liste de notes - Projet


De même, créez une page détaillée. Les différences sont uniquement en présence d'un titre dans le navigateur et d'une entrée supplémentaire. Dans le navigateur, définissez le titre - le nom du projet. Si l'ID du projet n'est pas spécifié, nous vous proposerons de saisir le nom du projet et d'en créer un nouveau.

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

La page du projet est une liste de notes. Selon les TdR de chaque note, il existe un menu contextuel avec modification et suppression. Vous pouvez également supprimer la note d'un coup. Il existe une liste distincte dans React Native, avec la possibilité de glisser - SwipeableFlatList.

 <SwipeableFlatList data={this.props.notes} bounceFirstRowOnMount={false} keyExtractor={item => item.id} maxSwipeDistance={MAX_SWIPE_DISTANCE} renderQuickActions={this.renderQuickActions} renderItem={this.renderItem} /> 

Lors de la suppression d'une note, nous demanderons une confirmation, pour cela nous appellerons l'alerte système standard

 onRemoveNote = noteId => { Alert.alert( 'Remove Note', 'Do you want to remove note ?', [ { text: 'Cancel', onPress: () => {}}, { text: 'Remove', onPress: () => this.props.onRemoveNote(noteId) } ] ) } 



Il y a un point intéressant pour le menu contextuel. Contrairement à l'alerte, sa mise en œuvre dans RN pour Android et iOS est différente.

Pour Android, utilisez le menu contextuel

 showPopupMenu = () => { const button = findNodeHandle(this._buttonRef) UIManager.showPopupMenu( button, [ 'Edit', 'Delete' ], e => console.error(e), (e, i) => this.onPressMenu(i) ) } 

Pour iOS - actionSheet

 showActionSheet = () => { ActionSheetIOS.showActionSheetWithOptions({ options: [ 'Edit', 'Delete', 'Cancel' ], destructiveButtonIndex: 1, cancelButtonIndex: 2 }, this.onPressMenu ) } 

Il existe plusieurs façons de fractionner le code spécifique à la plate-forme. Nous utiliserons l'objet Platform.

 onOpenMenu = Platform.select({ android: this.showPopupMenu, ios: this.showActionSheet }) 



Page de note détaillée - Remarque


La page des notes est également assez primitive. Mais, contrairement aux précédents, nous utilisons state pour stocker les résultats d'entrée utilisateur intermédiaires.

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

L'écran détaillé de la note - le composant «idiot» classique - rend compte du changement de texte et montre le texte que le parent lui passe

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



Au total, nous avons reçu la demande comme en savoirs traditionnels. L'expérience est terminée. Le code d'application peut être consulté dans le référentiel général

Total, avantages et inconvénients de React Native


Avantages:


React Native est familier et compréhensible pour les développeurs familiers avec React et le framework Node.js et npm. Il est possible d'utiliser toutes les approches et bibliothèques, comme pour le React habituel.

Un grand nombre de packages js de npm. Très probablement, la plupart des tâches standard ont déjà été résolues et peut-être sous une licence MIT.

Grande communauté. Les développeurs individuels et les grandes entreprises ont utilisé RN pour le développement et continuent de l'utiliser.

De nombreux ensembles prêts à l'emploi de composants d'interface utilisateur, tels que NativeBase , React Native Elements , des bibliothèques de grandes entreprises telles que Facebook, Airbnb, Wix.com.

Boîte à outils claire pour le développement d'applications pratiques de Hello World à Instagram .

Inconvénients:


L'application démarre plus lentement que celle native et il y a quelques difficultés de débogage. Le code JS dans et sans débogueur s'exécute sur différents moteurs. Airbnb a très bien écrit sur ce problème dans une série d'articles expliquant pourquoi ils ont abandonné React Native en cours de développement.

Étant donné que la boîte à outils se compose de nombreux packages développés séparément, il existe un risque de conflit de version et de rupture.

Tout ne peut pas être fait sans code natif. Et lorsque vous apportez des modifications au code natif, vous perdez la possibilité d'utiliser Expo et vous forcez à créer l'application à l'aide d'outils de développement natifs standard.

Un grand merci à Mirimon et HeaTTheatR pour m'avoir invité à participer à cette expérience. C'était excitant. Enfin, ajoutez un vote.

Source: https://habr.com/ru/post/fr421571/


All Articles