javascript 如何在 Chrome 扩展的内容脚本中导入 ES6 模块
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/48104433/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to import ES6 modules in content script for Chrome Extension
提问by Ragnar
In Chrome 61, support for modules in JavaScript was added. Right now I am running Chrome 63.
在Chrome 61 中,添加了对 JavaScript 模块的支持。现在我正在运行 Chrome 63。
I am trying to use import/exportsyntax in Chrome extension content script to use modules.
我正在尝试在 Chrome 扩展内容脚本中使用import/export语法来使用模块。
In manifest.json:
在manifest.json:
"content_scripts": [
{
"js": [
"content.js"
],
}
]
In my-script.js(same directory as content.js):
在my-script.js(与 相同的目录中content.js):
'use strict';
const injectFunction = () => window.alert('hello world');
export default injectFunction;
In content.js:
在content.js:
'use strict';
import injectFunction from './my-script.js';
injectFunction();
I receive this error: Uncaught SyntaxError: Unexpected identifier
我收到此错误: Uncaught SyntaxError: Unexpected identifier
If I change the import syntax to import {injectFunction} from './my-script.js';I get this error: Uncaught SyntaxError: Unexpected token {
如果我将导入语法更改为出现import {injectFunction} from './my-script.js';此错误:Uncaught SyntaxError: Unexpected token {
Is there some issue with using this syntax in content.jsin Chrome extension (since in HTML you have to use <script type="module" src="script.js">syntax), or am I doing something wrong? It seems strange that Google would ignore support for extensions.
content.js在 Chrome 扩展程序中使用这种语法有什么问题(因为在 HTML 中你必须使用<script type="module" src="script.js">语法),还是我做错了什么?Google 会忽略对扩展的支持,这似乎很奇怪。
采纳答案by Ragnar
I managed to find a workaround.
我设法找到了解决方法。
Disclaimer
免责声明
First of all, it's important to say that content scripts don't support modules as of January 2018. This workaround sidesteps the limitation by embedding module scripttag into the page that leads back to your extension.
首先,重要的是要说明内容脚本自 2018 年 1 月起不支持模块。此变通方法通过将模块script标记嵌入到返回扩展程序的页面中来避开限制。
Workaround
解决方法
This is my manifest.json:
这是我的manifest.json:
"content_scripts": [ {
"js": [
"content.js"
]
}],
"web_accessible_resources": [
"main.js",
"my-script.js"
]
Note that I have two scripts in web_accessible_resources.
请注意,我在web_accessible_resources.
This is my content.js:
这是我的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);
This will insert main.jsinto the webpage as a module script.
这将main.js作为模块脚本插入到网页中。
All my business logic is now in main.js.
我所有的业务逻辑现在都在main.js.
For this method to work, main.js(as well as all scripts that I will import) mustbe in web_accessible_resourcesin the manifest.
要使此方法起作用,main.js(以及我将要使用的所有脚本 import)必须web_accessible_resources在清单中。
Example Usage: my-script.js
示例用法: my-script.js
'use strict';
const injectFunction = () => window.alert('hello world');
export {injectFunction};
And in main.jsthis is an example of importing the script:
而在main.js这导入脚本的例子:
'use strict';
import {injectFunction} from './my-script.js';
injectFunction();
This works! No errors are thrown, and I am happy. :)
这有效!没有抛出任何错误,我很高兴。:)
回答by otiai10
As it's already mentioned, for background script, it's good idea to use background.pageand use <script type="module">to kick your JavaScript.
正如已经提到的,对于后台脚本,使用background.page和使用<script type="module">来踢你的 JavaScript是个好主意。
The problem is content script, and injecting <script>tag with typeattribute can be a solution.
问题是content script,注入<script>带有type属性的标签可以是一个解决方案。
Another approach than injecting script tag is to use dynamic importfunction. By this approach, you don't need to loose scope of chromemodule and still can use chrome.runtimeor other modules.
除了注入脚本标签之外,另一种方法是使用dynamic import函数。通过这种方法,您不需要松散chrome模块的范围,仍然可以使用chrome.runtime或其他模块。
In content_script.js, it looks like
在content_script.js,它看起来像
(async () => {
const src = chrome.runtime.getURL("your/content_main.js");
const contentMain = await import(src);
contentMain.main();
})();
You'll also need to declare the imported scripts in manifest's Web Accessible Resources:
您还需要在清单的Web Accessible Resources 中声明导入的脚本:
{
"web_accessible_resources": [
"your/content_main.js"
]
}
For more details:
更多细节:
- How to use ES6 “import” with Chrome Extension
- Working Example of ES6 import in Chrome Extension
chrome.runtime.getURL
Hope it helps.
希望能帮助到你。
回答by Daniel Chan
I just stumbled across this question while trying to solve the same thing myself.
我只是在尝试自己解决同样的问题时偶然发现了这个问题。
Anyways, I think there's a simpler solution to injecting your own custom modules into your content script. I was looking at how Jquery is injected and it occurs to me you can do the same thing by creating an IIFE(Immediately Invoked Function Expression), and declaring it in your manifest.json
无论如何,我认为有一个更简单的解决方案可以将您自己的自定义模块注入您的内容脚本。我正在研究 Jquery 是如何注入的,我发现你可以通过创建一个IIFE(立即调用的函数表达式)来做同样的事情,并在你的manifest.json 中声明它
It goes something like this:
它是这样的:
In your manifest.json:
在您的 manifest.json 中:
"content_scripts": [
{
"matches": ["https://*"],
"css": ["css/popup.css"],
"js": ["helpers/helpers.js"]
}],
Then just create an IIFE in your helpers/helpers.js:
然后在 helpers/helpers.js 中创建一个 IIFE:
var Helpers = (function() {
var getRandomArbitrary = function(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
return {
getRandomArbitrary: getRandomArbitrary
}
})()
Now, you can freely use your helper functions in your content script:
现在,您可以在内容脚本中自由使用辅助函数:
Helpers.getRandomArbitrary(0, 10) // voila!
I think it's great if you use this method to refactor some of your generic functions. Hope this helps!
我认为如果您使用这种方法来重构您的一些通用功能,那就太好了。希望这可以帮助!
回答by avalanche1
imports are not available in content scripts.
imports 在内容脚本中不可用。
Here's a workaround using global scope.
这是使用全局范围的解决方法。
Since content scripts live in their own 'isolated world'- they share the same global namespace. It is only accessible to content scripts declared in manifest.json.
由于内容脚本存在于它们自己的“孤立世界”中- 它们共享相同的全局命名空间。它只能被 中声明的内容脚本访问manifest.json。
Here's the implementation:
这是实现:
manifest.json
清单文件.json
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": [
"content-scripts/globals.js",
"content-scripts/script1.js",
"content-scripts/script2.js"
]
}
],
globals.js
globals.js
globalThis.foo = 123;
script1.js
脚本1.js
some_fn_that_needs_foo(globalThis.foo);
Same way you can factor out re-usable functions and other actors you would otherwise importin content script files.
以同样的方式,您可以将可重用的功能和其他演员分解import出内容脚本文件中的其他角色。
N.B.: global namespace of content scripts is not available to any pages besides content scripts - so there is little to no global scope pollution.
注意:内容脚本的全局命名空间对除内容脚本外的任何页面都不可用 - 因此几乎没有全局范围污染。
In case you need to import some libs - you will have to use a bundler like Parcelto package up your content script files along with the needed libs into one huge-content-script.jsand then feed it to manifest.json.
如果您需要导入一些库 - 您将不得不使用捆绑器Parcel将您的内容脚本文件与所需的库打包成一个文件huge-content-script.js,然后将其提供给manifest.json.
P.S.: docs on globalThis
回答by Dhruvil Shah
The best way would be to use bundlers like webpack or Rollup.
最好的方法是使用像 webpack 或 Rollup 这样的打包器。
I got away with basic configuration
我摆脱了基本配置
const path = require('path');
module.exports = {
entry: {
background: './background.js',
content: './content.js',
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, '../build')
}
};
Run the file with the command
使用命令运行文件
webpack --config ./ext/webpack-ext.config.js
Bundlers combine the related files and we can use modularisation in chrome extensions! :D
捆绑器组合相关文件,我们可以在 chrome 扩展中使用模块化!:D
You will need to keep all other files like manifest and static files in build folder.
您需要在构建文件夹中保留所有其他文件,如清单和静态文件。
Play around with it and you will eventually find a way to make it work!
玩弄它,你最终会找到让它工作的方法!
回答by Andrew Willems
Short Answer:
简答:
You can mimic some of the functionality and get some of the benefits of import/exportin browser extensions by creating the following file and listing it early in your manifest.json:
你可以模仿一些功能,并得到一些好处import/export通过创建以下文件并在上市初期就在浏览器扩展manifest.json:
let exportVars, importVarsFrom;
{
const modules = {};
exportVars = varsObj => ({
from(nameSpace) {
modules[nameSpace] || (modules[nameSpace] = {});
for (let [k,v] of Object.entries(varsObj)) {
modules[nameSpace][k] = v;
}
}
});
importVarsFrom = nameSpace => modules[nameSpace];
}
Then, export from one file/module like this:
然后,像这样从一个文件/模块导出:
exportVars({ var1, var2, var3 }).from('my-utility');
Import into another file/module like this:
像这样导入另一个文件/模块:
const { var1, var3: newNameForVar3 } = importVarsFrom('my-utility');
Discussion:
讨论:
This strategy:
这个策略:
- allows modular codein a browser extension such that you can split code into multiple files but don't have variable clashes due to shared global scope between different files,
- still allows you to export and import variablesout of and into different JavaScript files/modules,
- introduces only two global variables, namely the exporting function and the importing function,
- maintains full browser extension functionalityin each file (e.g.
chrome.runtime, etc.) that is eliminated by, e.g., the approach in another answer (currently the accepted answer) using module script tag embedding, - uses a concise syntax similar to the true
importandexportfunctions in JavaScript, - allows name-spacingwhich could be the file names of the exporting modules in a manner similar to how the true
importandexportcommands work in JavaScript, but doesn't have to be (i.e. the name-space names could be anything you want), and - allows variable renamingupon import similar to how
import { fn as myFn }...works.
- 允许在浏览器扩展中使用模块化代码,这样您就可以将代码拆分为多个文件,但不会因不同文件之间共享全局范围而发生变量冲突,
- 仍然允许您从不同的 JavaScript 文件/模块中导出和导入变量,
- 只引入了两个全局变量,即导出函数和导入函数,
- 在每个文件(例如,等)中维护完整的浏览器扩展功能
chrome.runtime,例如,使用模块脚本标记嵌入的另一个答案(当前已接受的答案)中的方法消除了这些功能, - 使用类似于
importexportJavaScript 中的 true和函数的简洁语法, - 允许命名空间,它可以是导出模块的文件名,其方式类似于 true
import和export命令在 JavaScript 中的工作方式,但不必如此(即命名空间名称可以是您想要的任何名称),并且 - 允许在导入时重命名变量,类似于
import { fn as myFn }...工作原理。
To do this, your manifest.jsonneeds to load your JavaScript as follows:
为此,您manifest.json需要按如下方式加载 JavaScript:
- the file establishing the exporting/importing functions first (named
modules-start.jsin the example below), - the exporting files next, and
- the importing files last.
- 首先建立导出/导入功能的文件(
modules-start.js在下面的示例中命名), - 接下来是导出文件,以及
- 最后导入文件。
Of course, you might have a file that both imports and exports. In that case, just ensure it is listed after the files it imports from but before the files it exports to.
当然,您可能有一个同时导入和导出的文件。在这种情况下,只需确保它列在它导入的文件之后但导出到的文件之前。
Working Example
工作示例
The following code demonstrates this strategy.
以下代码演示了此策略。
It is important to note that all of the code in each module/file is contained within curly braces. The only exception is the first line in modules-start.jswhich establishes the exporting and importing functions as global variables.
需要注意的是,每个模块/文件中的所有代码都包含在花括号内。唯一的例外是将modules-start.js导出和导入函数设置为全局变量的第一行。
The code in the snippet below is necessarily contained in a single "place". In a real project, however, the code could be split into separate files. Note, though, that even in this artificial context here (i.e. within the single code snippet below), this strategy allows the different sections of code it contains to be modular and yet still interconnected.
下面片段中的代码必须包含在一个“位置”中。但是,在实际项目中,代码可以拆分为单独的文件。但是请注意,即使在此处的人工上下文中(即在下面的单个代码片段中),该策略也允许它包含的不同代码部分是模块化的,但仍然相互连接。
// modules-start.js:
let exportVars, importVarsFrom; // the only line NOT within curly braces
{
const modules = {};
exportVars = varsObj => ({
from(nameSpace) {
modules[nameSpace] || (modules[nameSpace] = {});
for (let [k,v] of Object.entries(varsObj)) {
modules[nameSpace][k] = v;
}
}
});
importVarsFrom = nameSpace => modules[nameSpace];
}
// *** All of the following is just demo code
// *** showing how to use this export/import functionality:
// my-general-utilities.js (an example file that exports):
{
const wontPolluteTheGlobalScope = 'f';
const myString = wontPolluteTheGlobalScope + 'oo';
const myFunction = (a, b) => a + b;
// the export statement:
exportVars({ myString, myFunction }).from('my-general-utilities');
}
// content.js (an example file that imports):
{
// the import statement:
const { myString, myFunction: sum } = importVarsFrom('my-general-utilities');
console.log(`The imported string is "${myString}".`);
console.log(`The renamed imported function shows that 2 + 3 = ${sum(2,3)}.`);
}
With this example, your manifest.jsonshould list the files in the following order:
在此示例中,您manifest.json应该按以下顺序列出文件:
{ ...
"content_scripts": [
{
"js": [
"modules-start.js",
"my-general-utilities.js",
"content.js"
]
}
], ...
}
回答by u5465023
Export the module as a object:
将模块导出为对象:
'use strict';
const injectFunction = () => window.alert('hello world');
export {injectFunction};
Then you can import its property:
然后你可以导入它的属性:
'use strict';
import {injectFunction} from './my-script.js';

