react native guide

85
React Native GUIDE ⼊⾨からその裏側まで覗いてみよう

Upload: dcubeio

Post on 07-Jan-2017

323 views

Category:

Engineering


4 download

TRANSCRIPT

Page 1: React Native GUIDE

React Native GUIDE⼊⾨からその裏側まで覗いてみよう

Page 2: React Native GUIDE

About Me

Page 3: React Native GUIDE

ビズリーチ・キャンパス学⽣、OB/OG 向けサイト

• Java8+Spring Boot(Thymeleaf)

• ES2015+KnockoutJS

社内管理者向けサイト

• Java8+Spring Boot

• ES2015+React(+Redux)

アプリ

• Java8+Spring Boot

• Swift

Page 4: React Native GUIDE

React とは

Page 5: React Native GUIDE

React とは 1/3https://facebook.github.io/react/ Facebook 製

• UI 構築⽤の JavaScript ライブラリ

• Declarative(宣⾔的)� アプリの状態に応じたインタラクティブ

な UI を簡単に構築できる� コードもわかりやすい

• Component-Based� いわゆるコンポーネント(部品)指向

� ⾃分⾃⾝の状態も管理する

• Learn Once,Write Anywhere� これだけ覚えればサーバサイドもスマホ

アプリも OK

Page 6: React Native GUIDE

React とは 2/3https://speakerdeck.com/katamuki/es2015-react-dot-jswoxue-bu 弊社のおっさんが学んだ話(宣伝)

• コンポーネントを組み合わせて構築するコンポーネント指向の View ライブラリ

• Facebook、インスタ、Yahoo!、Airbnb などでも使われている

• VirtualDOMを⽤いた画⾯の差分描画を⾏い⾼速な画⾯表⽰を提供する

Page 7: React Native GUIDE

React とは 3/3https://facebook.github.io/react/tutorial/tutorial.html

• Tutorial おすすめ

• ポイント� ES2015� React.Component� this.prop� this.state� componentXXXMount

• ES2015 は以下ざっと読んでおくのがおすすめ� http://postd.cc/es6-cheatsheet/

� 「...」とか検索難易度⾼すぎる

Page 8: React Native GUIDE

React Nativeとは

Page 9: React Native GUIDE

React Native とは 1/3https://facebook.github.io/react-native/ Facebook 製

• ネイティブアプリを JavaScript とReact を使⽤して構築できる

• ちゃんとサクサク動く本当のスマホアプリが作れる

• (アプリなのに)ホットリローディングでさくさく開発できる

• Native コードも使える

Page 10: React Native GUIDE

React Native とは 2/3https://facebook.github.io/react-native/docs/getting-started.html#content

アプリ⽤のコンポネントを確認しておくと効率が良い

• こちらも Getting started からTutorial がおすすめ

Page 11: React Native GUIDE

React Native とは 3/3http://www.slideshare.net/TadeuZagallo/a-tour-of-react-native?qid=ddc291a4-7988-46dc-b086-9cf3c92a7235&v=&b=&from_search=4 メリットとか

• 今後ますますアプリの需要は⾼まっていく

• Android と iPhone で 70% 〜 90% コードを共有可能

• ネイティブの勉強をゴリッとやらないで良い

Page 12: React Native GUIDE

Question

Page 13: React Native GUIDE

React Nativeの裏側を覗く

Page 14: React Native GUIDE

React Native の裏側を覗くhttps://facebook.github.io/react-native/docs/getting-started.html#content Getting Started

• ⾊々インストール� node� watchman� react-native-cli� …

• 初期化

• 起動

react-native init AwesomeProject

react-native run-ios

Page 15: React Native GUIDE

React Nativeの裏側を覗く初期化処理を react-native init からみてみる

Page 16: React Native GUIDE

React Native の裏側を覗く

view `which react-native`

Page 17: React Native GUIDE

