如何在Chrome Extension的内容脚本中导入ES6模块

Rag*_*nar 31 javascript google-chrome google-chrome-extension ecmascript-6

Chrome 61中,增加了对JavaScript模块的支持.现在我正在运行Chrome 63.

我试图弄清楚如何在Chrome扩展内容脚本中使用导入/导出语法来使用模块.

manifest.json中:

"content_scripts":[{

"content_scripts": [
    {
        "js": [
            "content.js"
        ],
    }
]
Run Code Online (Sandbox Code Playgroud)

my-script.js中,与content.js位于同一目录中

'use strict';

const injectFunction = () => window.alert('hello world');

export default injectFunction;
Run Code Online (Sandbox Code Playgroud)

content.js中

'use strict';

import injectFunction from './my-script.js';
injectFunction();
Run Code Online (Sandbox Code Playgroud)

我收到此错误: Uncaught SyntaxError:意外的标识符

如果我将导入语法更改为import {injectFunction} from './my-script.js';I,则会收到以下错误:Uncaught SyntaxError:Unexpected token {

在Chrome扩展中的content.js中使用此语法是否存在一些问题,因为在HTML中您必须使用<script type="module" src="script.js">语法,或者我做错了什么?谷歌会忽略对扩展的支持,这似乎很奇怪.

Rag*_*nar 21

我设法找到了解决方法.

首先,重要的是说内容脚本自2018年1月起不支持模块.但是您可以通过将模块脚本标记嵌入到返回扩展的页面中来使其工作.

这是我的表现

    "content_scripts": [ {
       "js": [
         "content.js"
       ]
    }],
    "web_accessible_resources": [
       "main.js",
       "my-script.js"
    ]
Run Code Online (Sandbox Code Playgroud)

请注意,我在Web可访问资源中有两个脚本.

我的content.js只包含这个逻辑:

    'use strict';

    const script = document.createElement('script');
    script.setAttribute("type", "module");
    script.setAttribute("src", chrome.extension.getURL('main.js'));
    const head = document.head || document.getElementsByTagName("head")[0] || document.documentElement;
    head.insertBefore(script, head.lastChild);
Run Code Online (Sandbox Code Playgroud)

这会将main.js作为模块脚本插入到网页中.我的所有业务逻辑现在都在main.js和main中,而且我将要导入的所有脚本都必须位于清单中的web_accessible_resources中.

这是my-script.js的示例内容:

    'use strict';

    const injectFunction = () => window.alert('hello world');

    export {injectFunction};
Run Code Online (Sandbox Code Playgroud)

main.js中,这是导入脚本的示例:

    'use strict';

    import {injectFunction} from './my-script.js';
    injectFunction();
Run Code Online (Sandbox Code Playgroud)

这工作,没有错误被抛出,我很高兴:)

  • 好的解决方案 唯一的问题是chrome.runtime API变得不可用:( (13认同)
  • 例如,如果在上面的main.js中,你有`chrome.runtime.onMessage.addListener((request,sender,sendResponse),()=> ...)`处理程序永远不会被调用.由于这是设计原因,没有解决方法.但我正在使用Webpack将我的扩展编译成一个文件,所以这对我来说不是问题. (6认同)
  • 我不推荐这种模式,因为它从内容脚本中删除了上下文隔离。除此之外,它还允许网站检测到您的扩展名。这是一个安全漏洞。 (4认同)
  • 另一个缺点:“模块”是异步的。如果您尝试注入应该在目标页面自己的 js 之前执行的 js,那将不再起作用。当然,这可能并不重要,这取决于个人的需要。 (2认同)
  • ↑ 没有人“推荐”它,这是一种解决方法,直到浏览器实现赶上。@ragnar:感谢您提供了清晰的示例;根据记录,这项技术似乎对我来说在 Firefox 56 中也很有效(使用“dom.moduleScripts.enabled”) (2认同)
  • 为了在 2021 年实现这项工作,我必须将 `chrome.extension.getURL` 替换为 `chrome.runtime.getURL`,并将 `"web_accessible_resources": ["main.js", "my-script.js"]` 替换为`"web_accessible_resources": {"resources": ["main.js", "my-script.js"], "matches": ["*://*/*"]}` (2认同)

oti*_*i10 15

正如已经提到的那样,对于后台脚本,最好使用background.page和使用<script type="module">来踢你的JavaScript.

问题是content script,<script>使用type属性注入标记可以是一个解决方案.

注入脚本标记的另一种方法是使用dynamic import函数.通过这种方法,您不需要放宽chrome模块的范围,仍然可以使用chrome.runtime或其他模块.

content_script.js,它看起来像

(async () => {
  const src = chrome.runtime.getURL("your/content_main.js");
  const contentMain = await import(src);
  contentMain.main();
})();
Run Code Online (Sandbox Code Playgroud)

更多细节:

希望能帮助到你.

  • 不错的解决方案。值得一提的是,使用此技术导入的脚本需要在清单中作为“ web_accessible_resources”被允许列出。不允许列出它们将导致`GET chrome-extension:// invalid / net :: ERR_FAILED`错误。这可能并不明显,因为常规内容脚本不需要被允许列出。 (6认同)
  • 上面链接的错误已经解决,这个策略应该在 Firefox 89 中有效。它已经在最新的 Nightly 中有效! (2认同)
  • 如果您使用的是 webpack,请在“import”调用中添加“/*webpackIgnore: true*/”,以使 webpack 不进行导入(如果没有它,此方法对我来说不起作用)。 (2认同)

ava*_*he1 13

imports 在内容脚本中不可用。

这是使用全局范围的解决方法。

由于内容脚本存在于它们自己的“孤立世界”中- 它们共享相同的全局命名空间。它只能被 中声明的内容脚本访问manifest.json

这是实现:

清单文件

"content_scripts": [
  {
    "matches": ["<all_urls>"],
    "js": [
      "content-scripts/globals.js",
      "content-scripts/script1.js",
      "content-scripts/script2.js"
    ]
  }
],
Run Code Online (Sandbox Code Playgroud)

globals.js

globalThis.foo = 123;
Run Code Online (Sandbox Code Playgroud)

脚本1.js

some_fn_that_needs_foo(globalThis.foo);
Run Code Online (Sandbox Code Playgroud)

以同样的方式,您可以将可重用的功能和其他角色分解import出内容脚本文件中的其他角色。

注意:除了内容脚本之外,任何页面都无法使用内容脚本的全局命名空间 - 因此几乎没有全局范围污染。

如果您需要导入一些库 - 您将不得不使用一个Parcel打包器,比如将您的内容脚本文件与所需的库huge-content-script.js打包成一个文件,然后在manifest.json.

PS:关于 globalThis 的文档

  • 重要的!manifest.json 中脚本的顺序至关重要。如果您依赖的脚本低于您发出呼叫的脚本,则该脚本将不可用。 (4认同)
  • 出色的。如果你不习惯的话,这不是一个容易的心态。 (2认同)

小智 12

最好的方法是使用像 webpack 或 Rollup 这样的打包器。

我摆脱了基本配置

const path = require('path');

module.exports = {
  entry: {
    background: './background.js',
    content: './content.js',
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, '../build')
  }
};
Run Code Online (Sandbox Code Playgroud)

使用命令运行文件

webpack --config ./ext/webpack-ext.config.js
Run Code Online (Sandbox Code Playgroud)

捆绑器组合相关文件,我们可以在 chrome 扩展中使用模块化!:D

您需要在构建文件夹中保留所有其他文件,如清单和静态文件。

玩弄它,你最终会找到让它工作的方法!

  • 这怎么没有得到更多的支持?比动态导入和其他 hack 好得多:) (3认同)
  • 使用 Rollup 代替 → https://www.extend-chrome.dev/rollup-plugin#usage (3认同)

Dan*_*han 6

我只是在尝试自己解决同样的问题时偶然发现了这个问题。

无论如何,我认为有一个更简单的解决方案可以将您自己的自定义模块注入您的内容脚本。我正在研究 Jquery 是如何注入的,我发现您可以通过创建一个IIFE(立即调用的函数表达式),并在你的manifest.json 中声明它

它是这样的:

在您的 manifest.json 中:

"content_scripts": [
{
  "matches": ["https://*"],
  "css": ["css/popup.css"],
  "js": ["helpers/helpers.js"]
}],
Run Code Online (Sandbox Code Playgroud)

然后在 helpers/helpers.js 中创建一个 IIFE:

var Helpers = (function() {
  var getRandomArbitrary = function(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
  }
  return {
    getRandomArbitrary: getRandomArbitrary
  }
})()
Run Code Online (Sandbox Code Playgroud)

现在,您可以在内容脚本中自由使用辅助函数:

Helpers.getRandomArbitrary(0, 10) // voila!
Run Code Online (Sandbox Code Playgroud)

我认为如果您使用这种方法来重构您的一些通用函数,那就太好了。希望这可以帮助!


Gou*_*J.M 5

Vite用户:CRXJS

有一个名为crxjs的很棒的插件,您只需更新它vite.config.ts并提供您的路径manifest.json(它仅适用于manifets_version >= 3)。

请按照以下步骤运行脚本:

  1. 将 crxjs 添加到您的项目中:
    npm install --save-dev @crxjs/vite-plugin
    
    Run Code Online (Sandbox Code Playgroud)
  2. 创建或更新manifest.json
    {
      "manifest_version": 3,
      "name": "CRXJS React Vite Example",
      "version": "1.0.0",
      "action": { "default_popup": "index.html" }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. vite.config.ts使用清单的路径 更新您的文件:
    import { defineConfig } from 'vite'
    import react from '@vitejs/plugin-react'
    import { crx } from '@crxjs/vite-plugin'
    import manifest from './manifest.json'
    
    export default defineConfig({
      plugins: [
        react(),
        crx({ manifest }),
      ],
    })
    
    Run Code Online (Sandbox Code Playgroud)

设置完成后,运行您的项目。现在config.js将被捆绑,您可以在其中导入包。