如何用油猴提升前端開發效率

1. 起因

時隔一年,油猴腳本系列又來了,起因是那天洗澡的時候,突然靈光一閃,想到平時在前端開發中,有很多工作完全是一些機械的,沒有什麼難度的活,但是又異常的花時間!其實就是對一些表格字段。

目前爲止我還在開發之前的那個百萬級 ERP 項目,項目中的列表非常的多,目前大概有幾百個吧,而且每個列表都有非常多的字段,通常情況下都在 20 個字段左右,而後端寫的時候會在對應的接口中返回一大堆字段,你需要從返回的字段中挑選出列表中需要的字段。

可能一個兩個還好,但是幾十個幾百個列表一個字段一個字段的對起來就很耗時耗力,所以這個時候我就有一個想法:有沒有那麼一種方法,將原型圖上列表字段提取出來,然後與 swagger 中該接口下面的字段註釋做一個對比,如果對的上,則生成 Antd 可以使用的表頭代碼,如果沒有對上,那麼就統一將這些沒有對上的字段展示出來,然後用肉眼去對,如果依然沒有找到這些字段,那麼就發給後端,讓後端添加上這些字段。

一想到這個可能性,馬上就想迫不及待的想要進行實現,這時我回憶起一年前用過油猴腳本做了一些事情,那麼這次這個突發奇想是否也可以用油猴腳本進行實現。

2. 油猴腳本

我在去年已經寫過幾篇關於油猴腳本的文章,其中提到過通過 Webpack 打包,可以開發特別複雜的腳本,甚至可以開發一些工程級的腳本,同時也可以使用 npm 上的包。

但是由於 webpack 配置的複雜性,所以我首先是沒有考慮使用 webpack 打包工具的,而是先想到了另一個零配置打包工具:parcel。

但是很遺憾,失敗了,不知道爲什麼通過 parcel 打包後的 js 文件直接通過油猴引入外部資源的方法會報錯,於是這個時候我還想到了一個打包 js 文件的工具:Rollup。

遺憾的是,同樣失敗了,不知道爲什麼引入油猴後就是會報錯,於是這個時候我就放棄抵抗,選擇了 webpack 進行打包,結果不出意料,webpack 打包後的 js 文件引入油猴中沒有任何問題。

因爲我之前已經這麼做過了,還總結了相關的文章:強大的油猴 Tampermonkey 腳本開發環境搭建,所以關於油猴的基礎部分就不再贅述。

2.1 打包的好處

爲什麼要通過 webpack 打包?

因爲在編寫一些複雜腳本時,我們往往會需要使用到非常多的庫,而且也會將腳本進行模塊化,分成非常多的文件,同時如果你還想編寫一些 CSS 樣式,那麼你也可以直接寫 CSS 文件或者 Less 或者 Sass 文件,通過 webpack 可以將這些文件統一起來,打包成一個 js,然後通過油猴提供的外部引入方式進行引入。

隨着我對 React 越發的得心應手,要實現上面說的自動比對字段的功能 React 也是不可少的一環,而我個人非常喜歡使用 TypeScript,因爲它會給予代碼更多的提示,讓你在寫代碼的時候不容易犯一些低級錯誤。

而使用 React 的時候就不得不提到 Antd,Antd 這個 UI 組件庫提供了非常多的方便的組件,不光是解決了一些樣式問題,同時它還解決了很多交互層面的東西,比如它的 Form 表單,也是非常好用的。

綜上所述,如果你要開發一個油猴腳本,那麼通過 webpack 進行打包就是一個非常好的選擇。

3. jQuery

正如我年前的那篇文章所說,jQuery 是一個非常值得學習的庫,因爲對於編寫油猴腳本來說,沒有什麼比用 jQuery 去提取界面上的信息更方便的了。

由於 jQuery 的易用性,使用 jQuery 你可以輕易的從界面上提取到你要的信息,雖然使用正則可以達到差不多的效果,但 jQuery 代碼寫起來比正則更快,也更不容易出錯。

4. 正則

編寫腳本,非常多的情況會使用到正則,因爲要精確的匹配到對應的字符,正則是你的不二之選,在做一些自動生成代碼工具的時候,因爲涉及到字符匹配的問題,學會正則就顯得非常的重要。

這裏就要推薦一個非常好的正則測試網站:RegExr: 學習、構建和測試正則表達式 Test RegEx,因爲有時候你無法判斷你寫的正則對不對,所以這個時候你就可以先在這個網站上面測試一下,如果測試結果符合你的預期,再將正則表達式複製到你的項目中進行測試,這樣編寫起正則來就方便的多,使用方法也非常簡單,去該網站上面點兩下大致就能明白如何使用。

5. 爬蟲基礎

如果你要提取一些界面上的信息,那麼你得會一些爬蟲基礎知識,知道怎麼獲取到界面上的某些信息。

理論上一個網頁上面所有的信息,都能通過正則表達式進行提取,但由於正則表達式在編寫的過程中可能會比較容易出錯,這個時候 jQuery 就能有效的幫你提取出這些頁面數據。

這裏就對爬蟲入門時 90% 的人都會選擇的豆瓣排行榜做一個頁面信息提取。

6. 實例

上面所想的那個腳本我其實已經實現了,但礙於 swagger 上面有公司項目的接口信息,所以這裏就不拿那個腳本作爲演示。

那麼這裏我就拿 jQuery 爬取到的豆瓣排行榜信息作爲演示吧:

主要實現的功能就是,在輸入框中輸入電影名稱,然後將電影相關的信息進行匹配並且展示出來:

6.1 觸發事件構建

要執行你已經寫好的代碼,你通常可能需要一個按鈕、一個輸入框或者其它元素進行觸發,在本腳本的開發中,我就使用了 Antd 的按鈕 + Antd 的模態框 + Antd 的輸入框這 3 種組件來搭建腳本。

6.2 最終效果

最終的效果像下面這樣,代碼其實並不難。這裏貼上最終的代碼:

import { Button, ConfigProvider, Form, Modal } from "antd";
import "antd/dist/antd.css";
import TextArea from "antd/es/input/TextArea";
import zhCN from "antd/lib/locale/zh_CN";
import * as $ from "jquery";
import { useState } from "react";
import { render } from "react-dom";

interface InfoDataList {
  /** 電影的名稱 */
  title: string;
  /** 分數 */
  score: string;
  /** 評價數 */
  number: string;
  /** 簡介 */
  info: string;
}

function AppButton() {
  const [infoDataList, setInfoDataList] = useState<InfoDataList[]>([]);

  const [showData, setShowData] = useState<InfoDataList>();

  const [visible, setVisible] = useState(false);

  return (
    <>
      <Button
        onClick={() => {
          /** 整理後的信息 */
          const info: InfoDataList[] = [];

          // 這裏是提取界面信息
          $(".info").each(function (this) {
            const infoItem: InfoDataList = {} as InfoDataList;
            // 這裏是所有的信息,下面組件提取各種信息
            $(this)
              .find(".title")
              .each(function (this, index) {
                // 只取第一個電影名字
                if (index === 0) infoItem.title = this.innerHTML;
              });

            // 取分數
            $(this)
              .find(".rating_num")
              .each(function (this) {
                infoItem.score = this.innerHTML;
              });

            // 評價數
            $(this)
              .find(".star > span:nth-child(4)")
              .each(function (this) {
                infoItem.number = this.innerHTML;
              });

            // 取信息
            $(this)
              .find(".inq")
              .each(function (this) {
                infoItem.info = this.innerHTML;
              });

            info.push(infoItem);
          });

          setInfoDataList(info);
          // 打開彈窗
          setVisible(true);
        }}
        style={{ position: "fixed", right: "100px", bottom: "100px" }}
      >
        點我
      </Button>
      <Modal
        title="自動對字段"
        visible={visible}
        centered
        destroyOnClose
        width={1000}
        onCancel={() => {
          setVisible(false);
        }}
      >
        <Form
          preserve={false}
          onValuesChange={async (value) => {
            // 如果沒有值,則不匹配
            if (!value.text) {
              setShowData(undefined);
              return;
            }
            const rgx = RegExp(value.text);

            const data = infoDataList.find((item) => rgx.test(item.title));
            setShowData(data);
          }}
        >
          <Form.Item >
            <TextArea rows={10} />
          </Form.Item>
        </Form>
        {/* 這裏是將對的字段展示出來 */}
        <ul>
          <li>電影名{showData?.title}</li>
          <li>分數{showData?.score}</li>
          <li>評價數{showData?.number}</li>
          <li>簡介{showData?.info}</li>
        </ul>
      </Modal>
    </>
  );
}

// 添加一個div作爲React的入口文件
$("body").append(`<div/>`);

render(
  <ConfigProvider locale={zhCN}>
    <AppButton />
  </ConfigProvider>,
  document.getElementById("ccll-app")
);
複製代碼

7. webpack 配置

下面是我自己搭建並且使用的一套webpack.config.js,添加了對CSSTypeScriptLess的支持,並且還對JavaScript做了兼容性處理,可以直接將下面的代碼複製過去嘗試。

const { resolve } = require("path");

module.exports = {
  entry: ["./index.js"],
  output: {
    path: resolve(__dirname, "build"),
    filename: "index.js",
    publicPath: "/",
  },
  module: {
    rules: [
      {
        test: /.(js|jsx|mjs)$/,
        exclude: /node_modules/,
        use: [
          {
            loader: "babel-loader",
            options: {
              // 預設:指示babel做怎麼樣的兼容性處理。
              presets: [
                [
                  "@babel/preset-env",
                  {
                    corejs: {
                      version: 3,
                    }, // 按需加載
                    useBuiltIns: "usage",
                  },
                ],
                "@babel/preset-react",
              ],
            },
          },
        ],
      },
      {
        test: /.tsx?$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
      {
        test: /.css$/,
        // 使用哪些 loader 進行處理
        use: [
          // use 數組中 loader 執行順序:從右到左,從下到上 依次執行
          // 創建 style 標籤,將 js 中的樣式資源插入進行,添加到 head 中生效
          "style-loader",
          // 將 css 文件變成 commonjs 模塊加載 js 中,裏面內容是樣式字符串
          "css-loader",
        ],
      },
      {
        test: /.less$/,
        // 使用哪些 loader 進行處理
        use: [
          // use 數組中 loader 執行順序:從右到左,從下到上 依次執行
          // 創建 style 標籤,將 js 中的樣式資源插入進行,添加到 head 中生效
          "style-loader",
          // 將 css 文件變成 commonjs 模塊加載 js 中,裏面內容是樣式字符串
          "css-loader",
          {
            loader: "less-loader",
            options: {
              lessOptions: {
                javascriptEnabled: true,
              },
            },
          },
        ],
      },
    ],
  },
  resolve: {
    extensions: [".tsx", ".ts", ".js"],
  },
  mode: "production",
  devtool: "source-map",
};
複製代碼

8. 最後

本篇文章沒有拿我的那個最終實現的腳本進行演示,而是模擬了另一個例子,主要是起到一個拋磚引玉的作用,我想表達的意思就是油猴被我們前端開發者所忽略了,其實它是一個非常好的能夠提升開發效率的工具

從文章中可以看到,編寫一個腳本需要掌握的知識還是比較多的,尤其是 jQuery 和正則,當然如果你對原生 js 熟悉的話,你完全可以使用原生 js 代碼來代替 jQuery 的那些代碼。

就我個人編寫油猴腳本的經驗來講,jQuery 幾乎在每個腳本中都會用到,雖然用到的東西不是特別深,主要是提取界面信息,而正則用到的也非常多。

其實這裏還留下了一個問題,就是引入了React+Antd後,打包的時間往往會達到 10s 左右,而打包後的代碼通常會達到 2M 左右,下一篇文章就講如何在油猴中使用 CDN 引入,讓打包時間減少到 1s 左右,代碼體積減少到幾十 kb 左右。

通過這次的靈光乍現,我想到平時在寫業務代碼時,一些重複的機械的工作都可以交給腳本去實現,提高自己的開發效率,做一個準點下班的程序員。

關於本文

作者:滄滄涼涼

https://juejin.cn/post/7075237968205578277

最後

歡迎關注【前端瓶子君】✿✿ヽ (°▽°) ノ✿

如果這篇文章對你有幫助,「在看」是最大的支持

前端瓶子君 瓶子君,2021 致力於幫助前端開啓技術專項 + 算法之路!

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