Configuración de entorno para desarrollar en Cordova usando ReactJS

Recientemente me embarqué en el viaje de utilizar ReactJS y Cordova en un mismo ambiente de desarrollo. Al no encontrar ningún tutorial fácil y explicativo de como utilizar ambas herramientas decidí investigar por cuenta propia y crear un pequeño y conciso tutorial de como configurar un ambiente cómodo y que funcione lo mejor posible.

Esto está dirigido a todos aquellos que, como yo en su inicio, no tienen ningún conocimiento acerca de Cordova, pero que tienen conocimiento acerca de ReactJS y desean utilizarlo en la mayor cantidad de instancias posibles.

¿Qué utilizaremos?

Utilizaremos distintas herramientas que han probado ser muy útiles para mi en el pasado y que son fundamentales para asegurar un proceso de desarrollo fácil ...:

  • ReactJS: Ya conoces a tu buen amigo React y sus funciones, ¿no?. React es una librería para desarrollar interfaces de usuario con javascript y su principal magia función es que permite fragmentar el contenidos estático y dinámico de las aplicaciones web en componentes, y que obliga a utilizar un paradigma orientado a componentes que, creeme, una vez que adoptas no podrás abandonar.
  • Babel: Babel es un compilador que permite transformar la sintaxis de JavaScript ES6 en JavaScript corriente y que además posee un plugin para compilar sintaxis JSX, esencial para react ya que JSX permite la inclusión de XML y HTML de manera sencilla dentro de JavaScript.
  • Gulp: Gulp Es un ejecutor de tareas dinámicas que permitirá automatizar parcialmente el proceso de compilado, y que hará del proceso de desarrollo menos engorroso.
  • Sass: ¡Traigamos el poder de Sass a nuestro entorno! Sass corresponde a una extensión de la sintaxis de CSS que, entre otras cosas, permite la anidación de componentes, la inclusión de variables y creación de hojas de estilo intuitivas y fáciles de leer.

Lo primero: ¿Y qué es Cordova?

Cordova es un framework que permite desarrollar una sola aplicación web para luego portarla fácilmente a diferentes pltaformas, como Android y iOS. Es un framework muy robusto con una amplia comunidad por detrás y una gran variedad de plugins para resolver las necesidades de distintas aplicaciones.

Se puede ver a Cordova como un servidor HTTP corriente (como Apache) que sirve archivos HTML, JS y CSS y responde a las peticiones de navegadores o cualquier sistema que utilice el protocolo HTTP. Por tanto, lo que sea que puedas servir a través de un servidor HTTP corriente, lo puedes servir en Cordova (AKA: ¡React aquí vamos!).

Requerimientos

  • npm: Se debe tener instalado el gestor de paquetes npm o similar antes de continuar. Para Ubuntu o sistemas basados en ubuntu recomiendo el reciente tutorial redactado en TecAdmin.

  • Node.js: Cordova utiliza Node.js por lo que no funcionará si no está presente en tu sistema.

  • Un café o frapuccino para leer este tutorial con calma: Requerimiento para todo en la vida

Instalando herramientas y dependencias

Primero, debemos instalar Cordova y crear un nuevo proyecto:

npm install -g cordova  
cordova create nuevo-proyecto  

Reemplaza nuevo-proyecto por el nombre del proyecto en el que deseas trabajar. Esto creará un nuevo sub directorio con el nombre del proyecto. Movamonos al nueo directorio:

cd nuevo-proyecto  

Para compilar la aplicación para las distintas plataformas disponibles se deben primero añadir a Cordova. Por defecto, Cordova viene sin plataformas y es necesario añadir la plataforma web:

cordova platform add browser  

Para asegurarnos de que Cordova está correctamente instalado, ejecutemos la aplicación web utilizando el siguiente comando:

cordova emulate browser  

Debería abrirse una nueva ventana de tu navegador favorito y aparecer el logo de cordova con un mensaje de "DEVICE IS READY".

