Skip to content

Commit

Permalink
[优化及bug修改]
Browse files Browse the repository at this point in the history
	1.优化RN控件可视化埋点支持
	2.优化页面自动采集
  • Loading branch information
analysyszhangyufei committed Mar 11, 2021
1 parent 21a68d8 commit 28a4ea5
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 78 deletions.
225 changes: 148 additions & 77 deletions ansHook.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
var path = require("path"),
fs = require("fs"),
dir = path.resolve(__dirname, "..");
var version = "4.5.0"
var reactNativeRenderer6X = [
var version = "4.5.1"

var reactNativeRenderer6X = [
dir + "/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js",
dir + "/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js",
dir + "/react-native/Libraries/Renderer/oss/ReactNativeRenderer-dev.js",
Expand All @@ -22,44 +22,41 @@ var reactNativeRenderer6X = [
dir + "/react-native/Libraries/vendor/react_contrib/interactions/Touchable/Touchable.js", //
dir + "/react-native/Libraries/ReactNative/ReactNativeBaseComponent.js" // 0.11
]

var reactNavigationPath = [
dir + '/@react-navigation/core/src/BaseNavigationContainer.tsx'
]
var ansClickHookCode = `
(function(){
try{
var ReactNative = require('react-native');
var isActiveEvent = false
var ansNativeTag = stateNode._nativeTag
if(registrationName === 'onTouchEnd'&&props && typeof props === 'object'){
if( props.hasOwnProperty('onPress') || props.hasOwnProperty('onClick')||props.hasOwnProperty('onRNCSliderValueChange') || props.hasOwnProperty('onChange') || props.hasOwnProperty('selected')){
isActiveEvent = true
}
}
if (isActiveEvent === true) {
if (registrationName === 'onTouchEnd' && ReactNative.ansModelEventMap &&ReactNative.ansModelEventMap[ansNativeTag]&&ReactNative.ansModelEventMap[ansNativeTag]['isClick'] === true) {
var ReactNative = require('react-native');
var ansModule = ReactNative.NativeModules.RNAnalysysAgentModule
ansModule && ansModule.viewClicked && ansModule.viewClicked(ansNativeTag);
if( ReactNative.ansModelEventMap[ansNativeTag]['route']){
var route = ReactNative.ansModelEventMap[ansNativeTag]['route'];
ansModule && ansModule.pageViewWithArgsAuto && ansModule.pageViewWithArgsAuto(route.title,{'$url':route.url})
}
}
}catch(e){}
}catch(e){
}
})()
/* ANALYSYSAGENT HOOK */
`
var ansEventMapHookCode = `
try {
var ReactNative = require('react-native');
if(!ReactNative['ansModelEventMap']){
ReactNative.ansModelEventMap = {}
ReactNative.ansScreenTimer = null
}
var ansModelEventMap = ReactNative.ansModelEventMap
var ansScreenTimer = ReactNative.ansScreenTimer
var ansModule = ReactNative.NativeModules.RNAnalysysAgentModule
var ansTag = tag
(function(){
try {
var ReactNative = require('react-native');
if(!ReactNative['ansModelEventMap']){
ReactNative.ansModelEventMap = {}
ReactNative.ansScreenTimer = null
}
var ansModelEventMap = ReactNative.ansModelEventMap
var ansScreenTimer = ReactNative.ansScreenTimer
var ansModule = ReactNative.NativeModules.RNAnalysysAgentModule
var ansTag = tag
var eventStatus = false
if(props && typeof props === 'object'){
if( props.hasOwnProperty('onPress') || props.hasOwnProperty('onClick')||props.hasOwnProperty('onRNCSliderValueChange') || props.hasOwnProperty('onChange') || props.hasOwnProperty('selected')){
if( props.hasOwnProperty('onPress') || props.hasOwnProperty('onClick')||props.hasOwnProperty('onRNCSliderValueChange') || props.hasOwnProperty('onChange') ){
eventStatus = true
}
}
Expand All @@ -70,43 +67,85 @@ var ansEventMapHookCode = `
}
}catch (e) {}
try{
if (props.pendingProps.enabled === true || props.pendingProps.onSelect){
if(props.return.type.propTypes.hasOwnProperty('onPress')||props.return.type.propTypes.hasOwnProperty('onClick')||props.return.type.propTypes.hasOwnProperty('onSelect')){
eventStatus = true
}
}catch (e) {}
try{
if(props.return.type.propTypes.hasOwnProperty('onPress')){
if(props.pendingProps.hasOwnProperty('onClick')||props.pendingProps.hasOwnProperty('onPress')){
eventStatus = true
}
}catch (e) {}
if (!ansModelEventMap[ansTag] || (ansModelEventMap[ansTag] && !ansModelEventMap[ansTag].isClick)) {
try{
var InstanceTag = getInstanceFromTag(ansTag)
var ansInstance = InstanceTag.return.return._debugOwner.pendingProps
if(ansInstance.hasOwnProperty('onPress')||ansInstance.hasOwnProperty('onClick')||ansInstance.hasOwnProperty('onSelect')){
eventStatus = true
}
}catch(e){
ansModelEventMap[ansTag] = {
'isClick': eventStatus
};
}
if(!ansModelEventMap[ansTag]||(ansModelEventMap[ansTag]&&!ansModelEventMap[ansTag]['isClick'])){
ansModelEventMap[ansTag] = {
'isClick': eventStatus
};
}
clearTimeout(ReactNative.ansScreenTimer)
ReactNative.ansScreenTimer = setTimeout(function(){
try{
clearTimeout(ReactNative.ansScreenTimer)
ansModule.setViewClickableMap(Object.assign({}, ansModelEventMap))
}catch(e){
}
},300)
} catch (e) {
}
var InstanceTag = getInstanceFromTag(ansTag)
if(InstanceTag && InstanceTag.return && InstanceTag.return.return && InstanceTag.return.return._debugOwner && InstanceTag.return.return._debugOwner.pendingProps && InstanceTag.return.return._debugOwner.pendingProps.to ){
var pendingProps = InstanceTag.return.return._debugOwner.pendingProps
ansModelEventMap[ansTag] = {
'isClick': true,
'route':{
title:pendingProps.route.name || '',
url:pendingProps.to || ''
})()
/* ANALYSYSAGENT HOOK */
`
var ansNavigation5HookCode = `
(function(){
try{
function ansGetParams (state: any): any {
if (!state) {
return {};
}
var ansRoute = state.routes[state.index];
var params = ansRoute.params||{};
if (ansRoute.state) {
var stateParams = ansGetParams(ansRoute.state);
if (stateParams) {
params = stateParams||{};
}
}
return params||{};
}
clearTimeout(ReactNative.ansScreenTimer)
ReactNative.ansScreenTimer = setTimeout(function(){
try{
clearTimeout(ReactNative.ansScreenTimer)
ansModule.setViewClickableMap(Object.assign({}, ansModelEventMap))
}catch(e){}
},300)
} catch (e) {}
/* ANALYSYSAGENT HOOK */
function ansGetPageViewParam (ansRootState: any): void {
let ansParam = {}
if (ansRootState) {
let ansRoute = ansRootState.routes[ansRootState.index];
if (ansRoute.name === 'Root') {
return ansGetPageViewParam(ansRoute.state)
}
let ansParam = ansGetParams(ansRootState);
while (ansRootState.routes[ansRootState.index].state !== undefined) {
ansRootState = ansRootState.routes[ansRootState.index].state as NavigationState;
}
var ansUrl = ansRootState.routes[ansRootState.index].name;
ansParam['$url'] = ansUrl
return ansParam
} else {
return{}
}
}
var ansPageviewParam = ansGetPageViewParam(getRootState())
var ReactNative = require('react-native');
var ansModule = ReactNative.NativeModules.RNAnalysysAgentModule
ansModule && ansModule.pageViewWithArgsAuto && ansModule.pageViewWithArgsAuto(ansPageviewParam.$url,ansPageviewParam)
}catch(e){}
})()
/* ANALYSYSAGENT HOOK */
`

var ansCheckClickCode = [
{
code: "inst = getFiberCurrentPropsFromNode(inst);",
Expand All @@ -126,36 +165,31 @@ var ansCheckClickCode = [
{
code: "var props = EventPluginUtils_1.getFiberCurrentPropsFromNode(stateNode);",
rep: [],
tag: [],
status: true
tag: []
},
{
code: "const props = EventPluginUtils.getFiberCurrentPropsFromNode(stateNode);",
rep: [],
tag: [],
status: true
tag: []

},
{
code: `const props = EventPluginUtils.getFiberCurrentPropsFromNode(
inst.stateNode
);`,
rep: ["inst.stateNode"],
tag: ["stateNode"],
status: true
tag: ["stateNode"]
},
{
code: `var bankForRegistrationName = listenerBank[registrationName];
var key = getDictionaryKey(inst);`,
rep: ["inst", "bankForRegistrationName[key]"],
tag: ["stateNode", "props"],
status: true
tag: ["stateNode", "props"]
},
{
code: "this.touchableHandlePress(e);",
rep: ["ReactNative.findNodeHandle(this)", "'onTouchEnd'", "this.props"],
tag: ["stateNode._nativeTag", "registrationName", "props"],
status: true
tag: ["stateNode._nativeTag", "registrationName", "props"]
}
]
var ansCheckEventMapCode = [
Expand Down Expand Up @@ -234,6 +268,15 @@ var ansCheckEventMapCode = [
tag: ["stateNode._nativeTag", 'getInstanceFromTag\(ansTag\)']
}
]
var ansNavigationCode = [
{
code: `if (!isFirstMountRef.current && onStateChangeRef.current) {
onStateChangeRef.current(getRootState());
}`,
rep: [],
tag: []
}
]
var substringCode = function (fileContent, scriptStr, hookCode) {
// 获取 hook 的代码插入的位置
var hookIndex = fileContent.indexOf(scriptStr);
Expand All @@ -255,15 +298,21 @@ var substringCode = function (fileContent, scriptStr, hookCode) {
// var hookedContent = `${fileContent.substring(0, hookIndex + scriptStr.length)} \n${hookCode} \n${fileContent.substring(hookIndex + scriptStr.length)} `;
return hookedContent
}

var ansReplaceCode = function (fileCode, replaceCode, hookCode, type) {
var firstSubstringCode = function (fileContent, scriptStr, hookCode) {
// 获取 hook 的代码插入的位置
var hookIndex = fileContent.indexOf(scriptStr);
// 判断文件是否异常,不存在 touchableHandlePress 方法,导致无法 hook 点击事件
if (hookIndex == -1) {
throw "Can't not find updateFiberProps function";
};
// 插入 hook 代码
var hookedContent = `${fileContent.substring(0, hookIndex)} \n${hookCode} \n${scriptStr} ${fileContent.substring(hookIndex + scriptStr.length)} `;
return hookedContent
}
var ansReplaceCode = function (fileCode, replaceCode, hookCode) {
var code = replaceCode.code
var req = replaceCode.rep
var tag = replaceCode.tag
var status = replaceCode.status
if (clickStatus === false && status === true) {
return fileCode
}
if (fileCode.indexOf(code) > -1) {
if (req.length > 0) {
for (var i = 0; i < req.length; i++) {
Expand All @@ -285,31 +334,48 @@ var ansReplaceCode = function (fileCode, replaceCode, hookCode, type) {
}

fileCode = substringCode(fileCode, code, hookCode)
if (type === 'click') {
clickStatus = false
}
}
return fileCode
}
var clickStatus = true

var ansClickHookFN = function (filePaths) {

filePaths.forEach(function (filePath) {
if (fs.existsSync(filePath)) {
// 读取文件内容
var fileContent = fs.readFileSync(filePath, 'utf8');
// 已经 hook 过了,不需要再次 hook
if (fileContent.indexOf('ANALYSYSAGENT HOOK') > -1) {
return;
}
for (var i = 0; i < ansCheckClickCode.length; i++) {
fileContent = ansReplaceCode(fileContent, ansCheckClickCode[i], ansClickHookCode, 'click')
fileContent = ansReplaceCode(fileContent, ansCheckClickCode[i], ansClickHookCode)
}
for (var y = 0; y < ansCheckEventMapCode.length; y++) {
fileContent = ansReplaceCode(fileContent, ansCheckEventMapCode[y], ansEventMapHookCode)
}
// 备份源文件
// 备份 filePath 源文件
fs.renameSync(filePath, `${filePath}_analysysagent_backup`);
// 重写 filePath 文件
fs.writeFileSync(filePath, fileContent, 'utf8');
console.log(`found and modify file : ${filePath} `);
}
})
}
var ansNavigationHookFN = function (filePaths, checkList, code) {
filePaths.forEach(function (filePath) {
if (fs.existsSync(filePath)) {
// 读取文件内容
var fileContent = fs.readFileSync(filePath, 'utf8');
// 已经 hook 过了,不需要再次 hook
if (fileContent.indexOf('ANALYSYSAGENT HOOK') > -1) {
return;
}
for (var i = 0; i < checkList.length; i++) {
fileContent = ansReplaceCode(fileContent, checkList[i], code)
}
// 备份 filePath 源文件
fs.renameSync(filePath, `${filePath}_analysysagent_backup`);
// 重写文件
// 重写 filePath 文件
fs.writeFileSync(filePath, fileContent, 'utf8');
console.log(`found and modify file : ${filePath} `);
}
Expand All @@ -318,11 +384,12 @@ var ansClickHookFN = function (filePaths) {
var analysysAgentResetFN = function (resetFilePaths) {
resetFilePaths.forEach(function (resetFilePath) {

// 判断文件是否存在
// 判断需要被恢复的文件是否存在
if (!fs.existsSync(resetFilePath)) {
return;
}
var fileContent = fs.readFileSync(resetFilePath, 'utf8');
// 未被 hook 过代码,不需要处理
if (fileContent.indexOf('ANALYSYSAGENT HOOK') == -1) {
return;
}
Expand All @@ -331,17 +398,21 @@ var analysysAgentResetFN = function (resetFilePaths) {
if (!fs.existsSync(backFilePath)) {
throw `File: ${backFilePath} not found, Please rm - rf node_modules and npm install again`;
}
// 将备份文件重命名恢复 + 自动覆盖被
// 将备份文件重命名恢复 + 自动覆盖被 hook 过的同名 Touchable.js 文件
fs.renameSync(backFilePath, resetFilePath);
console.log(`found and reset file: ${resetFilePath} `);
})
};
// 全部文件恢复
// 全部 hook 文件恢复
var resetAllAnalysysAgentFN = function () {
analysysAgentResetFN(reactNativeRenderer6X)
analysysAgentResetFN(reactNavigationPath)
// analysysAgentResetFN(reactNativeRenderer5X)
}
var allAnalysysAgentHookFN = function () {
ansClickHookFN(reactNativeRenderer6X)
ansNavigationHookFN(reactNavigationPath, ansNavigationCode, ansNavigation5HookCode)
// ansClickHookFN(reactNativeRenderer5X, 5)
}
switch (process.argv[2]) {
case '-run':
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-analysys",
"version": "4.5.0",
"version": "4.5.1",
"private": false,
"description": "易观方舟React Native插件",
"main": "index.js",
Expand Down

0 comments on commit 28a4ea5

Please sign in to comment.