埋點自動收集方案-路由依賴分析

2021-02-19 程式設計師成長指北

1.一個項目總共有多少組件?每個頁面又有多少組件構成?

2.有哪些組件是公共組件,它們分別被哪些頁面引用?

對於這兩個問題,我們先思考一會。sleep……

跟隨這篇文章我們一起探討下,希望能幫你找到答案。

隨著組件化思想深入人心,開發中遇到特定的功能模塊或UI模塊,我們便會想到抽成組件,高級一點的做法就是把多個頁面相似的部分抽成公共的組件。

組件化的「詛咒」

但是往往對一件事物依賴越強,越容易陷入它的「詛咒」當中。當項目有越多的組件時,開發者越不容易建立它們之間的關係,特別當改動了某個組件的一行代碼,甚至不能準確的判斷由於這行代碼變動,都影響了哪些頁面。我暫且稱之為「組件化的詛咒」。如果我們有個完整的組件依賴關係,就可以很好的解決這個問題。

我們以下面的場景為例,看一看依賴分析的重要性和必要性。

通過前一篇文章,想必大家對埋點自動收集方案有了宏觀且全面的了解。在這裡再簡單概述下:

埋點自動收集方案是基於jsdoc對注釋信息的搜集能力,通過給路由頁面中所有埋點增加注釋的方式,在編譯時建立起頁面和埋點信息的對應關係。

點擊查看《埋點自動收集方案-概述》

在整個方案中,埋點的數據源很重要,而數據源與頁面的對應關係又是保證數據源完整性的關鍵。比如:首頁和個人主頁的商品流都採用相同的商品卡片,開發者自然會將商品卡片抽離為一個公共組件。如下:

//Index.vue 首頁
import Card from './common/Card.vue' //依賴商品卡片組件

//Home.vue 個人主頁
import Card from './common/Card.vue' //依賴商品卡片組件

//Card.vue 商品卡片組件
goDetail(item) {
    /**
    * @mylog 商品卡片點擊
    */
    this.$log('card-click') // 埋點發送
}

這就帶來一個問題:商品卡片的點擊信息(埋點的數據源),既可能是首頁的,也可能是個人主頁的,而jsdoc搜集埋點注釋時,對這種歸屬情況的判斷無能為力。所以必須找到一種方法可以拿到組件和頁面的映射關係。

項目中的實際依賴關係:


對應的依賴分析關係:(每個組件,與引用它的頁面路由的映射)


那麼,怎麼做依賴分析?在思考這個問題之前,我們先看一看有哪些常見的建立依賴的語法。

//a.ts
import B from './b.ts'
import getCookie from '@/libs/cookie.ts'

//c.ts
const C = require('./b.ts')

//b.ts
div {
    background: url('./assets/icon.png') no-repeat;
}
import './style.css'
// c.vue
import Vue from Vue
import Card from '@/component/Card.vue'

這裡給出三種依賴分析的思路:

1 遞歸解析

從項目的路由配置文件開始,分別對每個路由頁面,進行依賴的遞歸解析。這種思路想法簡單直接,但實現起來可能較為繁瑣,需要解析頁面中所有形式的依賴關係。

2 藉助webpack工具的統計分析數據,進行二次加工

實際項目中我們都是採用webpack打包工具,而它的一大特點就是會自動幫開發者做依賴分析(獨立的enhanced-resolve庫)。相較於第一種重寫解析的方法,為何不站在webpack的肩膀上解決問題呢。

先來看下webpack的整體編譯流程:


可以看到,每一個文件都會經過resolve階段,最終在編譯結束後,得到本次編譯的統計分析信息。

//done是compiler的鉤子,在完成一次編譯結束後的會執行
compiler.hooks.done.tapAsync("demoPlugin",(stats,cb)=>{
  fs.writeFile(appRoot+'/stats.json', JSON.stringify(stats.toJson(),'','\t'), (err) => {
      if (err) {
          throw err;
      }
  })
  cb()
})

詳細的編譯數據,就是done事件中的回調參數stats,經過處理後,大致如下:


