65.9K
CodeProject 正在变化。 阅读更多。
Home

如何在 React Native 中将外部类和 js 作为包解析

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2022 年 9 月 5 日

CPOL

2分钟阅读

viewsIcon

5106

生成并在 React Native 和 Node.js 中执行完整的外部 js 代码

背景

我构建了一个应用程序,用于解析网站的外部数据,如果您了解这一点,那么您也会知道解析器的更改是持续的,并且我希望避免重新发布我的应用程序,因为用户获取更改需要时间。

因此,我考虑了一种尽可能快地向用户交付更新的方法。

问题是解析器将包含外部数据,以及函数等。

并且使用 EvalFunction,您将无法 import 这些外部数据和库。

我还需要确保即使从外部来源获取数据,数据也是安全的。

在本技巧中,您将学习如何在 react-native 中解析和开发 代码包

Using the Code

您需要的库

1- crypto-js

crypto-js 将加密 js 文件内容。

2- js-base64

js-base64 将加密的字符串转换为 base64。

3- react-native-fs

react-native-fs 仅在创建包时需要。

您需要创建两个应用程序,您的 Android IOS 或 Windows 应用程序和一个 windows/linux 应用程序,以便您可以创建包。

让我们从构建一个将位于名为 myparsers 的文件夹中的 js 文件开始 - 让我们将其命名为 parser.js

当然,myparsers 可以包含多个 js 文件。

// let's say we have the following class that we want to be an external class.
import userClass from '../myobjects'
import http from '../http'
import Parser from '../interfaces'

export default class UserParser implements Parser{
  parserName: string;
  displayName: string;
  constructor(){
   this.parserName = "userParser_1";
   this.displayName = "User Parser";
  }  
  
  async getUsers(){
    const data = await http.fetchJSON("https://xxxx.com/myUsres");
    return data.map(x=> new userClass(x.firstName + x.lastName, x.password, x.userName))
  }
}

现在,对上述类进行更改将需要重新发布整个应用程序。

现在,让我们将这个类更改为 js 类,以便我们可以将其转换为包。

export default (http, userClass)=> {

   function userParser(){
       this.parserName = "userParser_1";
       this.displayName = "User Parser";
   }

   userParser.prototype = {
     getUsers: async function(){
       const data = await http.fetchJSON("https://xxxx.com/myUsres");
       return data.map(x=> new userClass(x.firstName + x.lastName, 
                                         x.password, x.userName))
     }
  }    

  return new userParser();

}

现在我们已经将类转换为可解析的 js 代码,我们将必须构建包,并且要创建包,我们将必须使用 react-native-fs 来访问 js 文件。

import RNFS from 'react-native-fs'
import cryptoJs from 'crypto-js';
import { Base64 } from 'js-base64';

export const enCryptString = async (key: string, str: string) => {
    if (str.startsWith("#"))
        return str;
    str = cryptoJs.AES.encrypt(str, key).toString();
    str = Base64.encode(str);
    return "##" + str;
}

export const deCryptString = async (key: string, str: string) => {
    if (!str.startsWith("#"))
        return str;
    str = str.substring(2);
    str = Base64.decode(str);
    var bytes = cryptoJs.AES.decrypt(str, key);
    return bytes.toString(cryptoJs.enc.Utf8);
}

export class PackageCreator {
 jsFilePath = "D:\\Projects\\myapp\\myparsers";
 packagPath = "D:\\Projects\\myapp-public\\parsers-container\\1.0.0\\mypackage.pkg";
 packageKey = "my.parser.key";
 async getFiles(p?: string){
  console.log("gettings the js files")
  const files = await RNFS.readDir(p || this.jsFilePath);
  const result = [];
  const serach = ".js";
  for (let path of files) {
            if (path.isDirectory()) {
                if (path.path.endsWith(".ts"))
                    continue;
                (await this.getFiles(path.path)).forEach(x => result.push(x));
            }
            else if (path.name.endsWith(serach)) result.push(path.path));
    }
   
   return result;
  }

 async createPackage (){
    const files = await = this.getFiles();
    const contents = [];
    for (const file in files){
        const content = await RNFS.readFile(file, 'utf8')
        const eContent = enCryptString
                         (this.packageKey, content.replace("export default", ""));
        contents.push(eContent)
    }
    
    if (await RNFS.exists(this.packagPath))
       {
           // delete if it already exist
           await RNFS.unlink(this.packagPath);
       }

      await RNFS.writeFile(this.packagPath, contents.join("&"), 'utf8');
  }
}

现在,当在您的外部应用程序中执行上述 createPackage 时,一个名为 mypackage.pkg 的文件将被创建,该文件将包含您需要的所有 js 文件。

对于您的 react-native 应用程序,您需要 fetch 并读取上述创建的包,并将其解析为可用的代码。

所以现在,让我们创建一个类并将其命名为 OnlineParsers

import cryptoJs from 'crypto-js';
import { Base64 } from 'js-base64';
import Parser from '../interfaces'
import UserParser from `../MyParsers`
import userClass from '../myobjects' 
import http from '../http'
export const deCryptString = async (key: string, str: string) => {
    if (!str.startsWith("#"))
        return str;
    str = str.substring(2);
    str = Base64.decode(str);
    var bytes = cryptoJs.AES.decrypt(str, key);
    return bytes.toString(cryptoJs.enc.Utf8);
}

import default class OnlineParsers {
  
  async getParser(){
     try{
     // this URL could also be a public github url, that can contain the package
     const uri = "https://myserver/1.0.0/mypackage.pkg"
     const data = await fetch(uri);
     const textCode = await data.text();
     const parsers = [] as Parser[];
     for(const code in textCode.split("&")){
         const dCode =deCryptString("my.parser.key", dCode); 
         const fn = eval(dCode);   
         parsers.push(fn(http,userClass));
     }
    
     return parsers;

    } catch(e){

     // when the app is offline or maybe the myserver is offline,
     // then load the existing parsers
     return [UserParser(http,userClass)]
    }
   }
} 

现在,您可能会问为什么使用 Eval 而不是 Function,原因很简单。对我来说,使用 eval 运行外部代码速度更快,因为它在当前线程中运行。

使用 Function 会使外部代码运行得慢得多,因为它在外部线程/上下文中运行。

关注点

一种创建外部包代码并在 react-native 中运行它的巧妙方法,这将避免在您知道代码需要持续更改时重新发布您的应用程序。

请告诉我您对这个的看法,因为我没有找到在 react-native 中执行外部脚本的任何方法。

历史

  • 2022 年 9 月 5 日:初始版本
© . All rights reserved.