放棄 plasmo-webpack5 搭建基礎插件

由於業務需要,最近在做chrome插件,於是尋找構建插件社區,找到了plasmo, 應該是社區中比較優秀的構建chrome插件的腳手架,但最終還是沒有選擇用,原生插件非常簡單,具體可以參考【寫個自己的 chrome 插件】, 爲滿足當下業務需要,於是基於webpack5搭建一個構建插件的基礎腳手架,本文是一篇搭建chrome插件的實踐總結,希望看完在項目中有所幫助。

插件基本能力

前置

在使用webpack5搭建插件,可以參考筆者在之前寫的【webpack5】系列筆記中有部分搭建案例,chrome插件本質上是運行在 chrome 瀏覽器的網頁,因此本質上也是用網頁來展現的,只是一個插件的必須滿足根目錄必須有mainifest.json,我們具體以下面一張圖來重新回顧下插件的基本要素

插件核心

manifest.json

在這個文件中,構建一個插件manifest.json是必不可少的,其中注意一點manifest_version3版本,因爲好多新特性2版本並不支持,同時當我們需要操作chromeapi時,在permissions開放操作權限

{

    "name": "base chrome plugin chrome", //插件的名稱

    "description": "a simple chrome plugin", // 插件描述

    "version": "1.0.0", // 當前插件版本

    "manifest_version": 3, // chrome插件必填版本3

    "action": {

        "default_icon": "assets/imgs/icon.png", // 插件icon

        "default_title": "demo chrome plugin",

        "default_popup": "popup.html" // popup頁面

    },

    "background": {

        "service_worker": "./src/background/index.js",

        "type": "module"

    },

    "icons":

	{

		"16": "assets/imgs/icon.png"

	},

    "content_scripts": [

        {

            "matches": [

                "<all_urls>" // 插件在任何頁面可用

            ],

            "exclude_matches": [ // 排除插件在部分頁面中不可用

               

            ],

            "js": [

                "src/content/index.js" // 匹配的頁面中加載content.js

            ],

            "css": [

                "assets/css/index.css" //匹配的頁面加載css

            ],

            

            "run_at": "document_end",

            "all_frames": false

        }

    ],



    "host_permissions": [

    

    ],

    "permissions": [

        "tabs", // chrome api操作的權限

        "contextMenus",

        "notifications",

        "webRequest",

        "activeTab"

    ],

    "omnibox": { "keyword" : "demo" },

    "commands": {

        "_execute_action": {

            "suggested_key": {

                "default": "Ctrl+Shift+Y",

                "mac": "Command+Shift+Y"

            },

            "description": "Opens tabs.html"

        }

    }

}

webpack.config.js

這是插件的基礎配置文件, 主要講解幾個關鍵的配置

我們看到entry主要是backgroundpopupcontentset的幾個文件,這是打包的入口文件,在webpack入口文件,會根據entry找到自己依賴的模塊,從而進行加載

// webpack.confg.js

const path = require("path");

const resolvePath = (dir) => {

  return path.resolve(__dirname, dir);

};

module.exports = (env) => {

  return {

    entry: {

        background: resolvePath("src/background/index"),

        popup: resolvePath("src/pages/popup/index"),

        content: resolvePath("src/pages/content/index"),

        set: resolvePath("src/pages/tabs/set/index"),

    }

  }

}

output會根據entry的文件,輸出到指定path的文件夾中,其中publicPatch指定絕對路徑訪問打包後的資源

 module.exports = (env) => {

  const PluginFileAssetsName = `dist/${env.mode}/${fileName}`;

   return {

     ...

    output: {

      publicPath: "/",

      path: path.join(__dirname, PluginFileAssetsName),

      filename: "src/[name]/index.js",

    },

   }

 }

在這個配置中,主要做了一下幾件事情

