如何自动重新加载正在开发的Chrome扩展程序?


263

我希望我的chrome扩展程序每次在扩展程序文件夹中保存文件时都可以重新加载,而不必在chrome:// extensions /中明确单击“重新加载”。这可能吗?

编辑:我知道我可以更新 Chrome重新加载扩展的间隔,这是一种半途而废的解决方案,但是我宁愿让我的编辑器(emacs或textmate)触发保存重新加载或要求Chrome进行监视更改目录。


2
当从chrome:// flags /#enable-apps-devtool-app中启用的ui触发重新加载时,似乎重新加载的速度更快
Eric

我分叉了LiveJS,以便可以实时重新加载打包的应用程序。只要将文件包含在您的应用程序中,每次保存文件时,该应用程序都会自动重新加载。
奥兹·拉莫斯

您的LiveJS叉子在哪里,@ Oz Ramos?
2014年

嗯,我明白了:您的“ LiveJS”链接实际上是到您的fork的链接,而不是到LiveJS的链接。
2014年

6
这不能回答所要求的问题,但我想提一提的是,在意识到可以通过按ctrl-R或cmd-R(取决于操作系统)在扩展程序的后台窗口中。我发现这比我尝试过的其他方法更适合我的工作流程,并且还避免了文件处于不一致状态时自动重新加载的问题。
唐·哈奇

Answers:


162

您可以使用扩展Reloader Chrome浏览器:

使用扩展程序的工具栏按钮或浏览到“ http://reload.extensions ”,重新加载所有未打包的扩展程序

如果您曾经开发过Chrome扩展程序,则可能需要自动化重新加载解压缩的扩展程序的过程,而无需浏览扩展程序页面。

“扩展程序重新加载程序”使您可以使用2种方法重新加载所有未打包的扩展程序:

1-扩展程序的工具栏按钮。

2-浏览到“ http://reload.extensions ”。

单击该工具栏图标将重新加载解压的扩展程序。

“通过浏览重新加载”旨在使用“构建后”脚本自动执行重新加载过程-只需使用Chrome将浏览器添加到“ http://reload.extensions ”中,即可刷新脚本。

更新:自2015年1月14日起,该扩展是开源的,可在GitHub上使用


3
到目前为止,这是最好的解决方案,但是,每次修改正在使用的文件时,仍然无法轻松地更新扩展名,这正是我理想中的样子。
安德烈·费多罗夫

2
谢谢。要提及的是,我使用了PhpStorm,并添加了一个工具栏按钮,该按钮使用“ reload.extensions” URL调用chrome.exe。每当我想测试代码时,我都按下该按钮,Chrome弹出并显示更新后的插件。
2012年

30
哇,我只是通过在文件被修改时使用grunt-open打开来使gruntjs与该插件一起工作。现在它的行为与livereload完全相同,但是每次我修改指定的插件文件之一:P时,都会打开一个讨厌的chrome窗口。谢谢!http://reload.extensions
GabLeRoux

1
@GabLeRoux我来这里只是为了寻找是否有人已经这样做了。它像一种魅力。
polkovnikov.ph 2014年

1
不幸的是,@ AlexanderMills每次都没有打开新选项卡就无法做到这一点。如果您找到更好的解决方案。至少,可以使用github.com/arikw/chrome-extensions-reloader
GabLeRoux

58

更新:我已经添加了一个选项页面,因此您不必再手动查找和编辑扩展程序的ID。CRX和源代码位于:https : //github.com/Rob--W/Chrome-Extension-Reloader
更新2:添加了快捷方式(请参阅我在Github上的存储库)。
包含基本功能的原始代码如下所示


创建一个扩展,然后将Browser Action方法与chrome.extension.managementAPI 结合使用以重新加载解压缩的扩展。

下面的代码在Chrome中添加了一个按钮,单击该按钮即可重新加载扩展程序。

manifest.json

{
    "name": "Chrome Extension Reloader",
    "version": "1.0",
    "manifest_version": 2,
    "background": {"scripts": ["bg.js"] },
    "browser_action": {
        "default_icon": "icon48.png",
        "default_title": "Reload extension"
    },
    "permissions": ["management"]
}

bg.js

