热更新:CodePush、react-native-pushy、

2019-08-28  本文已影响0人  意一ineyee

目录

一. 安装并注册CodePush
 1. CodePush是什么
 2. 安装并注册CodePush
二. 为我们的项目部署CodePush
 1. 在CodePush服务器注册我们的App
 2. 在我们的项目中集成CodePush SDK
三. 使用CodePush实现热更新
 1. 制定热更新策略
 2. 最常用的更新策略实现
 3. 把JS包挂到CodePush服务器上
 4. 打ipa包发布到AppStore
 5. App上线后,假设我们又修改了RN的代码,进行热更新
四. 其它热更新方案

一. 安装并注册CodePush


1. CodePush是什么

CodePush是微软提供的一个中央仓库,我们可以把最新的JS包挂在它的服务器上(如更改了JS、HTML、CSS、图片资源等),而客户端则可以从它的服务器上加载到最新的JS包,它开源了RN版本——react-native-code-push

2. 安装并注册CodePush

使用CodePush之前首先要安装CodePush客户端,在终端输入npm install -g code-push-cli指令,就可以安装了。

安装结束后,输入code-push -v指令,若有版本号输出,则代表安装成功。

然后我们需要注册一个CodePush账号。在终端输入code-push register指令,会打开如下注册页面让你选择授权账号。

授权通过之后,CodePush会告诉你“token”,复制此“token”到终端即可完成注册,并且帮你自动登录进去了。

登录成功后,你的session文件将会写在/Users/你的用户名/.code-push.config下。

CodePush相关命令

code-push login:登录
code-push loout:注销
code-push access-key ls:列出登陆的token
code-push access-key rm <accessKye>:删除某个 access-key

二. 为我们的项目部署CodePush


1. 在CodePush服务器注册我们的App

为了让CodePush服务器知道我们的App,我们需要向它注册一下,在终端输入code-push app add <appName> <os> <platform>指令即可完成注册。注册完成之后会返回一套deployment key,该key在后面步骤中会用到,可以先复制下来。

注意:如果我们的应用分为Android和iOS版,那么在向CodePush注册App的时候需要注册两个App获取两套deployment key,如:

code-push app add GitHub_RN_iOS ios react-native
code-push app add GitHub_RN_Android android react-native

CodePush相关命令

code-push app add:在账号里面添加一个新的App
code-push app remove:在账号里移除一个App
code-push app rename: 重命名一个存在的App
code-push app list:列出账号下面的所有App
code-push app transfer:把App的所有权转移到另外一个账号

2. 在我们的项目中集成CodePush SDK

第一步:打开我们的RN项目,集成react-native-code-push组件。

yarn add react-native-code-push

第二步:终端cd到我们的RN项目下,运行react-native link react-native-code-push,这条命令将会自动帮我们在iOS项目中添加好CodePush相关的设置。

在终端运行此命令之后,终端会提示让你输入deployment key,你只需按提示,输入你生产环境下的key,当然如果你不想输入,也可以直接回车跳过,而是选择在项目中自己配置。

在项目中自己配置deployment key

我们可以通过code-push deployment ls appName -k命令来查看deployment key

这样就完成了我们RN项目和iOS项目的CodePush部署,接下来我们可以使用CodePush来实现热更新了。

三. 使用CodePush实现热更新


1. 制定热更新策略

在使用CodePush实现热更新之前,我们需要根据自己的实际需求来制定一下App热更新的策略,即:

AppState.addEventListener("change", (newState) => {
    newState === "active" && codePush.sync();
});

2. 最常用的更新策略实现

最常用的更新策略就是,APP一启动就检查是否有更新,然后根据更新的必要程度由我们决定是否强制用户更新,通常情况下我们选择更新后立马重启App把更新呈现给用户,接下来我们简单实现一下。

我们在App根组件中来做是否有更新的检测。

// DynamicBottomNavigator.js

import CodePush from "react-native-code-push";
import CodePushPage from "../Controller/Mine/CodePushPage";

// staging:'Hqspd-sYzTo-FrAfpR7py9P0pBnF7627d37f-ad25-47e2-a0ef-8a9b98f656cc'
// release:'sMIGG-ocNzGwkB2ZoxpnT1Sr62-47627d37f-ad25-47e2-a0ef-8a9b98f656cc'
const CODEPUSH_KEY = 'sMIGG-ocNzGwkB2ZoxpnT1Sr62-47627d37f-ad25-47e2-a0ef-8a9b98f656cc';


