React-Native 服务启动过程

启动React-Native 服务

执行 react-native init AwsomeProject 会初始化一个名为AwsomeProject的RN Demo,编译运行就会自动启动一个服务。
工程根目录下面会有
package.json ,node_modules, ios,android 几个文件夹以及 index.ios.js,index.android.js 程序主文件。

package.json 是node的依赖配置文件,node_modules 是 node的所有依赖文件,这些都是开发时候使用的,最后运行到真机的时候,会从主文件开始,把所有依赖的JS文件,根据依赖配置打包成一个bundle文件,拷贝到App的文件夹下去。

先看 ReactNative 服务启动的过程

image.png
  • HMR (hot module replace) 模块热更新服务
  • Haste Map(node-haste)用来转换 JS 到 Module

Package.json 里可以看到服务的入口文件。

"scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "jest --no-cache"
  }

cli

image.png

ReactNative 代码生成 bundle 有两种方式,一个是 cli start 命令启动server 实时转换。 一个是 react-native bundle 命令生成。

Server.js

react-native 把所有的命令注册在了 commands.js 里, server.js 是cli start的真身,也就是服务的起点。

commands.js

module.exports = {
  name: 'start',
  func: server,
  description: 'starts the webserver',
  options: [{
    command: '--port [number]',
    default: 8081,
    parse: (val) => Number(val),
  }, {
    command: '--host [string]',
    default: '',
  }
  ...

同时我们也找到了服务启动后控制台前两条输出的代码

 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,
    })
  );
  
console.log(
    'Looking for JS files in\n  ',
    chalk.dim(args.projectRoots.join('\n   ')),
    '\n'
  );

Server.js 的作用只是收集参数,做事情的是 runServer.js

runServer.js

runServer 使用 node 的connect 创建了服务,同时注册了一些中间件和webScoket

loadRawBodyMiddleware
compress
DevToolsMiddleware
openStackFrameInEditorMiddleware
copyToClipBoardMiddleware
statusPageMiddleware
systraceProfileMiddleware
heapCaptureMiddleware
cpuProfilerMiddleware
jscProfilerMiddleware
indexPageMiddleware
packagerServer
logger
errorHandler

中间件对各自的请求做了不同的处理。

以systraceProfileMiddleware 举例,其中有这么一段代码

if (error.code === 127) {
        var response = '\n** Failed executing `' + cmd + '` **\n\n' +
          'Google trace-viewer is required to visualize the data, ' +
          'You can install it with `brew install trace2html`\n\n' +
          'NOTE: Your profile data was kept at:\n' + dumpName;
        console.log(response);
        res.end(response);
      } else {
        console.error(error);
        res.end('Unknown error: ' + error.message);
      }

摇一摇模拟器,点击start systrace,然后stop,弹出的信息和代码中的是对应的,说明systraceProfileMiddleware 就是处理模拟器的systrace调试信息的中间件

image.png
image.png

再比如说 getDevToolsMiddleware 开启了debugger-ui 服务

image.png

最后一个中间件是 packagerServer,它负责转换RN代码到js,它才是最核心的内容。这里只是把参数传递给了ReactPackager ,干活的还在另一个文件ReactPackager.js里。

function getPackagerServer(args, config) {
  const transformModulePath =
    args.transformer ? path.resolve(args.transformer) :
    typeof config.getTransformModulePath === 'function' ? config.getTransformModulePath() :
    undefined;

  return ReactPackager.createServer({
    assetExts: defaultAssetExts.concat(args.assetExts),
    blacklistRE: config.getBlacklistRE(),
    cacheVersion: '3',
    extraNodeModules: config.extraNodeModules,
    getTransformOptions: config.getTransformOptions,
    platforms: defaultPlatforms.concat(args.platforms),
    projectRoots: args.projectRoots,
    resetCache: args.resetCache,
    transformModulePath: transformModulePath,
    verbose: args.verbose,
    watch: !args.nonPersistent,
  });
}

关于 connect 可以参照

http://blog.fens.me/nodejs-connect/?utm_source=tuicool&utm_medium=referral

ReactPackager.js

本文件只代理了一下 server ,并使用 server 提供了一些方法。

 var Server = require('./src/Server');
  return new Server(options);
  
  暴露的方法
  ...
  exports.buildBundle
  
  exports.getOrderedDependencyPaths
  

Server/index.js

从 Babel 开始分析 ReactNative 代码转换逻辑

babel-cli 的原文
https://babeljs.io/docs/usage/cli/

阮一峰翻译
http://www.ruanyifeng.com/blog/2016/01/babel.html

