前言
Tips的重大更新又來了债蜜,如果不知道Tips是什么的你梨州,可以移步這里Tips——將你從頻繁的文案修改中解救出來祖秒,如果看過這篇文章或者使用過這個(gè)平臺(tái)的诞吱,那就可以直接往下看了。今天將向大家介紹Tips的新功能——自動(dòng)埋點(diǎn)竭缝,之前如果想要使用Tips都是手動(dòng)在代碼中埋點(diǎn)房维,現(xiàn)在可以使用我們提供的cli
工具,使用簡(jiǎn)單的命令就可以自動(dòng)埋點(diǎn)抬纸,然后就可以去修改你的文案了咙俩。感興趣的你可以繼續(xù)往下看哈,我將從如下幾個(gè)方面來進(jìn)行介紹湿故。
1 設(shè)計(jì)初衷
如果使用過工具或看過之前的文章阿趁,你可能會(huì)因?yàn)槿缦聨讉€(gè)問題而被勸退。
問題一
在平臺(tái)創(chuàng)建了服務(wù)后坛猪,需要手動(dòng)在自己的項(xiàng)目中進(jìn)行埋點(diǎn)脖阵,任何文案都有被修改的可能,所以需要大量的埋點(diǎn)墅茉,無疑該操作是浪費(fèi)時(shí)間的独撇,所以可能因?yàn)槭謩?dòng)埋點(diǎn)這個(gè)操作你放棄了這個(gè)便利的工具。
問題二
你接入平臺(tái)的時(shí)候在第一次可能埋了大量的點(diǎn)躁锁,但是下次需要修改的并不是你之前埋點(diǎn)處的文案,你還需要在自己的代碼中重新埋點(diǎn)卵史,然后發(fā)起上線流程战转,重新打包上線,所以可能覺得這個(gè)平臺(tái)并不能很好的解決目前遇到的問題而放棄使用它以躯。
綜上兩個(gè)原因槐秧,就需要一個(gè)自動(dòng)埋點(diǎn)的工具啄踊,代替手動(dòng)埋點(diǎn),從而進(jìn)一步降低使用成本刁标。
2 工具形態(tài)
首先Tips的使用方式就是在前端HTML標(biāo)簽上綁定特殊屬性data-tip-id-"xxx"
颠通,我們要實(shí)現(xiàn)的就是在標(biāo)簽上自動(dòng)加上這個(gè)特殊屬性。如下代碼段所示:
<span>我是一個(gè)按鈕</span>
埋點(diǎn)后變成
<span data-tip-id="xxx">我是一個(gè)按鈕</span>
實(shí)現(xiàn)如上這個(gè)過程膀懈,我想很多人都想到了Babel
顿锰,它可以幫我們?nèi)プ鲞@個(gè)工作,也就是需要去實(shí)現(xiàn)一個(gè)Babel Plugin
來解析抽象語法樹并修改節(jié)點(diǎn)启搂。但是只有Plugin還是不夠的硼控,我們還要考慮到,如下幾個(gè)問題:
- 插件應(yīng)該在什么時(shí)候觸發(fā)去執(zhí)行埋點(diǎn)操作
這個(gè)過程應(yīng)該成為用戶可控的胳赌,所以最合理的應(yīng)該是用戶自己去手動(dòng)觸發(fā)牢撼,所以就需要一個(gè)簡(jiǎn)單的
命令行工具
,用戶執(zhí)行相關(guān)命令去觸發(fā)埋點(diǎn)操作疑苫。
- 什么樣的文件需要埋點(diǎn)熏版,什么樣的不需要
在項(xiàng)目中文件類型是多種多樣的,就前端項(xiàng)目來說捍掺,我們的代碼都在src目錄下撼短,但是也并不是所有的文件都需要被掃描埋點(diǎn),因次就需要提供一個(gè)
config
文件來配置用戶自己的埋點(diǎn)規(guī)則乡小,比如埋點(diǎn)的入口和需要忽略的文件阔加。
綜上兩個(gè)原因我們需要的是一個(gè)Babel Plugin + cli
這樣的工具。
3 如何實(shí)現(xiàn)
上面已經(jīng)確定了工具的形態(tài)满钟,接下來的重點(diǎn)就是如何實(shí)現(xiàn)了胜榔。下面將按照用戶的使用流程來慢慢展開。其實(shí)也很簡(jiǎn)單湃番,就是兩步夭织。
工具初始化
// 初始化配置文件
fs.writeFileSync(
'./tips.config.js',
prettier.format(
`module.exports = ${JSON.stringify({
entry: 'src',
exclude: [],
})}`,
{
parser: 'typescript',
singleQuote: true,
trailingComma: 'es5',
}
),
'utf8'
);
在上面的示例代碼中我們創(chuàng)建了一個(gè)名為tips.config.js
的文件,在文件中配置了簡(jiǎn)單的埋點(diǎn)入口和忽略文件選項(xiàng)吠撮,然后將它寫入當(dāng)前項(xiàng)目中尊惰。這樣就完成了初始化操作,用戶可以去修改這個(gè)文件來配置簡(jiǎn)單的埋點(diǎn)規(guī)則泥兰。
開始埋點(diǎn)
const prettier = require('prettier');
const parser = require('@babel/parser');
const traverse = require('@babel/traverse');
const generate = require('@babel/generator');
const buryPointPlugin = require('../plugin');
function buryPoint(filePath, options) {
const fileContent = fs.readFileSync(filePath, 'utf-8');
const ast = parser.parse(fileContent, {
sourceType: 'module',
plugins: [
'typescript',
'jsx',
],
});
traverse(ast, buryPointPlugin());
let { code } = generate(ast, {});
code = prettier.format(code, options.prettier);
fs.writeFileSync(filePath, code, { encoding: 'utf-8' });
}
在上述代碼中:
- 首先用
@babel/parser
的parse
方法弄屡,并結(jié)合現(xiàn)成的babel plugin,來將需要轉(zhuǎn)換的代碼文件轉(zhuǎn)換為AST(抽象語法樹)
鞋诗; - 然后用
@babel/traverse
來遍歷該語法樹膀捷,并利用buryPointPlugin
來修改AST,將它改為想要的樣子削彬,也就是在JSX節(jié)點(diǎn)上加上data-tip-id
屬性全庸; - 最后使用
@babel/generator
來將AST轉(zhuǎn)化為最終的代碼秀仲。
buryPointPlugin做了什么
const t = require('@babel/types');
// 構(gòu)造屬性節(jié)點(diǎn)
function buildAttribute(t, id) {
return t.jsxAttribute(t.jSXIdentifier('data-tip-id'), t.stringLiteral(id));
}
// 構(gòu)造openElement節(jié)點(diǎn)
function buildTipsIdAttributeOpeningElement(path, t, id) {
let buildTagAttributes = path.node.attributes;
return t.jSXOpeningElement(
path.node.name, // object: JSXMemberExpression | JSXIdentifier
buildTagAttributes,
path.node.selfClosing // 是否自閉合,不設(shè)的話默認(rèn)為false壶笼,需要根據(jù)標(biāo)簽原有特性去設(shè)置
);
}
module.exports = function() {
return {
JSXOpeningElement: (path) => {
const children = path.parent.children;
const tipsUniqId = `${getSeconds()}-${_.uniqueId()}`;
const newOpeningElement = buildTipsIdAttributeOpeningElement(path, t, tipsUniqId);
path.replaceWith(newOpeningElement);
},
}
}
上述是最核心的一部分代碼神僵,也就是構(gòu)造帶有data-tip-id
的新節(jié)點(diǎn)去替換原來的節(jié)點(diǎn)。有同學(xué)可能要問了覆劈,我怎么知道自己構(gòu)造的節(jié)點(diǎn)類型需要哪些屬性保礼,其實(shí)這些都可以去babel官網(wǎng)查看。所以寫插件時(shí)的重點(diǎn)工作應(yīng)該是確定插件要做的事墩崩,然后將源語法樹氓英,修改為目標(biāo)語法樹,在這里推薦一個(gè)很好用的工具鹦筹,可以很方便的查看語法樹的結(jié)構(gòu):查看工具铝阐。
4 如何定制規(guī)則
用過Tips的都知道,我們?cè)诰庉嬑陌傅臅r(shí)候會(huì)在頁面上看到紅色的編輯按鈕铐拐,如果不做埋點(diǎn)控制徘键,那么打開開關(guān)你將會(huì)看到滿屏的紅色按鈕,那體驗(yàn)簡(jiǎn)直是災(zāi)難性的遍蟋。所以在掃到的代碼中吹害,并不是所有的標(biāo)簽都需要埋點(diǎn),所以需要定義一些特殊的規(guī)則虚青,避免埋太多的無用點(diǎn)它呀。這些規(guī)則考慮到通用性和實(shí)現(xiàn)的復(fù)雜度的問題,并沒有開放在用戶的配置文件中棒厘。
如下是所有的不埋點(diǎn)情況:
- 1 標(biāo)簽的子節(jié)點(diǎn)只有JSXElement
<div>
<span>我的父元素不埋點(diǎn)</span>
<span>我的父元素不埋點(diǎn)</span>
</div>
- 2纵穿、標(biāo)簽的子節(jié)點(diǎn)只有CallExpression
<div>{render()}</div>
- 3、標(biāo)簽的子節(jié)點(diǎn)只有ConditionalExpression
<div>{a ? <span>我是真</span> : <span>我是假</span>}</div>
4奢人、標(biāo)簽的子節(jié)點(diǎn)只有LogicalExpression
<div>
{
a && <span>我是真</span>
}
</div>
- 5谓媒、標(biāo)簽有特殊屬性
tips-bp-ignore
的不埋點(diǎn)
<div tips-bp-ignore>
我是被忽略的標(biāo)簽,不埋點(diǎn)
</div>
5 如何使用
說了這么多何乎,那究竟如何使用呢句惯?請(qǐng)往下看。
下載
npm i tips-burypoint-cli --save-dev
初始化
tips-bp init
初始化生成tips.config.js
文件支救,可進(jìn)行相關(guān)配置抢野。
開始埋點(diǎn)
tips-bp start
6 總結(jié)
在上述總結(jié)了Tips自動(dòng)埋點(diǎn)工具的整個(gè)實(shí)現(xiàn)過程, 也附上了一些插件的實(shí)現(xiàn)源碼各墨,可以看到具體的coding并不難指孤,難的是整個(gè)設(shè)計(jì)和思考的過程,感謝團(tuán)隊(duì)小伙伴給的改進(jìn)建議欲主,文章就寫到這里邓厕,目前工具已經(jīng)上傳在npm上,希望有需要的你可以試著用用哈扁瓢。
具體如何使用详恼,請(qǐng)戳這里:https://github.com/didi/Tips