[extention] extention 項(xiàng)目構(gòu)建

是這樣的,最近想要開(kāi)發(fā)一個(gè)瀏覽器拓展的應(yīng)用,剛開(kāi)始就給我惡心到了,每天都用著第三方模塊和 import export 切分文件的我面對(duì)一個(gè)個(gè)獨(dú)立的js竟無(wú)從下手,那么就用webpack來(lái)構(gòu)建一個(gè)模塊化的 extention 項(xiàng)目吧~

項(xiàng)目地址:https://gitee.com/boboanzuiniubi/ext-xhr-proxy
這個(gè)是個(gè)xhr劫持的拓展工具,我會(huì)在做完功能之后,把項(xiàng)目模板拆出來(lái)~

先分析一下要做什么吧

manifest.json:

{
    "manifest_version": 2,
    "name": "zcr",
    "description": "ceshi",
    "version": "1.0",
    "content_scripts": [{ "matches": ["<all_urls>"], "css": [], "js": ["./content_scripts/inject_xhr.js"] }]
}

manifest.json 是位于項(xiàng)目根目錄下的拓展應(yīng)用的清單文件,這里面是拓展應(yīng)用的描述,和 content_scripts/page/icon 等等資源的地址。大多屬性是靜態(tài)的,資源地址 是整個(gè)構(gòu)建流程需要處理的,要做的是將構(gòu)建后的資源目錄,寫(xiě)入 manifest.json ,亦或者是按照 manifest 上寫(xiě)的路徑去構(gòu)建資源。

load scripts:

拓展中的 content_scriptinject_script 都是需要打包起來(lái)的 js 文件,各種單頁(yè)面 page 也需要打包一個(gè)入口文件,那我們就需要寫(xiě)一段 js 去按路徑讀取這些 js 入口文件,并且要將output的文件地址寫(xiě)入 manifest

load pages:

拓展(extention)中是有一些頁(yè)面的,比如 popup page / background page / devtools page 都是一個(gè)html,我們可以用 html-webpack-plugin 和三大框架構(gòu)建一個(gè)單頁(yè)面應(yīng)用來(lái)快速開(kāi)發(fā)

目錄結(jié)構(gòu)與構(gòu)建流程

image.png

細(xì)化

eslint

(todo 但又不完全todo) 在糾結(jié)要不要引,因?yàn)轫?xiàng)目不是很大,不是很關(guān)鍵

friendly-errors-webpack-plugin

friendly-errors-webpack-plugin用來(lái)輸出webpackl的報(bào)錯(cuò)真是簡(jiǎn)單又好用,省得你去研究怎么輸出異常。

plugin

我這里用的 plugin 是為了在每個(gè)模塊構(gòu)建完成時(shí),記錄一下需要寫(xiě)入 manifest 的 output 地址的
找一個(gè)合適的鉤子獲取 output 的地址


1623833203(1).png

比如傳入一個(gè)記事本對(duì)象,并在構(gòu)建結(jié)束后重寫(xiě)manifest.json

const manifest_content_scripts = {
  matches: ['<all_urls>'],
  css: [],
  js: []
}
config.plugins.push(new ContentScriptPlugin(manifest_content_scripts))

webpack(config, (err, stats) => {
  if (err || stats.hasErrors()) {

  } else {
    // 重寫(xiě)manifest
    fs.writeFileSync(path.resolve(__dirname, '../output/manifest.json'), JSON.stringify({
      ...manifest,
      web_accessible_resources: mainfest_web_accessible_resources,
      content_scripts: [manifest_content_scripts]
    }))
  }
});
...
// 這個(gè)plugin在構(gòu)建模塊時(shí),記錄一條需要注入的content_script

const isContentJs = (name) => /content_scripts\/.+\.js$/.test(name)
const isContentCss = (name) => /content_scripts\/.+\.css$/.test(name)

module.exports = class ContentScriptPlugin {
  constructor(manifest_content_script) {
    this.manifest_content_script = manifest_content_script
  }
  apply(compiler) {
    compiler.hooks.assetEmitted.tap(
      'ContentScriptPlugin',
      (file, { content, source, outputPath, compilation, targetPath }) => {
        if (isContentJs(file)) {
          this.manifest_content_script.js.push(file)
        } else if (isContentCss(file)) {
          this.manifest_content_script.css.push(file)
        }
      }
    )
  }
}

react

用 react 加其周邊的組件庫(kù) 可以很快速的開(kāi)發(fā) html 頁(yè)面,需要babel-loaderhtml-webpack-plugin去執(zhí)行jsx語(yǔ)法轉(zhuǎn)換和創(chuàng)建頁(yè)面。

配置 babel

// babel.config.json
{
  "presets": [
    "@babel/env",
    "@babel/preset-react"
  ]
}

添加 rules

// rules 只處理頁(yè)面部分的js就可以了,可以加載ext的瀏覽器并沒(méi)有兼容性問(wèn)題
rules: [{
      test: /\.jsx?$/,
      loader: "babel-loader",
      include: [src('./popup')]
    },
    ...
]

打包入口

entry: {
    ...content_scripts,
    ...inject_scripts,
    background: src('./background.js'),
    popup: src('./popup/index.js')
},

配置 html plugin

plugins: [
    new HtmlWebpackPlugin({
      filename: 'popup.html',
      template: src('./popup/index.html'),
      chunks: ['popup']
    }),
   ...
  ],

shelljs

用 node 的 file system 處理文件會(huì)有各個(gè)node版本fs api的兼容性問(wèn)題,太煩了,用shelljs兼容性問(wèn)題會(huì)少點(diǎn)

file-loader

拓展應(yīng)用中的資源文件會(huì)有 icon img 這種 圖片資源,統(tǒng)一就用file-loader 去放到一個(gè)img目錄下好了,在manifest中就按照命名引用img目錄下的資源

rules: [
    ...   
   ,{
      test: /\.(png|jpe?g|gif)$/i,
      use: [
        {
          loader: 'file-loader',
          options: {
            name: 'img/[name].[ext]'
          }
        }
      ]
    }]
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容