通過對這份統計分析信息的二次加工和分析,也可以得到預期的依賴關係(插件webpack-bundle-analyzer也是基於這份數據生成的分析圖表)。這份數據看上去更像基本chunk和module的依賴分析,對於組件或公共組件的依賴關係問題,需要對chunks和modules綜合分析才能解決。同時我們還發現,這份數據的數據量相當大,且有大量開發者不關心的數據(截圖是只有兩個路由頁面的情況下的數據量)。接下來討論的方案是作者實際採用的方案,也是基於webpack,不同之處在於分析和收集依賴關係的時機。

3 在webpack的解析階段,分析並收集依賴

我們看到雖然webpack的分析數據非常臃腫,但是它確實幫助開發者做了這份繁重的工作。只是我們希望能定製數據的範圍,主動收集期望數據,所以推想,可否在每個文件解析階段進行一定的「幹預」,即通過條件判斷或過濾篩選達成目的。那麼問題來了,應該在resolve的哪個階段進行「幹預」,如何「幹預」?

好,我們先要總覽下webpack事件流過程:


很顯然,afterResolve是每個文件解析階段的最後,應該就從這裡下手啦。

先奉上流程圖


1 初始化

首先這是一個webpack插件,在初始化階段,指定解析的路由文件地址(比如src/route)以及排除解析的文件地址(比如src/lib、src/util),原因是這些排除的文件不會存在埋點數據。

2 收集依賴關係

在afterResolve鉤子函數中,獲取當前被解析文件的路徑及其父級文件路徑。

apply(compiler) {
  compiler.hooks.normalModuleFactory.tap(
    "demoPlugin",
    nmf => {
      nmf.hooks.afterResolve.tapAsync(
        "demoPlugin",
        (result, callback) => {
          const { resourceResolveData } = result;
          // 當前文件的路徑
          let path = resourceResolveData.path; 
          // 父級文件路徑
          let fatherPath = resourceResolveData.context.issuer; 
          callback(null,result)
        }
      );
    }
  )
}

3 建立依賴樹

根據上一步獲取的引用關係,生成依賴樹。

// 不是nodemodule中的文件,不是exclude中的文件,且為.js/.jsx/.ts/.tsx/.vue
if(!skip(this.ignoreDependenciesArr,this.excludeRegArr,path, fatherPath) && matchFileType(path)){ 
  if(fatherPath && fatherPath != path){ // 父子路徑相同的排除
    if(!(fatherPath.endsWith('js') || fatherPath.endsWith('ts')) || !(path.endsWith('js') || path.endsWith('ts'))){ 
      // 父子同為js文件,認為是路由文件的父子關係,而非組件,故排除
      let sonObj = {};
      sonObj.type = 'module';
      sonObj.path = path;
      sonObj.deps = []
      // 如果本次parser中的path,解析過,那麼把過去的解析結果copy過來。
      sonObj = copyAheadDep(this.dependenciesArray,sonObj);
      let obj = checkExist(this.dependenciesArray,fatherPath,sonObj);
      this.dependenciesArray = obj.arr;
      if(!obj.fileExist){
        let entryObj = {type:'module',path:fatherPath,deps:[sonObj]};
        this.dependenciesArray.push(entryObj);
      }
    }
} else if(!this.dependenciesArray.some(it => it.path == path)) {
// 父子路徑相同,且在this.dependenciesArray不存在,認為此文件為依賴樹的根文件
    let entryObj = {type:'entry',path:path,deps:[]};
    this.dependenciesArray.push(entryObj);
  }
}

那麼這時生成的依賴樹如下:


4 解析路由信息

通過上一步基本上得到組件的依賴樹,但我們發現對於公共組件Card,它只存在首頁的依賴中,卻不見在個人主頁的依賴中,這顯然不符合預期(在第6步中專門解釋)。那麼接下來就要找尋,這個依賴樹與路由信息的關係。

compiler.hooks.done.tapAsync("RoutePathWebpackPlugin",(stats,cb)=>{
  this.handleCompilerDone()
  cb()
})

