├── src
├── common
│ └── styles
│ │ ├── frame.styl
│ │ ├── global.styl
│ │ └── reset.styl
├── popup
│ ├── views
│ │ ├── login
│ │ │ ├── logo.png
│ │ │ └── login.vue
│ │ ├── entry
│ │ │ └── entry.vue
│ │ ├── account
│ │ │ └── account.vue
│ │ └── home
│ │ │ └── home.vue
│ ├── popup.vue
│ ├── main.js
│ ├── components
│ │ └── nav
│ │ │ └── nav.vue
│ └── router
│ │ └── index.js
├── content
│ ├── images
│ │ └── content-icon.png
│ ├── element-plus.scss
│ ├── index.js
│ ├── content.vue
│ └── components
│ │ └── mainDialog
│ │ └── mainDialog.vue
├── mock.js
├── background
│ └── index.js
└── api
│ └── index.js
├── public
├── favicon.ico
├── images
│ └── app.png
├── insert.js
└── manifest.json
├── globalConfig.js
├── .gitignore
├── index.html
├── package.json
├── vite.background.config.js
├── vite.popup.config.js
├── vite.content.config.js
├── README.md
└── yarn.lock
/src/common/styles/frame.styl:
--------------------------------------------------------------------------------
1 | @import './reset.styl';
2 | @import './global.styl';
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yuezi32/vite-vue-crx-2023autumn/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/images/app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yuezi32/vite-vue-crx-2023autumn/HEAD/public/images/app.png
--------------------------------------------------------------------------------
/src/popup/views/login/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yuezi32/vite-vue-crx-2023autumn/HEAD/src/popup/views/login/logo.png
--------------------------------------------------------------------------------
/public/insert.js:
--------------------------------------------------------------------------------
1 | console.log('insert.js loaded')
2 | // supportedLangs是Element Plus官网的一个js变量
3 | console.log(window.supportedLangs)
--------------------------------------------------------------------------------
/src/content/images/content-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Yuezi32/vite-vue-crx-2023autumn/HEAD/src/content/images/content-icon.png
--------------------------------------------------------------------------------
/src/content/element-plus.scss:
--------------------------------------------------------------------------------
1 | @forward 'element-plus/theme-chalk/src/mixins/config.scss' with (
2 | $namespace: 'CRX-el'
3 | );
4 |
5 | @use "element-plus/theme-chalk/src/index.scss" as *;
--------------------------------------------------------------------------------
/src/common/styles/global.styl:
--------------------------------------------------------------------------------
1 | html, body, #root
2 | height: 100%
3 | /*清浮动*/
4 | .clearfix:after
5 | content: "."
6 | display: block
7 | height: 0
8 | clear: both
9 | visibility: hidden
10 | .clearfix
11 | display:block
--------------------------------------------------------------------------------
/globalConfig.js:
--------------------------------------------------------------------------------
1 | // Chrome Extension 最终build目录
2 | export const CRX_OUTDIR = 'build'
3 | // 临时build content script的目录
4 | export const CRX_CONTENT_OUTDIR = '_build_content'
5 | // 临时build background script的目录
6 | export const CRX_BACKGROUND_OUTDIR = '_build_background'
--------------------------------------------------------------------------------
/src/mock.js:
--------------------------------------------------------------------------------
1 | import Mock from 'mockjs'
2 | import { mockFetch } from 'mockjs-fetch'
3 | mockFetch(Mock)
4 |
5 | // 模拟登录
6 | Mock.mock(/login/, {
7 | code: 200,
8 | msg: 'OK',
9 | data: {
10 | nickname: '兔子先生',
11 | accessToken: 'fqh0i-LyINZ-RvK5d-Akj3a-uBYRl',
12 | }
13 | })
--------------------------------------------------------------------------------
/src/popup/popup.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | build
12 | dist
13 | dist-ssr
14 | *.local
15 |
16 | # Editor directories and files
17 | .vscode/*
18 | !.vscode/extensions.json
19 | .idea
20 | .DS_Store
21 | *.suo
22 | *.ntvs*
23 | *.njsproj
24 | *.sln
25 | *.sw?
26 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite Vue CRX
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/popup/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | // 全局样式
3 | import '@/common/styles/frame.styl'
4 | import ElementPlus from 'element-plus'
5 | import 'element-plus/dist/index.css'
6 | import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
7 | import Popup from '@/popup/popup.vue'
8 | import router from './router'
9 |
10 | const app = createApp(Popup)
11 | app.use(ElementPlus, {
12 | locale: zhCn,
13 | })
14 | app.use(router)
15 | app.mount('#app')
16 |
--------------------------------------------------------------------------------
/src/popup/views/entry/entry.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/popup/views/account/account.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Account Page
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/popup/views/home/home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Home Page
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vite-vue-crx-2023autumn",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite -c vite.popup.config.js",
8 | "build": "vite build -c vite.popup.config.js && vite build -c vite.content.config.js && vite build -c vite.background.config.js && node build.js",
9 | "preview": "vite preview -c vite.popup.config.js"
10 | },
11 | "dependencies": {
12 | "element-plus": "^2.3.9",
13 | "mockjs": "^1.1.0",
14 | "mockjs-fetch": "^2.0.0",
15 | "vue": "^3.3.4",
16 | "vue-router": "^4.2.4"
17 | },
18 | "devDependencies": {
19 | "@vitejs/plugin-vue": "^4.3.1",
20 | "less": "^4.2.0",
21 | "sass": "^1.66.1",
22 | "stylus": "^0.59.0",
23 | "vite": "^4.4.9"
24 | },
25 | "packageManager": "yarn@1.22.19"
26 | }
27 |
--------------------------------------------------------------------------------
/vite.background.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 | import path from 'path'
4 | import { CRX_BACKGROUND_OUTDIR } from './globalConfig'
5 |
6 | // https://vitejs.dev/config/
7 | export default defineConfig({
8 | build: {
9 | // 输出目录
10 | outDir: CRX_BACKGROUND_OUTDIR,
11 | lib: {
12 | entry: [path.resolve(__dirname, 'src/background/index.js')],
13 | // background script不支持ES6,因此不用使用es模式,需要改为cjs模式
14 | formats: ['cjs'],
15 | // 设置生成文件的文件名
16 | fileName: () => {
17 | // 将文件后缀名强制定为js,否则会生成cjs的后缀名
18 | return 'background.js'
19 | }
20 | },
21 | },
22 | resolve: {
23 | alias: {
24 | '@': path.resolve(__dirname, 'src'),
25 | },
26 | },
27 | plugins: [vue()],
28 | })
--------------------------------------------------------------------------------
/src/content/index.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import ElementPlus from 'element-plus'
3 | import '@/content/element-plus.scss'
4 | import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
5 | import Content from '@/content/content.vue'
6 |
7 | // 创建id为CRX-container的div
8 | const crxApp = document.createElement('div')
9 | crxApp.id = 'CRX-container'
10 | // 将刚创建的div插入body最后
11 | document.body.appendChild(crxApp)
12 |
13 | // 创建Vue APP
14 | const app = createApp(Content)
15 | // 集成Element Plus
16 | app.use(ElementPlus, {
17 | locale: zhCn,
18 | })
19 | // 将Vue APP插入刚创建的div
20 | app.mount('#CRX-container')
21 |
22 | // 向目标页面驻入js
23 | try {
24 | let insertScript = document.createElement('script')
25 | insertScript.setAttribute('type', 'text/javascript')
26 | insertScript.src = window.chrome.runtime.getURL('insert.js')
27 | document.body.appendChild(insertScript)
28 | } catch (err) {}
29 |
--------------------------------------------------------------------------------
/src/content/content.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
{isShowMainDialog = false}"
17 | />
18 |
19 |
20 |
21 |
22 |
35 |
--------------------------------------------------------------------------------
/vite.popup.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 | import path from 'path'
4 | import { CRX_OUTDIR } from './globalConfig'
5 |
6 | // https://vitejs.dev/config/
7 | export default defineConfig({
8 | build: {
9 | // 输出目录
10 | outDir: CRX_OUTDIR,
11 | },
12 | server: {
13 | // 指定dev sever的端口号,默认为5173
14 | port: 3000,
15 | // 自动打开浏览器运行以下页面
16 | open: '/',
17 | // 设置反向代理
18 | proxy: {
19 | // 以下示例表示:请求URL中含有"/api",则反向代理到http://localhost
20 | // 例如: http://localhost:3000/api/login -> http://localhost/api/login
21 | // 如果反向代理到localhost报错Error: connect ECONNREFUSED ::1:80,
22 | // 则将localhost改127.0.0.1
23 | '/api': {
24 | target: 'http://127.0.0.1/',
25 | changeOrigin: true,
26 | },
27 | },
28 | },
29 | resolve: {
30 | alias: {
31 | '@': path.resolve(__dirname, 'src'),
32 | },
33 | },
34 | plugins: [vue()],
35 | })
36 |
--------------------------------------------------------------------------------
/vite.content.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 | import path from 'path'
4 | import { CRX_CONTENT_OUTDIR } from './globalConfig'
5 |
6 | // https://vitejs.dev/config/
7 | export default defineConfig({
8 | build: {
9 | // 输出目录
10 | outDir: CRX_CONTENT_OUTDIR,
11 | lib: {
12 | entry: [path.resolve(__dirname, 'src/content/index.js')],
13 | // content script不支持ES6,因此不用使用es模式,需要改为cjs模式
14 | formats: ['cjs'],
15 | // 设置生成文件的文件名
16 | fileName: () => {
17 | // 将文件后缀名强制定为js,否则会生成cjs的后缀名
18 | return 'content.js'
19 | },
20 | },
21 | rollupOptions: {
22 | output: {
23 | assetFileNames: (assetInfo) => {
24 | // 附属文件命名,content script会生成配套的css
25 | return 'content.css'
26 | },
27 | },
28 | },
29 | },
30 | resolve: {
31 | alias: {
32 | '@': path.resolve(__dirname, 'src'),
33 | },
34 | },
35 | // 解决代码中包含process.env.NODE_ENV导致无法使用的问题
36 | define: {
37 | 'process.env.NODE_ENV': null,
38 | },
39 | plugins: [vue()],
40 | })
41 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Chrome插件V3",
3 | "version": "1.0",
4 | "description": "基于Vite的chrome插件V3 Demo",
5 | "manifest_version": 3,
6 | "background": {
7 | "service_worker": "background.js"
8 | },
9 | "content_scripts": [
10 | {
11 | "matches": [""],
12 | "css": ["content.css"],
13 | "js": ["content.js"],
14 | "run_at": "document_end"
15 | }
16 | ],
17 | "permissions": ["storage","declarativeContent"],
18 | "host_permissions":[],
19 | "web_accessible_resources": [
20 | {
21 | "resources": [ "/images/app.png" ],
22 | "matches": [""]
23 | },
24 | {
25 | "resources": [ "insert.js" ],
26 | "matches": [""]
27 | }
28 | ],
29 | "action": {
30 | "default_popup": "index.html",
31 | "default_icon": {
32 | "16": "/images/app.png",
33 | "32": "/images/app.png",
34 | "48": "/images/app.png",
35 | "128": "/images/app.png"
36 | },
37 | "default_title": "Vue CRX MV3"
38 | },
39 | "icons": {
40 | "16": "/images/app.png",
41 | "32": "/images/app.png",
42 | "48": "/images/app.png",
43 | "128": "/images/app.png"
44 | }
45 | }
--------------------------------------------------------------------------------
/src/popup/components/nav/nav.vue:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
28 |
29 |
30 |
31 | 退出
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/popup/router/index.js:
--------------------------------------------------------------------------------
1 | import { createRouter, createWebHashHistory } from 'vue-router'
2 | import Login from '@/popup/views/login/login.vue'
3 | import Home from '@/popup/views/home/home.vue'
4 | import Account from '@/popup/views/account/account.vue'
5 | import Entry from '@/popup/views/entry/entry.vue'
6 |
7 | const routes = [
8 | // // URL未包含路由hash,则跳转至Home页面
9 | // { path: '/', redirect: '/home', exact: true },
10 | // 精确匹配 #/login,指向Login页面
11 | { path: '/login', component: Login, exact: true },
12 | // 匹配 #/,指向Entry页面
13 | {
14 | path: '/',
15 | component: Entry,
16 | // 这里是Entry的二级路由配置
17 | children: [
18 | // 精确匹配 #/home,指向Home页面
19 | {
20 | path: 'home',
21 | component: Home,
22 | exact: true,
23 | },
24 | // 精确匹配 #/account,指向Account页面
25 | {
26 | path: 'account',
27 | component: Account,
28 | exact: true,
29 | },
30 | // 空hash,则跳转至Home页面
31 | { path: '', redirect: 'home' },
32 | // 未匹配,则跳转至Home页面
33 | { path: '/:pathMatch(.*)', redirect: 'home' },
34 | ],
35 | },
36 | ]
37 |
38 | const router = createRouter({
39 | history: createWebHashHistory(),
40 | routes,
41 | })
42 |
43 | export default router
--------------------------------------------------------------------------------
/src/content/components/mainDialog/mainDialog.vue:
--------------------------------------------------------------------------------
1 |
39 |
40 |
41 |
47 |
52 |
53 | Submit
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/src/popup/views/login/login.vue:
--------------------------------------------------------------------------------
1 |
31 |
32 |
33 |
34 |

35 |
36 |
37 |
38 |
39 |
45 |
46 |
47 | 登录
48 |
49 |
50 |
51 |
52 |
67 |
--------------------------------------------------------------------------------
/src/background/index.js:
--------------------------------------------------------------------------------
1 | /*global chrome*/
2 | import { apiRequest } from '@/api'
3 | // manifest.json的Permissions配置需添加declarativeContent权限
4 | chrome.runtime.onInstalled.addListener(function () {
5 | // 默认先禁止Page Action。如果不加这一句,则无法生效下面的规则
6 | chrome.action.disable()
7 | chrome.declarativeContent.onPageChanged.removeRules(undefined, () => {
8 | // 设置规则
9 | let rule = {
10 | // 运行插件运行的页面URL规则
11 | conditions: [
12 | new chrome.declarativeContent.PageStateMatcher({
13 | pageUrl: {
14 | // 适配所有域名以“www.”开头的网页
15 | // hostPrefix: 'www.'
16 | // 适配所有域名以“.element-plus.org”结尾的网页
17 | // hostSuffix: '.element-plus.org',
18 | // 适配域名为“element-plus.org”的网页
19 | hostEquals: 'element-plus.org',
20 | // 适配https协议的网页
21 | // schemes: ['https'],
22 | },
23 | }),
24 | ],
25 | actions: [new chrome.declarativeContent.ShowAction()],
26 | }
27 | // 整合所有规则
28 | const rules = [rule]
29 | // 执行规则
30 | chrome.declarativeContent.onPageChanged.addRules(rules)
31 | })
32 | })
33 |
34 | chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
35 | // 接收来自content script的消息,requset里不允许传递function和file类型的参数
36 | chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
37 | const { contentRequest } = request
38 | // 接收来自content的api请求
39 | if (contentRequest === 'apiRequest') {
40 | let { config } = request
41 | // API请求成功的回调
42 | config.success = (data) => {
43 | data.result = 'succ'
44 | sendResponse(data)
45 | }
46 | // API请求失败的回调
47 | config.fail = (msg) => {
48 | sendResponse({
49 | result: 'fail',
50 | msg,
51 | })
52 | }
53 | // 发起请求
54 | apiRequest(config)
55 | }
56 | })
57 | return true
58 | })
59 |
--------------------------------------------------------------------------------
/src/common/styles/reset.styl:
--------------------------------------------------------------------------------
1 | /***
2 | The new CSS reset - version 1.9 (last updated 19.6.2023)
3 | GitHub page: https://github.com/elad2412/the-new-css-reset
4 | ***/
5 |
6 | /*
7 | Remove all the styles of the "User-Agent-Stylesheet", except for the 'display' property
8 | - The "symbol *" part is to solve Firefox SVG sprite bug
9 | - The "html" element is excluded, otherwise a bug in Chrome breaks the CSS hyphens property (https://github.com/elad2412/the-new-css-reset/issues/36)
10 | */
11 | *:where(:not(html, iframe, canvas, img, svg, video, audio):not(svg *, symbol *)) {
12 | all: unset;
13 | display: revert;
14 | }
15 |
16 | /* Preferred box-sizing value */
17 | *,
18 | *::before,
19 | *::after {
20 | box-sizing: border-box;
21 | }
22 |
23 | /* Reapply the pointer cursor for anchor tags */
24 | a, button {
25 | cursor: revert;
26 | }
27 |
28 | /* Remove list styles (bullets/numbers) */
29 | ol, ul, menu {
30 | list-style: none;
31 | }
32 |
33 | /* For images to not be able to exceed their container */
34 | img {
35 | max-inline-size: 100%;
36 | max-block-size: 100%;
37 | }
38 |
39 | /* removes spacing between cells in tables */
40 | table {
41 | border-collapse: collapse;
42 | }
43 |
44 | /* Safari - solving issue when using user-select:none on the text input doesn't working */
45 | input, textarea {
46 | -webkit-user-select: auto;
47 | }
48 |
49 | /* revert the 'white-space' property for textarea elements on Safari */
50 | textarea {
51 | white-space: revert;
52 | }
53 |
54 | /* minimum style to allow to style meter element */
55 | meter {
56 | -webkit-appearance: revert;
57 | appearance: revert;
58 | }
59 |
60 | /* preformatted text - use only for this feature */
61 | :where(pre) {
62 | all: revert;
63 | }
64 |
65 | /* reset default text opacity of input placeholder */
66 | ::placeholder {
67 | color: unset;
68 | }
69 |
70 | /* remove default dot (•) sign */
71 | ::marker {
72 | content: initial;
73 | }
74 |
75 | /* fix the feature of 'hidden' attribute.
76 | display:revert; revert to element instead of attribute */
77 | :where([hidden]) {
78 | display: none;
79 | }
80 |
81 | /* revert for bug in Chromium browsers
82 | - fix for the content editable attribute will work properly.
83 | - webkit-user-select: auto; added for Safari in case of using user-select:none on wrapper element*/
84 | :where([contenteditable]:not([contenteditable="false"])) {
85 | -moz-user-modify: read-write;
86 | -webkit-user-modify: read-write;
87 | overflow-wrap: break-word;
88 | -webkit-line-break: after-white-space;
89 | -webkit-user-select: auto;
90 | }
91 |
92 | /* apply back the draggable feature - exist only in Chromium and Safari */
93 | :where([draggable="true"]) {
94 | -webkit-user-drag: element;
95 | }
96 |
97 | /* Revert Modal native behavior */
98 | :where(dialog:modal) {
99 | all: revert;
100 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 2023金秋版:基于Vite4+Vue3的Chrome插件开发教程
2 |
3 | 基于Vite4的Chrome Extension Manifest V3工程脚手架。
4 |
5 | 本项目架构实现了以下功能:
6 |
7 | - 基于Vite 4.x搭建
8 | - 基于Chrome Extension Manifest V3规范
9 | - 集成Sass/Scss/Less/Stylus
10 | - 集成Element Plus
11 | - 集成mock.js、mockjs-fetch模拟请求
12 | - 集成vue-router
13 | - 将popup、content、background目录互相独立,便于团队协作开发维护
14 | - 按照Chrome Extension最终生成目录要求配置Vite
15 | - 封装fetch,满足popup、content script、background script跨域请求
16 | - 实现了完整的Chrome Extension MV3项目Demo。
17 |
18 |
19 | ## 安装项目
20 | 执行:
21 | ```
22 | npm install
23 | ```
24 | 或
25 | ```
26 | yarn
27 | ```
28 |
29 | ## 使用方法:开发环境
30 |
31 | > ※注:为方便演示接口请求,本Demo使用了mock.js,也配置了反向代理。
32 | > mock.js便于直接调试,使用前请修改src/api/index.js,将import '@/mock'前的注释去掉,保证mock.js的正确引入。
33 | > 如使用反向代理,需要自行在本地搭建API服务,接口返回数据可参考src/mock.js中的数据。
34 |
35 | 执行:
36 | ```
37 | npm run dev
38 | ```
39 | 或
40 | ```
41 | yarn dev
42 | ```
43 |
44 | ## 使用方法:build项目
45 |
46 | > ※注:
47 | > 1. 执行build前一定检查是否取消mock.js,即确认src/api/index.js中,将import '@/mock'注释掉。这是因为mock.js使用window变量,而运行background script的Service Worker不支持window,将导致插件运行失败。
48 | > 2. 执行build前一定检查src/popup/popup.vue代码中,注释掉import '@/content'。这段代码是用于方便在开发环境调试content script的,否则content script会被集成到popup页面中。
49 |
50 | 执行:
51 | ```
52 | npm run build
53 | ```
54 | 或
55 | ```
56 | yarn build
57 | ```
58 |
59 | build完成后,打开Chrome浏览器,地址栏输入:
60 | ```
61 | chrome://extensions/
62 | ```
63 | 进入扩展程序界面。
64 |
65 | 1. 开启右上角的“开发者模式”。
66 | 2. 点击左上角的“加载已解压的扩展程序”。
67 | 3. 选择项目工程中刚生成的build目录。
68 |
69 | ## 关于真实API请求(非mock.js模拟请求)
70 |
71 | 本Demo不包含后端API服务。如在开发环境使用反向代理请求API,或者在build后的正式插件中请求API,请自行搭建后端API服务,返回的数据可参照src/mock.js或教程中相应章节的截图。
72 |
73 | ## 配套教程
74 |
75 | 📚📚本项目有详细的讲解教程,原文请关注我的微信公众号【卧梅又闻花】📚📚
76 |
77 | 查阅本项目完整教程:[《2023金秋版:基于Vite4+Vue3的Chrome插件开发教程》](https://mp.weixin.qq.com/s/sQI1gvMFu8W2SrWNm0WvYw)
78 |
79 | ### 教程目录
80 |
81 | 先看下目录了解本教程都有哪些内容。强烈建议按照以下章节一步一步边学边做,可以快速掌握整个项目的原理和细节,在以后遇到新问题的时候,可以知道从哪个环节入手。
82 |
83 | ```
84 | 1 初始化项目
85 | • 1.1 使用Vite新建项目
86 | • 1.2 安装并运行项目
87 | • 1.3 精简项目
88 | 2 Vite基础配置
89 | • 2.1 配置国内镜像源
90 | • 2.2 支持Sass/Scss/Less/Stylus
91 | • 2.3 设置dev环境的Server端口号
92 | • 2.4 设置dev环境自动打开浏览器
93 | • 2.5 设置路径别名
94 | 3 Chrome Extension基础
95 | • 3.1 Manifest V3概述
96 | • 3.2 Manifest V3 主要新特性
97 | • 3.3 Chrome Extension的组成
98 | • 3.4 规划build生成的目录结构
99 | • 3.5 配置manifest.json
100 | 4 项目目录结构设计
101 | 5 针对Chrome Extension的Vite配置
102 | • 5.1 设置全局配置
103 | • 5.2 设置popup的build配置
104 | • 5.3 设置content script的build配置
105 | • 5.4 设置background script的build配置
106 | • 5.5 通过补充脚本合并三个build
107 | 6 设置公用样式并集成Element Plus
108 | • 6.1 关于样式命名规范
109 | • 6.2 设置全局公用样式
110 | • 6.3 集成Element Plus
111 | • 6.4 设置Element Plus为中文语言
112 | 7 Popup开发
113 | • 7.1 迁移main.js至popup目录
114 | • 7.2 构建popup的Login页面
115 | • 7.3 构建popup的Home页面
116 | • 7.4 构建popup的Account页面
117 | • 7.5 配置popup页面路由
118 | • 7.6 构建Nav导航组件
119 | • 7.7 构建Entry二级路由框架页面
120 | • 7.8 调整popup入口页面,打通全部路由
121 | • 7.9 完善Login页面的登录跳转
122 | • 7.10 设置popup页面尺寸
123 | 8 build项目并载入插件
124 | 9 background script开发
125 | • 9.1 设置允许运行popup的页面规则
126 | • 9.2 为什么插件图标在禁用页面不变成灰色
127 | 10 content script开发
128 | • 10.1 向目标页面注入悬浮球
129 | • 10.2 在content script中集成Element Plus并解决样式污染
130 | • 10.3 在content script中使用Element Plus
131 | 11 在开发环境中调试content script
132 | 12 API请求
133 | • 12.1 background pages不支持XMLHttpRequest(axios)
134 | • 12.2 使用mock.js和mockjs-fetch模拟请求
135 | • 12.3 封装API及fetch业务逻辑
136 | • 12.4 委托background script完成API请求
137 | • 12.5 实现popup的Login页面API请求
138 | • 12.6 设置开发环境的反向代理请求
139 | • 12.7 实现content script的API请求
140 | • 12.8 关键知识点小结
141 | 13 其他说明
142 | • 13.1 permission权限配置
143 | • 13.2 以