Ahora, debemos instalar todas las herramientas de nuestro entorno. Para esto, debemos inicializar nuestro archivo package.json en donde se guardaran los requerimientos de la aplicación y que podemos utilizar para instalar las dependencias rápidamente en el futuro. Para inicializar, debemos hacer:

npm init  

Y ahora, instalamos todas nuestras herramientas:

npm install --save-dev gulp gulp-rename gulp-sass vinyl-source-stream browserify babelify babel-preset-es2015 babel-preset-react  

Y también las dependencias de la aplicación, incluyendo nuestro protagonista favorito:

npm install --save react react-dom  

Ya tenemos todo instalado, ahora solo falta configurar nuestro ambiente.

Configuración del ambiente

Primero, debemos crear las carpetas necesarias para nuestro ambiente. Para esto, es necesario explicar la estructura inicial de Cordova:

nuevo_proyecto  
│   config.xml
│   package.json    
│
└───hooks
│   
└───node_modules
│   
└───platforms
│   
└───plugins
│   
└───www
  • El archivo config.xml contiene la configuración general de la aplicación, como el nombre y la descripción de esta
  • package.json contiene una lista de todas las herramientas y dependencias requeridas para la aplicación y que son instaladas utilizando npm
  • La carpeta hooks contiene los hooks configurados de la aplicación
  • node_modules contiene los archivos y binarios de las herramientas que han sido instaladas con npm
  • platforms contiene las diversas plataformas que han sido añadidas a Cordova y que contienen archivos especiales para cada una.
  • plugins contiene los archivos y binarios de todos los plugins añadidos a Cordova
  • www es donde residen los archivos que son servidos por Cordova y en donde se deben añadir todos los archivos javascript, html y css a utilizar. Cabe notar que nosotros haremos esto pero de manera indirecta.

Primero, debemos añadir una nueva carpeta a esta estructura src en donde estarán todos nuestros componentes React y archivos .scss. Dentro, debemos crear la carpeta js para nuestros componentes React y sass para los archivos de hoja de estilo.

En la carpeta js debemos crear el archivo index.js que será nuestro punto de entrada para los componentes react.

Lo anterior se puede resumir en los siguientes comandos:

mkdir src  
cd src  
mkdir js  
mkdir sass  
cd js  
touch index.js  
cd ../..  

A modo de ejemplo, se tiene la siguiente estructura en src:

src  
│
└───js
│   │    index.js
│   └─── containers
│   └─── components
└───sass
    │    main.scss
    └─── partials
            _navbar.scss
            _footer.scss

Y además, un index.js de referencia:

import React from 'react';  
import ReactDOM from 'react-dom';

//Containers
import AppContainer from './containers/AppContainer.js';