// ast解析路由文件
handleCompilerDone(){
  if(this.dependenciesArray.length){
    let tempRouteDeps = {};
    // routePaths是項目的路由文件數組
    for(let i = 0; i < this.routePaths.length;i ++){
        let code = fs.readFileSync(this.routePaths[i],'utf-8');
        const tsParsedScript = ts.transpileModule(code, { compilerOptions: {target: 'ES6' }});
        code = tsParsedScript.outputText;
        let ast = Parser.parse(code,{'sourceType':'module',ecmaVersion:11});
        const walk = inject(acornWalk);
        let that = this;
        walk.ancestor(ast,{
            Literal(_, ancestors) {
                // 以下操作為獲取單獨的route配置文件中,name和頁面的映射關係
                ……
                }
            }
        })
    }
    // 合併多個路由文件的映射關係
    let tempDeps = []
    for(let arr of Object.values(tempRouteDeps)){
        tempDeps = tempDeps.concat(arr)
    }
    this.routeDeps = tempDeps.filter(it=>it && Object.prototype.toString.call(it) == "[object Object]" && it.components);
    // 獲取真實插件傳入的router配置文件的依賴,除去main.js、filter.js、store.js等文件的依賴
    this.dependenciesArray = 
    getRealRoutePathDependenciesArr(this.dependenciesArray,this.routePaths);
  }
}

通過這一步ast解析,可以得到如下路由信息:

[
  {
    "name": "index",
    "route": "/index",
    "title": "首頁",
    "components": ["../view/newCycle/index.vue"]
  },
  {
    "name": "home",
    "route": "/home",
    "title": "個人主頁",
    "components": ["../view/newCycle/home.vue"]
  }
]

5 對依賴樹和路由信息進行整合分析

// 將路由頁面的所有依賴組件deps,都存放在路由信息的components數組中
const getEndPathComponentsArr = function(routeDeps,dependenciesArray) {
  for(let i = 0; i < dependenciesArray.length; i ++){//可能存在多個路由配置文件
    let pageArr = dependenciesArray[i].deps;
    pageArr.forEach(page=>{
      routeDeps = routeDeps.map(routeObj=>{
        if(routeObj && routeObj.components){
          let relativePath = 
          routeObj.components[0].slice(routeObj.components[0].indexOf('/')+1);
          if(page.path.includes(relativePath.split('/').join(path.sep))){
            // 鋪平依賴樹的層級
            routeObj = flapAllComponents(routeObj,page);
            // 去重操作
            routeObj.components = dedupe(routeObj.components);
          }
        }
        return routeObj;
      })
    })
  }
  return routeDeps;
}
//建立一個map數據結構,以每個組件為key,以對應的路由信息為value
//  {
//    'path1' => Set { '/index' },
//    'path2' => Set { '/index', '/home' },
//    'path3' => Set { '/home' }
//  }
const convertDeps = function(deps) {
    let map = new Map();
    .
    return map;
}

整合分析後依賴關係如下:

{
    A: ["index&_&首頁&_&index"],// A代表組件A的路徑
    B: ["index&_&首頁&_&index"],// B代表組件B的路徑
    Card: ["index&_&首頁&_&index"],
    // 映射中只有和首頁的映射
    D: ["index&_&首頁&_&index"],// D代表組件D的路徑
    E: ["home&_&個人主頁&_&home"],// E代表組件E的路徑
}

因為上一步依賴收集部分,Card組件並沒有成功收集到個人主頁的依賴中,所以這步整合分析也無法建立準確的映射關係。且看下面的解決。

6 修改unsafeCache配置

為什麼公共組件Card在收集依賴的時候,只收集到一次?這個問題如果不解決,意味著只有首頁的商品點擊埋點被收集到,其他引用這個組件的頁面商品點擊就會丟失。有問題,就有機會,機會意味著解決問題的可能性。

webpack4提供了resolve的配置入口,開發者可以通過幾項設置決定如何解析文件,比如extensions、alias等,其中有一個屬性——unsafeCache成功引起了作者的注意,它正是問題的根結。

