We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
在开发中经常会遇到需要将配置数据转换成代码的情况,如果只有几个配置的话还好, ( ̄▽ ̄)~* 我们直接 ctrl + c、ctrl + v 操作就好了。(ಥ_ಥ) 然而,产品大佬通常只会甩一个几百行数据的谷歌表格给到前端,让前端自行录入奖励配置、图片配置等映射关系。 o(´^`)o作为一枚有追求(能动脑就不动手)的切图仔,肯定不能一行行录入或者复制到记事本改数据格式的,费时耗力不说还容易出错。若针对每个谷歌表格都写一个读取脚本,显然也是不可取的(每次都要改代码也不行),所以这时候就需要一个高效的开发工具可以满足:
( ̄▽ ̄)~*
ctrl + c
ctrl + v
(ಥ_ಥ)
o(´^`)o
Json
Array
综上,打算撕一个浏览器插件工具来提效我们的研发,让开发者把时间花在更有意义的事情上。
作为还没入门过浏览器插件开发的萌新,于是就去扫盲了下 chrome 插件开发的基础知识:
chrome-plugin-demo ├── background.js // 后台执行脚本 ├── images │ └── 128.png // 插件图标 ├── manifest.json // 清单文件,用于配置插件相关信息,声明脚本事件、用途,声明资源信息等 ├── popup.html // 点击插件图标后的弹窗页面 └── popup.js // 弹窗页面执行的 js
background
content_scripts
popup
manifest.json
加载已解压的插件
Jcanno/vue-chrome-extension
browser_action
webpack hot reload
chrome.tabs.query
chrome.tabs.reload
首先,我们先在 manifest.json 配置下插件相关的信息:
"manifest_version": 2, // 清单版本 "name": "chrome-sheetToCode", // 应用名称 "description": "transform sheet to code", // 应用描述,会显示在插件管理页面中 "version": "1.0.0", // 插件版本号 "browser_action": { // 浏览器工具栏配置 "default_title": "chrome-sheetToCode", // 弹窗标题 "default_icon": "assets/logo.png", // 插件图标 "default_popup": "popup.html" // 点击插件图标后,显示的UI弹窗页面 }, "icons": { // 不同尺寸下的图标 "16": "assets/logo.png", "48": "assets/logo.png", "128": "assets/logo.png" }
background 是运行在插件后台的脚本,整个浏览器插件的生命周期都会存在,且提供了丰富的 chrome API 供调用,这里配置一下我们的background脚本文件(虽然没用到):
// 后台执行脚本文件配置 "background": { "scripts": ["js/background.js"] },
然后我们再配置下content_scripts, content_scripts是页面执行脚本,属于页面的一部分,只是浏览器在打开页面的时候自动帮你执行而已,跟页面共用一个 dom,使得我们可以操作页面元素,在第三方网页执行我们自己的 js 代码。我们还可以自定义 js 加载的条件,这里我们配成谷歌表格的域名 https://docs.google.com/,说明只有匹配到这个域名的时候才会执行我们的 js,且在页面加载完成后进行执行:
https://docs.google.com/
"content_scripts": [ { "matches": [ "https://docs.google.com/*" // 映射到谷歌文档的域名才会加载js ], "css": [ "css/content.css" // css 路径 ], "js": [ "js/content.js" // 注入的 js 文件路径 ], "run_at": "document_end" // js 文件加载的时机 } ],
由于弹窗页面其实只提供了一个入口,用来触发我们注入到页面的代码,所以这里只写了一个按钮来控制页面侧边栏的显示隐藏:
<template> <div> <button id="open-btn" @click="handleClick">open panel</button> </div> </template>
#open-btn { color: black; border-radius: 20px; height: 40px; width: 100px; padding: 10px 20px; margin: 20px auto; display: flex; align-items: center; justify-content: center; }
再声明下点击回调,使用 chrome.tabs.sendMessage 来与当前选中页面的 content_scripts 脚本进行通信:
chrome.tabs.sendMessage
methods: { handleClick() { chrome.tabs.query( { active: true, currentWindow: true }, tabs => { let message = { info: "open-panel" }; chrome.tabs.sendMessage(tabs[0].id, message, () => { }); } ); } },
写完之后长这样:
首先我们写 UI 界面,让它position:fixed;固定在谷歌表格的最右边、最顶层,并给个关闭按钮: 然后在这个侧边栏渲染到 dom 的时候使用 chrome.runtime.onMessage 添加事件监听,用来接受 popup 弹窗的指令:
position:fixed;
chrome.runtime.onMessage
mounted() { chrome.runtime.onMessage.addListener(request => { if (request.info === "open-panel") { // 显示侧边栏面板 this.showPanel = true; // 调用页面的 focus 来关闭 popup window.focus(); } }); }
监听整个谷歌文档页面的 copy 事件:
created() { document.addEventListener("copy", this.copyEvent); }
待触发ctrl+c回调后我们就可以从用户的剪切板拿到选中的单元格元素,并给到封装好的 sheetToCode 插件来进行数据格式化处理:
ctrl+c
sheetToCode
copyEvent(event) { var clipboardData = event.clipboardData || window.clipboardData; if (!clipboardData) { return; } var text = clipboardData.getData("text/html"); if (!text) { return; } const result = sheetToCode(text); // 将处理后的返回结果更新到当前实例 this.data = result; }
封装 sheetToCode 插件来应对多种情况的数据格式处理:
export default function htmlTransform(text) { const arr = htmlToArr(text); if (arr.length === 0) { alert( "操作可能失败!如果文档表格有背景色,请将删除背景色或者将该文档**剔除格式**拷贝到新文档" ); } const dbkeyJson_row = arrToJson_doublekey(arr, "row"); const dbkeyJson_col = arrToJson_doublekey(arr, "col"); const json_row = arrToJson(arr, "row"); const json_col = arrToJson(arr, "col"); const dbkeyPhpRow = dbkeyJsonToPhpCode(dbkeyJson_row); const dbkeyPhpCol = dbkeyJsonToPhpCode(dbkeyJson_col); const jsonPhpRow = jsonToPhpCode(json_row); const jsonPhpCol = jsonToPhpCode(json_col); return { dbkeyJson_row, dbkeyJson_col, json_row, json_col, dbkeyPhpRow, dbkeyPhpCol, jsonPhpRow, jsonPhpCol, xmlObj, }; }
我们从用户剪切板拿到表格数据后,可以通过遍历每个单元格生成带详细信息的数组列表:
function htmlToArr(text) { let dom = document.createElement(`div`); dom.innerHTML = text; dom = dom.querySelector("table tbody"); if (!dom) { return []; } // raw arr const cellArr = Array.prototype.map.call(dom.children || [], (it) => { return Array.prototype.map.call(it.children || [], (cell) => { return { row: cell.getAttribute("rowspan") - 0 || 1, col: cell.getAttribute("colspan") - 0 || 1, val: cell.innerText, }; }); }); // map arr for (let i = 0; i < cellArr.length; i++) { const row = cellArr[i]; for (let j = 0; j < row.length; j++) { const cell = row[j]; const id = cell.id || `${i}-${j}`; if (cell.col > 1) { row.splice(j + 1, 0, { ...cell, id, col: cell.col - 1, }); } if (cell.row > 1) { cellArr[i + 1].splice(j, 0, { ...cell, id, row: cell.row - 1, }); } row[j] = { id, val: cell.val, }; } } return cellArr; }
生成普通 json 格式的数据:
// 单键json function arrToJson(arr, major = "col") { if (arr.length < 2 || arr[0].length < 2) { return {}; } if (major === "col") { return arr.reduce((res, cur) => { res[cur[0].val] = cur.slice(1).map((it) => it.val); return res; }, {}); } if (major === "row") { const body = arr.slice(1); return arr[0].reduce((res, cur, idx) => { res[cur.val] = body.map((it) => it[idx].val); return res; }, {}); } }
因为在生成的数据格式中,还有键值对的映射形式,所以我们还需要处理合并单元格的情况:
// 双键json // row0,col0不应该有重复键 todo function arrToJson_doublekey(arr, major = "col") { if (arr.length < 2 || arr[0].length < 2) { return {}; } if (major === "row") { const body = arr.slice(1); const obj = arr[0].slice(1).reduce((res, cur, idx) => { const colsObj = body.reduce((cols, it) => { cols[it[0].val] = it[idx + 1].val; return cols; }, {}); res[cur.val] = colsObj; return res; }, {}); return obj; } else { const subKey = arr[0].slice(1); const body = arr.slice(1); const obj = body.reduce((res, cur) => { const rowObj = cur.slice(1).reduce((rows, it, idx) => { rows[subKey[idx].val] = it.val; return rows; }, {}); res[cur[0].val] = rowObj; return res; }, {}); return obj; } }
适配一下生成 php 代码格式的数据:
// 双键json转php function dbkeyJsonToPhpCode(json) { const keys = Object.keys(json); const subKeys = Object.keys(json[keys[0]]); return keys .map((it) => { const item = json[it]; const val = subKeys .map( (subKey) => `\t"${(subKey || "").replace(/"/g, '\\"')}"=>"${( item[subKey] || "" ).replace(/"/g, '\\"')}"` ) .join(",\n"); return `"${(it || "").replace(/"/g, '\\"')}" => [\n${val}\n]`; }) .join(",\n"); } // 单键json转php function jsonToPhpCode(json) { const keys = Object.keys(json); const rows = keys .map((it) => { const item = json[it]; return `\t"${(it || "").replace(/"/g, '\\"')}" => [${item .map((it) => `"${(it || "").replace(/"/g, '\\"')}"`) .join(", ")}]`; }) .join(",\n"); return `[\n${rows}\n]`; }
最后来看一下我们最终生成的数据格式: php 代码格式:
我们可以使用crx来进行插件的打包,首次使用浏览器打包的话会生成 .pem 密钥,这个对我们之后发布到插件商城、更新插件版本都要用到,所以需要妥善备份。这里我们可以直接运行 crx 脚本来进行打包:
crx
npm run build:crx
const fs = require("fs"); const path = require("path"); const manifest = require(path.resolve(__dirname, "../chrome/manifest.json")); const ChromeExtension = require("crx"); const crxName = `${manifest.name}-v${manifest.version}.crx`; const crx = new ChromeExtension({ privateKey: fs.readFileSync(path.resolve(__dirname, "../../dist.pem")), }); crx .load(path.resolve(__dirname, "../../dist")) .then((crx) => crx.pack()) .then((crxBuffer) => { fs.writeFile(crxName, crxBuffer, (err) => err ? console.error(err) : console.log(`>>>>>>> ${crxName} <<<<<<< 已打包完成`) ); }) .catch((err) => { console.error(err); });
本来打算发布到谷歌商城的,但发现需要绑定信用卡支付,再缴纳个 $5 来进行开发者注册。觉得太麻烦就算了,反正解压后的代码也同样可以加载并使用,此工具也更新了使用文档到公司内部 wiki 供大伙使用。
到此我们的浏览器插件算是开发完成了,本人也是从上午还是 0 基础开始的浏览器插件开发,通过一顿 google 的学习、参考多方入门文章、官方文档,到下午插件撸出来可以正常使用才正式完工。若文中有理解/描述不当处欢迎及时指正,共同交流学习。 同时也印证我们身为前端开发者的学习能力、资源检索能力、总结输出能力也需要在实践中不断培养和锻炼,这些都会在今后的职业生涯中不断累积个人的影响力,提升自己的核心竞争力。
The text was updated successfully, but these errors were encountered:
请问选用转换为php格式的原因是项目中用的是php吗?
php
Sorry, something went wrong.
No branches or pull requests
背景
在开发中经常会遇到需要将配置数据转换成代码的情况,如果只有几个配置的话还好,
![four.png](https://camo.githubusercontent.com/86543df56760b81c1cd4bc25ad28eb1103cbd4f97a4d5c7a9659f4cb52a774c2/687474703a2f2f747661312e73696e61696d672e636e2f6c617267652f303037684f554b3167793167767534756939337a376a33313463306570616e622e6a7067)
( ̄▽ ̄)~*
我们直接ctrl + c
、ctrl + v
操作就好了。(ಥ_ಥ)
然而,产品大佬通常只会甩一个几百行数据的谷歌表格给到前端,让前端自行录入奖励配置、图片配置等映射关系。o(´^`)o
作为一枚有追求(能动脑就不动手)的切图仔,肯定不能一行行录入或者复制到记事本改数据格式的,费时耗力不说还容易出错。若针对每个谷歌表格都写一个读取脚本,显然也是不可取的(每次都要改代码也不行),所以这时候就需要一个高效的开发工具可以满足:ctrl + c
,就得到我们需要的数据格式Json
或Array
。综上,打算撕一个浏览器插件工具来提效我们的研发,让开发者把时间花在更有意义的事情上。
准备工作
作为还没入门过浏览器插件开发的萌新,于是就去扫盲了下 chrome 插件开发的基础知识:
什么是 chrome 插件?
chrome 插件能做什么?
chrome 插件由什么组成?
background
、content_scripts
、popup
之间的关系:chrome 插件开发流程是什么样的?
manifest.json
中声明插件信息、各资源、脚本用途等加载已解压的插件
功能来进行预览和查看。Jcanno/vue-chrome-extension
搭建好的开发模板。好处是我们可以通过开发 vue 页面的形式来写 UI 跟交互,模板会给我们打包成对应的manifest.json
、background
、content_scripts
、browser_action
等。webpack hot reload
的方案,原理是通过chrome.tabs.query
去查找当前的活动标签页,再使用chrome.tabs.reload
进行刷新页面,不用每次保存完再去按 F5 刷新页面跟插件了。着手开发
1、基础配置
首先,我们先在
manifest.json
配置下插件相关的信息:background
是运行在插件后台的脚本,整个浏览器插件的生命周期都会存在,且提供了丰富的 chrome API 供调用,这里配置一下我们的background
脚本文件(虽然没用到):然后我们再配置下
content_scripts
,content_scripts
是页面执行脚本,属于页面的一部分,只是浏览器在打开页面的时候自动帮你执行而已,跟页面共用一个 dom,使得我们可以操作页面元素,在第三方网页执行我们自己的 js 代码。我们还可以自定义 js 加载的条件,这里我们配成谷歌表格的域名https://docs.google.com/
,说明只有匹配到这个域名的时候才会执行我们的 js,且在页面加载完成后进行执行:2、功能开发
开发 popup 弹窗
由于弹窗页面其实只提供了一个入口,用来触发我们注入到页面的代码,所以这里只写了一个按钮来控制页面侧边栏的显示隐藏:
再声明下点击回调,使用
chrome.tabs.sendMessage
来与当前选中页面的content_scripts
脚本进行通信:写完之后长这样:
![popup.png](https://camo.githubusercontent.com/5e9a32d1176fe1e0accbce68613dd0a834ff28697d567813f5b7f1bb36c98e68/687474703a2f2f747661312e73696e61696d672e636e2f6c617267652f303037684f554b31677931677675333939716274346a3330396430346e3379772e6a7067)
开发
content_scripts
首先我们写 UI 界面,让它
![first.png](https://camo.githubusercontent.com/747679ba001170425087293127311a2af5d1a6e146b7c0853c9e962efb01ed89/687474703a2f2f747661312e73696e61696d672e636e2f6c617267652f303037684f554b31677931677675336474697466666a3330636a30683564696e2e6a7067)
position:fixed;
固定在谷歌表格的最右边、最顶层,并给个关闭按钮:然后在这个侧边栏渲染到 dom 的时候使用
chrome.runtime.onMessage
添加事件监听,用来接受popup
弹窗的指令:监听整个谷歌文档页面的 copy 事件:
待触发
ctrl+c
回调后我们就可以从用户的剪切板拿到选中的单元格元素,并给到封装好的sheetToCode
插件来进行数据格式化处理:封装
sheetToCode
插件来应对多种情况的数据格式处理:我们从用户剪切板拿到表格数据后,可以通过遍历每个单元格生成带详细信息的数组列表:
生成普通 json 格式的数据:
因为在生成的数据格式中,还有键值对的映射形式,所以我们还需要处理合并单元格的情况:
适配一下生成 php 代码格式的数据:
最后来看一下我们最终生成的数据格式:
![second.png](https://camo.githubusercontent.com/8e23fdc72e843154c26f0992d13067e829618257d5d137e8a6883860bb3126b4/687474703a2f2f747661312e73696e61696d672e636e2f6c617267652f303037684f554b31677931677675343639763866356a3331306f306e343767722e6a7067)
![third.png](https://camo.githubusercontent.com/1c441648e5c4788b3e109d0bb0e1d2c129d0283837727fd956d8188630124df4/687474703a2f2f747661312e73696e61696d672e636e2f6c617267652f303037684f554b3167793167767534366b306d756c6a33313039306e623136712e6a7067)
php 代码格式:
打包
我们可以使用
crx
来进行插件的打包,首次使用浏览器打包的话会生成 .pem 密钥,这个对我们之后发布到插件商城、更新插件版本都要用到,所以需要妥善备份。这里我们可以直接运行crx
脚本来进行打包:本来打算发布到谷歌商城的,但发现需要绑定信用卡支付,再缴纳个 $5 来进行开发者注册。觉得太麻烦就算了,反正解压后的代码也同样可以加载并使用,此工具也更新了使用文档到公司内部 wiki 供大伙使用。
总结
到此我们的浏览器插件算是开发完成了,本人也是从上午还是 0 基础开始的浏览器插件开发,通过一顿 google 的学习、参考多方入门文章、官方文档,到下午插件撸出来可以正常使用才正式完工。若文中有理解/描述不当处欢迎及时指正,共同交流学习。
同时也印证我们身为前端开发者的学习能力、资源检索能力、总结输出能力也需要在实践中不断培养和锻炼,这些都会在今后的职业生涯中不断累积个人的影响力,提升自己的核心竞争力。
The text was updated successfully, but these errors were encountered: