前言
RN代码正在如火如荼的重构,在此之前,发现RN代码有很多不合理的地方。
不合理
工程里有很多自己的组件库,一一对应着不同的功能,例如登录,支付等等。在这之前,为了实现组件注册,使用标签的方式来对组件进行注册,这样的做法导致,自己的组件需要依赖RN自己的component的生命周期,而且写法上也不是很舒服。
生成代码的网址:ts-ast-viewer.com/
重构
现在代码重构,使用最新版本的ts,配合metro-config来进行组件注册和事件分发。
以下代码只是一个demo,整体逻辑就是将所有组件的相关初始化注入到service.tsx的init方法中
// decorator.tsx
import EvenStore from './eventStore';
export const component = (title: string): ClassDecorator => {
return (target: any) => {
target.prototype.title = title
}
}
export const event = (event: string) => {
return (target: any, key: string, descriptor: PropertyDescriptor) => {
EvenStore.addEvent(event, descriptor.value);
}
}
// testa.tsx
import { component, event } from "./decorator";
@component('hello')
export class Testa {
@event('aaa')
inita(params: string) {
console.log(`bbbb ${params}`);
}
a() {
}
}
// service.tsx
export class Service {
init() {
}
}
// metro.config.js
const fs = require('fs-extra');
const ts = require('typescript')
const moduleDir = "./node_modules";
// 组件装饰器
const componentDecoratorName = "component";
module.exports = {
reporter: {
update: (event) => {
// 打印日志(后续可以优化打印格式)
if (event.type === 'client_log') {
event.data.forEach(data => {
console.log(data);
});
return;
}
if (event.type === 'dep_graph_loading') {
// 初始化组件服务文件
const path = "service.tsx";
const sourceFile = ts.createSourceFile(path, fs.readFileSync(path).toString(), ts.ScriptTarget.ES2015, true);
// 清理缓存语法
// 删除import语句
sourceFile.statements = sourceFile.statements.filter(item => {
return item.kind !== ts.SyntaxKind.ImportDeclaration
});
// 删除init方法中的语句
sourceFile.statements[sourceFile.statements.length - 1].members[0].body.statements.length = 0;
// 组件格式 ./node_modules/ts-rn-xxxx/xxxxComponent.tsx(暂定)
// 遍历模块文件夹,找出自己相关组件
const moduleNames = fs.readdirSync(moduleDir).filter(item => {
return item.startsWith("ts-rn-");
});
moduleNames.forEach(moduleName => {
// 找出模块下所有组件
const componentDir = `${moduleDir}/${moduleName}`;
const componentFileNames = fs.readdirSync(componentDir).filter(item => {
return item.endsWith("Component.tsx");
});
componentFileNames.forEach(componentFileName => {
// 生成语句
const { importNode, componentObjNode, componentInitNode } = generateStatement(componentFileName, moduleName);
sourceFile.statements = [importNode, ...sourceFile.statements];
sourceFile.statements[sourceFile.statements.length - 1].members[0].body.statements.push(componentObjNode);
sourceFile.statements[sourceFile.statements.length - 1].members[0].body.statements.push(componentInitNode);
});
});
const result = ts.createPrinter().printFile(sourceFile);
// 写入文件
fs.writeFileSync(path, result);
}
}
},
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
}
};
/**
* 生成语句
*/
generateStatement = (componentFileName, moduleName) => {
// 解析组件文件
const result = parseComponentFile(`${moduleName}/${componentFileName}.tsx`);
// 不是组件跳过
if (!result) {
return;
}
// 组件名规则:首字母大写,和文件名保持一致
const componentName = componentFileName[0].toUpperCase() + componentFileName.substr(1);
// 导入组件
const importNode = ts.factory.createImportDeclaration(
undefined,
ts.factory.createImportClause(false, undefined,
ts.factory.createNamedImports([ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(componentName))])
),
ts.factory.createStringLiteral(`${moduleName}/${componentFileName}`),
undefined
);
// 创建组件对象
const componentObjNode = ts.factory.createVariableStatement(
undefined,
ts.factory.createVariableDeclarationList(
[ts.factory.createVariableDeclaration(
ts.factory.createIdentifier(componentFileName),
undefined,
undefined,
ts.factory.createNewExpression(ts.factory.createIdentifier(componentName), undefined, [])
)],
ts.NodeFlags.Let
)
);
// 组件初始化代码
// 存放组件对象
const componentInitNode = ts.factory.createExpressionStatement(ts.factory.createCallExpression(
ts.factory.createPropertyAccessExpression(
ts.factory.createIdentifier("console"),
ts.factory.createIdentifier("log")
),
undefined,
[ts.factory.createPropertyAccessExpression(
ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(componentName), ts.factory.createIdentifier("prototype")),
ts.factory.createIdentifier("title")
)]
));
return { importNode: importNode, componentObjNode: componentObjNode, componentInitNode: componentInitNode };
}
/**
* 解析组件文件
*/
parseComponentFile = (componentFile) => {
const sourceFile = ts.createSourceFile(componentFile, fs.readFileSync(componentFile).toString(), ts.ScriptTarget.ES2015, true);
// 过滤类
const classStatement = sourceFile.statements.filter(item => {
return item.kind === ts.SyntaxKind.ClassDeclaration;
});
// 组件文件规定只能有一个类
const classNode = classStatement[0];
// 查找类的装饰器是否是组件装饰器
const classDecorator = ts.getDecorators(classNode);
if (!classDecorator) {
return false;
}
const classDecoratorName = classDecorator[0].expression.expression.escapedText;
if (componentDecoratorName !== classDecoratorName) {
return false;
}
return true;
}