美文网首页
React-Native实践

React-Native实践

作者: 面试小集 | 来源:发表于2018-06-03 09:44 被阅读76次

    关于React-Native

    React-Native号称是跨平台开发且具有原生应用的体验,早在两年前我曾经尝试过使用React-Native,但是环境搭建过程及其复杂,可参见我之前写的文章:iOS现有项目手动集成ReactNative。最近公司新开项目需要实现iOS和Android平台热更新方案,以便快速开展业务。本文用来梳理React-Native在iOS上的一些实践。包括如下内容:

    • 搭建一个React-Native项目。
    • 现有iOS项目中集成React-Native模块。
    • 以bundle的形式在真机上运行React-Native。
    • 热更新中bundle的拆分。

    由于React-Native项目一直在更新,不同的版本在使用的过程中会遇到不同的问题,所以强烈建议你使用英文官方文档作为开发参考。

    搭建一个React-Native项目

    根据React-Native的官方文档一路能一路很顺利的搭建成功。我这里简单说一下主要步骤,详细可参考:安装文档

    1. 安装依赖,包括Node、Watchman、React Native命令行界面、Xcode。

      1. 建议使用homebrew安装node和Watchman
      brew install node
      brew install watchman
      
      1. 安装 React Native CLI
      npm install -g react-native-cli
      
      1. Xcode,作为一个iOS开发,就不多说了。
    2. 创建项目

      react-native init AwesomeProject
      
    3. 运行项目

      cd AwesomeProject
      react-native run-ios
      
    4. 如果顺利的话你可以看到你的第一个React-Native项目了。

    集成React Native到现有项目

    同样我建议你参考官方文档,我现有项目是Swift,你可以根据你的项目情况选择对应的安装方法。

    1. 调整项目结构如下,新建iOS目录,将iOS相关的代码都放到iOS目录下。
    2. 在iOS同级目录下创建package.json文件
    {
      "name": "MyReactNativeApp",
      "version": "0.0.1",
      "private": true,
      "scripts": {
        "start": "node node_modules/react-native/local-cli/cli.js start",
        "postinstall": "sed -i '' 's/#import <RCTAnimation\\/RCTValueAnimatedNode.h>/#import <React\\/RCTValueAnimatedNode.h>/' ./node_modules/react-native/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.h; sed -i '' 's/#import <fishhook\\/fishhook.h>/#import <React\\/fishhook.h>/' ./node_modules/react-native/Libraries/WebSocket/RCTReconnectingWebSocket.m"
      },
      "dependencies": {
        "react": "^16.4.0",
        "react-native": "^0.55.4"
      }
    }
    
    
    1. 安装yarn
    brew install yarn
    
    1. 安装React-native
    yarn add react-native
    

    如果出现警告

    warning "react-native@0.52.2" has unmet peer dependency "react@16.2.0".

    则你需要安装React

    yarn add react@version_printed_above // version_printed_above 具体版本号
    
    1. 在iOS目录里添加podfile文件
    # Uncomment the next line to define a global platform for your project
    platform :ios, '9.0'
    
    target 'ReactDemo' do
      # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
      use_frameworks!
    
      # Pods for ReactDemo
    
    pod 'React', :path => '../node_modules/react-native', :subspecs => [
    'Core',
    'CxxBridge', # Include this for RN >= 0.47
    'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43
    'RCTText',
    'RCTNetwork',
    'RCTWebSocket', # needed for debugging
     Add any other subspecs you want to use in your project
    ]
    pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
    
    # Third party deps podspec link
    pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
    pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
    pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
    
    
      target 'ReactDemoTests' do
        inherit! :search_paths
        # Pods for testing
      end
    
      target 'ReactDemoUITests' do
        inherit! :search_paths
        # Pods for testing
      end
    
    end
    
    def fix_cplusplus_header_compiler_error
        filepath = '../node_modules/react-native/React/Base/Surface/SurfaceHostingView/RCTSurfaceSizeMeasureMode.h'
        
        contents = []
        
        file = File.open(filepath, 'r')
        file.each_line do | line |
            contents << line
        end
        file.close
        
        if contents[32].include? "&"
            contents.insert(26, "#ifdef __cplusplus")
            contents[36] = "#endif"
            
            file = File.open(filepath, 'w') do |f|
                f.puts(contents)
            end
        end
    end
    
    def fix_unused_yoga_headers
        filepath = './Pods/Target Support Files/yoga/yoga-umbrella.h'
        
        contents = []
        
        file = File.open(filepath, 'r')
        file.each_line do | line |
            contents << line
        end
        file.close
        
        if contents[12].include? "Utils.h"
            contents.delete_at(15) # #import "YGNode.h"
            contents.delete_at(15) # #import "YGNodePrint.h"
            contents.delete_at(15) # #import "Yoga-internal.h"
            contents.delete_at(12) # #import "Utils.h"
            
            file = File.open(filepath, 'w') do |f|
                f.puts(contents)
            end
        end
    end
    
    def react_native_fix
        fix_cplusplus_header_compiler_error
        fix_unused_yoga_headers
    end
    
    post_install do |installer|
        react_native_fix
    end
    
    

    执行pod install命令。

    1. 在iOS同级目录创建index.ios.js,写入相关内容。
    import React from 'react';
    import {AppRegistry, StyleSheet, Text, View} from 'react-native';
    
    class RNHighScores extends React.Component {
      render() {
        var contents = this.props['scores'].map((score) => (
          <Text key={score.name}>
            {score.name}:{score.value}
            {'\n'}
          </Text>
        ));
        return (
          <View style={styles.container}>
            <Text style={styles.highScoresTitle}>2048 High Scores!</Text>
            <Text style={styles.scores}>{contents}</Text>
          </View>
        );
      }
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#FFFFFF',
      },
      highScoresTitle: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
      },
      scores: {
        textAlign: 'center',
        color: '#333333',
        marginBottom: 5,
      },
    });
    
    // Module name
    AppRegistry.registerComponent('RNHighScores', () => RNHighScores);
    
    1. 在iOS项目中调用React Native模块
    @IBAction func highScoreButtonTapped(sender : UIButton) {
      NSLog("Hello")
      let jsCodeLocation = URL(string: "http://localhost:8081/index.bundle?platform=ios")
      let mockData:NSDictionary = ["scores":
          [
              ["name":"Alex", "value":"42"],
              ["name":"Joel", "value":"10"]
          ]
      ]
    
      let rootView = RCTRootView(
          bundleURL: jsCodeLocation,
          moduleName: "RNHighScores",
          initialProperties: mockData as [NSObject : AnyObject],
          launchOptions: nil
      )
      let vc = UIViewController()
      vc.view = rootView
      self.present(vc, animated: true, completion: nil)
    }
    
    1. 配置info.plist
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>localhost</key>
            <dict>
                <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
                <true/>
            </dict>
        </dict>
    </dict>
    
    1. 启动npm
    npm start
    
    1. 运行
    react-native run-ios
    

    真机运行

    在真机上运行ReactNative项目有两种方式,本文只说第二种

    • 手机和电脑在同一个局域网,将上述代码的localhost改为机器ip。
    • 打包出jsbundle

    在iOS目录创建release_ios文件夹,执行如下命令

    react-native bundle --entry-file index.ios.js --platform ios --dev false --bundle-output release_ios/main.jsbundle --assets-dest release_ios/
    

    会将js打包成main.jsbundle,将图片等输出到asset目录。将这些内容拖入工程,修改加载React-Native代码

    let jsCodeLocation = Bundle.main.url(forResource: "main", withExtension: "jsbundle")
    

    这样就可以直接在真机上运行了。

    热更新bundle的拆分与合并

    这方面的详解我推荐necfol的方案,我就不多说废话了,人家的图那么清晰,还有Demo,我只能推荐了。

    参考

    含泪导入React-native 0.54到Swift原生项目

    相关文章

      网友评论

          本文标题:React-Native实践

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