从Package.json 看到 ReactNative 转换用到的转码规则有以下这些

    "babel-plugin-transform-class-properties": "*",
    "babel-polyfill": "*",
    "babel-preset-es2015": "*",
    "babel-preset-react": "*",
    "babel-preset-react-native": "1.9.0",

按照babel文档里面的步骤新建一个转换RN代码的工程

1.创建工程目录

image.png

工程名称为babel-test
src 源码文件夹
lib 转换后的文件夹

  1. package.json内容
{
  "name": "babel-test",
  "version": "1.0.0",
  "devDependencies": {
    "react": "15.4.2",
    "react-native": "^0.40.0",
    "babel-cli": "^6.24.0",
    "babel-register": "^6.24.0",
    "babel-plugin-transform-class-properties": "*",
    "babel-polyfill": "*",
    "babel-preset-es2015": "*",
    "babel-preset-react": "*",
    "babel-preset-react-native": "1.9.0"
  },
  "scripts": {
    "build": "babel src -d lib"//制作 npm build 命令
  }
}

  1. 执行npm install,安装依赖

4.编写 src/script.js 源码为ReactNative 代码

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View
} from 'react-native';
import ConferenceList from './conference'
class User extends Component {
  render() {
    return (
      
        
          Welcome to React Native!
        
        
          To get started, edit index.android.js
        
        
          Double tap R on your keyboard to reload,{'\n'}
          Shake or press menu button for dev menu
        
      
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

module.exports = User

  • 在目录下创建 .babelrc
{
  "presets": ["react-native"]
}

执行转换命令 npm build

image.png

转换后的代码为

var _createClass=function(){function defineProperties(target,props){for(var i=0;i

下面是执行 react-native bundle 命令转换后的代码。RN转换后的bundle 里require的内容都转换成了数字,为了方便查看,我保留了转换前的路径。


__d(/* AwsomeProject/user.js */function(global, require, module, exports) {





var _react=require("react/react.js" /* react */);var _react2=babelHelpers.interopRequireDefault(_react);
var _reactNative=require("react-native/Libraries/react-native/react-native.js" /* react-native */);




var _conference=require("AwsomeProject/conference/index.js" /* ./conference */);var _conference2=babelHelpers.interopRequireDefault(_conference);var
User=function(_Component){babelHelpers.inherits(User,_Component);function User(){babelHelpers.classCallCheck(this,User);return babelHelpers.possibleConstructorReturn(this,(User.__proto__||Object.getPrototypeOf(User)).apply(this,arguments));}babelHelpers.createClass(User,[{key:'render',value:function render()
{
return(
_react2.default.createElement(_reactNative.View,{style:styles.container},
_react2.default.createElement(_reactNative.Text,{style:styles.welcome},'Welcome to React Native!'),


_react2.default.createElement(_reactNative.Text,{style:styles.instructions},'To get started, edit index.android.js'),


_react2.default.createElement(_reactNative.Text,{style:styles.instructions},'Double tap R on your keyboard to reload,',
'\n','Shake or press menu button for dev menu')));




}}]);return User;}(_react.Component);


var styles=_reactNative.StyleSheet.create({
container:{
flex:1,
justifyContent:'center',
alignItems:'center',
backgroundColor:'#F5FCFF'},

welcome:{
fontSize:20,
textAlign:'center',
margin:10},

instructions:{
textAlign:'center',
color:'#333333',
marginBottom:5}});



module.exports=User;
}, "AwsomeProject/user.js", null, "AwsomeProject/user.js");
__d(/* AwsomeProject/conference/index.js */function(global, require, module, exports) {var _List=require("AwsomeProject/conference/List.js" /* ./List */);var _List2=babelHelpers.interopRequireDefault(_List);

module.exports=_List2.default;
}, "AwsomeProject/conference/index.js", null, "AwsomeProject/conference/index.js");
__d(/* AwsomeProject/conference/List.js */function(global, require, module, exports) {Object.defineProperty(exports,"__esModule",{value:true});var _react=require("react/react.js" /* react */);var _react2=babelHelpers.interopRequireDefault(_react);
var _reactNative=require("react-native/Libraries/react-native/react-native.js" /* react-native */);


这样就能看出两个文件的差异是react-native bundle 命令多了以下内容

__d(/* AwsomeProject/user.js */function(global, require, module, exports) {
    ...code
}, "AwsomeProject/user.js", null, "AwsomeProject/user.js");

说明 react-native bundle 对于一个模块的转换的过程是

1.定义 __d 方法,模块化组件
2.内部代码调用 babel 进行转换
3.以index.ios.js/index.android.js 为入口,找到所有的依赖,全部转换成普通JS。

你可能感兴趣的:(React-Native 服务启动过程)