This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.
Concepts related to
This article focuses on how to achieve a simple micro channel small program without trace buried point scheme, so the conceptual knowledge of buried point do not do too much interpretation, we can Google. There are only two key issues we need to understand here.
What is a burial point
Buried point is a term in the field of data acquisition. Simply put, it is used to track some specific behaviors and events of users and record and report them. Common burial points are divided into three categories:
- Manual embedding (code embedding)
- Automatic embedding point (traceless embedding point)
- Visual burial point
The effect of buried point
I think it will be clearer in terms of users and their products:
- For users: The collected data can be analyzed to provide accurate information push and personalized recommendation to users. Of course, the behavioral information of users can also improve the operation experience of the product.
- For products: analyze the existing problems of products and provide follow-up optimization ideas by analyzing the user’s dwell time on each page and the interactive nodes and other information.
Reference link: xw.qq.com/partner/viv…
In fact, from the concept, the tracking of information and the reporting of information are completely independent. So we can design these two parts independently.
Ok, next is hand touch time!
The implementation process
The overall train of thought
First of all we need to know that in fact a small wechat program is aApp
And then thisApp
There are multiplePage
(page) andComponent
(Custom components).What’s interesting is that bothApp
Register, orPage
andComponent
The registration of each method is passed in the corresponding parameters(options)
(This form can also be seen in the figure above)
Let’s take the Page registration as an example, and write the registration method in the figure in a different way to fit our description.
const options = {
data: {
msg: 'Hello World',},// User-defined events
bindViewTap() {
wx.navigateTo({
url: '.. /logs/logs'}); },// Page has its own life cycle
onLoad() {
if (wx.getUserProfile) {
this.setData({
canIUseGetUserProfile: true}); }}};// Register the current Page, which is completed by passing the corresponding parameters into the Page method provided by wechat
Page(options);
Copy the code
The App and Component registers are similar, if you are interested.
So so so so!We can rewrite itApp
,Page
,Component
These three methods to implement itoptions
Some method and lifecycle monitoring on the body (Yeah, that’s kind of what it means. You know what I mean.)
Ok, with the general idea in mind, let’s first summarize and then start doing:
- To design a
tracker
Come to the rightApp
,Page
,Component
Method to override - To design a
reporter
Record the monitored event data and send it to the server
Tracker
Before we start to knock code to confirm that we realize how to use this buried point, only to determine a correct way of use, after the opening can be supplemented according to our corresponding function.
Under normal circumstances, if we want to modify the native method of wechat applet, we need to import our rewrite method in its entry file app.js to achieve this purpose
// app.js
import init from './track/index';
init({
ak: 'minapp-001'.url: 'http://baidu.com'.autoTrack: {
appLaunch: false.appHide: false.appShow: false.pageShow: false.pageHide: false.pageUnload: false.onShare: false,},// other
});
Copy the code
The config is tentative, and various configuration parameters can be added according to specific requirements. Here, we only set three attributes to ensure simplicity
- Ak: ensure the unique value of the buried point program
- Url: indicates the address of the server to which the obtained information needs to be uploaded
- AutoTrack: indicates the solution for enabling full buried points. The corresponding field is set to
true
Enable automatic burying information capture
Once we know how to use it, we can use it to determine how our code will unfold.
1. init
// Save the three native methods for later use
const collector = {
oldApp = App,
oldPage = Page,
}
Copy the code
const init = (config) = > {
/ / generated cid
if(! storage.get('cid')) {
storage.set('cid', getUUID());
}
// Initialize the user's custom configuration. Store is a global data warehouse.
if(config ! = =undefined) store.set('config', config);
// Override the App&Page method
App = (options) = > collector.oldApp(proxyAppOptions(options));
Page = (options) = > collector.oldPage(proxyPageOptions(options));
};
Copy the code
As you can see here, we’ve implemented the most critical step in our init method, which is rewriting the App and Page methods.
One thing to note here is that we need to make it clear that the purpose of overriding the method is to get some properties and methods of the options argument passed to it. Therefore, we will rewrite his options here, and then pass the rewritten results into the native App and Page methods for execution.
The Collector we defined above is intended to hold these native methods for easy invocation here. (Of course these attributes are not the only ones in the Collector, but some additional information will be collected later.)
2. proxyAppOptions
So let’s take a look at what the proxyAppOptions method does
/** * Overwrite App options *@param {*} Options The original options parameter *@returns The new options parameter */
const _proxyAppOptions = (options) = > {
// Inject a manual burying method into the App
options.$ta = {
// Call track directly from getApp()
track: $ta.track.bind(reporter),
// The visitor access UID defaults to 0, and users need to manually update their user IDS after logging in
login: (uid) = > this.login(uid),
};
// launch event listener
options.onLaunch = useAppLaunch(options.onLaunch);
// onShow event listener
options.onShow = useAppShow(options.onShow);
// onHide event listener
options.onHide = useAppHide(options.onHide);
return options;
};
Copy the code
UseAppLaunch; useAppShow; useAppLaunch; useAppShow; Let’s take a look at the implementation of these hooks, which are as simple as adding some logic to collect data at their own buried points
/ * * = = = = = = = = = = = = = = = = = = = = App event broker = = = = = = = = = = = = = = = = = = = = = = = = * /
export const useAppLaunch = (oldOnLunch) = >
_proxyHooks(oldOnLunch, function () {
const data = {
event: 'appLaunch',
path,
title,
timemap,
};
$ta.track('devices', data);
});
export const useAppShow = (oldOnShow) = >
_proxyHooks(oldOnShow, function () {
const data = {
event: 'appShow'}; $ta.track('devices', data);
});
export const useAppHide = (oldOnHide) = > {
_proxyHooks(oldOnHide, function () {
const data = {
event: 'appHide'}; $ta.track('devices', data);
});
};
/** * proxies the original method and executes the callback function *@param {*} Fn requires the proxy method *@param {*} Cb Callback */ to be executed
function _proxyHooks(fn = function () {}, cb) {
return function () {
// If the callback exists
if (cb) {
cb.apply(this.arguments);
}
// Execute the original function
fn.apply(this);
};
}
Copy the code
This method is used to implement the original callback, which is then passed in by proxyHooks. In this callback, we need to add some data that we need to embed.
$ta: $ta: $ta: $ta: $ta: $ta: $ta: $ta: $ta: $ta: $ta: $ta: $ta
3. proxyPageOptions
In fact, the page part and the above app do the same thing, is to do some processing for the life cycle, but page page in addition to the life cycle, there will be a lot of custom events, so we can take a look at this.
The custom events here are usually just click events.
// Page's original declaration cycle collection
const PAGE_LIFE_METHOD = [
'onLoad'.'onShow'.'onReady'.'onHide'.'onUnload'.'onPullDownRefresh'.'onReachBottom'.'onShareAppMessage'.'onShareTimeline'.'onAddToFavorites'.'onPageScroll'.'onResize'.'onTabItemTap'.'onSaveExitState',];/** * Rewrite the Page options argument *@param {*} Options The original options parameter *@returns The new options parameter */
const proxyPageOptions = (options) = > {
// ...
// Customize event listening
for (let prop in options) {
// Make sure it is a function and not a native lifecycle function
if (
typeof options[prop] == 'function' &&
!PAGE_LIFE_METHOD.includes(prop)
) {
// Override custom methods on optionsoptions[prop] = usePageClickEvent(options[prop]); }}return options;
};
Copy the code
The pageClickEvent hook does this. The pageClickEvent hook does this. The pageClickEvent hook does this
/** * listen for page click events *@param {*} OldEvent native custom page events */
export const pageClickEvent = (oldEvent) = >
_proxyHooks(oldEvent, function (e) {
if (e && e.type === 'tap') {
$ta.track('event', {
event: 'pageClick'.// ...}); }});Copy the code
Reporter
What this part does is encapsulate a request method that sends the traced information to the server. But here are a few things to consider:
- Buried network requests should not preempt the original event request, i.e. the business request should be sent first
- Buried information should be sent in order to facilitate analysis, for example, the small program show information should be sent after hide
- When the network fluctuates, if the buried information fails to be sent, we should cache the data and wait for the next transmission to ensure the integrity of the information
import store from '.. /store';
import { storage } from '.. /.. /utils';
import platform from '.. /platform';
import qs from 'qs';
class Reporter {
constructor() {
// A queue of trace messages to send
this.queue = [];
this.timerId;
}
/** * Track buried data *@param {*} Data Indicates the data to be reported */
track(type, data = {}) {
// Add some public information fields
data.t = type;
this.queue.push(qs.stringify(data));
if (!this.timerId) {
// In order not to affect the normal business request, here send our buried point information delay
this.timerId = setTimeout(() = > {
this._flush();
}, store.get('config').delay); }}/** * Performs tasks in the queue (sends trace information to the background) */
_flush() {
const config = store.get('config');
// make a request when there is data in the queue
if (this.queue.length > 0) {
const data = this.queue.shift();
platform.request({
// Request an address
url: config.url,
// The timeout period
timeout: config.request_timeout,
method: 'POST'.header: { 'content-type': 'application/x-www-form-urlencoded' },
data: {
ak: config.ak,
cid: storage.get('cid'),
ns: store.get('networkType'),
uid: storage.get('uid') | |0.data: Date.now(),
data,
},
// When TODO fails to be sent, the message is saved to the storage
success: () = > {},
fail: ({ errMsg }) = > {
console.error(errMsg);
},
complete: () = > {
// Send the next message when the execution is complete
this._flush(); }}); }else {
this.timerId = null; }}}export default new Reporter();
Copy the code
conclusion
This article is just a simple implementation of the small program buried point scheme, from the overall framework to describe, a lot of details are not involved, we can discuss what problems.
This article originated from my official account. We can scan code to pay attention to the usual will send some strange things HH