6.1 unsafeCache是webpack提高編譯性能的優化措施。

unsafeCache默認為true,表示webpack會緩存已經解析過的文件依賴,待再次需要解析此文件時,直接從緩存中返回結果,避免重複解析。

我們看下源碼:

//webpack/lib/WebpackOptionsDefaulter.js
this.set("resolveLoader.unsafeCache", true);
//這是webpack初始化配置參數時對unsafeCache的默認設置

//enhanced-resolve/lib/Resolverfatory.js
if (unsafeCache) {
 plugins.push(
  new UnsafeCachePlugin(
   "resolve",
   cachePredicate,
   unsafeCache,
   cacheWithContext,
   "new-resolve"
  )
 );
 plugins.push(new ParsePlugin("new-resolve", "parsed-resolve"));
} else {
 plugins.push(new ParsePlugin("resolve", "parsed-resolve"));
}
//前面已經提到,webpack將文件的解析獨立為一個單獨的庫去做,那就是enhanced-resolve。
//緩存的工作是由UnsafeCachePlugin完成,代碼如下:
//enhanced-resolve/lib/UnsafeCachePlugin.js
apply(resolver) {
 const target = resolver.ensureHook(this.target);
 resolver
  .getHook(this.source)
  .tapAsync("UnsafeCachePlugin", (request, resolveContext, callback) => {
   if (!this.filterPredicate(request)) return callback();
   const cacheId = getCacheId(request, this.withContext);
   // !!劃重點,當緩存中存在解析過的文件結果,直接callback
   const cacheEntry = this.cache[cacheId];
   if (cacheEntry) {
    return callback(null, cacheEntry);
   }
   resolver.doResolve(
    target,
    request,
    null,
    resolveContext,
    (err, result) => {
     if (err) return callback(err);
     if (result) return callback(null, (this.cache[cacheId] = result));
     callback();
    }
   );
  });
}

在UnsafeCachePlugin的apply方法中,當判斷有緩存過的文件結果,直接callback,沒有繼續後面的解析動作。

6.2 這對我們收集依賴有什麼影響?

緩存了解析過的文件,意味著與這個文件再次相遇時,事件流將被提前終止,afterResolve的鉤子自然也就不會執行到,那麼我們的依賴關係就無從談起。

其實webpack的resolve 過程可以看成事件的串聯,當所有串聯在一起的事件執行完之後,resolve 就結束了。我們看下原理:

用來解析文件的庫是enhanced-resolve,在Resolverfatory生成resolver解析對象時,進行了大量plugins的註冊,正是這些plugins形成一系列的解析事件。

//enhanced-resolve/lib/Resolverfatory.js
exports.createResolver = function(options) {
    .
 let unsafeCache = options.unsafeCache || false;
 if (unsafeCache) {
  plugins.push(
   new UnsafeCachePlugin(
    "resolve",
    cachePredicate,
    unsafeCache,
    cacheWithContext,
    "new-resolve"
   )
  );
  plugins.push(new ParsePlugin("new-resolve", "parsed-resolve"));
  // 這裡的事件流大致是:UnsafeCachePlugin的事件源(source)是resolve,
  //執行結束後的目標事件(target)是new-resolve。
  //而ParsePlugin的事件源為new-resolve,所以事件流機制剛好把這兩個插件串聯起來。
 } else {
  plugins.push(new ParsePlugin("resolve", "parsed-resolve"));
 }
 . // 各種plugin
 plugins.push(new ResultPlugin(resolver.hooks.resolved));

 plugins.forEach(plugin => {
  plugin.apply(resolver);
 });

 return resolver;
}

每個插件在執行自己的邏輯後,都會調用resolver.doResolve(target, ...),其中的target是觸發下一個插件的事件名稱,如此往復,直到遇到事件源為result,遞歸終止,解析結束。

resolve的事件串聯流程圖大致如下:


