├── 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 | 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 | 11 | 12 | -------------------------------------------------------------------------------- /src/popup/views/account/account.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /src/popup/views/home/home.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | 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 | 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 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/popup/views/login/login.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 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 以