var id = "<extension_id here>";
function reloadExtension(id) {
    chrome.management.setEnabled(id, false, function() {
        chrome.management.setEnabled(id, true);
    });
}
chrome.browserAction.onClicked.addListener(function(tab) {
    reloadExtension(id);
});

icon48.png:选择任何不错的48x48图标,例如:
谷歌浏览器 谷歌浏览器


1
@trusktr Scott的答案仅重新加载背景页面。但是,两个答案都可以组合使用(斯科特答案+辅助扩展名),以便在扩展名的文件更改时自动重新加载扩展名。但是,我还是建议您反对这种做法,因为devtools在扩展重载时关闭。我经常摆弄测试扩展,即使语法不完整,也经常保存更改。如果启用了真正的自动重新加载功能,那会使我的扩展程序崩溃。
罗布W

@RobW好点。我经常节省编写代码的时间,因此代码肯定会破坏相关插件。
trusktr 2012年

1
@Sudarshan您建议使用chrome.i18n.getMessage("@@extension_id")。但是,这会返回当前扩展名的extensionID,这是没有用的,因为扩展名无法使用managementAPI 重新加载自身(禁用后,它没有机会再次启用该扩展名)。
罗伯W

40

在任何功能或事件中

chrome.runtime.reload();

将重新加载您的扩展程序(docs)。您还需要更改manifest.json文件,并添加:

...
"permissions": [ "management" , ...]
...

