Tips又更新啦!要来看看吗~
前言
Tips的重大更新又来了,如果不知道Tips是什么的你,可以移步这里Tips——将你从频繁的文案修改中解救出来,如果看过这篇文章或者使用过这个平台的,那就可以直接往下看了。今天将向大家介绍Tips的新功能——自动埋点,之前如果想要使用Tips都是手动在代码中埋点,现在可以使用我们提供的cli
工具,使用简单的命令就可以自动埋点,然后就可以去修改你的文案了。感兴趣的你可以继续往下看哈,我将从如下几个方面来进行介绍。
1 设计初衷
如果使用过工具或看过之前的文章,你可能会因为如下几个问题而被劝退。
问题一
在平台创建了服务后,需要手动在自己的项目中进行埋点,任何文案都有被修改的可能,所以需要大量的埋点,无疑该操作是浪费时间的,所以可能因为手动埋点这个操作你放弃了这个便利的工具。
问题二
你接入平台的时候在第一次可能埋了大量的点,但是下次需要修改的并不是你之前埋点处的文案,你还需要在自己的代码中重新埋点,然后发起上线流程,重新打包上线,所以可能觉得这个平台并不能很好的解决目前遇到的问题而放弃使用它。
综上两个原因,就需要一个自动埋点的工具,代替手动埋点,从而进一步降低使用成本。
2 工具形态
首先Tips的使用方式就是在前端HTML标签上绑定特殊属性data-tip-id-"xxx"
,我们要实现的就是在标签上自动加上这个特殊属性。如下代码段所示:
<span>我是一个按钮</span>
埋点后变成
<span data-tip-id="xxx">我是一个按钮</span>
实现如上这个过程,我想很多人都想到了Babel
,它可以帮我们去做这个工作,也就是需要去实现一个Babel Plugin
来解析抽象语法树并修改节点。但是只有Plugin还是不够的,我们还要考虑到,如下几个问题:
- 插件应该在什么时候触发去执行埋点操作
这个过程应该成为用户可控的,所以最合理的应该是用户自己去手动触发,所以就需要一个简单的
命令行工具
,用户执行相关命令去触发埋点操作。
- 什么样的文件需要埋点,什么样的不需要
在项目中文件类型是多种多样的,就前端项目来说,我们的代码都在src目录下,但是也并不是所有的文件都需要被扫描埋点,因次就需要提供一个
config
文件来配置用户自己的埋点规则,比如埋点的入口和需要忽略的文件。
综上两个原因我们需要的是一个Babel Plugin + cli
这样的工具。
3 如何实现
上面已经确定了工具的形态,接下来的重点就是如何实现了。下面将按照用户的使用流程来慢慢展开。其实也很简单,就是两步。
工具初始化
// 初始化配置文件
fs.writeFileSync(
'./tips.config.js',
prettier.format(
`module.exports = ${JSON.stringify({
entry: 'src',
exclude: [],
})}`,
{
parser: 'typescript',
singleQuote: true,
trailingComma: 'es5',
}
),
'utf8'
);
在上面的示例代码中我们创建了一个名为tips.config.js
的文件,在文件中配置了简单的埋点入口和忽略文件选项,然后将它写入当前项目中。这样就完成了初始化操作,用户可以去修改这个文件来配置简单的埋点规则。
开始埋点
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
方法,并结合现成的babel plugin,来将需要转换的代码文件转换为AST(抽象语法树)
; - 然后用
@babel/traverse
来遍历该语法树,并利用buryPointPlugin
来修改AST,将它改为想要的样子,也就是在JSX节点上加上data-tip-id
属性; - 最后使用
@babel/generator
来将AST转化为最终的代码。
buryPointPlugin做了什么
const t = require('@babel/types');
// 构造属性节点
function buildAttribute(t, id) {
return t.jsxAttribute(t.jSXIdentifier('data-tip-id'), t.stringLiteral(id));
}
// 构造openElement节点
function buildTipsIdAttributeOpeningElement(path, t, id) {
let buildTagAttributes = path.node.attributes;
return t.jSXOpeningElement(
path.node.name, // object: JSXMemberExpression | JSXIdentifier
buildTagAttributes,
path.node.selfClosing // 是否自闭合,不设的话默认为false,需要根据标签原有特性去设置
);
}
module.exports = function() {
return {
JSXOpeningElement: (path) => {
const children = path.parent.children;
const tipsUniqId = `${getSeconds()}-${_.uniqueId()}`;
const newOpeningElement = buildTipsIdAttributeOpeningElement(path, t, tipsUniqId);
path.replaceWith(newOpeningElement);
},
}
}
上述是最核心的一部分代码,也就是构造带有data-tip-id
的新节点去替换原来的节点。有同学可能要问了,我怎么知道自己构造的节点类型需要哪些属性,其实这些都可以去babel官网查看。所以写插件时的重点工作应该是确定插件要做的事,然后将源语法树,修改为目标语法树,在这里推荐一个很好用的工具,可以很方便的查看语法树的结构:查看工具。
4 如何定制规则
用过Tips的都知道,我们在编辑文案的时候会在页面上看到红色的编辑按钮,如果不做埋点控制,那么打开开关你将会看到满屏的红色按钮,那体验简直是灾难性的。所以在扫到的代码中,并不是所有的标签都需要埋点,所以需要定义一些特殊的规则,避免埋太多的无用点。这些规则考虑到通用性和实现的复杂度的问题,并没有开放在用户的配置文件中。
如下是所有的不埋点情况:
- 1 标签的子节点只有JSXElement
<div>
<span>我的父元素不埋点</span>
<span>我的父元素不埋点</span>
</div>
- 2、标签的子节点只有CallExpression
<div>{render()}</div>
- 3、标签的子节点只有ConditionalExpression
<div>{a ? <span>我是真</span> : <span>我是假</span>}</div>
4、标签的子节点只有LogicalExpression
<div>
{
a && <span>我是真</span>
}
</div>
- 5、标签有特殊属性
tips-bp-ignore
的不埋点
<div tips-bp-ignore>
我是被忽略的标签,不埋点
</div>
5 如何使用
说了这么多,那究竟如何使用呢?请往下看。
下载
npm i tips-burypoint-cli --save-dev
初始化
tips-bp init
初始化生成tips.config.js
文件,可进行相关配置。
开始埋点
tips-bp start
6 总结
在上述总结了Tips自动埋点工具的整个实现过程, 也附上了一些插件的实现源码,可以看到具体的coding并不难,难的是整个设计和思考的过程,感谢团队小伙伴给的改进建议,文章就写到这里,目前工具已经上传在npm上,希望有需要的你可以试着用用哈。
具体如何使用,请戳这里:https://github.com/didi/Tips
发表评论 (审核通过后显示评论):