module.exports = (env) => {

    return {

      ...,

       plugins: [

      new Dotenv({

        path: path.resolve(process.cwd(), `.env.${env.mode}`),

      }), // 讀取本地.env本地



      new Html({

        filename: "popup.html",

        template: "./public/index.html",

        chunks: ["popup"], // 打包後只會包含popup與content,避免將其他js引入

        hash: false,

        minify: {

          removeComments: true,

          collapseWhitespace: true,

          minifyCSS: true,

        },

        title: "test popup",

      }),

      new Html({

        filename: "set.html",

        template: "./public/index.html",

        chunks: ["set"],

        hash: false,

        title: "set",

        minify: {

          removeComments: true,

          collapseWhitespace: true,

          minifyCSS: true,

        },

      }),

      new MiniCssExtractPlugin({

        filename: "css/[name].css",

        chunkFilename: "css/[name].css",

      }),



      new copyWebpackPlugin({

        patterns: [

          {

            from: path.join(__dirname, "manifest.json"),

            to: path.join(__dirname, `${PluginFileAssetsName}/`),

          },

          {

            from: path.join(__dirname, "src/assets/imgs"),

            to: path.join(__dirname, `${PluginFileAssetsName}/assets/imgs`),

          },

          {

            from: path.join(__dirname, "src/assets/css"),

            to: path.join(__dirname, `${PluginFileAssetsName}/assets/css`),

          },

        ],

      }),

      new CleanWebpackPlugin(),

    ],

    }

}

支持jsxtsx主要是利用babel-loader, 在rules這個配置中, 同時我們也對.tsx,.ts單獨使用ts-loader去加載

module.exports = {

  rules: [

      {

      test: /\.(js|jsx|tsx)$/i,

      exclude: /node_modules/,

      use: [

        {

          loader: "babel-loader",

          options: {

            presets: ["@babel/preset-env", "@babel/preset-react"],

          },

        },

      ],

    },

    {

      test: /\.(tsx|ts)$/i,

      use: [

        {

          loader: "ts-loader",

        },

      ],

    },

  ]

}

同時,在tsconfig.json中我們也需要設置

{

    "compilerOptions": {

        ...

      "target": "ES5",

      "jsx": "react-jsx",

       "paths": {

          "@public/*": ["public/*"],

          "@comp/*": ["src/components/*"],

          "@utils/*": ["src/utils/*"],

          "@src/*": ["src/*"],

          "@assets": ["src/assets/*"]

        },

        "baseUrl": "."

    },

  

}

其中你看到有設置通用路徑別名,不過,除了整理設置,我們同時也需要設置resolve.alains

module.exports = {

    ...

    resolve: {

      extensions: [".tsx", ".ts", ".js", ".jsx"],

      alias: {

        "@public": resolvePath("public/"),

        "@utils": resolvePath("src/utils/"),

        "@src": resolvePath("src/"),

        "@comp": resolvePath("src/components/"),

        "@assets": resolvePath("src/assets/"),

      }

    }

}

這樣你就可以在tsx,jsx文件中構建我們的組件了。

module.exports = {

    plugins: {

      "postcss-import": {},

      "tailwindcss/nesting": {},

      tailwindcss: {},

      autoprefixer: {},

    },

  };

其實我們已經完成了一個插件,當我們執行pnpm run build:local時,就會打包成一個插件的最終輸出文件了

我們打開chrome瀏覽器,擴展程序>打開開發者模式>加載已解壓的擴展程序這個文件夾即可

安裝插件後,訪問任何一個網站,我們在content寫入的內容就生效了

不知道你有沒有好奇,當我們使用插件時,我們必須修改代碼,然後執行打包,然後重新加載插件看效果,這顯然與我們實際開發有些繁瑣,因此,你可以執行將content變成一個多頁頁面,這樣可以就可以高效的開發了, 不過你需要稍修改下manifest.jsonexclude_matches, 主要爲了插件不在當前訪問本地開發頁面生效

   "content_scripts": [

        {

            "matches": [

                "<all_urls>"

            ],

            "exclude_matches": [

               "http://localhost:8080/*"

            ],

            "js": [

                "src/content/index.js"

            ],

            "css": [

                "assets/css/index.css"

            ],

            

            "run_at": "document_end",

            "all_frames": false

        }

    ],

至於tab頁面,我們也是直接訪問/set.html就可以了

因此一個插件的基礎就基本完成了。

總結

[1]tailwindcss: https://tailwindcss.com/docs/installation

[2]code example: https://github.com/maicFir/base-chrome-plugin

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/gEJ8wL7Q0xqS66ZPXtxz3Q