Nut Mark


Nut Mark

Nut Mark is a browser extension to help you collect bookmarks more easily.

Nut Mark
Nut Mark

Why call "Nut Mark"

I like to collect and organize bookmarks like a squirrel hoards nuts, so I build this browser extension and call it "Nut Mark".

Feature

Nut Mark focus on the following features to make bookmarking step more smoothly and easily

✨ Clean "dirty" URL with one click

✨ Quick search to find the folder to hold the bookmark

Other Bonus features

🎈 Manage bookmarks with a user-friendly interface

🎈 Share bookmarks in different format, like Markdown links, JSON object, images with QR Code

预备技能

本项目使用 Vue(Vue 3.0 版本)和 Chrome 所提供的 API 进行开发(当然还使用了必不可少的前端三大基本技能 HTML、CSS、JavaScript),此外 CSS 主要使用 Tailwindcss 框架。

搭建开发环境

搭建一个使用 Vue + Vite + Tailwindcss 的开发环境,其中开发的扩展程序属于 Manifest V3 版本

说明
  • 在系统中需要先安装 node 环境
  • 如果在开发中使用 VS Code 文本编辑器,推荐安装 Vue3 开发的配套 Volar 插件。

项目模板

在终端输入以下命令,使用 Vite 初始化一个项目(提示选择 Vue 作为前端框架)

shell
yarn create vite
说明

本项目采用 Javascript 而不是 TypeScript 进行开发

然后安装依赖包

shell
# 安装依赖包
yarn install

代码规范

为了在项目中使用 ESLint 进行代码规范管理,需要安装 eslint-plugin-vue 模块

shell
npm install --save-dev eslint eslint-plugin-vue

然后在终端输入以下命令进行初始化配置,并根据自己的编程习惯和团队的要求,按照提示设置代码样式规范

shell
eslint --init
提示

对最新的版本的 ESLint(本文更新时 ESLint 的版本是 v8.30.0)还可以使用以下命令来进行初始化配置

shell
npm init @eslint/config

在本项目会采用以下 ESLint 配置项

  • To check syntax, find problems, and enforece code style
  • JavaScript modules (import/export)
  • Vue.js
  • Browser & Node(这里是同时选中,默认选中的是 Browser,使用空格键加选或取消)
  • Use a popular style guide -> Airbnb(采用 Airbnb 推出的关于 JavaScript 的代码规范)
  • JavaScript(生成的配置文件采用 JavaScript 格式)

接着会提示确认安装所需依赖的包

最后会在项目的根目录下生成一个 ESLint 配置文件 .eslintrc.cjs

由于在项目中使用的是 Vue3 版本,所以需要打开配置文件 .eslintrc.cjs 进行相应的修改,采用相应的插件对 Vue 的相关代码进行规范

.eslintrc.cjs
cjs
module.exports = {
    // ...
    extends: [
      'plugin:vue/vue3-recommended',
      'airbnb-base',
    ],
    // ...
}

另外还需要对 IDE 代码编辑器进行配置

本项目使用的代码编辑器是 VS Code,需要在 📁 文件夹 .vscode 中创建一个文件 settings.json 进行编辑器的 eslint 配置

.vscode/settings.json
json
{
    // 在文件保存时对代码进行修正(而且采用 eslint 作为唯一的修正器)
    "editor.codeActionsOnSave": {
        "source.fixAll.eslint": true
    },
    // eslint 会对以下类型的文件进行代码修改正
    "eslint.validate": [
        "javascript",
        "vue"
    ],
}
提示

更多关于使用 ESLint 进行代码规范管理的细节可以参考该 🎬 影片

插件

由于开发扩展程序的项目结构和一般开发网页的项目不同,例如最终 Build 后需要在根目录有一个 manifest.json 文档作为描述/入口文件,以描述扩展程序相关信息和权限。

所以还需要安装一个特殊的插件 CRXJS 以提供引导 Vite 正确打包,以及提供更友好的插件开发体验

在终端输入以下命令安装该插件

shell
# 本项目使用 Vite 3 进行开发
# 该插件对于 Vite 3 版本仍处于 beta 版本,请谨慎使用
yarn add @crxjs/vite-plugin@beta -D
注意

请确保 package.json 文件里的属性 "type": "module" 否则扩展无法根据 vite.config.ts 进行正确的构建

然后在文件 vite.config.js 对 Vite 进行配置

vite.config.js
js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { crx } from '@crxjs/vite-plugin' // 引入插件
// 导入 manifest
// 如果 Node 版本是 14 & 16 就采用该方式
import manifest from './manifest.json'
// 如果 Node >=17 就采用以下的方式
// import manifest from './manifest.json' assert { type: 'json' }

export default defineConfig({
  plugins: [
    vue(),
    crx({ manifest }),
  ],
})

CRXJS 插件会通过自动分析导入的扩展清单 manifest 文件,并将扩展所使用的文件(如 popup page 弹出页面、options page 配置页等)在 Vite 中进行配置,以提供 HMR 热重载、打包优化等更佳的开发体验,一般在 manifest 文件会声明/列出了扩展所使用的大部分页面/文件。

但是也有一些扩展会使用到的页面/文件未包含在 manifest 之中,可以在 vite.config.js 的属性 build.rollupOptions.input 中进行手动的配置追加

