如何绕过移动应用程序审核,感谢电容器,Ionic和Micro FrontendsðÖ
#javascript #教程 #电容器 #ionic

您是否已经厌倦了将应用程序发送到Apple或Google每次更新时对其进行审查?

在这里我提供了可能的解决方案。解决方案是因为您可以绕过90%的时间甚至更多。

您可能知道,我们有4种构建移动应用程序的方法:

  • 本机 - iOS的一个应用程序,一个用于Android的应用程序,
  • 编译 - 一个代码库编译为用React NativeFlutter编写的2个本机应用程序,例如
  • Hybrid- Web应用程序在移动应用程序内部的Web视图中打开 - 我们构建Web应用程序,然后使用Capacitor例如在本机移动应用程序中运行此Web应用程序,
  • PWA-基本上是一个网络应用程序,托管在网络上,但表现得像移动应用程序(处理离线模式等)。

在本文中,我想展示我们如何使用第三种方法来制作移动应用程序,该应用只需上传到App Store和Google Play,然后就不需要应用程序进行评论一次它在更新时不再了。

当我们更新此类应用程序时,我们可以将新代码部署到任何服务器,云,静态站点托管提供商或任何适合Web应用程序的地方,而不是:

  • 构建整个应用程序,
  • 将其上传到App Store和Google Play,
  • 然后等待新版本的批准。

让我们看看如何完成!

让我们从一些理论开始

我提到,在混合移动应用程序的情况下,我们手头有Capacitor

说实话,我们不仅有电容器,而且还有Cordova哪个电容器所基于的,而且由于电容器更受欢迎,社区更好,更好地解决了一些问题,并且与Ionic Framework合作,我将在第二秒内告诉更多,我将在第二秒内告诉更多问题我只推荐电容器。

和顺便说一句。人们喜欢科尔多瓦(Cordova)像The State of JS 2022或同年的StackOverflow Survey,建议推荐电容器而不是科尔多瓦(Cordova)看起来很合理:

Almost 3/4 of the people calls Cordova "dreadful" according to 2022 StackOverflow Survey

电容器是一个运行时环境,我们可以用它来打开移动本机应用程序内部的Web应用程序。

此外,它还提供了我们可以用来处理本地功能的插件,例如:

  • 地理位置,
  • sharing,
  • 通知,
  • 加速度计,
  • 和许多其他。

有一个很好的草图说明了我从文章中获得的电容器的工作方式:“CapacitorJS: Turn Your Web App into a Mobile App”

Capacitor provides a bridge between web app and native functionality

这对我们意味着什么?

这意味着要使用电容器制作移动应用程序,我们不需要了解如何创建Web应用程序!

处理天然功能的处理归结为使用简单的JavaScript API。例如:

import { Geolocation } from '@capacitor/geolocation';

const printCurrentPosition = async () => {
  const coordinates = await Geolocation.getCurrentPosition();

  console.log('Current position:', coordinates);
};

另外,正如我提到的,我们可以帮助自己使用Ionic-可以基于React,Vue或Angular的框架,并且提供路由,主题,样式,最重要的是 - 许多用于移动组件的内置组件喜欢:

警报:

Alerts

切换:

Toggles

还有许多其他:

Buttons, infinity loaders, cards, refreshers, and many, many others...

所有这些都具有针对iOS和Android的专用样式。

回到主要主题 - 如果我们的应用程序只是一个Web应用程序,它可以以与Web应用程序相同的方式使用微观前端!

因此,我们可以创建2个单独的微观前端(MFS):

  • 第一个消耗第二MF并且无能为力的MF,
  • 和第二个执行其他一切的MF。

然后,第一个MF将由我们构建,并将其上传到App Store和Play商店。

第二个MF将被放置在某些URL之下,例如https://our-awesome-micro-frontend.com,并由第一个MF在运行时检索(每次用户运行我们的应用程序)。