class DynamicBottomNavigatorClass extends Component {
    constructor(props) {
        super(props);

        this.state = {
            // 检测是否有更新
            update: false,
        };
    }

    render() {
        return (
            <View style={{flex: 1}}>
                <DynamicBottomNavigatorContainer
                    // 切换tab会触发
                    onNavigationStateChange={(preState, newState, action) => {
                        // 发出通知
                        DeviceEventEmitter.emit(NotificationName.DID_CHANGE_TAB, {
                            preState,
                            newState,
                        })
                    }}
                />

                {/*是否modal出更新提示界面*/}
                {this.state.update ? <CodePushPage/> : null}
            </View>
        )
    }

    componentDidMount() {
        // 检测是否有更新
        CodePush.checkForUpdate(CODEPUSH_KEY)
            .then((update) => {
                this.setState({update});
            });
    }
}

export default DynamicBottomNavigatorClass;

在这里我们封装了一个CodePushPage的组件来专门处理更新的逻辑。

import React, {Component} from 'react';
import {
    StyleSheet,
    View,
    Text,
    Modal,
} from 'react-native';

import CodePush from "react-native-code-push";
import ProjectSingleton from "../../Project/ProjectSingleton";
import * as Progress from 'react-native-progress';

export default class CodePushPage extends Component {
    /*-----------生命周期方法-----------*/

    constructor(props) {
        super(props);

        this.state = {
            // 状态提示文本
            syncMessage: '',
            // 因为我们并非纯自定义更新提示的那一套额,而是在CodePush.sync那个弹出框的下面又塞了一个这样的CodePushPage,
            // 所以有些界面的逻辑需要通过一些属性来控制一下,而这些界面逻辑正好可以由CodePush的状态来控制
            showUpdateView: false,
            // 下载进度
            currentProgress: 0,
            currentProgressPercent: '',
        };
    }

    render() {
        return (
            this._genContent()
        );
    }

    componentDidMount() {
        this.syncImmediate();
    }


    /*-----------CodePush相关方法-----------*/

    // syncImmediate
    syncImmediate() {
        // CodePush会通过该方法帮我们自动完成检查更新、弹出更新提示框、下载、安装、是否立即重启App呈现更新等一系列操作。
        CodePush.sync({
                // 弹出更新提示框
                updateDialog: {
                    // 标题
                    title: '发现新版本',
                    // 是否显示更新description,默认false
                    appendReleaseDescription: true,
                    // 更新description的前缀
                    descriptionPrefix: '\n更新内容:\n',

                    // 强制更新的message
                    mandatoryUpdateMessage: '',
                    // 强制更新的按钮文本
                    mandatoryContinueButtonLabel: '立即更新',

                    // 非强制更新的message
                    optionalUpdateMessage: '',
                    // 非强制更新的取消按钮文本
                    optionalIgnoreButtonLabel: '残忍拒绝',
                    // 非强制更新的更新按钮文本
                    optionalInstallButtonLabel: '立即更新',
                },

                // 立即重启App呈现更新
                installMode: CodePush.InstallMode.IMMEDIATE,
            },

            // CodePush状态的变化
            this.codePushStatusDidChange.bind(this),
            // CodePush下载进度的检测
            this.codePushDownloadDidProgress.bind(this)
        );
    }

    // CodePush状态的变化
    codePushStatusDidChange(syncStatus) {
        switch (syncStatus) {
            case CodePush.SyncStatus.CHECKING_FOR_UPDATE:
                this.setState({syncMessage: "检测更新中...", showUpdateView: false});
                break;
            case CodePush.SyncStatus.AWAITING_USER_ACTION:
                this.setState({syncMessage: "请做出你的选择...", showUpdateView: false});
                break;
            case CodePush.SyncStatus.DOWNLOADING_PACKAGE:
                this.setState({syncMessage: "下载更新中...", showUpdateView: true});
                break;
            case CodePush.SyncStatus.INSTALLING_UPDATE:
                this.setState({syncMessage: "安装更新中...", showUpdateView: true});
                break;
            case CodePush.SyncStatus.UP_TO_DATE:
                this.setState({syncMessage: "已经是最新的了...", showUpdateView: false});
                break;
            case CodePush.SyncStatus.UPDATE_IGNORED:
                this.setState({syncMessage: "您选择了残忍拒绝", showUpdateView: false});
                break;
            case CodePush.SyncStatus.UPDATE_INSTALLED:
                this.setState({syncMessage: "您选择了立即更新", showUpdateView: false});
                break;
            case CodePush.SyncStatus.UNKNOWN_ERROR:
                this.setState({syncMessage: "出错啦...", showUpdateView: false});
                break;
        }
    }

