最近探索的让ChatGPT写代码的心得。背景:在一个第三方网站上开发chrome插件,方便用户摘录信息。功能是在宿主网页上保存操作,最终发到background.js上调用接口保存到我方服务器。
谷歌插件的各部分通信逻辑限制,宿主网页上的vue不能直接调用background.js,因为是两个不同的运行环境, 只能通过插件api的通信方式进行通信。 谷歌插件(Chrome Extension)的技术知识这里不做过多介绍,可以参考谷歌官方网站developer.chrome.com/docs/extens…
宿主上的hack vue 由插件通过srcript标签注入,跟宿主网页是一样的运行环境。它跟content.js 通信要通过window.PostMessage的方式,见上图中的1和3通信。 content.js不建议运行占用时间长的任务,丢到background.js里执行,这两者的通信方式采用插件的chrome.runtime.SendMessage方式,见上图中的2。
如果只是让AI写一个函数,一小段代码这样的事情, 还要自己组织梳理各个函数之间的参数传参和结果保存,未免太琐碎。于是整理出一段逻辑,让AI自己去写吧。下面是一个问问题的示例。
(1)先写content.js到background.js之间的这部分逻辑
背景chrome扩展程序,请写代码完成如下功能。
1.在content.js中发送消息给background.js ,命令名为uploadSuit
消息内容包括 图片链接imgUrl,armpx, sex, enName, cnName
2.background.js接受到uploadSuit命令后,执行如下处理
(1)先获取xxx.com的cookie,等下作为请求api的cookie
(2)然后下载imgUrl的图像,并通过https://xxx.com/apiv1/uploadpic上传到服务器,服务器返回类似这样的json
{"code":"0","data":"2307/ec9c1b37-2e8f-11ee-bc51-061e5f54070f.png","msg":"succ"}
其中data为服务器图片存储地址
(3)执行保存,参考代码如下。补充上错误处理
fetch("https://xxx.com/apiv1/addsuit", {
"headers": {
"accept": "application/json, text/plain, */*",
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
"content-type": "application/json",
"sec-ch-ua": "\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Google Chrome\";v=\"114\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"macOS\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin"
},
"referrer": "https://xxx.com/",
"referrerPolicy": "strict-origin-when-cross-origin",
"body": "{\"enName\":\"Can you feel the Kenergy\",\"cnName\":\"你能感觉到Kenergy吗\",\"armpx\":3,\"sex\":0,\"img\":\"2307/ec9c1b37-2e8f-11ee-bc51-061e5f54070f.png\",\"onsale\":1}",
"method": "POST",
"mode": "cors",
"credentials": "include"
});
(4)成功后返回对象给content.js, 结果为{code:0, msg""},其中code=0代表成功,其他为失败, 在第(2)(3)步,有任何错误,中断并返回结果给content.js
ChatGPT给出的回答:
在content.js中,你可以使用chrome.runtime.sendMessage发送消息到background.js。示例代码如下:
chrome.runtime.sendMessage({
command: "uploadSuit",
data: {
imgUrl: 'http://example.com/image.jpg',
armpx: 2,
sex: 'male',
enName: 'John',
cnName: '约翰'
}
}, function(response) {
console.log(response);
});
然后,在background.js中,你可以使用chrome.runtime.onMessage.addListener来监听并处理这些消息。示例代码如下:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.command === 'uploadSuit') {
handleUploadSuit(request.data, sendResponse);
return true; // 返回true表示你将异步回复消息,这样发送消息的页面就不会立刻关闭连接。
}
});
function handleUploadSuit(data, sendResponse) {
// 获取cookie
chrome.cookies.get({url: 'https://xxx.com', name: 'cookieName'}, function(cookie) {
if (chrome.runtime.lastError) {
sendResponse({code: 1, msg: chrome.runtime.lastError.message});
return;
}
// 下载图片并上传到服务器
fetch(data.imgUrl)
.then(response => response.blob())
.then(blob => {
const formData = new FormData();
formData.append('file', blob);
return fetch('https://xxx.com/apiv1/uploadpic', {
method: 'POST',
body: formData,
headers: {'Cookie': cookie.value}
});
})
.then(response => response.json())
.then(result => {
if (result.code !== '0') {
throw new Error(result.msg);
}
// 保存数据
return fetch('https://xxx.com/apiv1/addsuit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Cookie': cookie.value
},
body: JSON.stringify({
enName: data.enName,
cnName: data.cnName,
armpx: data.armpx,
sex: data.sex,
img: result.data,
onsale: 1
})
});
})
.then(response => response.json())
.then(result => {
if (result.code !== '0') {
throw new Error(result.msg);
}
sendResponse({code: 0, msg: ''});
})
.catch(error => {
sendResponse({code: 1, msg: error.message});
});
});
}
这是一个基本的示例,你可能需要根据实际情况进行调整。例如,你可能需要处理cookie不存在的情况,或者添加更多的错误处理代码。
里面总共有3个异步函数,promise的方法嵌套多层异步看起来也很丑, 于是让ChatGPT给出更优雅的解决方案。问:
handleUploadSuit 可以改为同步的写法吗
ChatGPT回答还是不错的:
由于在调用API接口时涉及到了网络请求,这是一个异步操作,所以handleUploadSuit函数本身需要保持异步。但你可以使用async/await语法来让代码看起来更像同步的样子。以下是使用async/await语法的示例:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.command === 'uploadSuit') {
handleUploadSuit(request.data)
.then(result => sendResponse(result))
.catch(error => sendResponse({code: 1, msg: error.message}));
return true; // 返回true表示你将异步回复消息,这样发送消息的页面就不会立刻关闭连接。
}
});
async function handleUploadSuit(data) {
// 获取cookie
const cookie = await new Promise(resolve => chrome.cookies.get({url: 'https://xxx.com', name: 'cookieName'}, resolve));
if (chrome.runtime.lastError) {
throw new Error(chrome.runtime.lastError.message);
}
// 下载图片并上传到服务器
const response = await fetch(data.imgUrl);
const blob = await response.blob();
const formData = new FormData();
formData.append('file', blob);
const uploadResponse = await fetch('https://https://xxx.com/apiv1/uploadpic', {
method: 'POST',
body: formData,
headers: {'Cookie': cookie.value}
});
const uploadResult = await uploadResponse.json();
if (uploadResult.code !== '0') {
throw new Error(uploadResult.msg);
}
// 保存数据
const saveResponse = await fetch('https://https://xxx.com/apiv1/addsuit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Cookie': cookie.value
},
body: JSON.stringify({
enName: data.enName,
cnName: data.cnName,
armpx: data.armpx,
sex: data.sex,
img: uploadResult.data,
onsale: 1
})
});
const saveResult = await saveResponse.json();
if (saveResult.code !== '0') {
throw new Error(saveResult.msg);
}
return {code: 0, msg: ''};
}
上面给出的回答,有两点不符合预期的:(1)是uploadResult.code !== '0'类似这样的语句判断, 我给出的提示是整数0, 它把code当做字符串来全等判断了 (2)抛出的错误我期望除了msg,还要有code信息 throw new Error(result.msg); 第(1)点, 自己改一下就好了, 第(2)点, 再问一下ChatGPT。
throw new Error(saveResult.msg);中的Error能增加saveResult.code吗
ChatGPT回答:
JavaScript的Error类型默认只包含一个message字段,不过你可以创建自定义的错误类型来包含更多的信息。以下是一个创建自定义错误类型的示例:
class CustomError extends Error {
constructor(message, code) {
super(message);
this.code = code;
}
}
然后,你可以像下面这样抛出并处理自定义错误:
try {
throw new CustomError('Something went wrong', 123);
} catch (error) {
if (error instanceof CustomError) {
console.log('Caught a custom error:', error.message, error.code);
} else {
console.log('Caught a regular error:', error.message);
}
}
所以,你可以在handleUploadSuit函数中像下面这样抛出自定义错误:
if (saveResult.code !== '0') {
throw new CustomError(saveResult.msg, saveResult.code);
}
然后,在消息监听器中,你可以像下面这样处理自定义错误:
handleUploadSuit(request.data)
.then(result => sendResponse(result))
.catch(error => {
if (error instanceof CustomError) {
sendResponse({code: error.code, msg: error.message});
} else {
sendResponse({code: 1, msg: error.message});
}
});
完全可以解决我的问题,太棒了。
(2)再写hack vue到content.js这部分逻辑
在问下面这个问题的时候ChatGPT的API卡了, 那我去试试Claude吧,也挺好用的
背景chrome扩展程序,请写代码完成如下功能。
1.在vue中用window.postmessage给content.js发消息,并接收处理结果
消息参数如下
{
command: "uploadSuit",
data: {
imgUrl: this.img,
armpx: this.armpx,
sex: this.sex,
enName: this.enName,
cnName: this.cnName
}
}
2.在content.js接收消息,并把消息通过runtime发送给background.js,接收处理结果,透传给第1步中的vue
Claude回答
好的,代码实现如下:
Vue组件中发送消息:
// Vue组件
const data = {
imgUrl,
armpx,
sex,
enName,
cnName
}
window.postMessage({
command: 'uploadSuit',
data
}, '*');
window.addEventListener('message', evt => {
// 收到background的响应
const {result} = evt.data;
})
content.js中转发消息:
// content.js
window.addEventListener('message', evt => {
const {command, data} = evt.data;
chrome.runtime.sendMessage({
command,
data
}, response => {
window.postMessage({
result: response
}, '*');
})
})
background.js处理上传:
// background.js
chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
const {command, data} = req;
if(command === 'uploadSuit') {
// 处理上传逻辑
const result = {success: true};
sendResponse(result);
}
})
这样通过postMessage和runtime的消息机制,可以在不同页面间传递消息和结果。
这样的代码质量也可以,基本上拿来就可以用了。
(3)在整合一下(1)(2)中的通信
1.在hack vue中发起保存,由按钮触发
//hack vue
onOk:function(){
console.log("onOk")
this.okLoading = true
//发消息给background , 保存皮肤,要经过content.js中转
window.postMessage({
command: "reqUploadSuit",
data: {
imgUrl: this.img,
armpx: parseInt(this.armpx),
sex: parseInt(this.sex),
enName: this.enName,
cnName: this.cnName
}
}, '*');
},
2.在content.js中接收1的信息并中转给background.js, 收到background.js返回的response信息再通过(postMessage)发给hack vue
//content.js
function initSaveSuitMessage() {
window.addEventListener('message', evt => {
const { command, data } = evt.data;
if (command != "reqUploadSuit") {
return
}
console.log("initSaveSuitMessage", evt.data)
chrome.runtime.sendMessage({
command,
data
}, response => {
window.postMessage({
command: "uploadSuitResponse",
data: response
}, '*');
})
})
}
到此,借助ChatGPT和Claude, 原本要学习和探索至少两天的工作量,2小时解决了。撒花!