在这种情况下,要更新我们的移动应用程序,我们不需要构建它并每次上传到App Store和Play商店,而只是更新第二个远程MF并将其部署到服务器。第一个MF将在运行时检索第二个版本的最新版本。

但是,Apple和Google基本上可以忽略他们的审核流程吗?

我问自己同样的问题,答案是 - 他们对此都可以。

以这种方式实施的微观前端被视为从外部API检索的任何其他内容。

足够的理论!让我们通过创建我们的基本应用程序和微观前端来检查它,它将消费

对于我今天将要显示的所有内容的代码,您可以检查我的[ionic-module-federation github存储库](https://github.com/robert-orlinski/ionic-module-federation)!

我将使用Ionic脚手架,默认情况下使用电容器(前面提到)。

我将选择反应作为二手框架,但您可以选择Angular或Vue。

如果我选择反应作为二手框架,则Ionic的样板基于Create React App。因此,我想要一个编辑Webpack’s配置的工具,而无需ejecting Cra的配置。

为什么?

所有内容,因为我将使用WebPack的module federation处理我们的微观前端。

如果您不熟悉模块联合会,则可以在YouTube上检查这些视频:

我将使用用于更改我们的WebPack配置的工具是CRACO

让我们去

我们可以创建一个新的目录(让我们称其为ionic-module-federation):

mkdir ionic-module-federation
cd ionic-module-federation

内部我们创建了2个新项目:

  • 1对于我们的基本应用程序,它将上传到App Store和Google Play,
  • 和1用于基本应用程序将消费的微观前端。

要板条,我们可以安装@ionic/cli,然后为两个应用程序运行ionic start

npm install -g @ionic/cli
ionic start

然后,我们通过基本应用的向导:

? Use the app creation wizard? No
? Framework: React
? Project name: host
? Starter template: blank
? Create free Ionic account? No

浏览我选择的选项:

  • 我不想使用应用程序创建向导。如果这样做的话,我将被重定向到Ionic的网站,需要创建一个帐户,并且我的应用程序将自动添加到Appflow’s帐户中。我不需要这些。
  • 我选择反应作为我的JS框架。
  • host是我的基本应用的名称。
  • 我不想要我的基本应用程序的任何入门模板,所以我选择blank
  • 我不想像我在第一点提到的Ionic的帐户。

,然后对我们的基本应用程序消耗的微端端执行相同的操作:

? Use the app creation wizard? No
? Framework: React
? Project name: remote
? Starter template: list
? Create free Ionic account? No

这里唯一的区别是:

  • 名称(当然)。
  • 我正在使用启动模板的事实。在您的情况下可能不是必需的,但是要很好地显示我们的基本应用如何消耗remote应用程序,我将使用它。

然后我们与我们的两个前端项目降落

文件结构看起来像这样:

Very similar to Create React App. Contains additional Capacitor and Ionic configs

它们两个都基于Create React App。

当然,现在我们可以以离子和电容器的方式开发我们的应用程序,让我们开发它们:

  • 创建Web应用程序,
  • 使用电容器插件需要在我们需要的地方处理本地功能,
  • 为iOS和Android构建我们的应用程序,
  • 使用Xcode和Android Studio测试它们,
  • 将它们上传到App Store和Google Play。

但是这些动作不是我现在要采取的行动。您可以在IonicCapacitorAppleGoogle文档中阅读更多有关它们的信息。

现在,我想做微观前端的事情! ð¥³

因此,让我们为两个应用程序配置模块联合会!

正如我提到的,在我们的情况下,这项工作的理想工具是CRACO。它将让我们简单地覆盖Cra的配置而不会弹出。

开始,我们可以为两个应用程序安装它:

cd host
npm i -D @craco/craco

cd remote
npm i -D @craco/craco

您可以使用npm workspacesyarn workspaces安装一次。

,然后创建一个将我们的配置放置的文件。文件必须称为craco.config.js

我们将为两个应用程序创建它-hostremote

对于host,我们的craco.config.js将有这样的内容:

const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const deps = require('./package.json').dependencies;

module.exports = {
  webpack: {
    configure: {
      output: {
        publicPath: 'auto',
      },
    },
    plugins: {
      add: [
        new ModuleFederationPlugin({
          name: 'host',
          remotes: {
            remote: 'remote@http://localhost:3002/remoteEntry.js',
          },
          exposes: {},
          filename: 'remoteEntry.js',
          shared: {
            ...deps,
            react: {
              singleton: true,
              eager: true,
              requiredVersion: deps['react'],
            },
            'react-dom': {
              singleton: true,
              eager: true,
              requiredVersion: deps['react-dom'],
            },
          },
        }),
      ],
    },
  },
};

其中http://localhost:3002是我们的remote应用的地址。

如果您将remote应用程序上传到某些远程服务器,则需要用服务器的URL替换。

fyi-要始终在port 3002上打开remote应用程序,而不是默认的3000 i更改remoteremote的脚本从react-scripts startPORT=3002 react-scripts

反过来,对于remote app craco.config.js将具有此内容:

const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const deps = require('./package.json').dependencies;

module.exports = {
  webpack: {
    configure: {
      output: {
        publicPath: 'auto',
      },
    },
    plugins: {
      add: [
        new ModuleFederationPlugin({
          name: 'remote',
          remotes: {},
          exposes: {
            './App': './src/App',
          },
          filename: 'remoteEntry.js',
          shared: {
            ...deps,
            react: {
              singleton: true,
              eager: true,
              requiredVersion: deps['react'],
            },
            'react-dom': {
              singleton: true,
              eager: true,
              requiredVersion: deps['react-dom'],
            },
          },
        }),
      ],
    },
  },
};

非常相似。唯一的区别是,我们不用在模块联合配置中设置遥控器,而是公开特定的组件。

然后我们可以使用裸露的组件

就我而言,我只暴露了一个组件。这是主要的(App),因为我想基本上消耗整个remote应用程序。

然后,要消耗此组件,我们可以以这种方式将其导入host应用程序中:

import App from 'remote/App';

我将在host/index.tsx文件中进行。

btw。关于.tsx文件 - 默认情况下离子支持打字稿,甚至没有办法用经典JS编写的文件生成样板。

ionic建议,如果由于某种原因不想使用TS,您可以简单地重命名所有文件并从中删除类型注释。

可能您已经发现了当我们导入App时,您已经发现了Typescript的错误:

"Cannot find module" TS error

这是因为模块联合不包含任何类型。

我现在可以通过例如创建名为remote.d.ts的文件在我们的src目录中的文件,并在内部放置代码:

declare module 'remote/App';

但是,当然,这不是一个很好的解决方案。

您可以在我之前提到过几段的5th part of Five Module Federation/Micro-Frontend Mistakes视频中找到更好的解决方案。

现在我们可以运行我们的应用!

但还有一件事。

要使用craco,我们需要更改package.json文件中的startbuildtest命令。

// host:

"scripts": {
  "start": "PORT=3002 craco start",
  "build": "craco build",
  "test": "craco test --transformIgnorePatterns 'node_modules/(?!(@ionic/react|@ionic/react-router|@ionic/core|@stencil/core|ionicons)/)'",
  "eject": "react-scripts eject"
},

// remote: 

"scripts": {
  "start": "craco start",
  "build": "craco build",
  "test": "craco test --transformIgnorePatterns 'node_modules/(?!(@ionic/react|@ionic/react-router|@ionic/core|@stencil/core|ionicons)/)'",
  "eject": "react-scripts eject"
},

然后我们可以运行两个应用程序ð

让我们两次使用npm start-在hostremote应用程序中分别使用:

cd host
npm start

cd remote
npm start

aaaaand让我们在运行时获得此错误:

"Shared module is not available for eager consumption" error

ouch§

幸运的是,没有什么可担心的。

我们只需要创建新文件(让它调整为src/bootstrap.tsx),将src/index.tsx内容复制到其中,然后更改已经复制的src/index.tsx内容到:

import('./bootstrap');

export {};

您可以在本文中阅读有关该主题的更多信息:Module Federation. Advanced API in Webpack 5.0.0-beta.17

现在我们重新启动应用程序。

我们应该在[localhost:3000](http://localhost:3000/)host)和[localhost:3002](http://localhost:3002/)remote)下看到相同的内容:

Our app run inside of the browser

这意味着,我们的模块联合会工作! ð¥³

我们可以更改remote/App组件内部的任何内容,并看到在[localhost:3000](http://localhost:3000/)[localhost:3002](http://localhost:3002/)上可见更改。

这也意味着在将应用程序上传到App Store和Google Play之后,我们将能够跳过移动应用程序的评论!

我们不必编辑host Micro Frontend内部的任何内容(然后将其上传到App Store和Google Play)才能更新我们的移动应用程序。

我们始终只能更新remote Micro Frontend(我们可以在Internet上的某个地方托管的相同),然后在运行时检索它。

请注意 - 只要我们不需要在移动应用中添加任何本地功能,我们就可以做到这一点

如果我们要处理remote中的地理位置,通知或加速度计之类的内容,则必须在hostremote中安装适当的电容器插件,然后同步本机应用程序。

我们可以这样做以显示其工作原理。

让测试sharing native functionality

首先,我们可以在remote应用中添加一些代码,以处理内容共享。

我将在Home组件的末端添加非常简单的button,它将在单击时调用共享:

// ...

    <IonList>
      {messages.map((m) => (
        <MessageListItem key={m.id} message={m} />
      ))}
    </IonList>
  </IonContent>

  <IonButton
    onClick={async () => {
      await Share.share({
        title: 'See cool stuff',
    text: 'Really awesome thing you need to see right meow',
    url: 'http://ionicframework.com/',
    dialogTitle: 'Share with buddies',
      });
    }}
  >
    Share 
  </IonButton>
</IonPage>

// ...

Share是从处理共享功能的@capacitor/share插件导入的:

import { Share } from '@capacitor/share';

当然,必须安装我们的@capacitor/share插件:

# ...in `host` app to take it into consideration while updating native platforms:

cd host
npm install @capacitor/share

# ...and in `remote` app to import it properly and without errors:

cd remote
npm install @capacitor/share

然后我可以构建我们的host应用并为本机平台生成它。

出于测试目的,我将仅在我们的离子项目中添加iOS平台,然后在xcode的内部生成并打开我们的应用程序:

cd remote 
npm start # If we want to run our `host` app after building it and consume our micro frontend, `remote` app has to operate.

cd host
ionic capacitor add ios
npm run build
ionic capacitor sync --no-build # `ionic capacitor sync` builds our app using `react-scripts` so because I use CRACO, I don't use this default build to happen. I build our app using `npm run build` before running `ionic capacitor sync`.
ionic capacitor open ios

运行ionic capacitor open ios后,本机应用程序在xcode内部打开。当我们运行它时,我们会在家庭视图的底部看到我们的按钮:

Our app run inside of the simulator

然后,当我们单击它时,我们可以看到我们的共享菜单:

Our app run inside of the simulator after opening the sharing menu

,如果我们仅在remote应用程序中添加@capacitor/share软件包,那将不起作用。我们还必须将其添加到我们的主要应用程序中 - 即host App。

就是这样!

再次,您可以检查[ionic-module-federation repo](https://github.com/robert-orlinski/ionic-module-federation)在本教程中创建的代码。

以我显示的方式,我们可以随时部署新版本的新版本:

  • 不询问苹果和Google进行评论。
  • 每次不构建和上传我们的应用程序到应用商店和Google Play。

我们只能拥有1个应用程序,该应用程序消耗了远程托管的Micro Frontend。可以随时更改微端口(假设我们不添加任何新的本地功能)。

我希望本教程对您有用!