vite.config.js
js
export default defineConfig({
  build: {
    rollupOptions: {
      input: {
        // 插件首次安装成功后自动弹出的欢迎页面
        welcome: 'src/pages/welcome.html',
      },
    },
  },
})

manifest 文件

在根目录(与 vite.config.js 文件处于同一级)创建一个 manifest.json 文件

manifest.json
json
{
  "manifest_version": 3,
  "name": "Extension Name",
  "version": "1.0.0",
  "action": {
      "default_popup": "index.html"
  }
}

文档 manifest.json 是扩展程序的配置清单,其中包含的常见字段如下

manifest.json
json
{
    "name": "TagDown",
    "manifest_version": 3,
    "version": "1.0.0",
    "description": "TagDown is a bookmark manager.",
    "background": {
        "service_worker": "src/background/main.js",
        "type": "module"
    },
    "action": {
        "default_title": "TagDown",
        "default_popup": "src/popup/index.html",
        "default_icon": {
            "16": "src/assets/images/icon16.png",
            "32": "src/assets/images/icon32.png",
            "48": "src/assets/images/icon48.png",
            "64": "src/assets/images/icon64.png",
            "128": "src/assets/images/icon128.png"
        }
    },
    "options_page": "src/options/index.html",
    "icons": {
        "16": "src/assets/images/icon16.png",
        "32": "src/assets/images/icon32.png",
        "48": "src/assets/images/icon48.png",
        "64": "src/assets/images/icon64.png",
        "128": "src/assets/images/icon128.png"
    },
    "default_locale": "en"
}
提示

根据上一章节的代码可知,扩展的 manifest 文件会被导入到 vite.config.js 配置文件中,先经由 CRXJS 插件进行解析,分析出扩展的 manifest 文件,将扩展所使用的文件正确地到整合到 vite config 配置项中。

所以除了默认的 manifest.json 文件格式作为扩展的清单,采用了 CRXJS 插件的项目,还支持以 manifest.config.jsmanifest.config.ts 文件作为扩展的配置清单(打包构建时最终也是输出 manifest.json 文件)

如果采用 JavaScript 或 TypeScript 作为扩展的配置清单,利用 CRXJS 插件所提供的方法 defintManifest() 就可以实现参数的动态设置

manifest.config.ts
ts
import { defineManifest } from '@crxjs/vite-plugin'
import packageJson from './package.json' // 导入项目的依赖包管理文件
const { version } = packageJson

// Convert from Semver (example: 0.1.0-beta6)
const [major, minor, patch, label = '0'] = version
  // can only contain digits, dots, or dash
  .replace(/[^\d.-]+/g, '')
  // split into version parts
  .split(/[.-]/)

// 设置扩展的配置清单,支持异步函数
export default defineManifest(async (env) => ({
  manifest_version: 3,
  // 扩展的名称根据环境变量而不同
  name:
    env.mode === 'staging'
      ? '[INTERNAL] CRXJS Power Tools'
      : 'CRXJS Power Tools',
  // 扩展的版本和 package.json 里的版本相关
  // up to four numbers separated by dots
  version: `${major}.${minor}.${patch}.${label}`,
  // semver is OK in "version_name"
  version_name: version,
}))
说明

在扩展的 manifest 文件的配置项中,其指定的文件(路径)是基于 Vite 项目的根目录的(与 manifest 文件的实际位置无关)

正确

所以应该采用以下方式(以单词作为开头,即从根目录开始由目录和文件所构成的绝对路径)来指定所使用的文件

manifest.json
json
{
  "options_page": "src/options/index.html",
}
错误

以下则是错误的文件引用/指定,不应该采用先对路径

manifest.json
json
{
  "options_page": "./src/options/index.html",
  "devtools_page": "/root/user/.../devtools.html"
}

可以参考 chrome-extension-vite-vue-starter-template 这个模板,它也是采用了 CRXJS 插件

该模板为扩展所使用的每个页面都单独设置一个 Vue 实例,和入口文件 index.html,如下图所标注的 popup page 弹出页面和 options page 配置页面

上述模板所采用的结构
上述模板所采用的结构

所以应该可以 ❓ 删除使用 Vite CLI 自动生成项目模板时,在根目录下的 index.html 文件,以及它所引用的 src/main.js 文件(用于创建 Vue 实例),和对应的根组件 src/app.vue 文件

Tailwind

本项目使用 Tailwindcss 框架对页面进行样式设置,按照官方文档依次执行以下步骤

  1. 在终端输入以下命令安装 Tailwind CSS 及相关的依赖包
    shell
    yarn add -D tailwindcss postcss autoprefixer
    
  2. 在终端输入以下命令初始化 Tailwind CSS 以生成一个配置文件 tailwind.config.js
    shell
    npx tailwindcss init -p
    

    为了优化项目开发打包时的文件大小,在 Tailwind CSS 的配置文件中进行相应的设置,以删除未使用的样式代码
    tailwind.config.js
    js
    /** @type {import('tailwindcss').Config} */
    export default {
      content: [
        './index.html',
        './src/**/*.{js,vue}',
      ],
      theme: {
        extend: {},
      },
      plugins: [],
    };
    
  3. 在样式表入口文件通过 Tailwind 指令引入相关的样式
    src/index.css
    css
    @tailwind base;
    @tailwind components;
    @tailwind utilities;
    

Copyright © 2024 Ben

Theme BlogiNote

Icons from Icônes