美文网首页ReactNative
React-Native 服务启动过程

React-Native 服务启动过程

作者: zimo | 来源:发表于2017-08-01 09:53 被阅读1225次

    启动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 (
          <View style={styles.container}>
            <Text style={styles.welcome}>
              Welcome to React Native!
            </Text>
            <Text style={styles.instructions}>
              To get started, edit index.android.js
            </Text>
            <Text style={styles.instructions}>
              Double tap R on your keyboard to reload,{'\n'}
              Shake or press menu button for dev menu
            </Text>
          </View>
        );
      }
    }
    
    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<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||false;descriptor.configurable=true;if("value"in descriptor)descriptor.writable=true;Object.defineProperty(target,descriptor.key,descriptor);}}return function(Constructor,protoProps,staticProps){if(protoProps)defineProperties(Constructor.prototype,protoProps);if(staticProps)defineProperties(Constructor,staticProps);return Constructor;};}();
    
    
    
    
    
    var _react=require('react');var _react2=_interopRequireDefault(_react);
    var _reactNative=require('react-native');
    
    
    
    
    
    var _conference=require('./conference');var _conference2=_interopRequireDefault(_conference);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function");}}function _possibleConstructorReturn(self,call){if(!self){throw new ReferenceError("this hasn't been initialised - super() hasn't been called");}return call&&(typeof call==="object"||typeof call==="function")?call:self;}function _inherits(subClass,superClass){if(typeof superClass!=="function"&&superClass!==null){throw new TypeError("Super expression must either be null or a function, not "+typeof superClass);}subClass.prototype=Object.create(superClass&&superClass.prototype,{constructor:{value:subClass,enumerable:false,writable:true,configurable:true}});if(superClass)Object.setPrototypeOf?Object.setPrototypeOf(subClass,superClass):subClass.__proto__=superClass;}var
    User=function(_Component){_inherits(User,_Component);function User(){_classCallCheck(this,User);return _possibleConstructorReturn(this,(User.__proto__||Object.getPrototypeOf(User)).apply(this,arguments));}_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;
    

    下面是执行 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 服务启动过程

        本文链接:https://www.haomeiwen.com/subject/eaodlxtx.html