UnsafeCachePlugin插件在第一次解析文件時,因為沒有緩存,就會觸發target為new-resolve的事件,也就是ParsePlugin,同時將解析結果記入緩存。當判斷該文件有緩存結果,UnsafeCachePlugin的apply方法會直接callback,而沒有繼續執行resolver.doResolve(),意味著整個resolve事件流在UnsafeCachePlugin就終止了。這就解釋了,為什麼只建立了首頁與Card組件的映射,而無法拿到個人主頁與Card組件的映射。

6.3 解決辦法

分析了原因後,就好辦了,將unsafeCache設置為false(嗯,就這麼簡單)。這時你可能擔心會降低工程編譯速度,但深入一步想想,依賴分析這件事完全可以獨立於開發階段,只要在我們需要它的時候執行這個能力,比如由開發者通過命令行參數來控制。

//package.json
"analyse": "cross-env LEGO_ENV=analyse vue-cli-service build"

//vue.config.js
chainWebpack(config) {
    // 這一步解決webpack對組件緩存,影響最終映射關係的處理
    config.resolve.unsafeCache = process.env.LEGO_ENV != 'analyse'
}

7 最終依賴關係

{
    A: ["index&_&首頁&_&index"],// A代表組件A的路徑
    B: ["index&_&首頁&_&index"],// B代表組件B的路徑
    Card: ["index&_&首頁&_&index",
    "home&_&個人主頁&_&home"],
    // Card組件與多個頁面有映射關係
    D: ["index&_&首頁&_&index"],// D代表組件D的路徑
    E: ["home&_&個人主頁&_&home"],// E代表組件E的路徑
}

可以看到,與公共組件Card關聯的映射頁面中,多了個人主頁的路由信息,這才是準確的依賴數據。在埋點自動收集項目中,這份依賴關係數據交由jsdoc處理,就可以完成所有埋點信息與頁面的映射關係。

one more thing

webpack5,它來了,它帶著持久化緩存策略來了。前面提到的unsafeCache雖然可以提升應用構建性能,但是它犧牲了一定的 resolving 準確度,同時它意味著持續性構建過程需要反覆重新啟動決斷策略,這就要收集文件的尋找策略(resolutions)的變化,要識別判斷文件 resolutions 是否變化,這一系列過程也是有成本的,這就是為什麼叫unsafeCache,而不是safeCache(安全的)。

webpack5規定在配置信息的cache對象的type,可以設置為memory和fileSystem兩種方式。memory是指之前的unsafeCache緩存,fileSystem是指相對安全的磁碟持久化緩存。

module.exports = {
  cache: {
    // 1. Set cache type to filesystem
    type: 'filesystem',

    buildDependencies: {
      // 2. Add your config as buildDependency to get cache invalidation on config change
      config: [__filename]

      // 3. If you have other things the build depends on you can add them here
      // Note that webpack, loaders and all modules referenced from your config are automatically added
    }
  }
};

所以針對webpack5,如果需要做完整的依賴分析,只需將cache.type動態設置為memory,resolve.unsafeCache設置為false即可。(感興趣的童鞋可以試一試)

以上,我們解釋了組件化可能帶來的隱患,提到了路由依賴分析的重要性,給出三種依賴分析的思路,並基於埋點自動收集項目重點闡述了其中一種方案的具體實現。在此與你分享,期待共同成長~

1.看到這裡了就點個在看支持下吧,你的「點讚,在看是我創作的動力。

2.關注公眾號程式設計師成長指北,回復「1」加入高級前端交流群!「在這裡有好多 前端 開發者,會討論 前端 Node 知識,互相學習」!

3.也可添加微信【ikoala520】,一起成長。

「在看轉發」是最大的支持

