React-Native 服務(wù)啟動(dòng)過(guò)程

啟動(dòng)React-Native 服務(wù)

執(zhí)行 react-native init AwsomeProject 會(huì)初始化一個(gè)名為AwsomeProject的RN Demo,編譯運(yùn)行就會(huì)自動(dòng)啟動(dòng)一個(gè)服務(wù)。
工程根目錄下面會(huì)有
package.json ,node_modules, ios,android 幾個(gè)文件夾以及 index.ios.js,index.android.js 程序主文件。

package.json 是node的依賴配置文件,node_modules 是 node的所有依賴文件,這些都是開發(fā)時(shí)候使用的,最后運(yùn)行到真機(jī)的時(shí)候,會(huì)從主文件開始,把所有依賴的JS文件,根據(jù)依賴配置打包成一個(gè)bundle文件,拷貝到App的文件夾下去。

先看 ReactNative 服務(wù)啟動(dòng)的過(guò)程

image.png
  • HMR (hot module replace) 模塊熱更新服務(wù)
  • Haste Map(node-haste)用來(lái)轉(zhuǎn)換 JS 到 Module

Package.json 里可以看到服務(wù)的入口文件。

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

cli

image.png

ReactNative 代碼生成 bundle 有兩種方式,一個(gè)是 cli start 命令啟動(dòng)server 實(shí)時(shí)轉(zhuǎn)換。 一個(gè)是 react-native bundle 命令生成。

Server.js

react-native 把所有的命令注冊(cè)在了 commands.js 里, server.js 是cli start的真身,也就是服務(wù)的起點(diǎn)。

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: '',
  }
  ...

同時(shí)我們也找到了服務(wù)啟動(dòng)后控制臺(tái)前兩條輸出的代碼

 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 的作用只是收集參數(shù),做事情的是 runServer.js

runServer.js

runServer 使用 node 的connect 創(chuàng)建了服務(wù),同時(shí)注冊(cè)了一些中間件和webScoket

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

中間件對(duì)各自的請(qǐng)求做了不同的處理。

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

搖一搖模擬器,點(diǎn)擊start systrace,然后stop,彈出的信息和代碼中的是對(duì)應(yīng)的,說(shuō)明systraceProfileMiddleware 就是處理模擬器的systrace調(diào)試信息的中間件

image.png
image.png

再比如說(shuō) getDevToolsMiddleware 開啟了debugger-ui 服務(wù)

image.png

最后一個(gè)中間件是 packagerServer,它負(fù)責(zé)轉(zhuǎn)換RN代碼到j(luò)s,它才是最核心的內(nèi)容。這里只是把參數(shù)傳遞給了ReactPackager ,干活的還在另一個(gè)文件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,
  });
}

關(guān)于 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 代碼轉(zhuǎn)換邏輯

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

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

從Package.json 看到 ReactNative 轉(zhuǎn)換用到的轉(zhuǎn)碼規(guī)則有以下這些

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

按照babel文檔里面的步驟新建一個(gè)轉(zhuǎn)換RN代碼的工程

1.創(chuàng)建工程目錄

image.png

工程名稱為babel-test
src 源碼文件夾
lib 轉(zhuǎn)換后的文件夾

  1. package.json內(nèi)容
{
  "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"http://制作 npm build 命令
  }
}

  1. 執(zhí)行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

  • 在目錄下創(chuàng)建 .babelrc
{
  "presets": ["react-native"]
}

執(zhí)行轉(zhuǎn)換命令 npm build

image.png

轉(zhuǎn)換后的代碼為

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;

下面是執(zhí)行 react-native bundle 命令轉(zhuǎn)換后的代碼。RN轉(zhuǎn)換后的bundle 里require的內(nèi)容都轉(zhuǎn)換成了數(shù)字,為了方便查看,我保留了轉(zhuǎn)換前的路徑。


__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 */);


這樣就能看出兩個(gè)文件的差異是react-native bundle 命令多了以下內(nèi)容

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

說(shuō)明 react-native bundle 對(duì)于一個(gè)模塊的轉(zhuǎn)換的過(guò)程是

1.定義 __d 方法,模塊化組件
2.內(nèi)部代碼調(diào)用 babel 進(jìn)行轉(zhuǎn)換
3.以index.ios.js/index.android.js 為入口,找到所有的依賴,全部轉(zhuǎn)換成普通JS。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容