React Native の裏側を覗くswitch (commands[0]) {case 'init':if (!commands[1]) {console.error('Usage: react-native init <ProjectName> [--verbose]'

);process.exit(1);

} else {init(commands[1], argv.verbose, argv.version);

}break;

default:...

Page 18: React Native GUIDE

React Native の裏側を覗くfunction init(name, verbose, rnPackage) {validatePackageName(name);

if (fs.existsSync(name)) {createAfterConfirmation(name, verbose, rnPackage);

} else {createProject(name, verbose, rnPackage);

}}

Page 19: React Native GUIDE

React Native の裏側を覗くfunction createProject(name, verbose, rnPackage) {var root = path.resolve(name);var projectName = path.basename(root);

console.log('This will walk you through creating a new React Native project in',root

);

if (!fs.existsSync(root)) {fs.mkdirSync(root);

}

・・・

Page 20: React Native GUIDE

React Native の裏側を覗く・・・var packageJson = {

name: projectName,version: '0.0.1',private: true,scripts: {start: 'node node_modules/react-native/local-cli/cli.js start'

}};fs.writeFileSync(path.join(root, 'package.json'),

JSON.stringify(packageJson));process.chdir(root);

console.log('Installing react-native package from npm...');

・・・

Page 21: React Native GUIDE

React Native の裏側を覗く・・・

if (verbose) {runVerbose(root, projectName, rnPackage);

} else {run(root, projectName, rnPackage);

}}

Page 22: React Native GUIDE

React Native の裏側を覗くfunction run(root, projectName, rnPackage) {exec('npm install --save --save-exact ' + getInstallPackage(rnPackage),

function(e, stdout, stderr) {if (e) {console.log(stdout);console.error(stderr);console.error('`npm install --save --save-exact react-native`

failed');process.exit(1);

}checkNodeVersion();var cli = require(CLI_MODULE_PATH());cli.init(root, projectName);

});}

Page 23: React Native GUIDE

React Native の裏側を覗く

view [react-native init したディレクトリ]/[アプリ名]/node_modules/react-native/cli.js

Page 24: React Native GUIDE

React Native の裏側を覗く/*** Copyright (c) 2015-present, Facebook, Inc.* All rights reserved.** This source code is licensed under the BSD-style license found in the* LICENSE file in the root directory of this source tree. An additional grant* of patent rights can be found in the PATENTS file in the same directory.

*/'use strict';

require('./packager/babelRegisterOnly')([/private-cli¥/src/

]);

module.exports = require('./local-cli/cli.js');

Page 25: React Native GUIDE

React Native の裏側を覗く

view [react-native init したディレクトリ]/[アプリ名]/node_modules/react-native/local-cli/cli.js

Page 26: React Native GUIDE

React Native の裏側を覗く・・・

var cliEntry = require('./cliEntry');

if (require.main === module) {cliEntry.run();

}

module.exports = cliEntry;

Page 27: React Native GUIDE

たらい回し

Page 28: React Native GUIDE

React Native の裏側を覗く

view [react-native init したディレクトリ]/[アプリ名]/node_modules/react-native/local-cli/cliEntry.js

Page 29: React Native GUIDE

React Native の裏側を覗く・・・

const init = require('./init/init');

・・・

module.exports = {run: run,init: init,

};

Page 30: React Native GUIDE

React Native の裏側を覗く

これ!

function run(root, projectName, rnPackage) {exec('npm install --save --save-exact ' + getInstallPackage(rnPackage),

function(e, stdout, stderr) {if (e) {console.log(stdout);console.error(stderr);console.error('`npm install --save --save-exact react-native`

failed');process.exit(1);

}checkNodeVersion();var cli = require(CLI_MODULE_PATH());cli.init(root, projectName);

});}

Page 31: React Native GUIDE

React Native の裏側を覗く

view [react-native init したディレクトリ]/[アプリ名]/node_modules/react-native/local-cli/init/init.js

Page 32: React Native GUIDE

React Native の裏側を覗く/*** Creates the template for a React Native project given the provided* parameters:* @param projectDir Templates will be copied here.* @param argsOrName Project name or full list of custom arguments* for the generator.*/function init(projectDir, argsOrName) {console.log('Setting up new React Native app in ' + projectDir);

const args = Array.isArray(argsOrName)? argsOrName // argsOrName was e.g. ['AwesomeApp', '--verbose']: [argsOrName].concat(process.argv.slice(4)); // argsOrName was e.g.

'AwesomeApp’

・・・続く

Page 33: React Native GUIDE

React Native の裏側を覗く・・・続き

// args array is e.g. ['AwesomeApp', '--verbose']if (!args || args.length === 0) {console.error('react-native init requires a project name.');return;

}

const newProjectName = args[0];const options = minimist(args);

generateProject(projectDir, newProjectName, options);}

Page 34: React Native GUIDE

React Native の裏側を覗くfunction generateProject(destinationRoot, newProjectName, options) {

・・・中略

copyProjectTemplateAndReplace(path.resolve('node_modules', 'react-native', 'local-cli', 'templates',

'HelloWorld'),destinationRoot,newProjectName

);

・・・中略

printRunInstructions(destinationRoot, newProjectName);}

Page 35: React Native GUIDE

React Native の裏側を覗くconst copyProjectTemplateAndReplace =require('../generator/copyProjectTemplateAndReplace');

・・・中略

require('../generator/printRunInstructions');

Page 36: React Native GUIDE

余談

Page 37: React Native GUIDE

余談(init.js)const yarnVersion =

(!options.npm) &&yarn.getYarnVersionIfAvailable() &&yarn.isGlobalCliUsingYarn(destinationRoot);

・・・中略

if (yarnVersion) {console.log('Adding React...');execSync(`yarn add react@${reactVersion}`);

} else {console.log('Installing React...');execSync(`npm install react@${reactVersion} --save --save-exact`);

}

Page 38: React Native GUIDE

React Native の裏側を覗く

view [react-native init したディレクトリ]/[アプリ名]/node_modules/react-native/local-cli/generator/copyProjectTemplateAndReplace.js

Page 39: React Native GUIDE

React Native の裏側を覗く割愛します・・・

[react-native init したディレクトリ]/[アプリ名]/node_modules/react-native/local-cli/templates/HelloWorld

から

[react-native init したディレクトリ]/[アプリ名]

に HelloWorld を [アプリ名] に replace しながら頑張ってコピーしている処理

Page 40: React Native GUIDE

React Native の裏側を覗く

$ ls -ltotal 64drwxr-xr-x 4 ayumi.toukairin 386209875 136 12 10 19:23 __tests__-rw-r--r-- 1 ayumi.toukairin 386209875 31 12 8 20:25 _babelrc-rw-r--r-- 1 ayumi.toukairin 386209875 114 12 8 20:25 _buckconfig-rw-r--r-- 1 ayumi.toukairin 386209875 1425 12 8 20:25 _flowconfig-rw-r--r-- 1 ayumi.toukairin 386209875 16 12 8 20:25 _gitattributes-rw-r--r-- 1 ayumi.toukairin 386209875 415 12 8 20:25 _gitignore-rw-r--r-- 1 ayumi.toukairin 386209875 2 12 8 20:25 _watchmanconfigdrwxr-xr-x 10 ayumi.toukairin 386209875 340 12 10 19:23 android-rw-r--r-- 1 ayumi.toukairin 386209875 1106 12 8 20:25 index.android.js-rw-r--r-- 1 ayumi.toukairin 386209875 1072 12 8 20:25 index.ios.jsdrwxr-xr-x 5 ayumi.toukairin 386209875 170 12 10 19:23 ios

Page 41: React Native GUIDE

React Native の裏側を覗く

$ ls –ltotal 24drwxr-xr-x 4 ayumi.toukairin 386209875 136 12 10 19:23 __tests__drwxr-xr-x 10 ayumi.toukairin 386209875 340 12 10 19:23 android-rw-r--r-- 1 ayumi.toukairin 386209875 1091 12 10 20:55 index.android.js-rw-r--r-- 1 ayumi.toukairin 386209875 1057 12 10 20:55 index.ios.jsdrwxr-xr-x 5 ayumi.toukairin 386209875 170 12 10 19:23 iosdrwxr-xr-x 569 ayumi.toukairin 386209875 19346 12 10 20:55 node_modules-rw-r--r-- 1 ayumi.toukairin 386209875 421 12 10 20:55 package.json

Page 42: React Native GUIDE

React Native の裏側を覗く

$ ls –l iostotal 0drwxr-xr-x 8 ayumi.toukairin 386209875 272 12 10 19:23 [アプリ名]drwxr-xr-x 4 ayumi.toukairin 386209875 136 12 10 19:23 [アプリ名].xcodeprojdrwxr-xr-x 4 ayumi.toukairin 386209875 136 12 10 19:23 [アプリ名]Tests

Page 43: React Native GUIDE

というわけで

Page 44: React Native GUIDE

init 終わり!

Page 45: React Native GUIDE

Question

Page 46: React Native GUIDE

React Nativeの裏側を覗く続いて react-native run-ios をみてみる

Page 47: React Native GUIDE

React Native の裏側を覗く

view `which react-native`

Page 48: React Native GUIDE

React Native の裏側を覗く・・・

var cli;var cliPath = CLI_MODULE_PATH();if (fs.existsSync(cliPath)) {cli = require(cliPath);

}

// minimist apivar commands = argv._;if (cli) {cli.run();

} else {

・・・

Page 49: React Native GUIDE

view [react-native init したディレクトリ]/[アプリ名]/node_modules/react-native/local-cli/cliEntry.js

React Native の裏側を覗く

Page 50: React Native GUIDE

React Native の裏側を覗くfunction run() {const setupEnvScript = /^win/.test(process.platform)? 'setup_env.bat': 'setup_env.sh';

childProcess.execFileSync(path.join(__dirname, setupEnvScript));

const config = getCliConfig();commands.forEach(cmd => addCommand(cmd, config));

commander.parse(process.argv);

・・・

Page 51: React Native GUIDE

view [react-native init したディレクトリ]/[アプリ名]/node_modules/react-native/local-cli/setup_env.sh

React Native の裏側を覗く

Page 52: React Native GUIDE

React Native の裏側を覗く# Copyright (c) 2015-present, Facebook, Inc.# All rights reserved.## This source code is licensed under the BSD-style license found in the# LICENSE file in the root directory of this source tree. An additional grant# of patent rights can be found in the PATENTS file in the same directory.

# 2048 is the max for non root users on Maculimit -n 2048

Page 53: React Native GUIDE

React Native の裏側を覗くfunction run() {const setupEnvScript = /^win/.test(process.platform)? 'setup_env.bat': 'setup_env.sh';

childProcess.execFileSync(path.join(__dirname, setupEnvScript));

const config = getCliConfig();commands.forEach(cmd => addCommand(cmd, config));

commander.parse(process.argv);

・・・続く

Page 54: React Native GUIDE

React Native の裏側を覗く・・・続き

const isValidCommand = commands.find(cmd => cmd.name.split(' ')[0] ===process.argv[2]);

if (!isValidCommand) {printUnknownCommand(process.argv[2]);return;

}

if (!commander.args.length) {commander.help();

}}

Page 55: React Native GUIDE

迷⼦になった

Page 56: React Native GUIDE

React Native の裏側を覗くfunction run() {const setupEnvScript = /^win/.test(process.platform)? 'setup_env.bat': 'setup_env.sh';

childProcess.execFileSync(path.join(__dirname, setupEnvScript));

const config = getCliConfig();commands.forEach(cmd => addCommand(cmd, config));

commander.parse(process.argv);

・・・続く ここらしい!

Page 57: React Native GUIDE

React Native の裏側を覗くconst commander = require('commander');

・・・中略

function run() {const setupEnvScript = /^win/.test(process.platform)? 'setup_env.bat': 'setup_env.sh';

childProcess.execFileSync(path.join(__dirname, setupEnvScript));

const config = getCliConfig();commands.forEach(cmd => addCommand(cmd, config));

commander.parse(process.argv);

・・・

https://tj.github.io/commander.js/### Command#parse()Parse argv, settings options and invoking commands when defined.

Page 58: React Native GUIDE

React Native の裏側を覗くconst addCommand = (command: Command, config: ConfigT) => {const options = command.options || [];

const cmd = commander.command(command.name, undefined, {noHelp: !command.description,

}).description(command.description).action(function runAction() {const passedOptions = this.opts();const argv: Array<string> = Array.from(arguments).slice(0, -1);

Promise.resolve().then(() => {assertRequiredOptions(options, passedOptions);return command.func(argv, config, passedOptions);

}).catch(handleError);

});・・・

Page 59: React Native GUIDE

view [react-native init したディレクトリ]/[アプリ名]/node_modules/react-native/local-cli/runIOS/runIOS.js

React Native の裏側を覗く

Page 60: React Native GUIDE

React Native の裏側を覗くfunction runIOS(argv, config, args) {process.chdir(args.projectPath);const xcodeProject = findXcodeProject(fs.readdirSync('.'));if (!xcodeProject) {throw new Error('Could not find Xcode project files in ios folder');

}

・・・中略

if (args.device) {

・・・中略

} else {return runOnSimulator(xcodeProject, args, inferredSchemeName, scheme);

}}

Page 61: React Native GUIDE

React Native の裏側を覗くfunction runOnSimulator(xcodeProject, args, inferredSchemeName, scheme){

return new Promise((resolve) => {try {var simulators = JSON.parse(child_process.execFileSync('xcrun', ['simctl', 'list', '--json',

'devices'], {encoding: 'utf8'}));

} catch (e) {throw new Error('Could not parse the simulator list output');

}

const selectedSimulator = findMatchingSimulator(simulators, args.simulator);

if (!selectedSimulator) {throw new Error(`Could not find ${args.simulator} simulator`);

}・・・

Page 62: React Native GUIDE

React Native の裏側を覗く

Usage: xcrun [options] <tool name> ... arguments ...

Find and execute the named command line tool from the active developer directory.

The active developer directory can be set using `xcode-select`, or via theDEVELOPER_DIR environment variable. See the xcrun and xcode-select manualpages formore information.

Page 63: React Native GUIDE

React Native の裏側を覗く・・・

const simulatorFullName = formattedDeviceName(selectedSimulator);console.log(`Launching ${simulatorFullName}...`);try {child_process.spawnSync('xcrun', ['instruments', '-w',

selectedSimulator.udid]);} catch (e) {// instruments always fail with 255 because it expects more arguments,// but we want it to only launch the simulator

}resolve(selectedSimulator.udid)

})

・・・

Page 64: React Native GUIDE

React Native の裏側を覗く・・・

.then((udid) => buildProject(xcodeProject, udid, scheme, args.configuration)).then((appName) => {if (!appName) {appName = inferredSchemeName;

}let appPath = getBuildPath(args.configuration, appName);console.log(`Installing ${appPath}`);child_process.spawnSync('xcrun', ['simctl', 'install', 'booted',

appPath], {stdio: 'inherit'});

・・・

Page 65: React Native GUIDE

React Native の裏側を覗く・・・

const bundleID = child_process.execFileSync('/usr/libexec/PlistBuddy',['-c', 'Print:CFBundleIdentifier', path.join(appPath, 'Info.plist')],{encoding: 'utf8'}

).trim();

console.log(`Launching ${bundleID}`);child_process.spawnSync('xcrun', ['simctl', 'launch', 'booted',

bundleID], {stdio: 'inherit'});})

}

Page 66: React Native GUIDE

view [react-native init したディレクトリ]/[アプリ名]/node_modules/react-native/packager/react-native-xcode.sh

React Native の裏側を覗く

Page 67: React Native GUIDE

React Native の裏側を覗く#!/bin/bash# Copyright (c) 2015-present, Facebook, Inc.# All rights reserved.#・・・中略

case "$CONFIGURATION" inDebug)# Speed up build times by skipping the creation of the offline package

for debug# builds on the simulator since the packager is supposed to be running

anyways.if [[ "$PLATFORM_NAME" == *simulator ]]; thenecho "Skipping bundling for Simulator platform"exit 0;

fi

・・・

Page 68: React Native GUIDE

また迷⼦になった

Page 69: React Native GUIDE

xcodebuild -project [アプリ名].xcodeproj -configuration Debug -scheme [アプリ名] -destination id=[udid] -derivedDataPath build

React Native の裏側を覗く

Page 70: React Native GUIDE

vim [react-native init したディレクトリ]/[アプリ名]/node_modules/react-native/React/React.xcodeproj/project.pbxproj

React Native の裏側を覗く

Page 71: React Native GUIDE

React Native の裏側を覗く#!/bin/shif [ -z "${RCT_NO_LAUNCH_PACKAGER+xxx}" ] ; thenif nc -w 5 -z localhost 8081 ; thenif ! curl -s "http://localhost:8081/status" | grep -q "packager-

status:running" ; thenecho "Port 8081 already in use, packager is either not running or not

running correctly"exit 2

fielseopen "$SRCROOT/../packager/launchPackager.command" || echo "Can't start

packager automatically"fi

fi

Page 72: React Native GUIDE

vim [react-native init したディレクトリ]/[アプリ名]/node_modules/react-native/packager/launchPackager.command

React Native の裏側を覗く

Page 73: React Native GUIDE

React Native の裏側を覗く#!/usr/bin/env bash

# Copyright (c) 2015-present, Facebook, Inc.# All rights reserved.

・・・中略

# Set terminal titleecho -en "¥033]0;React Packager¥a"clear

THIS_DIR=$(dirname "$0")pushd "$THIS_DIR"source ./packager.shpopd

echo "Process terminated. Press <enter> to close the window"read

Page 74: React Native GUIDE

vim [react-native init したディレクトリ]/[アプリ名]/node_modules/react-native/packager/packager.sh

React Native の裏側を覗く

Page 75: React Native GUIDE

React Native の裏側を覗く#!/usr/bin/env bash

# Copyright (c) 2015-present, Facebook, Inc.# All rights reserved.## This source code is licensed under the BSD-style license found in the# LICENSE file in the root directory of this source tree. An additional grant# of patent rights can be found in the PATENTS file in the same directory.

THIS_DIR=$(dirname "$0")node "$THIS_DIR/../local-cli/cli.js" start "$@"

Page 76: React Native GUIDE

vim [react-native init したディレクトリ]/[アプリ名]/node_modules/react-native/local-cli/server/server.js

React Native の裏側を覗く

Page 77: React Native GUIDE

React Native の裏側を覗く/*** Starts the React Native Packager Server.*/

function server(argv, config, args) {

・・・中略

console.log(formatBanner('Running packager on port ' + args.port + '.¥n¥n' +'Keep this packager running while developing on any JS projects. ' +'Feel free to close this tab and run your own packager instance if you ' +'prefer.¥n¥n' +'https://github.com/facebook/react-native', {

marginLeft: 1,marginRight: 1,paddingBottom: 1,

}));

・・・中略

runServer(args, config, () => console.log('¥nReact packager ready.¥n'));}

Page 78: React Native GUIDE

vim [react-native init したディレクトリ]/[アプリ名]/node_modules/react-native/local-cli/server/runServer.js

React Native の裏側を覗く

Page 79: React Native GUIDE

いよいよHotDeploy部分

Page 80: React Native GUIDE

。。。

Page 81: React Native GUIDE

ごめんなさい。

Page 82: React Native GUIDE

まとめ

Page 83: React Native GUIDE

まとめ• React の知識を覚えるとスマホアプリが作れる、ということでそのメリットは

⼤きい

• といいつつも、ハマった時のことを考えると node とアプリ構築(xcode)の知識はある程度有ったほうが良さそう

• 割と⾟かった、でも勉強になった� Simulator ⽴ち上げるまでのコードは⼤体把握できたので、ある程度トラブルシュー

ティングもできるようになれば良いな・・・

• いつか続きも読みたいな

Page 84: React Native GUIDE

Question

Page 85: React Native GUIDE

Thank you