ReactDOM.render((<AppContainer />), document.getElementById('app));  

A continuación, debemos añadir el archivo gulpfile.js que nos permitirá configurar distintas tareas de desarrollo. Para crear el archivo, escribimos el comando touch gulpfile.js y escribimos el siguiente contenido:

var babelify = require('babelify'),  
    browserify = require('browserify'),
    gulp = require('gulp'),
    rename = require('gulp-rename')
    source = require('vinyl-source-stream'),
    sass = require('gulp-sass');

gulp.task('default', [ 'build' ]);  
gulp.task('build', [ 'js:bundle', 'sass']);

gulp.task('watch', function(){  
  gulp.watch(['./src/sass/**/*.scss', './src/js/**/*.js'], ['build']);
});

gulp.task('sass', function () {  
  return gulp.src('./src/sass/**/*.scss')
    .pipe(sass().on('error', sass.logError))
    .pipe(gulp.dest('./www/css'));
});

gulp.task('js:bundle', function () {  
  var bundler = browserify({
    entry: './src/js/index.js',
    debug: true
  });

  return bundler
    .add('./src/js/index.js')
    .transform(babelify)
    .bundle()
    .pipe(source('./src/js/index.js'))
    .pipe(rename('index.js'))
    .pipe(gulp.dest('./www/js'));
});

Cada vez que utilicemos el comando gulp build se compilaran los componentes de la aplicación a sintaxis regular de JavaScript en un solo archivo index.js, y se moverán a la carpeta www.

Para finalizar, es necesario crear un último archivo para poder utilizar Babel correctamente. Creamos el archivo .babelrc con touch .bablerc y añadimos lo siguiente:

{
  "presets" : ["es2015", "react"]
}

Todo esto no sirve de nada si no referenciamos el archivo index.js y no proorcionamos un punto de entrada en donde se pueda montar los componentes react.

Debemos ir a la carpeta www y modificar el archivo index.html, añadiendo:

<div id="app">  
</div>  

en algún lugar de <body>, y por último incluir nuestro index.js generado con:

<script type="text/javascript" src="js/index.js"></script>  

Al final de <body>.

El proceso de compilado

Para compilar se deben ejecutar dos simples comandos:

gulp build  
cordova prepare browser  

Para evitar tener que compilar a mano y poder acceder a los cambios de forma inmediata en la plataforma browser se puede ejecutar:

gulp watch  

Que ejecutará la tarea build cada vez que se realice un cambio en archivos dentro de la carpeta src. Esto, sin embargo, no quita la necesidad de ejecutar el comando cordova prepare browser debido a la inexistencia de filewatchers en Cordova.

Para solucionar lo anterior, encontré un pequeño truco que corresponde a una gema de ruby llamada filewatcher y que realiza una tarea similar a gulp watch.

Para instalarla:

gem install filewatcher  

Para ejecutar el filewatcher:

filewatcher "www/**/*.*" "cordova prepare browser"  

¡Y listo! Tenemos un ambiente completamente automático y cómodo para utilizar nuestro viejo y confiable React.

Subiendo solo lo necesario a Git

Siempre es dificil encontrar un buen .gitignore para evitar subir archivos innecesarios al repositorio, por lo que aprovecho de adjuntar uno que asegurará solo archivo necesarios para las plataformas Android, iOS, Web y Wp8:

# Node
/node_modules

# Mac
.DS_Store

# Plugins
plugins/

# iOS
platforms/ios/build/  
platforms/ios/www/  
platforms/ios/cordova/console.log  
*.xcuserdatad

# android
platforms/android/assets/www  
platforms/android/bin  
platforms/android/gen  
platforms/android/local.properties  
platforms/android/ant-build  
platforms/android/ant-gen  
platforms/android/CordovaLib/ant-build  
platforms/android/CordovaLib/ant-gen  
platforms/android/CordovaLib/bin  
platforms/android/CordovaLib/gen  
platforms/android/CordovaLib/local.properties

# wp8
platforms/wp8/bin  
platforms/wp8/obj  
platforms/wp8/www  
platforms/wp8/.staging  
platforms/wp8/*.suo  
platforms/wp8/*.csproj.user  

Configurando luego de clonar el repositorio

Luego de clonar un repositorio con esta estructura se deben ejecutar los siguientes comandos:

npm install  
cordova prepare  

Y ya podremos continuar el desarrollo.

Algunas consideraciones

Al utilizar ReactJS en Cordova me vi enfrentado a algunos problemas que me tomaron tiempo en resolver y que listo a continuación para ahorrar tiempo a los lectores

  • browserHistory de ReactRouter no funciona en Android: El primer problema con el que me topé corresponde al uso ReactRouter en donde browserHistory no puede ser utilizado en la plataforma Android. Para solucionarlo, se debe utilizar hashHistory disponible también en la librería
  • Compatibilidad con mapas: Existe un buen wrapper para google maps para la plataforma browser, pero no para android. En el caso de aplicación web recomiendo utilizar GoogleMapReact que presenta muchas facilidades para el manejo de mapas. Sin embargo, no existe aún una librería auxilia para android, pero si un plugin de Cordova.