如何在 React Native 中将外部类和 js 作为包解析
生成并在 React Native 和 Node.js 中执行完整的外部 js 代码
背景
我构建了一个应用程序,用于解析网站的外部数据,如果您了解这一点,那么您也会知道解析器的更改是持续的,并且我希望避免重新发布我的应用程序,因为用户获取更改需要时间。
因此,我考虑了一种尽可能快地向用户交付更新的方法。
问题是解析器将包含外部数据,以及函数等。
并且使用 Eval
或 Function
,您将无法 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 日:初始版本