对我来说,这种解决方案只有在我按照以下方式在清单文件中指定的index.html中的脚本中编写了chrome.runtime.reload()时才有效:**“ background”:{“ page”:“ index。 html”,**。在我的content_script js文件中,我无法访问此函数。我相信这是针对安全领域的。
titusfx

您的内容脚本无法与chrome API通讯。为此,您需要使用消息
marcelocra

当您打电话时chrome.runtime.reload(),实际发生了什么?多个页面/选项卡刷新吗?
亚历山大·米尔斯

5
我猜我们应该chrome.runtime.reload()从后台脚本中打电话给我
亚历山大·米尔斯

为我工作。谢谢
MikeMurko '18

39

我做了一个简单的可嵌入脚本来进行热重装:

https://github.com/xpl/crx-hotreload

它监视扩展目录中的文件更改。检测到更改时,它将重新加载扩展并刷新活动选项卡(以重新触发更新的内容脚本)。

  • 通过检查文件的时间戳来工作
  • 支持嵌套目录
  • 在生产配置中自动禁用自身

3
如果您正在寻找“口香糖味”的解决方案,就是这样。
ow3n

1
很棒。您是否考虑过制作npm软件包?
pixelearth

@pixelearth是的,为什么不呢。嗯,那用例呢?您npm install crx-hotreload进入扩展程序的目录,然后引用"scripts": ["./node_modules/crx-hotreload/hot-reload.js"]?之类的脚本。如果我错了,请纠正我。
维塔利·戈登

1
@MilfordCubicle我建议您导出一个对象,该对象的方法可以在background.js文件中调用。因此import reloader from 'hot-reload',在脚本reloader.check()中执行了一些操作
pixelearth

3
答案需要更多支持。与Webpack结合使用也--watch可以很好地工作,因为您可以将其放在构建文件夹中,以便仅在构建过程完成后才进行更新。没有复杂的Webpack生成后命令插件。
V. Rubinetti

10

另一个解决方案是创建自定义livereload脚本(extension-reload.js):

// Reload client for Chrome Apps & Extensions.
// The reload client has a compatibility with livereload.
// WARNING: only supports reload command.

var LIVERELOAD_HOST = 'localhost:';
var LIVERELOAD_PORT = 35729;
var connection = new WebSocket('ws://' + LIVERELOAD_HOST + LIVERELOAD_PORT + '/livereload');

connection.onerror = function (error) {
  console.log('reload connection got error:', error);
};

connection.onmessage = function (e) {
  if (e.data) {
    var data = JSON.parse(e.data);
    if (data && data.command === 'reload') {
      chrome.runtime.reload();
    }
  }
};

该脚本使用websockets连接到livereload服务器。然后,它将根据livereload的重载消息发出chrome.runtime.reload()调用。下一步将添加此脚本以在manifest.json中作为后台脚本运行,瞧!

注意:这不是我的解决方案。我只是张贴它。我在Chrome扩展程序生成器(出色的工具!)的生成代码中找到了它。我将其发布在这里,因为它可能会有所帮助。


9

Chrome扩展程序具有不允许使用的权限系统(SO 中的某些人与您有相同的问题),因此要求他们“添加此功能”不适用于IMO。有来自铬扩展谷歌群组邮件与建议的解决方案(理论)使用chrome.extension.getViews(),但不保证工作无论是。

如果可以添加到manifest.json某些Chrome内部页面(如)chrome://extensions/,则可以创建一个与Reload锚点进行交互的插件,并使用XRefresh(Firefox插件)等外部程序,可以使用Ruby和WebSocket 创建Chrome版本),您将实现所需的功能:

XRefresh是一个浏览器插件,由于所选文件夹中的文件更改,它将刷新当前网页。这样就可以使用您喜欢的HTML / CSS编辑器进行实时页面编辑。

这是不可能的,但是我认为您可以以不同的方式使用相同的概念。

您可以尝试找到第三方解决方案,而不是在看到文件中的修改后(我不知道emacs还是Textmate,但是在Emacs中可以将应用程序调用绑定到“保存文件”操作中),单击特定应用程序的特定坐标:在这种情况下,它就是Reload开发中的扩展程序的锚点(仅为此重新加载而打开了Chrome窗口)。

(疯狂的地狱,但它可能会起作用)


7

您可以使用此功能监视文件中的更改,并在检测到更改时重新加载。它通过AJAX轮询它们,然后通过window.location.reload()重新加载来工作。我想您不应该在分发包中使用它。

function reloadOnChange(url, checkIntervalMS) {
    if (!window.__watchedFiles) {
        window.__watchedFiles = {};
    }

    (function() {
        var self = arguments.callee;
        var xhr = new XMLHttpRequest();

        xhr.onreadystatechange = function() {
            if (xhr.readyState == 4) {
                if (__watchedFiles[url] &&
                    __watchedFiles[url] != xhr.responseText) {
                    window.location.reload();
                } else {
                    __watchedFiles[url] = xhr.responseText
                    window.setTimeout(self, checkIntervalMS || 1000);
                }
            }
        };

        xhr.open("GET", url, true);
        xhr.send();
    })();
}

reloadOnChange(chrome.extension.getURL('/myscript.js'));

可以添加以下内容:reloadOnChange(chrome.extension.getURL('/ manifest.json'));
Scott

我已经试过pastebin.com/mXbSPkeu,但是扩展名更新(清单条目,内容脚本,浏览器操作)(Ubuntu 11.10,Chrome 18)。该更新后的文件是背景页面(更改console.log消息并显示出来)。
罗布W

“监视文件中的更改,如果检测到更改,则重新加载。” 在我的一生中,我看不到这怎么知道文件已更改?听起来很棒,请帮助我理解!
詹森·戴维斯(JasonDavis)'16

oo,我现在看到的是,它正在存储文件内容的副本,并将缓存的版本与ajax实时版本.... clever进行比较!
贾森·戴维斯'16

6

也许我参加聚会有点晚了,但是我已经通过创建https://chrome.google.com/webstore/detail/chrome-unpacked-extension/fddfkmklefkhanofhlohnkemejcbamln为我解决了

chrome://extensions每当file.change事件通过websocket传入时,它都通过重新加载页面来工作。

file.change在扩展文件夹中更改文件后如何发出事件的基于Gulp的示例可以在这里找到:https : //github.com/robin-drexler/chrome-extension-auto-reload-watcher

为什么要重新加载整个标签,而不是仅使用扩展管理API重新加载/重新启用扩展?当前再次禁用和启用扩展会导致任何打开的检查窗口(控制台日志等)关闭,我发现在积极开发过程中这太烦人了。


真好!我看到您有一个新的升级reload()函数的PR。
caesarsol

4

如果您使用Webpack进行开发,则有一个自动重装插件:https : //github.com/rubenspgcavalcante/webpack-chrome-extension-reloader

const ChromeExtensionReloader = require('webpack-chrome-extension-reloader');

plugins: [
    new ChromeExtensionReloader()
]

如果您不想修改webpack.config.js,还附带CLI工具:

npx wcer

注意:即使您不需要背景脚本,也需要一个(空的)后台脚本,因为它是注入重新加载代码的地方。


已经存档:(指向github.com/rubenspgcavalcante/webpack-extension-reloader,它是跨浏览器,但仍处于活跃开发中:)
con--

3

您的内容文件具有html和清单文件,如果不安装扩展名就无法更改,但我相信JavaScript文件会动态加载,直到扩展名已打包。

我知道这是因为当前的项目正在通过Chrome扩展程序API进行,并且每次我刷新页面时似乎都会加载。


我不知道您使用的是哪个版本,但是Chrome浏览器会在16.0.912.63 m时自动重新加载所有文件。除清单外。
Zequez 2012年

@Zequez:这些文件没有为我重新加载。:(
Randomblue

你是对的。我正在开发“选项”页面,该页面从文档中调用脚本,然后重新加载它们。content_script脚本未重新加载= /
Zequez 2012年


3

我正在使用重新加载的快捷方式。保存文件时,我不想一直重新加载

所以我的方法是轻量级的,您可以将reload函数保留在

manifest.json

{
    ...
    "background": {
        "scripts": [
            "src/bg/background.js"
        ],
        "persistent": true
    },
    "commands": {
        "Ctrl+M": {
            "suggested_key": {
                "default": "Ctrl+M",
                "mac": "Command+M"
            },
            "description": "Ctrl+M."
        }
    },
    ...
}

src / bg / background.js

chrome.commands.onCommand.addListener((shortcut) => {
    console.log('lets reload');
    console.log(shortcut);
    if(shortcut.includes("+M")) {
        chrome.runtime.reload();
    }
})

现在在Chrome浏览器中按Ctrl + M重新加载


您还可以在browserAction中使用重新加载功能,以便在单击按钮时重新加载。stackoverflow.com/questions/30427908/...
的powerd

是的,您可以每次都加载chrome.runtime.reload()。我使用:编辑器> Ctrl + S> Alt + Tab> Ctrl +M。打开background..js控制台。快速,无需使用鼠标
Johan Hoeksma,

2

用于npm init在目录根目录中创建package.json,然后

npm install --save-dev gulp-open && npm install -g gulp

然后创建一个 gulpfile.js

看起来像:

/* File: gulpfile.js */

// grab our gulp packages
var gulp  = require('gulp'),
    open = require('gulp-open');

// create a default task and just log a message
gulp.task('default', ['watch']);

// configure which files to watch and what tasks to use on file changes
gulp.task('watch', function() {
  gulp.watch('extensionData/userCode/**/*.js', ['uri']);
});

gulp.task('uri', function(){
  gulp.src(__filename)
  .pipe(open({uri: "http://reload.extensions"}));
});

这对我使用CrossRider开发的作品很有效,您可能会想更改观看文件的路径,并假设已安装npm和node。


2

免责声明:我自己开发了此扩展程序。

Clerc-用于Chrome Live Extension重新加载客户端

连接到LiveReload兼容服务器,以在每次保存时自动重新加载扩展。

奖励:您需要做一些额外的工作,还可以自动重新加载扩展程序更改的网页。

大多数网页开发人员使用带有某种监视程序的构建系统,该监视程序可自动构建其文件并重新启动服务器并重新加载网站。

开发扩展不必如此不同。Clerc为Chrome开发人员带来了同样的自动化。使用LiveReload服务器设置构建系统,Clerc将侦听重新加载事件以刷新扩展。

唯一的难题是对manifest.json进行更改。清单中的任何微小拼写错误都可能导致进一步的重新加载尝试失败,并且您将不得不卸载/重新安装扩展程序以重新加载您的更改。

Clerc在重新加载后将完整的重新加载消息转发到您的扩展程序,因此您可以选择使用提供的消息来触发进一步的刷新步骤。



1

感谢@GmonC和@Arik以及一些业余时间,我设法使它正常工作。我必须更改两个文件才能完成此工作。

(1)为该应用程序安装LiveReload和Chrome扩展程序。这将在文件更改时调用一些脚本。

(2)开放 <LiveReloadInstallDir>\Bundled\backend\res\livereload.js

(3)变更线#509

this.window.location.href = "http://reload.extensions";

(4)现在安装另一个扩展Extensions Reloader,它具有有用的链接处理程序,该链接处理程序在导航到时重新加载所有开发扩展"http://reload.extensions"

(5)现在background.min.js以这种方式更改扩展名

if((d.installType=="development")&&(d.enabled==true)&&(d.name!="Extensions Reloader"))

用。。。来代替

if((d.installType=="development")&&(d.enabled==true)&&(d.name!="Extensions Reloader")&&(d.name!="LiveReload"))

打开LiveReload应用程序,隐藏Extension Reloader按钮并通过单击工具栏上的按钮激活LiveReload扩展,现在您将重新加载页面,并且每个文件的扩展名都会更改,同时使用LiveReload中的所有其他功能(css重新加载,图像重新加载等)。

唯一不好的是,您将不得不在每个扩展更新上重复更改脚本的过程。为避免更新,请将扩展名解压缩。

当我有更多时间解决这个问题时,我可能会创建扩展程序,从而消除了对这两个扩展程序的需求。

在此之前,我一直在开发扩展Projext Axeman



1

如果您使用Mac,最简单的方法就是使用Alfred App!

只需将Alfred App与Powerpack配合使用,然后添加以下链接中提供的工作流程并自定义所需的热键即可(我喜欢使用⌘+⌥+ R)。就这样。

现在,无论您当时使用的是哪种应用程序,每次使用热键时,都会重新加载Google Chrome

如果要使用其他浏览器,请在Alfred Preferences Workflows中打开AppleScript,然后将“ Google Chrome”替换为“ Firefox”,“ Safari”,...

我还将在这里显示ReloadChrome.alfredworkflow文件中使用的/ usr / bin / osascript脚本的内容,以便您了解它的作用。

tell application "Google Chrome"
  activate
  delay 0.5
  tell application "System Events" to keystroke "r" using command down
  delay 0.5
  tell application "System Events" to keystroke tab using command down
end tell

工作流程文件是ReloadChrome.alfredworkflow


1

我想在一夜之间重新加载(更新)我的扩展,这是我在background.js中使用的:

var d = new Date();
var n = d.getHours();
var untilnight = (n == 0) ? 24*3600000 : (24-n)*3600000; 
// refresh after 24 hours if hour = 0 else
// refresh after 24-n hours (that will always be somewhere between 0 and 1 AM)
setTimeout(function() {
    location.reload();
}, untilnight);

问候,彼得


0

我分叉了LiveJS,以便可以实时重新加载打包的应用程序。只要将文件包含在您的应用程序中,每次保存文件时,该应用程序都会自动重新加载。


0

如文档中所述:以下命令行将重新加载应用程序

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --load-and-launch-app=[path to the app ]

所以我刚刚创建了一个shell脚本,并从gulp中调用了该文件。超级简单:

var exec = require('child_process').exec;



gulp.task('reload-chrome-build',function(cb){

console.log("reload");

var cmd="./reloadchrome.sh"

exec(cmd,function (err, stdout, stderr) {
    console.log("done: "+stdout);

    cb(err);
    }
);});

在脚本上运行必要的监视命令,并在需要时调用重新加载任务。干净,简单。


0

这就是AutoIt或替代软件之类的地方。关键是编写一个脚本来模拟您当前的测试阶段。习惯使用其中至少一种,因为许多技术都没有清晰的工作流程/测试路径。

Run("c:\Program Files (x86)\Google\Chrome\Application\chrome.exe")
WinWaitActive("New Tab - Google Chrome")
Send("^l")
Send("chrome://extensions{ENTER}")
WinWaitActive("Extensions - Google Chrome")
Send("{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}")
Send("{ENTER}")
WinWaitActive("Extensions - Google Chrome")
Send("{TAB}{TAB}")
Send("{ENTER}")
WinWaitActive("Developer Tools")
Send("^`")

显然,您更改了代码以适合您的测试/迭代需求。确保选项卡上的点击确实符合chrome:// extensions网站中定位标记的位置。您还可以使用相对于窗口鼠标移动的宏和其他此类宏。

我将以类似于以下方式将脚本添加到Vim:

map <leader>A :w<CR>:!{input autoit loader exe here} "{input script location here}"<CR>

这意味着当我进入Vim时,按ENTER(通常负责:|和\)上方的按钮(称为领导按钮),并在其后加上大写字母“ A”,它会保存并开始我的测试阶段脚本。

请确保正确填写以上Vim / hotkey脚本中的{input ...}部分。

许多编辑器将允许您使用热键执行类似的操作。

AutoIt的替代品可以在这里找到。

对于Windows:AutoHotkey

对于Linux:xdotool,xbindkeys

对于Mac:Automator


0

我主要在Firefox中开发 web-ext run文件更改后会自动重新加载扩展名。然后,准备就绪后,我将在Chrome中进行最后一轮测试,以确保没有任何未在Firefox中出现的问题。

但是,如果您想主要在Chrome中进行开发,并且不想安装任何第三方扩展,那么另一种选择是test.html在扩展的文件夹中创建一个 文件,并向其中添加一堆SSCCE。该文件然后使用普通<script>标签注入扩展脚本。

您可以将其用于95%的测试,然后在要在实时站点上对其进行测试时手动重新加载该扩展。

这并不能完全相同扩展程序运行的环境,但是对于许多简单的事情来说已经足够了。



-1

是的,您可以间接做到!这是我的解决方案。

在manifest.json中

{
    "name": "",
    "version": "1.0.0",
    "description": "",
    "content_scripts":[{
        "run_at":"document_end",
        "matches":["http://*/*"],
        "js":["/scripts/inject.js"]
    }]
}

在inject.js中

(function() {
    var script = document.createElement('script'); 
    script.type = 'text/javascript'; 
    script.async = true;
    script.src = 'Your_Scripts';
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(script, s);
})();

您注入的脚本可以从任何位置注入其他脚本。

这种技术的另一个好处是您可以忽略孤立世界的局限性。查看内容脚本执行环境


不为我工作?如果尝试使用script.src ='foo.js',它将查找相对于当前网站的url。如果我尝试完整路径,则会得到:不允许加载本地资源:'file:///../foo.js'–
Jesse Aldridge

-2

浏览器同步

使用惊人的浏览器同步

  • 当源代码(HTML,CSS,图像等)更改时更新浏览器(任何)
  • 支持Windows,MacOS和Linux
  • 您甚至可以使用移动设备观看(实时)代码更新

在MacOS上安装(查看其帮助以在其他OS上安装)

安装NVM,因此您可以尝试任何Node版本

brew install nvm             # install a Node version manager
nvm ls-remote                # list available Node versions
nvm install v10.13.0         # install one of them
npm install -g browser-sync  # install Browser-Sync

如何将浏览器同步用于静态网站

让我们看两个例子:

browser-sync start --server --files . --host YOUR_IP_HERE --port 9000

browser-sync start --server --files $(ack --type-add=web:ext:htm,html,xhtml,js,css --web -f | tr \\n \ ) --host $(ipconfig getifaddr en0) --port 9000

--server选项允许您在终端中的任何位置运行本地服务器,并--files可以指定要跟踪更改的文件。我希望对跟踪的文件更具体,因此在第二个示例中,我ack用于列出特定的文件扩展名(这对于那些文件没有带空格的文件名很重要),并且还用于ipconfig在MacOS上查找我的当前IP。

如何对动态网站使用浏览器同步

如果您使用的是PHP,Rails等,则您已经有一个正在运行的服务器,但是当您更改代码时它不会自动刷新。因此,您需要使用该--proxy开关让浏览器同步知道该服务器的主机在哪里。

browser-sync start --files $(ack --type-add=rails:ext:rb,erb,js,css,sass,scss,coffee --rails -f | tr \\n \ ) --proxy 192.168.33.12:3000 --host $(ipconfig getifaddr en0) --port 9000 --no-notify --no-open

在上面的示例中,我已经在浏览器上运行了Rails应用 192.168.33.12:3000。它确实在使用Vagrant框的VM上运行,但是我可以使用该主机上的端口3000访问虚拟机。我想--no-notify停止每次浏览器更改代码时都向浏览器同步发送通知警报,并--no-open停止浏览器同步行为,该行为在服务器启动时立即加载浏览器选项卡。

重要信息:万一您在使用Rails,请避免在开发中使用Turbolinks,否则在使用该--proxy选项时将无法单击链接。

希望对某人有用。我已经尝试了许多技巧来刷新浏览器(甚至是我之前使用AlfredApp就此StackOverflow问题提交的一篇旧文章),但这确实是要走的路。不再有骇客,它只是在流动。

信誉: 使用一个命令启动本地实时重载Web服务器


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.