整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          GitHub和碼云上,7個h5頁面制作工具推薦

          GitHub和碼云上,7個h5頁面制作工具推薦

          一、ymm-tech/gods-pen

          star: 1.7k

          watch: 270

          fork: 557

          碼良是一個在線生成H5頁面并提供頁面管理和頁面編輯的平臺,用于快速制作H5頁面。用戶無需掌握復雜的編程技術,通過簡單拖拽、少量配置即可制作精美的頁面,可用于營銷場景下的頁面制作。同時,也為開發者提供了完備的編程接入能力,通過腳本和組件的形式獲得強大的組件行為和交互控制能力。

          支持功能

          1.編輯器

          • 面向不同角色切換工作臺,并可以自行拖拽
          • 組件,以及組合組件
          • 實時預覽的動畫編輯
          • 實時預覽,并可按幀拖動預覽的動畫編輯
          • 腳本功能,靈活的擴展編輯器面板的屬性(非常容易給組件額外添加自定義的屬性和方法,完全自己代碼控制)
          • 樣式編輯,基礎模式,代碼模式。方便開發和運營不同角色使用
          • 在線預覽
          • 二維碼預覽
          • 可導入psd
          • 支持pc端和移動端

          2.組件商城

          • 25+組件,也歡迎開發者貢獻自己的組件,查看所有組件
          • 可上傳,下載自己或別人的組件
          • 可出售自己開發的組件
          • 可購買別人的組件

          3.后臺管理與數據

          • 可按團隊管理頁面 類似gitlab管理項目。合理的權限管理
          • 表單數據收集,并有數據同居
          • 支持頁面的pv uv以及自定義埋點信息
          • 方便的資源和組件管理

          二、徐小夕 / H5-Dooring

          github: https://github.com/MrXujiang

          star: 6.9k

          fork: 1.5k

          (H5編輯器)H5-Dooring是一款功能強大,專業可靠的H5可視化頁面配置解決方案,致力于提供一套簡單方便、專業可靠、無限可能的H5落地頁最佳實踐。技術棧以react為主, 后臺采用nodejs開發.

          1.編輯器界面:

          1. 頁面版本管理:
          1. 后臺:

          特點:

          1.【編輯器】

          • 參考線
          • 基礎組件
          • 可視化組件
          • 媒體組件
          • 商品組件
          • 拖拽器
          • 配置面板
          • 表單設計器
          • (多)頁面管理(復制,編輯, 刪除, 新建)
          • 組件動畫
          • 組件交互
          • 數據源管理
          • 快速預覽
          • 真機預覽
          • 撤銷、重做
          • 微信分享
          • 快捷鍵
          • 模版庫
          • 桌面端軟件Dooring-electron, 支持離線使用

          2.【增強功能】

          • 上傳 json,一鍵轉換為 H5
          • 圖片庫
          • 出碼能力(下載源碼, 下載dist包)

          3.【后端 API】

          • 創建、保存、更新作品
          • 用戶管理, 權限管理
          • 一鍵智能分析
          • 數據看版
          • 表單數據收集
          • 表單數據展示
          • 表單數據分析, 一鍵導出excel, 表單多條件搜索
          • 在線預覽
          • 二維碼預覽
          • 模版管理
          • 出碼接口

          三、mantou / H5DS

          star: 478

          watch: 110

          fork: 165

          H5DS(HTML5 Design software) 這是一款基于WEB的 H5制作工具。讓不會寫代碼的人也能輕松快速上手制作H5頁面。

          特點:

          1.【高維護性】: 采用react+mobx模塊化開發,源碼結構清晰,注釋規范,高可維護,方便進行二次開發和迭代升級。

          2.【高擴展性】: 編輯器內核獨立存在,官方提供了插件開發教程以及CLI工具,可獨立開發插件,動態加載插件。

          3.【高性能】:采用mobx統一管理數據,底層proxy數據監聽,做了大量節流和防抖優化,插件按需加載,不占內存。

          4.【多終端支持】:支持手機頁面、PC頁面獨立制作,設計界面可自由切換,采用縮放模式兼容各種屏幕分辨率,PC/Mobile一網打盡。

          三、ly525 / luban-h5

          star: 1.1k

          watch: 187

          fork: 403

          A mobile page builder/editor, similar with amolink.zh:類似易企秀的H5制作、建站工具,開源可視化搭建系統

          特點:

          1.【編輯器】

          • 參考線/參考線
          • 吸附線
          • 通過拖放更改插件形狀
          • 編輯元素(畫布)
          • 復制元素(畫布)
          • 刪除元素(畫布)
          • 編輯頁面
          • 復制頁面
          • 刪除頁面
          • 快速預覽
          • 撤消、重做
          • 插件系統

          2.【發短信】

          • “正常”按鈕
          • “表單”按鈕
          • 表單字段
          • 圖片
          • 背景圖片
          • 視頻(內嵌框架格式)

          3.【增強功能】

          • 將 PSD 文件解析為 HTML 頁面
          • 照片庫
          • 第三方無版權圖片搜索

          4.【后端接口】

          • 創建工作
          • 保存工作
          • 更新工作
          • 表單統計
          • 在線預覽
          • 二維碼預覽

          四、猿計劃 / vite-vue3-lowcode

          star: 563

          watch: 59

          fork: 260

          vue3.x vite2.x vant element-plus H5移動端低代碼平臺lowcode可視化拖拽 可視化編輯器visual editor類似易企秀的H5制作、建站工具、可視化搭建工具

          功能清單:

          • 動態添加頁面
          • 拖拽式生成組件
          • service worker + indexeddb 實現無服務端的前端交互
          • 數據源管理(支持導入 swagger JSON 生成數據模型及接口)
          • 提供預置函數
          • 更多組件的封裝

          五、huangwei9527/quark-h5

          star: 345

          watch: 48

          fork: 134

          基于vue2 + koa2的 H5制作工具。讓不會寫代碼的人也能輕松快速上手制作H5頁面。類似易企秀、百度H5等H5制作、建站工具

          技術棧

          1.前端:

          • vue: 模塊化開發少不了angular,react,vue三選一,這里選擇了vue。
          • vuex: 狀態管理
          • sass: css預編譯器。
          • element-ui:不造輪子,有現成的優秀的vue組件庫當然要用起來。沒有的自己再封裝一些就可以了。
          • loadsh:工具類

          2.服務端:

          • koa:后端語言采用nodejs,koa文檔和學習資料也比較多,express原班人馬打造,這個正合適。
          • mongodb:一個基于分布式文件存儲的數據庫,比較靈活。

          編輯器整體設計

          • 一個組件選擇區,提供使用者選擇需要的組件
          • 一個編輯預覽畫板,提供使用者拖拽排序頁面預覽的功能
          • 一個組件屬性編輯,提供給使用者編輯組件內部props、公共樣式和動畫的功能

          六、眾邦科技/CRMEB-H5

          star: 316

          watch: 44

          fork: 278

          CRMEBv3.0版微信公眾號和H5前端頁面,采用VUE-CLI框架

          特點:

          1.【獨立部署】:源碼交付獨立部署,數據私有安全可靠

          2.【二開方便】:代碼開源規范,注釋清晰,二次開發更友好

          3.【高性可靠】:使用高性能框架開發,系統穩定、支持高并發

          4.【文檔齊全】:提供完善的安裝、使用文檔、接口文檔、開發文檔

          七、jaycie/makeH5Tools

          star: 61

          watch: 6

          fork: 41

          freeH5,open source html5,open source h5,免費html5制作工具,freeH5 APP,h5營銷,h5頁面制作,H5在線制作工具

          運行環境

          • 后端 nodejs+mysql
          • 前端 seajs


          更多推薦

          想提高工作效率?快來看看這6個辦公神器!

          推薦! 開源即用的CMS系統,輕松幫助企業和個人搭建知識管理系統

          文主要介紹如何實現 React Native 的全埋點,主要是控件點擊 $AppClick 事件。該內容,會默認你有一定的 React Native 開發經驗,(若沒有,也可參與文末贈書)。

          作者

          王灼洲 合肥研發中心負責人

          《Android 全埋點解決方案》和《iOS 全埋點解決方案》一書作者,有 10+ 年 Android & iOS 相關開發經驗,是國內第一批從事 Android 研發工作,開發和維護國內第一個商用的開源 Android & iOS 數據埋點 SDK。


          一、React Native 簡介

          React Native 是由 Facebook 推出的移動應用開發框架,可以用來開發 iOS、Android、Web 等跨平臺應用程序,官網為:

          https://facebook.github.io/react-native/。

          React Native 和傳統的 Hybrid 應用最大的區別就是它拋開了 WebView 控件。React Native 產出的并不是“網頁應用”、“HTML5 應用”或者“混合應用”,而是一個真正的移動應用,從使用感受上和用 Objective-C 或 Java 編寫的應用相比幾乎是沒有區分的。React Native 所使用的基礎 UI 組件和原生應用完全一致。我們要做的就是把這些基礎組件使用 JavaScript 和 React 的方式組合起來。React Native 是一個非常優秀的跨平臺框架。

          下面我們先用 React Native 創建一個簡單的 Demo。

          1.1 創建項目

          使用 React Native 開發移動應用, 首先需要安裝 React Native 相關的組件。具體的安裝方法,可以參照 React Native 官方介紹。

          React Native 安裝完成之后,就可以使用命令行工具創建新項目了。

          react-native init AwesomeProject

          上面的命令創建了一個名為 AwesomeProject 的項目,然后就可以通過下面的命令進入 AwesomeProject 文件夾并運行 iOS 程序。

          cd AwesomeProject
          react-native run-ios

          然后等待一會,iOS 模擬器將會啟動,就可以看到如下圖 1-1 所示的運行結果。

          圖 1-1 模擬器

          在命令行輸入下面的命令,Xcode 將會打開上面創建的 AwesomeProject 項目。

          open ./ios/AwesomeProject.xcworkspace

          Xcode 中,就可以看到 AwesomeProject 項目相關的代碼。首先,需要把 SensorsSDK 項目添加進來。Xcode 中,點擊 File → Add Files to "AwesomeProject" ...,會彈出如下圖 1-2 所示的對話框,選擇 SensorsSDK.xcodeproj 文件,并勾選相應的 Target,最后點擊 Add 按鈕。

          圖 1-2 添加項目

          然后,還需要添加相應的依賴關系。選中 AwesomeProject 項目,在 General 標簽的 Frameworks 欄中點擊加號(+)按鈕,添加 SensorsSDK.framework。

          最后, AppDelegate.m 中引入 SensorsSDK,并在 - application:didFinishLaunchingWithOptions: 方法中調用 SensorsAnalyticsSDK 的 - startWithServerURL: 初始化方法初始化 SDK。

          #import <SensorsSDK/SensorsSDK.h>
          
          @implementation AppDelegate
          
          - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
          {
            [SensorsAnalyticsSDK startWithServerURL:@"xxxxx"];
            
            ......
          
            return YES;
          }
          
          @end

          運行 AwesomeProject 項目,我們就可以在 Xcode 控制臺中看到 $AppStart 事件信息。

          {
            "properties": {
              "$model": "x86_64",
              "$manufacturer": "Apple",
              "$lib_version": "1.0.0",
              "$os": "iOS",
              "$app_version": "1.0",
              "$os_version": "12.3",
              "$lib": "iOS"
            },
            "event": "$AppStart",
            "time": 1576141146301,
            "distinct_id": "D13CE550-1EE4-45B4-AB83-CDF7601C9C77"
          }

          按 Home 鍵或上滑 HomeBar 讓應用程序進入后臺,將會在 Xcode 控制臺中看到 $AppEnd 事件。

          {
            "properties": {
              "$model": "x86_64",
              "$manufacturer": "Apple",
              "$lib_version": "1.0.0",
              "$os": "iOS",
              "$event_duration": 434917.40625,
              "$app_version": "1.0",
              "$os_version": "12.3",
              "$lib": "iOS"
            },
            "event": "$AppEnd",
            "time": 1576141581203,
            "distinct_id": "D13CE550-1EE4-45B4-AB83-CDF7601C9C77"
          }

          從而也可以說明,對于 React Native 項目的 $AppStart 和 $AppEnd 事件,我們無需做任何特殊處理,即可直接支持。

          其實在控制臺中也會打印頁面瀏覽($AppViewScreen)事件,但是這個事件在嚴格意義上來說不應該屬于 ReactNative 應用程序的頁面瀏覽事件,這個只是應用程序中的 UIWindow 控件的根視圖控制器的頁面瀏覽事件。而實際上在 React Native 中,是使用 react-navigation 進行頁面間的跳轉。對于 iOS 來說,跳轉的新頁面并不是一個視圖控制器,而是彈出一個視圖,因此并不能采集到正確的頁面瀏覽事件。

          1.2 基礎控件

          React Native 支持的控件有很多,詳細可以參照 React Native 官網的相關介紹和說明:

          https://facebook.github.io/react-native/docs/activityindicator。

          下面我們以 React Native 的 Switch 控件為例來做介紹。

          可以通過修改 AwesomeProject 項目中的 App.js 文件,在頁面中添加一個 Switch 組件。

          import React, { Component } from 'react';
          import {
            SafeAreaView,
            StyleSheet,
            ScrollView,
            View,
            Text,
            Switch,
            StatusBar,
          } from 'react-native';
          
          import {
            Header,
            LearnMoreLinks,
            Colors,
            DebugInstructions,
            ReloadInstructions,
          } from 'react-native/Libraries/NewAppScreen';
          
          export default class App extends Component {
            state={
              value: false,
            }
            render() {
              return (
                <>
                  <StatusBar barStyle="dark-content" />
                  <SafeAreaView>
                    <ScrollView
                      contentInsetAdjustmentBehavior="automatic"
                      style={styles.scrollView}>
                      <Header />
                      {global.HermesInternal==null ? null : (
                        <View style={styles.engine}>
                          <Text style={styles.footer}>Engine: Hermes</Text>
                        </View>
                      )}
                      <View style={styles.body}>
                        <View style={styles.sectionContainer}>
                          <Text style={styles.sectionTitle}>Components</Text>
                          <View style={styles.sectionContainer}>
                          <Switch style={{ marginLeft: 20 }} value={this.state.value} thumbColor='black' onValueChange={(value)=> {
                            this.setState({
                              value: value
                            })
                          }} />
                          </View>
                        </View>
                      </View>
                    </ScrollView>
                  </SafeAreaView>
                </>
              );
            }
          };
          
          const styles=StyleSheet.create({
            body: {
              backgroundColor: Colors.white,
            },
            sectionContainer: {
              marginTop: 32,
              paddingHorizontal: 24,
            },
          });

          使用 Xcode 運行應用程序,可以得到如下圖 1-3 顯示效果。

          圖 1-3 運行效果

          React Native 的 Switch 控件和 iOS 原生中的 UISwitch 控件是類似的。打開或者關閉 Switch,在 Xcode 的控制臺中,均可以看到正常觸發了 $AppClick 事件 。

          #import <SensorsSDK/SensorsSDK.h>@implementation AppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{  [SensorsAnalyticsSDK startWithServerURL:@"xxxxx"];    ......  return YES;}@end

          從而也可以看出,即使我們在 SensorsSDK 中沒有做任何修改,也可以正常采集 React Native 中 Switch 控件的點擊事件。從 $element_type 屬性可以看到,在 React Native 中,Switch 控件所對應的類是 RCTSwitch 。

          下面,我們繼續查看 RCTSwitch 相關的源碼。

          RCTSwitch.h 定義如下:

          {  "properties": {    "$model": "x86_64",    "$manufacturer": "Apple",    "$lib_version": "1.0.0",    "$os": "iOS",    "$app_version": "1.0",    "$os_version": "12.3",    "$lib": "iOS"  },  "event": "$AppStart",  "time": 1576141146301,  "distinct_id": "D13CE550-1EE4-45B4-AB83-CDF7601C9C77"}

          RCTSwitch.m 實現如下:

          /**
           * Copyright (c) Facebook, Inc. and its affiliates.
           *
           * This source code is licensed under the MIT license found in the
           * LICENSE file in the root directory of this source tree.
           */
          
          #import "RCTSwitch.h"
          
          #import "RCTEventDispatcher.h"
          #import "UIView+React.h"
          
          @implementation RCTSwitch
          
          - (void)setOn:(BOOL)on animated:(BOOL)animated {  _wasOn=on;  [super setOn:on animated:animated];
          }
          
          @end

          從代碼中可以看出,RCTSwitch 其實是繼承自 UISwitch 的子類控件。在前面的章節,我們已經在 SensorsSDK 中實現了 iOS 原生 UISwitch 控件的 $AppClick 事件全埋點,所以自然也就支持了 React Native 的 RCTSwitch 控件的 $AppClick 事件全埋點。

          在 React Native 中,類似于 Switch 控件的還有 Slider、SegmentedControlIOS 等控件。因此,對于這些控件來說,已可以支持采集它們的 $AppClick 事件。但是,對于 React Native 中的 Button 控件來說,情況就不太一樣了。

          我們可以先試驗一下,修改 App.js 文件, 在頁面中 UISwitch 控件的下方添加一個Button 控件。

          import React, { Component } from 'react';
          import {
            SafeAreaView,
            StyleSheet,
            ScrollView,
            View,
            Text,
            Switch,
            Button,
            Alert,
            StatusBar,
          } from 'react-native';
          
          import {
            Header,
            Colors,
          } from 'react-native/Libraries/NewAppScreen';
          
          export default class App extends Component {
            state={
              value: false,
            }
            render() {
              return (
                <>
                  <StatusBar barStyle="dark-content" />
                  <SafeAreaView>
                    <ScrollView
                      contentInsetAdjustmentBehavior="automatic"
                      style={styles.scrollView}>
                      <Header />
                      {global.HermesInternal==null ? null : (
                        <View style={styles.engine}>
                          <Text style={styles.footer}>Engine: Hermes</Text>
                        </View>
                      )}
                      <View style={styles.body}>
                        <View style={styles.sectionContainer}>
                          <Text style={styles.sectionTitle}>Components</Text>
                          <View style={styles.sectionContainer}>
                            <Switch style={{ marginLeft: 20 }} value={this.state.value} thumbColor='black' onValueChange={(value)=> {
                              this.setState({
                                value: value
                              })
                            }} />
                          </View>
                          <View style={styles.sectionContainer}>
                            <Button title="Press me" onPress={()=> Alert.alert('Simple Button pressed')} />
                          </View>
                        </View>
                      </View>
                    </ScrollView>
                  </SafeAreaView>
                </>
              );
            }
          };
          
          const styles=StyleSheet.create({
            scrollView: {
              backgroundColor: Colors.lighter,
            },
            engine: {
              position: 'absolute',
              right: 0,
            },
            body: {
              backgroundColor: Colors.white,
            },
            sectionContainer: {
              marginTop: 32,
              paddingHorizontal: 24,
            },
            sectionTitle: {
              fontSize: 24,
              fontWeight: '600',
              color: Colors.black,
            },
            highlight: {
              fontWeight: '700',
            },
            footer: {
              color: Colors.dark,
              fontSize: 12,
              fontWeight: '600',
              padding: 4,
              paddingRight: 12,
              textAlign: 'right',
            },
          });

          保存,可以看到在 Switch 控件的下方就出現了我們剛添加的按鈕。點擊按鈕,彈出了一個提示窗口,和 iOS 系統里的 UIAlert 的顯示效果相同,如下圖 1-4 所示。

          圖 1-4 提示窗口

          但是此時,我們在 Xcode 的控制臺中并沒有看到觸發了 $AppClick 事件。

          那么我們如何實現 React Native 中 Button 控件的 $AppClick 事件全埋點呢?

          二、React Native 全埋點

          在實現 Button 控件的 $AppClick 事件全埋點之前,我們先簡單的介紹一下 React Native 的事件響應機制。

          2.1 事件響應

          在 React Native 中,觸摸事件響應會涉及到 JavaScript 端和 Native 端,這里的 Native 端指的是 iOS 端,本章的內容暫不涉及 Android 部分。

          我們使用 Xcode 打開 AwesomeProject 項目,查看 Pod 工程中 React Native 的源碼,通過類名我們很容易找到兩個與觸摸事件相關的類:

          • RCTTouchEvent
          • RCTTouchHandler

          RCTTouchEvent 類實現了 RCTEvent 協議。從觸摸開始、移動到觸摸結束或取消,都會創建一個 RCTTouchEvent 類的對象,用來描述觸摸的各個不同階段。在 Native 端,將觸摸狀態發送到 JavaScript 端的過程中,傳遞的也是 RCTTouchEvent 類的對象。其實, RCTTouchEvent 類的對象就是在 RCTTouchHandler 類中創建的。

          RCTTouchHandler 類繼承自

          UIGestureRecognizer 類,也就是說

          RCTTouchHandler 類其實就是一個手勢識別器,它重寫了觸摸響應傳遞的以下幾個方法。

          - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event;

          - (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent *)event;

          - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent *)event;

          - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent *)event;

          事實上,以上幾個方法都會調用 - _updateAndDispatchTouches:eventName: 方法。在該方法中,使用 RCTTouchEvent 類的對象來描述當前的觸摸狀態。由于 RCTTouchHandler 類也是一個手勢識別器,因此需要將其添加到一個視圖中才能響應觸摸事件。

          我們先來看看, AwesomeProject 項目 AppDelegate.m 文件中- application:didFinishLaunchingWithOptions: 方法的實現。

          {  "properties": {    "$model": "x86_64",    "$manufacturer": "Apple",    "$lib_version": "1.0.0",    "$os": "iOS",    "$event_duration": 434917.40625,    "$app_version": "1.0",    "$os_version": "12.3",    "$lib": "iOS"  },  "event": "$AppEnd",  "time": 1576141581203,  "distinct_id": "D13CE550-1EE4-45B4-AB83-CDF7601C9C77"}

          可以看到,首先創建了一個 RCTRootView 對象作為一個視圖控制器的視圖,然后將該視圖控制器設置為 window 對象的根視圖控制器。在 RCTRootView 類中,有一個很重要的視圖對象,即 RCTRootContentView 類型的 _contentView。

          這個視圖對象是在 JavaScript 包加載完成之后創建的。在 React Native 中,所有 JavaScript 端生成的頁面其實都是添加在這個視圖對象中。在 _contentView 創建的時候,同時也會創建 RCTTouchHandler 類的對象并調用 - attachToView: 方法,將手勢識別器添加到 _contentView 中。這也就意味著,在 _contentView 中發生的所有觸摸事件都會交由 RCTTouchHandler 類的對象進行處理。

          那么,是否交換了 RCTTouchHandler 類的 - _updateAndDispatchTouches:eventName: 方法就可以采集到控件的 $AppClick 事件了呢?

          雖然我們通過這種方法,能接收到所有的觸摸事件,但是在這個方法中,我們無法知道在 JavaScript 端到底是哪個控件響應了觸摸事件。因此,此種實現方案并不可取,不能滿足我們實際的全埋點采集需求。

          我們繼續往下分析。

          在 RCTTouchHandler 類的對象進行處理完成之后,會通過一系列方法將觸摸事件發送到 JavaScript 端。在 JavaScript 端也實現了類似于 Native 端的觸摸事件處理機制——手勢響應系統。每個觸摸事件都可以通過手勢響應系統找到能夠響應的組件,并執行響應事件。當觸摸事件找到響應者時,會觸發 ReactNativeGlobalResponderHandler.js 的 onChange 方法,相關代碼片段如下。

          import React, { Component } from 'react';import {  SafeAreaView,  StyleSheet,  ScrollView,  View,  Text,  Switch,  Button,  Alert,  StatusBar,} from 'react-native';import {  Header,  Colors,} from 'react-native/Libraries/NewAppScreen';export default class App extends Component {  state={    value: false,  }  render() {    return (      <>        <StatusBar barStyle="dark-content" />        <SafeAreaView>          <ScrollView            contentInsetAdjustmentBehavior="automatic"            style={styles.scrollView}>            <Header />            {global.HermesInternal==null ? null : (              <View style={styles.engine}>                <Text style={styles.footer}>Engine: Hermes</Text>              </View>            )}            <View style={styles.body}>              <View style={styles.sectionContainer}>                <Text style={styles.sectionTitle}>Components</Text>                <View style={styles.sectionContainer}>                  <Switch style={{ marginLeft: 20 }} value={this.state.value} thumbColor='black' onValueChange={(value)=> {                    this.setState({                      value: value                    })                  }} />                </View>                <View style={styles.sectionContainer}>                  <Button title="Press me" onPress={()=> Alert.alert('Simple Button pressed')} />                </View>              </View>            </View>          </ScrollView>        </SafeAreaView>      </>    );  }};const styles=StyleSheet.create({  scrollView: {    backgroundColor: Colors.lighter,  },  engine: {    position: 'absolute',    right: 0,  },  body: {    backgroundColor: Colors.white,  },  sectionContainer: {    marginTop: 32,    paddingHorizontal: 24,  },  sectionTitle: {    fontSize: 24,    fontWeight: '600',    color: Colors.black,  },  highlight: {    fontWeight: '700',  },  footer: {    color: Colors.dark,    fontSize: 12,    fontWeight: '600',    padding: 4,    paddingRight: 12,    textAlign: 'right',  },});

          從上面的代碼可以看出,當響應控件觸摸事件的時候,JavaScript 端會調用 UIManager 中的 - setJSResponder: 方法,然后調用

          Native 端的 RCTUIManager 類中的

          - setJSResponder:blockNativeResponder: 方法。該方法的實現代碼較少,參考如下。

          /**
           * JS sets what *it* considers to be the responder. Later, scroll views can use
           * this in order to determine if scrolling is appropriate.
           */
          RCT_EXPORT_METHOD(setJSResponder:(nonnull NSNumber *)reactTag
                            blockNativeResponder:(__unused BOOL)blockNativeResponder)
          {
            [self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
              _jsResponder=viewRegistry[reactTag];
              if (!_jsResponder) {
                RCTLogWarn(@"Invalid view set to be the JS responder - tag %@", reactTag);
              }
            }];
          }

          這個方法有兩個參數,通過第一個參數 reactTag 我們可以獲取到響應者。

          介紹到這里,我們已經有實現 React Native 中 Button 控件 $AppClick 事件的全埋點方案了,那就是交換 RCTUIManager 類中的 - setJSResponder:blockNativeResponder: 方法。

          2.2 $AppClick 事件

          下面,我們詳細介紹實現步驟。

          第一步:在項目 SensorsSDK 中給 SensorsAnalyticsSDK

          新增一個類別 ReactNative,并新增一個

          - enableTrackReactNativeEvent 方法,用來開啟 React Native 中 $AppClick 事件的全埋點功能。

          在 SensorsAnalyticsSDK.h 文件中,類別 ReactNative 聲明如下:

          @interface SensorsAnalyticsSDK (ReactNative)
          
          - (void)enableTrackReactNativeEvent;
          
          @end

          在 SensorsAnalyticsSDK.m 文件中,類別 ReactNative 實現如下:

          #import <objc/runtime.h>
          
          @implementation SensorsAnalyticsSDK (ReactNative)
          
          /**
          * 交換兩個方法的實現
          *
          * @param className 需要交換的類名稱
          * @param methodName1 被交換的方法名,即原始的方法
          * @param methodName2 交換后的方法名,即新的實現方法
          * @param method2IMP 交換后的方法實現
          */
          static inline void sensorsdata_method_exchange(const char *className, const char *methodName1, const char *methodName2, IMP method2IMP) {
              // 通過類名獲取類
              Class cls=objc_getClass(className);
              // 獲取原始方法的名
              SEL selector1=sel_getUid(methodName1);
              // 通過方法名獲取方法指針
              Method method1=class_getInstanceMethod(cls, selector1);
              // 獲得指定方法的描述
              struct objc_method_description *desc=method_getDescription(method1);
              if (desc->types) {
                  // 把交換后的實現方法注冊到 runtime 中
                  SEL selector2=sel_registerName(methodName2);
                  // 通過運行時,把方法動態添加到類中
                  if (class_addMethod(cls, selector2, method2IMP, desc->types)) {
                      // 獲取實例方法
                      Method method2=class_getInstanceMethod(cls, selector2);
                      // 交換方法
                      method_exchangeImplementations(method1, method2);
                  }
              }
          }
          
          - (void)enableTrackReactNativeEvent {
              sensorsdata_method_exchange("RCTUIManager", "setJSResponder:blockNativeResponder:", "sensorsdata_setJSResponder:blockNativeResponder:", (IMP)sensorsdata_setJSResponder);
          }
          
          static void sensorsdata_setJSResponder(id obj, SEL cmd, NSNumber *reactTag, BOOL blockNativeResponder) {
          
          }
          
          @end

          第二步:實現交換后的

          sensorsdata_setJSResponder 函數。

          在該函數中,需要做三件事情:

          1. 調用原始的方法,保證 React Native 可以繼續完成觸摸事件的響應

          2. 獲取觸發事件響應的視圖控件

          3. 觸發 $AppClick 事件

          完整的代碼實現如下:

          @implementation SensorsAnalyticsSDK (ReactNative)
          
          ......
          
          static void sensorsdata_setJSResponder(id obj, SEL cmd, NSNumber *reactTag, BOOL blockNativeResponder) {
              // 先執行原來的方法
              SEL oriSel=sel_getUid("sensorsdata_setJSResponder:blockNativeResponder:");
              // 獲取原始方法的實現函數指針
              void (*imp)(id, SEL, id, BOOL)=(void (*)(id, SEL, id, BOOL))[obj methodForSelector:oriSel];
              // 完成第一步調用原始方法,讓 React Native 完成事件響應
              imp(obj, cmd, reactTag, blockNativeResponder);
          
              dispatch_async(dispatch_get_main_queue(), ^{
                  // 獲取 viewForReactTag: 的方法名,目的是獲取觸發當前觸摸事件的控件
                  SEL viewForReactTagSelector=NSSelectorFromString(@"viewForReactTag:");
                  // 完成第二步,獲取響應觸摸事件的視圖
                  UIView *view=((UIView * (*)(id, SEL, NSNumber *))[obj methodForSelector:viewForReactTagSelector])(obj, viewForReactTagSelector, reactTag);
          
                  // 觸發 $AppClick 事件
                  [[SensorsAnalyticsSDK sharedInstance] trackAppClickWithView:view properties:nil];
              });
          }
          
          @end

          第三步:在 AppDelegate.m 的

          - application:(UIApplication *)application didFinishLaunchingWithOptions: 中,初始化 SDK 之后,調用 SensorsAnalyticsSDK 的 - enableTrackReactNativeEvent 方法開啟 React Native 的 $AppClick 事件全埋點。

          @implementation AppDelegate
          
          - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
          {
            [SensorsAnalyticsSDK startWithServerURL:@"xxxx"];
            [[SensorsAnalyticsSDK sharedInstance] enableTrackReactNativeEvent];
          
            ......
          
            return YES;
          }
          
          ......
          
          @end

          第四步:測試驗證。

          運行 AwesomeProject 項目,點擊 Press me 按鈕,在 Xcode 控制臺中可以看到 $AppClick 事件。

          {
            "properties": {
              "$model": "x86_64",
              "$manufacturer": "Apple",
              "$element_type": "RCTView",
              "$lib_version": "1.0.0",
              "$os": "iOS",
              "$app_version": "1.0",
              "$screen_name": "UIViewController",
              "$os_version": "12.3",
              "$lib": "iOS"
            },
            "event": "$AppClick",
            "time": 1576146363711,
            "distinct_id": "E934E526-6517-4CA1-A61E-0DCE2172D56A"
          }

          但是,我們發現沒有 $element_content 屬性,其實按鈕上是有文本的(Press me)。

          也就是說之前獲取視圖控件顯示內容的方法并沒有覆蓋到 React Native 的控件,因此需要修改之前實現的 UIView+SensorsData.m 中獲取控件顯示內容的擴展方法 - sensorsdata_elementContent。

          通過 RCTView 的源碼可知,獲取控件上的內容可以通過 accessibilityLabel 屬性進行獲取,因此在 - sensorsdata_elementContent 方法中,當獲取到的內容為空時,返回 accessibilityLabel 屬性即可。

          @implementation UIView (SensorsData)
          
          - (NSString *)sensorsdata_elementContent {
              // 如果是隱藏控件,則不獲取控件內容
              if (self.isHidden || self.alpha==0) {
                  return nil;
              }
              // 初始化數組,用于保存子控件的內容
              NSMutableArray *contents=[NSMutableArray array];
              for (UIView *view in self.subviews) {
                  // 獲取子控件的內容
                  // 如果子類有內容,例如:UILabel 的 text,獲取到的就是 text 屬性;
                  // 如果沒有就遞歸調用此方法,獲取其子控件的內容。
                  NSString *content=view.sensorsdata_elementContent;
                  if (content.length > 0) {
                      // 當該子控件中有內容時,保存在數組中
                      [contents addObject:content];
                  }
              }
              // 當未獲取到子控件內容時返回空。如果獲取到多個子控件內容時,使用 - 拼接
              return contents.count==0 ? self.accessibilityLabel : [contents componentsJoinedByString:@"-"];
          }
          
          @end

          再次運行 AwesomeProject 項目,點擊 Press me 按鈕,就能看到 $AppClick 事件已有 $element_content 屬性了。

          {
            "properties": {
              "$model": "x86_64",
              "$manufacturer": "Apple",
              "$element_type": "RCTView",
              "$lib_version": "1.0.0",
              "$os": "iOS",
              "$element_content": "Press me",
              "$app_version": "1.0",
              "$screen_name": "UIViewController",
              "$os_version": "12.3",
              "$lib": "iOS"
            },
            "event": "$AppClick",
            "time": 1576146741159,
            "distinct_id": "E934E526-6517-4CA1-A61E-0DCE2172D56A"
          }

          不過問題并沒有就此結束!當點擊 Switch 控件的時候,發現會觸發兩次 $AppClick 事件。之前有提到一些特殊的控件其實已經支持了采集 $AppClick 事件,但是當點擊這些控件的時候,React Native 同樣也會走觸摸事件的響應流程,因此造成了觸發兩次 $AppClick 事件。對于這種情況,我們需要在采集 React Native 的 $AppClick 事件時,把這些特殊的控件給剔除。修改 sensorsdata_setJSResponder 函數的實現,在觸發 $AppClick 事件之前判斷如果是 UIControl 類的控件直接返回。

          @implementation SensorsAnalyticsSDK (ReactNative)
          
          ......
          
          static void sensorsdata_setJSResponder(id obj, SEL cmd, NSNumber *reactTag, BOOL blockNativeResponder) {
              // 先執行原來的方法
              SEL oriSel=sel_getUid("sensorsdata_setJSResponder:blockNativeResponder:");
              // 獲取原始方法的實現函數指針
              void (*imp)(id, SEL, id, BOOL)=(void (*)(id, SEL, id, BOOL))[obj methodForSelector:oriSel];
              // 完成第一步調用原始方法,讓 React Native 完成事件響應
              imp(obj, cmd, reactTag, blockNativeResponder);
          
              dispatch_async(dispatch_get_main_queue(), ^{
                  // 獲取 viewForReactTag: 的方法名,目的是獲取觸發當前觸摸事件的控件
                  SEL viewForReactTagSelector=NSSelectorFromString(@"viewForReactTag:");
                  // 完成第二步,獲取響應觸摸事件的視圖
                  UIView *view=((UIView * (*)(id, SEL, NSNumber *))[obj methodForSelector:viewForReactTagSelector])(obj, viewForReactTagSelector, reactTag);
                  // 如果是 UIControl 的子類,例如:RCTSwitch、RCTSlider 等,直接返回
                  if ([view isKindOfClass:UIControl.class]) {
                      return;
                  }
                  // 觸發 $AppClick 事件
                  [[SensorsAnalyticsSDK sharedInstance] trackAppClickWithView:view properties:nil];
              });
          }
          
          @end

          如果此時你以為已經考慮到了所有情況,那你就錯了。當我們滾動頁面的時候,同樣也會觸發 $AppClick 事件 !

          {
            "properties": {
              "$model": "x86_64",
              "$manufacturer": "Apple",
              "$element_type": "RCTScrollView",
              "$lib_version": "1.0.0",
              "$os": "iOS",
              "$element_content": "Welcome to React-Components-checked-Press me",
              "$app_version": "1.0",
              "$screen_name": "UIViewController",
              "$os_version": "12.3",
              "$lib": "iOS"
            },
            "event": "$AppClick",
            "time": 1576147089922,
            "distinct_id": "E934E526-6517-4CA1-A61E-0DCE2172D56A"
          }

          這是因為在滾動頁面時,React Native 的 JavaScript 端同樣會回調該響應方法,因此這種情況同樣需要排除在外。

          我們通過代碼實現發現,在滾動頁面時,在 sensorsdata_setJSResponder 函數中獲取到的視圖其實是 RCTScrollView 類型的。因此,實現也比較簡單。

          /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */#import "RCTSwitch.h"#import "RCTEventDispatcher.h"#import "UIView+React.h"@implementation RCTSwitch- (void)setOn:(BOOL)on animated:(BOOL)animated {  _wasOn=on;  [super setOn:on animated:animated];}@end

          到此,我們已實現了 React Native 的 $AppClick 事件的全埋點。

          015HTML5游戲生態大會于5月22日召開,運營分會場壓軸大戲“精品游戲品鑒會”中展示數款基于白鷺引擎開發的HTML5游戲,品鑒會將運營會場參會者的關注度推向高潮。

          《愚公移山完美版》

          HTML5 版《愚公移山》是一款休閑放置類手游, 整體采用暴漫風格,玩法簡單,點擊愚公的房子生孩子從而提高挖掘能力即可,趣味性十足,由《黑桃互動》發行。

          試玩鏈接:http://yg.heitao.com/

          《女神沖我來》

          在《女神沖我來》中,玩家扮演一個穿越要拯救女神的勇者,完成屌絲逆襲的夢想。游戲采用手操自動回合制戰斗模式,擁有非常華麗的戰斗特效,并且戰斗節奏快而刺激。

          試玩鏈接:http://snscwl.egret-labs.org/v201504151700/?pf=et&platInfo=open_85_9166

          《萌戰姬》

          火緣布甲打造的《萌戰姬》繼承了經典飛機大戰簡單爽快的操作體驗,以清新明快的卡通風格呈現精美游戲畫面,超流暢的手感、超華麗的彈幕、超爽快的升級,都將帶給玩家最佳游戲體驗!

          試玩鏈接:http://mzj.egret-labs.org/dfj/h5egret/index.html?platInfo=open_86_9166

          《萌萌愛消除》

          《萌萌愛消除》是為“小白”玩家準備的一款超好玩的休閑游戲,畫面精美、上手簡單、休閑益智。百余個精心設計的關卡和萌萌噠的動物形象,是平時休閑娛樂、打發時間的最佳選擇!

          試玩鏈接:http://mmaxc.gz.1251278653.clb.myqcloud.com/game/wanba?platInfo=open_129_9166

          《最游記物語》

          《最游記物語》是一款日系畫風的2D動作卡牌游戲產品。這款產品的核心戰斗采用了標準回合制戰斗內容,但在操控上,創新性的選擇了消除類的玩法體驗,將給玩家帶來最具有想象力的視覺體驗。

          試玩鏈接:http://static.egret-labs.org/h5game/zxy/index.html?platInfo=open_98_9166

          《盜墓英雄》

          經典像素風格的沙盒式手機頁游,游戲以組隊、戰斗、冒險、盜墓為核心元素,成長采用放置式戰斗系統,非常輕松。在線時帶領英雄橫行霸道,順便羞辱boss和菜鳥,離線后掛機賺賺金幣升升級!

          《武林英雄》

          在支持野外掛機打怪的《武林英雄》中,您將作為一名穿越時空來到古代戰國的小蝦米,經過無數的歷練,終于成為一名攜六國之眾,拯救天下蒼生的曠古爍今的一代大俠。

          《進擊吧!勇者》

          《進擊吧!勇者》是一款動漫迷輕松上手的卡牌游戲…,400+勇者同萌&500+羅莉盟友陪你展開ACG二次元冒險,劇情主線埋于副本,在戰斗中隨時觸發,讓人陷其境,超爽華麗戰斗三倍速直攻敵人巢穴。

          《我們的萌萌》

          《我們的萌萌》采用經典電子寵物+Hey Day的玩法,淡化游戲屬性, 降低非核心游戲玩家的體驗門檻。好友交互, 分享以及換裝集中在下方社交區域, 方便隨時隨地社交體驗

          隨著白鷺完成了從引擎、工具到HTML5移動游戲Runtime等產品的開發和布局,一解決了HTML5游戲在開發技術上的諸多難題,精品游戲不斷涌現。與此同時,HTML5游戲的研發、支付、渠道、發行等環境正在不斷被完善,可謂是萬事俱備只欠東風了,優質的HTML5爆款游戲很可能在今年出現,大家拭目以待!


          主站蜘蛛池模板: 少妇人妻偷人精品一区二区| 国产精品区一区二区三| 一级毛片完整版免费播放一区| 亚洲一区二区三区影院 | 乱色精品无码一区二区国产盗| 国产成人高清亚洲一区91| 在线播放偷拍一区精品| av一区二区三区人妻少妇| 3d动漫精品一区视频在线观看| 国产乱码一区二区三区爽爽爽| 亚洲AⅤ无码一区二区三区在线| 人妻体体内射精一区二区| 亚洲国产成人一区二区三区| 一区三区三区不卡| 免费一区二区三区四区五区| 精品免费国产一区二区| 好看的电影网站亚洲一区| 亚洲国产高清在线一区二区三区 | 五月婷婷一区二区| 一区二区三区日韩| 中文字幕一区二区人妻性色 | 日本精品一区二区久久久| 91一区二区视频| 亚洲熟妇无码一区二区三区| 精品视频一区二区三区四区| 久久久久人妻一区精品色| 一区二区三区四区视频在线| 国产精品毛片VA一区二区三区 | 国产香蕉一区二区三区在线视频| 一区二区3区免费视频| 高清无码一区二区在线观看吞精 | 亚洲一区二区三区影院| 亚洲视频一区在线| 内射女校花一区二区三区| 久久一区二区精品| 国产成人精品无码一区二区三区| 末成年女A∨片一区二区| 精品人妻码一区二区三区| 无码人妻精品一区二区| 久久久精品人妻一区二区三区四| 搡老熟女老女人一区二区|