    // CodePush下载进度的检测
    codePushDownloadDidProgress(progress) {
        // number转小数,并四舍五入保留指定位数
        const currentProgress = (parseFloat(progress.receivedBytes / progress.totalBytes)).toFixed(2);
        const currentProgressPercent = progress.receivedBytes + '/' + progress.totalBytes + ' bytes';
        this.setState({
            currentProgress,
            currentProgressPercent,
        });
    }


    /*-----------私有方法-----------*/

    _genContent() {
        return(
            this.state.showUpdateView ? (
                <Modal
                    // 背景层是否显示
                    visible={true}
                    // 背景层显示和消失时的动画效果
                    animationType={'fade'}
                    // 背景层是否透明
                    transparent={true}
                    // 在安卓上用户按下设备的后退按键时触发,该属性在安卓设备上为必填,且会在modal处于开启状态时阻止BackHandler事件
                    onRequestClose={() => {

                    }}
                >
                    <View style={styles.container}>
                        <Progress.Bar
                            style={styles.progress}
                            progress={this.state.currentProgress}
                            indeterminate={false}
                            color={'white'}
                        />
                        <View style={{flexDirection: 'row'}}>
                            <Text style={[styles.text, {textAlign: 'right'}]}>{this.state.syncMessage}</Text>
                            <Text style={[styles.text, {textAlign: 'left'}]}>{this.state.currentProgressPercent}</Text>
                        </View>
                    </View>
                </Modal>
            ) : null
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
        backgroundColor: 'rgba(0, 0, 0, 0.4)',
    },
    progress: {
        margin: 10,
    },
    text: {
        marginTop: 30,
        color: 'white',
    },
});

不要看着代码不少,耐心去看,很简单的!这样我们就完成了CodePush热更新的代码,确实很简单!

3. 把JS包挂到CodePush服务器上

编写完了CodePush热更新的代码,我们的App就算正是开发完毕,准备打包上线了。那么此时,App第一次编写完,我们需要把JS包挂到CodePush服务器上。

挂包的方式也很简单,只需要在终端cd到我们的RN项目下,执行

code-push release-react AppName ios --t 1.0.0 --dev false --d Production --des "init" --m false指令即可,该指令会自动帮我们打出RN项目的JS包并上传到CodePush服务器上去。

指令参数介绍:

  • App的名字。
  • App的平台。
  • --t:App的版本号,要和iOS或安卓项目的版本号一致。
  • --dev:是否启用开发者模式,默认为false
  • --d:发布更新的环境是Production(生产)还是Staging(开发),默认为Staging
  • --des:更新说明。
  • --m:本次更新是否要强制更新,默认为false

上传完后,可以通过code-push deployment ls appName指令,来查看我们针对名为“ GitHub_RN”的App发布过的JS包。

4. 打ipa包发布到AppStore

我们把JS包挂到CodePush服务器后,就可以打ipa包发布到AppStore了。

5. App上线后,假设我们又修改了RN的代码,进行热更新

App上线后,假设我们做了某些更新,就只需要把最新的JS包重新往CodePush服务器挂一下即可,当然挂的时候注意此次更新是否要强制。

比如,我们这里要把最热模块的标题改为“最热哈哈哈”,而且强制更新,那么修改完RN的代码后,挂JS包的指令就是code-push release-react GitHub_RN ios --t 1.0.0 --dev false --d Production --des "修改最热模块的标题" --m true,当我们重新打开App的时候就会检测到更新了。

这就完成热更新了!但是使用CodePush进行热更新也有一些缺点:

四. 其它热更新方案


我们还可以使用RN中文网提供的react-native-pushy组件来实现热更新,这个可以设置淘宝镜像源,访问速度的问题可以得到解决。

当然我们也可以自己搭建服务器来实现热更新或者增量热更新,可以搜索相应的文章来学习。

上一篇下一篇

猜你喜欢

热点阅读