背景:
之前在网上找了好多chrome浏览器插件搭建环境的博客,但是发现都很糟糕,我的目标是实现ts + react + webpack进行插件开发,但是看的一些教程却非常的繁琐,需要一步一步创建文件、初始化等待,或者就是直接用react脚手架创建项目再进行修改,对于新手也不是很友好,所以给出我目前使用的几个方法
探索:
首先,我在github上面搜索了一些chrome插件项目,查看了他们的源码,然后做了一些尝试下的修改。
其次,由于国内无法直接访问chrome插件商店,所以我从国内的一个chrome插件网站极简插件上面下载下来crx插件包,然后在 crxviewer.com/ 这个网站上面解压源码进行查看。学习到了插件的大致构成。
环境搭建
在vscode中下载chrome extension相关插件,然后command+P弹出命令面板,输入“chroem”
然后按步骤进行操作,进入项目文件后,运行npm install & npm run build,然后在build目录就会生成源码,就可以直接在chrome浏览器加载插件了。
支持ts和react
添加tsconfig.json:
{
"compilerOptions": {
"experimentalDecorators": true,
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["./src/**/*.ts", "./src/**/*.tsx"]
}
初始ts相关的库:
npm install typescript
npm install babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript @babel/plugin-proposal-decorators --save-dev
修改config/webpack.common.js:
...
module: {
rules: [
{
test: /\.(ts|tsx)$/,
use: [
{
loader: "babel-loader",
options: {
presets: [
[
"@babel/preset-env",
{
corejs: 3,
useBuiltIns: "usage",
},
],
"@babel/preset-react",
"@babel/preset-typescript",
],
plugins: [
["@babel/plugin-proposal-decorators", { legacy: true }],
].filter(Boolean),
},
},
],
},
]
}
...
在config/webpack.config.js加入自己需要的ts文件:
...
const config = merge(common, {
entry: {
options: PATHS.src + "/modules/options/index.tsx",
popup: PATHS.src + "/modules/popup/index.tsx",
background: PATHS.src + "/modules/background/index.ts",
content: PATHS.src + "/modules/content/index.ts",
},
...
在public加入对应的html文件,在文件中引入js,以popup.html为例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>New Tab</title>
<link rel="stylesheet" href="popup.css" />
</head>
<body>
<div class="container">
<p id="day" class="day"></p>
<div id="clock" class="clock"></div>
<hr class="divider" />
<p class="title">Chrome Extension is Ready!</p>
<p class="subtitle">Start by updating <code>index.html</code></p>
</div>
<div id="root"></div>
<script src="popup.js"></script>
</body>
</html>
后就是编写src/modules/popup/index.tsx:
import ReactDOM from "react-dom";
import React from "react";
import "./popup.css";
function HelloWord(props: { desc?: string }) {
return <h1>hello word! {props.desc}</h1>;
}
function main() {
const root = document.querySelector("#root");
if (root) {
ReactDOM.render(<HelloWord />, root);
}
}
main();
这样,所有的功能就都具备了,如果不需要react的话,可以将@babel/preset-react去掉将tsx文件路径改为ts
优化
构建时去掉一些不需要的文件,并复制到dist目录下。
添加config/pack.js:
const fs = require("fs");
const path = require("path");
const build = path.join(__dirname, "../build");
const target = path.join(build, "manifest.json");
const pack = path.join(__dirname, "../dist");
const manifest = require(target);
delete manifest["$schema"];
fs.writeFileSync(target, JSON.stringify(manifest, null, "\t"), "utf-8");
if (fs.existsSync(pack)) {
fs.rmSync(pack, { recursive: true, force: true });
}
fs.mkdirSync(pack);
function replace(target) {
if (!/\.(js|css)$/.test(target)) {
return;
}
let content = fs.readFileSync(target, "utf-8");
if (target.endsWith("js")) {
content = content
.replace(/^\/\*![^\*]*\*\//g, "")
.replace(/\/\/#.*$/g, "")
.replace(/\n{2,}/g, "\n");
} else if (target.endsWith(".css")) {
content = content.replace(/\/\*#.*\*\/$/g, "").replace(/\n{2,}/g, "\n");
}
fs.writeFileSync(target, content, "utf-8");
}
function copy(_build, _pack) {
const files = fs.readdirSync(_build);
files.forEach((filename) => {
if (filename.endsWith(".map") || filename.endsWith(".js.LICENSE.txt")) {
return;
}
const p = path.join(_build, filename);
const stat = fs.statSync(p);
if (stat.isFile()) {
const t = path.join(_pack, filename);
fs.copyFileSync(p, t);
replace(t);
} else if (stat.isDirectory()) {
const s = path.join(_pack, filename);
fs.mkdirSync(s);
copy(p, s);
}
});
}
copy(build, pack);
修改package.json
...
"scripts": {
"watch": "node ./node_modules/webpack/bin/webpack.js --mode=development --watch --config config/webpack.config.js",
"build": "node ./node_modules/webpack/bin/webpack.js --mode=production --config config/webpack.config.js && node config/pack.js"
},
...
这部分的代码可以根据个人需求进行调整,这样,项目的基本架构就完成了。