相關焦點

  • 小白也能搞定的寬帶升級及路由組網方案——華為路由Q2 Pro
    家庭主路由要使用千兆路由器。這個條件最容易滿足,目前市面上可供挑選的千兆路由器品牌數量眾多,可以根據個人預算選擇不同價位的產品。2. 家庭網絡線路至少要是超 5 類線。這個條件相對麻煩一些,畢竟很多家庭網線都是埋牆鋪設的,更換起來不太容易,而如果採用明敷布線,又會破壞家裝環境。
  • 如何結合分析需求,設計數據埋點?
    因此,如果我們僅僅知道要採集哪些數據,僅僅掌握一些數據分析的技巧,但卻不清楚這些數據是如何收集和統計到的,那麼我們對於數據的應用不僅會處處受限,更會喪失對數據的敏感度。從另一個角度來說,埋點也是數據分析的完整路徑中必不可少的第一步。
  • Android自動埋點實踐
    文章開頭我們有提到過,無論是無痕埋點還是可視化埋點,都是基於自動化採集埋點的方式來做的,在這樣的採集方式下,我們無法通過埋點攜帶更多的信息,這也是我們面臨的一個痛點。基於這樣的需求之下,我們考慮可以用DSL來解決這個問題。
  • 乾貨| 如何做好數據埋點,選擇方式很重要
    通過埋點就可以取得這部分數據。通常是業務方、PD、BI,通過實際需求設計埋點方案。開發人員在代碼(前端或後端)的相應位置點植入統計代碼,對用戶的行為進行記錄,這就是埋點。埋點後,通過打點將埋點數據發送出去,存儲到伺服器上,在資料庫中產生記錄。包括2個步驟:①日誌收集:獲取每次用戶行為,發送伺服器;②日誌處理:將日誌處理、提取需要的信息,存儲在資料庫中。
  • AWS/Azure IOT方案分析
    所以這個就用到了IOT解決方案。通過傳感器把數據收集到雲上去,實時監控和分析電梯,發現問題,節省人工成本。二、AWS的IOT解決方案IOT很多廠商都在投入,我們看看AWS提供了什麼IOT解決方案。IOT解決方案核心是要解決無數的設備怎麼連接到平臺上進行處理,下面看看AWS給出的解決方案都能做些什麼?
  • 數據埋點常見的幾種方式
    數據埋點,是一種常用的數據採集方法。埋點是數據的來源,採集的數據可以幫助業務人員分析網站或者App的使用情況、用戶行為習慣等,是後續建立用戶畫像、用戶行為路徑等數據產品的基礎。前端的埋點方式主要分為代碼埋點、可視化埋點、無埋點三種。
  • 無線傳感器網絡路由分析 無線傳感器網絡路由算法介紹【詳解】
    由於無線傳感器網絡具有不依賴與任何預設網絡設施等特點,所以在軍事應用、大型設備監控和環境監測和預報等領域,傳感器網絡都有著廣泛的應用前景。傳感器網絡中節點分布數量眾多,且能量是由容量有限的電池供電,更換不易。傳感器節點消耗能量的模塊包括傳感器模塊、處理器模塊和無線通信模塊。隨著集成電路工藝的進步,處理器和傳感器模塊的功耗變得很低,絕大部分能量消耗在無線通信模塊上。
  • vpp路由模塊分析
    路由模塊設計比較複雜,深入學習、分析、填坑,一方面是能夠更好的掌控,另外一方面作為最核心的模塊,其蘊含著對於報文轉發過程本質的一些思考,分析、學習對NFV研發很有意義。筆者就是通過該篇文章入門vpp的路由模塊的。為了方便後續說明,結合筆者的理解對該篇文章的關鍵點通過問答方式總結如下:報文轉發的本質是什麼?報文轉發的本質是通過三層頭的目的ip查找二層封裝信息及其出接口;如何快速的進行報文的路由查找轉發?
  • 從業務視角,分析埋點思路
    對產品經理來說,輸出埋點文檔可以說是家常便飯了,但是對產品新人來說大多數人還是不知道埋點規範與注意事項,於是本文與大家分享從業務視角設計埋點的思路,希望對你有所幫助。
  • 實踐復盤:產品經理該怎麼寫埋點文檔?
    ​編輯導語:埋點,就是在用戶使用產品時記錄下用戶行為數據,以便後面對用戶行為進行數據分析。對於產品經理來說,工作內容可能經常涉及到埋點文檔。那麼,對於產品經理新人來說,如何上手寫埋點文檔呢?本文作者通過復盤自己的實操經歷,為我們做出了總結。
  • 5個步驟,學懂PC站和H5站數據埋點
    埋點是網站和APP等產品進行日常改進及數據分析的數據採集基礎,我們主要用來採集用戶行為數據(例如:頁面訪問路徑,點擊了哪一個按鈕)進行數據分析,從而讓運營同學更加合理的安排運營計劃讓產品經理更好的優化產品路徑。現在市面上有很多第三方埋點服務商,百度統計、友盟、growingIO 等。
  • 可靠與完善 華為S7706智能路由交換機
    【IT168 北京行情】路由交換機在企業網絡中是不可分割的一部份。現如今,隨著網絡技術的快速推進,路由交換機也展現出更完善的一面。今天為大家帶來的華為S7706智能路由交換機,就是一款智能式產品。
  • 組件間跳轉 & ARouter路由
    但是組件化中,兩個功能模塊是不存在直接依賴關係的(通過baseModule間接依賴),那麼包裝intent時就會發現引用不了其他module中的activity類(xxx.class)。ARouter路由跳轉原理借用計算機網絡中的路由器概念,將各組件看成不同的區域網,通過路由做中轉站,這個中轉站可以攔截一些不安全的跳轉,或者設定一些特定的攔截服務;路由和原生跳轉的對比:1.
  • 數據埋點怎麼做,你知道嗎?
    編輯導語:對於數據分析師來說,好的數據源是數據分析的根基,採集到的數據質量決定了數據廣度和質量,影響著分析報告的所有環節。在獲得高質量數據的過程中,數據埋點這一動作尤為必要,這是針對特定用戶行為或事件進行捕獲、處理和發送的相關技術及其實施過程。今天本文作者就為我們總結,如何做好數據埋點。
  • IP路由基礎,直連路由,靜態路由,動態路由,FIB,一分鐘了解下
    路由表簡介RIB(Routing Information Base,路由信息庫),是一個集中管理路由信息的資料庫,包含路由表信息以及路由周邊信息(路由迭代信息、路由共享信息以及路由擴展信息)等。路由器通過對路由表進行優選,把優選路由下發到 FIB(Forwarding Information Base,轉發信息庫)表中,通過 FIB 表指導報文轉發。
  • 一個簡單的例子讓你讀懂數據埋點
    不同屬性的多個事件應該命名成多個埋點事件ID,此時也儘量不用Key-Value的形式埋點。我們來看一個例子,就能明白這個原則的重要性。例如,美團上線了活動A和活動B兩個活動,都在酒店和旅遊的入口頁面進行了Banner展現,想要知道這兩個活動的用戶訪問情況,應該如何埋點呢?小李和小王針對這個問題分別設計了兩種埋點方案,我們分別看一下兩者的方案。
  • 認識數據埋點:基本屬性及流程
    數據埋點,對於產品迭代而言,有很重要的指向意義。本文從常規埋點屬性和常規埋點流程兩個方面帶我們認識了數據埋點。數據埋點屬於數據採集的階段,是網際網路分析業務閉環中的起點,為之後的許多日常及專題的分析提供數據源。本篇文章從兩個角度來闡述數據埋點的預備知識。橫向角度為,每個埋點事件可以埋一些什麼內容(用戶、時間、地點、方式及內容等),即埋點指標的基本屬性。
  • 盤點信息收集!信息收集的工具你聽過幾種
    被動信息搜集被動信息收集:不與目標直接交互,通過公開渠道可獲得的可用的信息。信息收集內容:IP位址段 、域名信息 、郵件地址 、文檔圖片數據 、公司地址 、公司組織架構 、聯繫電話 / 傳真號碼 、人員姓名 / 職務 、目標系統使用的技術架構 、公開的商業信息。
  • 大戶型的無線漫遊方案,mesh路由和ac+ap組合方式哪種好?
    於是就有了很多無線漫遊方案:你拿著手機從客臥走到主臥, 手機連接的AP可能會自動切換, 這個無需人工幹預設備切換AP的過程就是無線漫遊。mesh路由器和AC+AP組合是目前兩種比較成熟的無線漫遊方案。今天就來說一說這兩種方案的優劣。