From 630921fb09289ecf034de5ed363ef617c081c0cf Mon Sep 17 00:00:00 2001 From: devin-cwd Date: Sun, 29 Sep 2024 14:56:00 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E7=AB=AF=EF=BC=8C=E8=A7=A3=E5=86=B3=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E7=AB=AF=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 +- packages/analytics/src/open-analytics.ts | 258 ++++++++++++----------- packages/analytics/src/storage.ts | 11 +- packages/analytics/src/utils.ts | 5 + 4 files changed, 151 insertions(+), 126 deletions(-) diff --git a/package.json b/package.json index 224f199..18373b5 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "dev": "pnpm --filter demo dev" + "dev:demo": "pnpm --filter demo dev", + "dev": "pnpm --filter @opensig/open-analytics dev" }, "keywords": [], "author": "", diff --git a/packages/analytics/src/open-analytics.ts b/packages/analytics/src/open-analytics.ts index 07cbd08..1bf937a 100644 --- a/packages/analytics/src/open-analytics.ts +++ b/packages/analytics/src/open-analytics.ts @@ -1,5 +1,5 @@ import { Storage } from './storage'; -import { whenDocumentReady, isFunction, isPromise, uniqueId } from './utils'; +import { whenDocumentReady, isFunction, isPromise, uniqueId, isClient } from './utils'; import { Constant } from './constant'; import { getInnerEventData, isInnerEvent, CollectorOptions } from './events'; import packageJson from '../package.json'; @@ -24,68 +24,29 @@ class AnalyticsStoreKey { } } type StoreKeyIns = InstanceType; - -let store: InstanceType; -/** - * 初始化存储器 - * @param appId {string} - */ -function initStorage(target?: globalThis.Storage) { - store = new Storage(target || globalThis.localStorage); -} - /** - * 初始化通用数据 - * @param keys {string} - * @param appId {string} + * 创建存储对象 */ -function initHeader(keys: StoreKeyIns, appId: string): EventHeader { - const aKey = keys.client; - const client = store.getAlways(aKey, { - defaultValue: () => ({ - id: uniqueId(), - }), - setOption: { - expire: Date.now() + Constant.CLIENT_EXPIRE_TIME, - }, - onValid() { - store.setExpire(aKey, Date.now() + Constant.CLIENT_EXPIRE_TIME); - }, - }).value; - +function createStorageTarget() { + if (isClient) { + return globalThis.localStorage; + } + const store: Record = {}; return { - cId: client.id, - aId: appId, - oa_version: packageJson.version, - viewport_width: window.innerWidth, - viewport_height: window.innerHeight, - screen_width: window.screen.width || window.innerWidth, - screen_height: window.screen.height || window.innerHeight, - }; -} -/** - * 获取会话id,每次获取,延长有效期 - * @param sKey session key - */ -function getSessionId(sKey: string) { - const session = store.getAlways(sKey, { - defaultValue: () => ({ - id: uniqueId(), - }), - setOption: { - expire: Date.now() + Constant.SESSION_EXPIRE_TIME, + setItem(key: string, value: string) { + store[key] = value; }, - onValid() { - store.setExpire(sKey, Date.now() + Constant.SESSION_EXPIRE_TIME); + getItem(key: string) { + return store[key]; }, - }).value; - - return session.id; + removeItem(key: string) { + delete store[key]; + }, + }; } export class OpenAnalytics { - enabled: boolean; - + #store: InstanceType; #timer: number | null; #firstReport: boolean; #request: ReportRequest; @@ -100,12 +61,14 @@ export class OpenAnalytics { // 上报间隔,默认3s #requestInterval: number; #maxEvents: number; + + enabled: boolean; /** * 构造函数 * @param params {OpenAnalyticsParams} */ constructor(params: OpenAnalyticsParams) { - initStorage(); + this.#store = new Storage(createStorageTarget()); this.#request = params.request; this.#immediate = params.immediate ?? false; this.#appKey = params.appKey ?? ''; @@ -116,20 +79,68 @@ export class OpenAnalytics { this.#firstReport = true; - this.enabled = store.get(this.#StoreKey.enabled).value === Constant.OA_ENABLED; + this.enabled = this.#store.get(this.#StoreKey.enabled).value === Constant.OA_ENABLED; if (this.enabled) { - store.set(this.#StoreKey.enabled, Constant.OA_ENABLED); - this.#eventData = store.getAlways(this.#StoreKey.events, { + this.#store.set(this.#StoreKey.enabled, Constant.OA_ENABLED); + this.#eventData = this.#store.getAlways(this.#StoreKey.events, { defaultValue: () => [], }).value; - this.#header = initHeader(this.#StoreKey, this.#appKey); + this.#header = this.#initHeader(this.#StoreKey, this.#appKey); } else { this.#header = {}; this.#eventData = []; - store.remove(this.#StoreKey.events); + this.#store.remove(this.#StoreKey.events); } } + /** + * 初始化通用数据 + * @param keys {string} + * @param appId {string} + */ + #initHeader(keys: StoreKeyIns, appId: string): EventHeader { + const aKey = keys.client; + const client = this.#store.getAlways(aKey, { + defaultValue: () => ({ + id: uniqueId(), + }), + setOption: { + expire: Date.now() + Constant.CLIENT_EXPIRE_TIME, + }, + onValid: () => { + this.#store.setExpire(aKey, Date.now() + Constant.CLIENT_EXPIRE_TIME); + }, + }).value; + + return { + cId: client.id, + aId: appId, + oa_version: packageJson.version, + viewport_width: window.innerWidth, + viewport_height: window.innerHeight, + screen_width: window.screen.width || window.innerWidth, + screen_height: window.screen.height || window.innerHeight, + }; + } + /** + * 获取会话id,每次获取,延长有效期 + * @param sKey session key + */ + #getSessionId(sKey: string) { + const session = this.#store.getAlways(sKey, { + defaultValue: () => ({ + id: uniqueId(), + }), + setOption: { + expire: Date.now() + Constant.SESSION_EXPIRE_TIME, + }, + onValid: () => { + this.#store.setExpire(sKey, Date.now() + Constant.SESSION_EXPIRE_TIME); + }, + }).value; + + return session.id; + } /** * 搜集数据 */ @@ -141,9 +152,60 @@ export class OpenAnalytics { this.#eventData.shift(); } if (this.enabled) { - store.set(this.#StoreKey.events, this.#eventData); + this.#store.set(this.#StoreKey.events, this.#eventData); - this.runRequestPlan(immediate); + this.#runRequestPlan(immediate); + } + } + + /** + * 执行上报策略 + * @param immediate + */ + #runRequestPlan(immediate?: boolean) { + if (immediate || this.#immediate) { + this.#doSendEventData(); + } else if (this.#firstReport) { + this.#firstReport = false; + whenDocumentReady(() => this.#doSendEventData()); + } else { + if (isFunction(this.#requestPlan)) { + this.#requestPlan(this.#doSendEventData); + } else { + const run = () => { + this.#timer = window.setTimeout(() => { + this.#doSendEventData(); + run(); + }, this.#requestInterval); + }; + if (!this.#timer) { + run(); + } + } + } + } + /** + * 发起数据上报 + */ + #doSendEventData() { + if (!this.#request || !this.enabled || this.#eventData.length === 0) { + return; + } + const reportData = { + header: this.#header, + body: this.#eventData, + }; + const rlt = this.#request(reportData); + if (isPromise(rlt)) { + rlt.then((isSuccess) => { + if (isSuccess) { + this.#eventData = []; + this.#store.set(this.#StoreKey.events, []); + } + }); + } else { + this.#eventData = []; + this.#store.set(this.#StoreKey.events, []); } } /** @@ -162,10 +224,10 @@ export class OpenAnalytics { } if (this.enabled) { - store.set(this.#StoreKey.enabled, Constant.OA_ENABLED); - this.#header = Object.assign(initHeader(this.#StoreKey, this.#appKey), this.#header); + this.#store.set(this.#StoreKey.enabled, Constant.OA_ENABLED); + this.#header = Object.assign(this.#initHeader(this.#StoreKey, this.#appKey), this.#header); // 初始化sessionId - this.#sessionId = getSessionId(this.#StoreKey.session); + this.#sessionId = this.#getSessionId(this.#StoreKey.session); // 给内存中事件添加sessionId this.#eventData.forEach((event) => { if (event.sId === '') { @@ -173,17 +235,17 @@ export class OpenAnalytics { } }); // 将数据存储到本地 - store.set(this.#StoreKey.events, this.#eventData); + this.#store.set(this.#StoreKey.events, this.#eventData); // 执行上报策略 - this.runRequestPlan(); + this.#runRequestPlan(); } else if (this.#timer) { clearTimeout(this.#timer); this.#timer = 0; this.#eventData = []; - store.remove(this.#StoreKey.enabled); - store.remove(this.#StoreKey.events); - store.remove(this.#StoreKey.client); - store.remove(this.#StoreKey.session); + this.#store.remove(this.#StoreKey.enabled); + this.#store.remove(this.#StoreKey.events); + this.#store.remove(this.#StoreKey.client); + this.#store.remove(this.#StoreKey.session); } } /** @@ -209,59 +271,9 @@ export class OpenAnalytics { event: event, time: Date.now(), properties: reportData, - sId: this.enabled ? getSessionId(this.#StoreKey.session) : '', + sId: this.enabled ? this.#getSessionId(this.#StoreKey.session) : '', }; this.#collect(eventData, immediate); } - /** - * 执行上报策略 - * @param immediate - */ - runRequestPlan(immediate?: boolean) { - if (immediate || this.#immediate) { - this.doSendEventData(); - } else if (this.#firstReport) { - this.#firstReport = false; - whenDocumentReady(() => this.doSendEventData()); - } else { - if (isFunction(this.#requestPlan)) { - this.#requestPlan(this.doSendEventData); - } else { - const run = () => { - this.#timer = window.setTimeout(() => { - this.doSendEventData(); - run(); - }, this.#requestInterval); - }; - if (!this.#timer) { - run(); - } - } - } - } - /** - * 发起数据上报 - */ - doSendEventData() { - if (!this.#request || !this.enabled || this.#eventData.length === 0) { - return; - } - const reportData = { - header: this.#header, - body: this.#eventData, - }; - const rlt = this.#request(reportData); - if (isPromise(rlt)) { - rlt.then((isSuccess) => { - if (isSuccess) { - this.#eventData = []; - store.set(this.#StoreKey.events, []); - } - }); - } else { - this.#eventData = []; - store.set(this.#StoreKey.events, []); - } - } } diff --git a/packages/analytics/src/storage.ts b/packages/analytics/src/storage.ts index b58abe2..3fdb9be 100644 --- a/packages/analytics/src/storage.ts +++ b/packages/analytics/src/storage.ts @@ -8,10 +8,17 @@ interface StorageSetOptions { interface StorageOptions { checkExpiration?: (time: number) => boolean; } + +export interface StorageTarget { + setItem: (key: string, value: any) => void; + removeItem: (key: string) => void; + getItem: (key: string) => string | null; +} + export class Storage { - store: globalThis.Storage = globalThis.localStorage; + store: StorageTarget; checkExpiration: (expire: number) => boolean; - constructor(target: globalThis.Storage, options?: StorageOptions) { + constructor(target: StorageTarget, options?: StorageOptions) { this.store = target; this.checkExpiration = isFunction(options?.checkExpiration) ? options?.checkExpiration diff --git a/packages/analytics/src/utils.ts b/packages/analytics/src/utils.ts index a86fd82..17d9246 100644 --- a/packages/analytics/src/utils.ts +++ b/packages/analytics/src/utils.ts @@ -47,3 +47,8 @@ export function whenWindowLoad(callback: () => any): void { callback(); } } + +/** + * 是否是浏览器环境 + */ +export const isClient = typeof window !== 'undefined'; -- Gitee From 0e822b0a6a799417cd4f0e4de610e0b1ea201b28 Mon Sep 17 00:00:00 2001 From: devin-cwd Date: Sun, 29 Sep 2024 15:21:27 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=E7=A7=BB=E9=99=A4=20enable=20?= =?UTF-8?q?=E6=A0=87=E8=AF=86=E8=AE=B0=E5=BF=86=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E9=80=9A=E8=BF=87=E8=B0=83=E7=94=A8=E8=80=85=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=20enableReporting=20=E7=A1=AE=E8=AE=A4=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E4=B8=8A=E6=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/analytics/README.md | 5 +++++ packages/analytics/package.json | 2 +- packages/analytics/src/open-analytics.ts | 18 ++++++------------ packages/analytics/test/main.ts | 5 ++--- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/packages/analytics/README.md b/packages/analytics/README.md index 4294838..f56ba5c 100644 --- a/packages/analytics/README.md +++ b/packages/analytics/README.md @@ -28,6 +28,11 @@ 1. 内置事件字段添加前缀"$" 2. cid,sid 使用 uuid 生成 +# 0.0.7 + +1. 支持服务端执行,解决 SSR\SSG 构建报错 +2. 移除 enable 标识记忆功能,通过调用者控制 enableReporting 确认是否上报 + # TODO 1. 支持在 html 引入; diff --git a/packages/analytics/package.json b/packages/analytics/package.json index ad77b2d..f737215 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -1,6 +1,6 @@ { "name": "@opensig/open-analytics", - "version": "0.0.6", + "version": "0.0.7", "description": "opendesign analytics", "main": "dist/open-analytics.mjs", "types": "dist/open-analytics.d.ts", diff --git a/packages/analytics/src/open-analytics.ts b/packages/analytics/src/open-analytics.ts index 1bf937a..18fff8d 100644 --- a/packages/analytics/src/open-analytics.ts +++ b/packages/analytics/src/open-analytics.ts @@ -79,19 +79,13 @@ export class OpenAnalytics { this.#firstReport = true; - this.enabled = this.#store.get(this.#StoreKey.enabled).value === Constant.OA_ENABLED; + this.#eventData = this.#store.getAlways(this.#StoreKey.events, { + defaultValue: () => [], + }).value; - if (this.enabled) { - this.#store.set(this.#StoreKey.enabled, Constant.OA_ENABLED); - this.#eventData = this.#store.getAlways(this.#StoreKey.events, { - defaultValue: () => [], - }).value; - this.#header = this.#initHeader(this.#StoreKey, this.#appKey); - } else { - this.#header = {}; - this.#eventData = []; - this.#store.remove(this.#StoreKey.events); - } + this.#header = {}; + + this.enabled = false; } /** * 初始化通用数据 diff --git a/packages/analytics/test/main.ts b/packages/analytics/test/main.ts index 2655088..7938f63 100644 --- a/packages/analytics/test/main.ts +++ b/packages/analytics/test/main.ts @@ -23,9 +23,8 @@ function enabledOA(enabled) { oa.enableReporting(enabled); localStorage.setItem('enabled', enabled ? '1' : '0'); } -if (localStorage.getItem('enabled') !== '1') { - enabledOA(false); -} + +enabledOA(localStorage.getItem('enabled') === '1'); oa.report(OpenEventKeys.PV, () => ({ id: 'home', -- Gitee From ef15c17d0f915a45e7990faf72b651102fa0bbb1 Mon Sep 17 00:00:00 2001 From: devin-cwd Date: Sun, 29 Sep 2024 17:48:03 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20remove=20=E5=BA=9F=E5=BC=83?= =?UTF-8?q?=E7=9A=84enable=20key?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/analytics/src/constant.ts | 2 -- packages/analytics/src/open-analytics.ts | 4 ---- 2 files changed, 6 deletions(-) diff --git a/packages/analytics/src/constant.ts b/packages/analytics/src/constant.ts index 13d198a..9006f23 100644 --- a/packages/analytics/src/constant.ts +++ b/packages/analytics/src/constant.ts @@ -1,7 +1,5 @@ export const Constant = { OA_PREFIX: 'oa', // 本地存储key的前缀 - OA_ENABLED: '1', // 开启采集标识 - OA_DISABLED: '0', // 关闭采集标识 SESSION_EXPIRE_TIME: 30 * 60 * 1000, // 会话标识有效期 CLIENT_EXPIRE_TIME: 180 * 24 * 60 * 60 * 1000, // 设备标识有效期 DEFAULT_REQUEST_INTERVAL: 5 * 1000, // 默认上报间隔 diff --git a/packages/analytics/src/open-analytics.ts b/packages/analytics/src/open-analytics.ts index 18fff8d..8054a29 100644 --- a/packages/analytics/src/open-analytics.ts +++ b/packages/analytics/src/open-analytics.ts @@ -9,14 +9,12 @@ class AnalyticsStoreKey { appPrefix: string; client: string; session: string; - enabled: string; events: string; constructor(appKey: string = '') { this.appPrefix = appKey ? `${appKey}-` : ''; this.client = this.getKey('client'); this.session = this.getKey('session'); - this.enabled = this.getKey('enabled'); this.events = this.getKey('events'); } getKey(key: string) { @@ -218,7 +216,6 @@ export class OpenAnalytics { } if (this.enabled) { - this.#store.set(this.#StoreKey.enabled, Constant.OA_ENABLED); this.#header = Object.assign(this.#initHeader(this.#StoreKey, this.#appKey), this.#header); // 初始化sessionId this.#sessionId = this.#getSessionId(this.#StoreKey.session); @@ -236,7 +233,6 @@ export class OpenAnalytics { clearTimeout(this.#timer); this.#timer = 0; this.#eventData = []; - this.#store.remove(this.#StoreKey.enabled); this.#store.remove(this.#StoreKey.events); this.#store.remove(this.#StoreKey.client); this.#store.remove(this.#StoreKey.session); -- Gitee