首页 > web前端 > js教程 > 使管道可见以监控博客部署

使管道可见以监控博客部署

Linda Hamilton
发布: 2025-01-14 14:29:46
原创
219 人浏览过

Computaria 让我困扰的一件事是无法跟踪博客本身的部署。那么,既然这让我烦恼,为什么不解决它呢?

管道

目前有两种方法可以知道部署是否正在运行:

  • 在作业/管道页面上打开存储库并查看最新的正在运行的
  • 在存储库中打开并滚动到 README.md

这两种解决方案对我来说似乎都不太好。我想要计算本身更轻松的东西。

这个想法

与 Kauê 短暂协商后,我决定遵循他的提示:在 /about 上发帖。

在第一个实验中:

Deixando a pipeline visível para acompanhar deploy do blog

不,它变得丑陋了。我已经知道我不希望它默认出现。但带上信息就足够了。我只需要隐藏丑陋的东西,并在明确要求的情况下使其可用,即使是丑陋的。

概念证明:除非指定否则会崩溃

嗯,首先要做的是知道我们是否应该采取任何行动。为此,将查询参数 status 的值为 true 的存在定义为 API。

为了获取 URL,我使用了 window.location。 Location 对象内部有一个搜索字段,它精确地用于维护用于访问特定 URL 的查询参数。

例如,对于 http://localhost:4000/blog/about?q=1,window.location.search 的值为 ?q=1。为了更容易处理查询参数中的内容,有一个 URLSearchParams 类型的对象。据我从文档中可以理解,要实例化 URLSearchParams,我需要查询字符串,但没有 ?的前缀。我可以使用 window.location.search.substring(1).

来实现这一点

现在,有了这个对象,我可以简单地查询我想要的任何查询参数的值:

const queryParams = new URLSearchParams(window.location.search.substring(1));

if (queryParams.get("status") === "true") {
    console.log("oba, vamos exibir o pipeline!")
} else {
    console.log("nops, não vamos exibir nada")
}
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

有了这个,我需要采取显示管道徽章的操作。为了方便起见,我决定将其作为可包含的 HTML 片段:_includes/pipeline.html。因此,我可以按照我认为合适的方式操作免费的 HTML。

一开始,它只是一个

看不见的:

<div>



<p>Para importar, no /about só precisei colocar {%include pipeline.html%} no começo do arquivo, o Jekyll se encarregou de montar tudo certo.</p>

<p>Ok, vamos por o script para detectar se deveria ou não exibir a tag:<br>
</p>

<pre class="brush:php;toolbar:false"><script>
    const queryParams = new URLSearchParams(window.location.search.substring(1));

    if (queryParams.get("status") === "true") {
        console.log("oba, vamos exibir o pipeline!")
    } else {
        console.log("nops, não vamos exibir nada")
    }
</script>
<div>



<p>So far, so good. Agora, vamos mudar a exibição para display: block caso seja para exibir o pipeline, ou sumir logo de uma vez com a <div>. Pelo console da web, bastaria fazer algo nesse esquema:<br>
</p>

<pre class="brush:php;toolbar:false">const pipeline = document.getElementById("pipeline")

if (...) {
    pipeline.style.display = "block"
} else {
    pipeline.remove()
}
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

放置在 HTML 片段中:

<script>
    const queryParams = new URLSearchParams(window.location.search.substring(1));
    const pipeline = document.getElementById("pipeline")

    if (queryParams.get("status") === "true") {
        pipeline.style.display = "block"
    } else {
        pipeline.remove()
    }
</script>
<div>



<p>E... falhou. Por quê? Porque no momento que a função rodar ainda não tem definido quem é o elemento com id pipeline. Então preciso mudar o ciclo de vida para rodar o script apenas quando a página for carregada. Basta colocar o <script defer>, certo? Bem, não. Porque defer não funciona bem com inline, apenas com arquivo de source explícito. Veja a documentação.

<p>Ou seja, precisei colocar o arquivo JavaScript explicitamente para o Computaria. Como a priori tudo que está solto na pasta do blog é colocado como asset disponível para o Jekyll publicar, criei o js/pipeline-loader.js:<br>
</p>

<pre class="brush:php;toolbar:false"><script src="{{ "/js/pipeline-loader.js" | prepend: site.baseurl }}" defer>
</script>
<div>



<p>E no script:<br>
</p>

<pre class="brush:php;toolbar:false">const queryParams = new URLSearchParams(window.location.search.substring(1));
const pipeline = document.getElementById("pipeline")

if (queryParams.get("status") === "true") {
    pipeline.style.display = "block"
} else {
    pipeline.remove()
}
登录后复制
登录后复制
登录后复制
登录后复制

太好了,让我们做一些有用的事情并发布图片?要动态创建元素,只需使用 document.createElement 即可。然后我输入徽章网址:

const queryParams = new URLSearchParams(window.location.search.substring(1));
const pipeline = document.getElementById("pipeline")

if (queryParams.get("status") === "true") {
    pipeline.style.display = "block"

    const pipelineImg = document.createElement("img")
    pipelineImg.src = "{{site.repository.base}}/badges/master/pipeline.svg"

    pipeline.appendChild(pipelineImg)
} else {
    pipeline.remove()
}
登录后复制
登录后复制
登录后复制

但是它显示了一个破碎的图像...嗯,控制台上显示的消息是什么?

GET http://localhost:4000/blog/about/{{site.repository.base}}/badges/master/pipeline.svg [HTTP/1.1 404 Not Found 4ms]
登录后复制
登录后复制

奇怪,他应该得到了可爱的存储库 URL 吗?哦,我注意到了。他根本没有处理Liquid。为了解决这个问题,我决定遵循 css/main.scss 中的示例,一个空的 frontmatter。

const queryParams = new URLSearchParams(window.location.search.substring(1));

if (queryParams.get("status") === "true") {
    console.log("oba, vamos exibir o pipeline!")
} else {
    console.log("nops, não vamos exibir nada")
}
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

这会给出错误消息,因为 frontmatter 不是 javascript,并且错误显示在第一个 const 中。由于这让我很困扰,我想到的最直接的处理方法就是尽早创建一个“无害的错误”。我添加了一个 ;紧接在前言之后:

<div>



<p>Para importar, no /about só precisei colocar {%include pipeline.html%} no começo do arquivo, o Jekyll se encarregou de montar tudo certo.</p>

<p>Ok, vamos por o script para detectar se deveria ou não exibir a tag:<br>
</p>

<pre class="brush:php;toolbar:false"><script>
    const queryParams = new URLSearchParams(window.location.search.substring(1));

    if (queryParams.get("status") === "true") {
        console.log("oba, vamos exibir o pipeline!")
    } else {
        console.log("nops, não vamos exibir nada")
    }
</script>
<div>



<p>So far, so good. Agora, vamos mudar a exibição para display: block caso seja para exibir o pipeline, ou sumir logo de uma vez com a <div>. Pelo console da web, bastaria fazer algo nesse esquema:<br>
</p>

<pre class="brush:php;toolbar:false">const pipeline = document.getElementById("pipeline")

if (...) {
    pipeline.style.display = "block"
} else {
    pipeline.remove()
}
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

烦恼...

当我继续测试时,我注意到网络选项卡中不断出现308,但为什么会出现?嗯,因为展开 Liquid 时,它最终在徽章前出现了一个双条。

我最初得到的是这个:

  • https://gitlab.com/computaria/blog//badges/master/pipeline.svg

重定向至:

  • https://gitlab.com/computaria/blog/badges/master/pipeline.svg

当我分析我是否使用缓存时,这开始困扰我。为了解决这个问题,我应该去掉双斜杠。我可以通过不在 Liquid 值展开后放置斜杠来摆脱它,因为毕竟我可以先验地知道 {{site.repository.base}} 字符串以 / 结尾。但是,为了以防万一,将斜线放在 /badges/master/pipeline.svg 之前实际上并没有什么坏处,它甚至是我作为读者的一个指标。

但是,由于我不想依赖先验知识来判断此栏是否存在,因此我有两个选择:

  • 处理 Liquid 扩展级别以删除终端斜线
  • 在 JavaScript 级别处理此字符串的创建

JavaScript 方面对我来说似乎更容易。所以只需将/替换为/,对吗?嗯,不。由于协议出现在 :// 之前,因此仅进行粗略替换就会导致 url 开头如下:https://computaria.gitlab.io。为了解决这个问题,我进行了以下替换:

<script>
    const queryParams = new URLSearchParams(window.location.search.substring(1));
    const pipeline = document.getElementById("pipeline")

    if (queryParams.get("status") === "true") {
        pipeline.style.display = "block"
    } else {
        pipeline.remove()
    }
</script>
<div>



<p>E... falhou. Por quê? Porque no momento que a função rodar ainda não tem definido quem é o elemento com id pipeline. Então preciso mudar o ciclo de vida para rodar o script apenas quando a página for carregada. Basta colocar o <script defer>, certo? Bem, não. Porque defer não funciona bem com inline, apenas com arquivo de source explícito. Veja a documentação.

<p>Ou seja, precisei colocar o arquivo JavaScript explicitamente para o Computaria. Como a priori tudo que está solto na pasta do blog é colocado como asset disponível para o Jekyll publicar, criei o js/pipeline-loader.js:<br>
</p>

<pre class="brush:php;toolbar:false"><script src="{{ "/js/pipeline-loader.js" | prepend: site.baseurl }}" defer>
</script>
<div>



<p>E no script:<br>
</p>

<pre class="brush:php;toolbar:false">const queryParams = new URLSearchParams(window.location.search.substring(1));
const pipeline = document.getElementById("pipeline")

if (queryParams.get("status") === "true") {
    pipeline.style.display = "block"
} else {
    pipeline.remove()
}
登录后复制
登录后复制
登录后复制
登录后复制

分解:

  • 代替替换,将在“第一组”中找到的内容放在后面加上斜线
  • 正则表达式匹配:除 :(在一组中)、斜杠、斜杠以外的任何内容

通过此更改,https:// 不再具有 match 与 ([^:])//,但路径中 // 的所有其他出现都具有完美匹配,因为它们不会前面一个:.更严格地说,我可以努力防止匹配在查询参数/片段中发生,但这似乎太过分了。

概念证明:无缓存加载

好的,定义了放置位置和锁定机制的详细信息后,我们需要一个重新加载机制。第一次尝试:简单地创建一个新的图像元素。但还是这样,怎么办?理想的情况是“一段时间后”。所以这给了我两个选择,可以说:

  • 设置超时
  • 设置间隔

好吧,我们来看看它有什么作用? setTimeout 接收一个命令,该命令将在一段时间间隔以及给定的时间间隔后执行。它会返回一个 ID,您可以使用clearTimeout 将其删除。如需重复调用,需要在最后再次调用setTimeout。

setInterval 几乎是同样的事情,只是它总是在时间间隔之后执行命令。返回值应该是一个 ID,您可以调用clearInterval 来删除它,但根据文档,它也可以与clearTimeout 一起使用(以防万一,不要相信它,请使用具有正确语义的 ID)。

使用setTimeout

我们应该使用 setTimeout 创建一个循环调用吗?在文本字段中打印“南瓜”一词 5 次怎么样?我将为这个实验放置一个文本区域:

const queryParams = new URLSearchParams(window.location.search.substring(1));

if (queryParams.get("status") === "true") {
    console.log("oba, vamos exibir o pipeline!")
} else {
    console.log("nops, não vamos exibir nada")
}
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

好的,我想通过 HTML 访问 3 个函数。他们分裂(即使是非常轻微的)一个国家。我很喜欢隐藏东西,所以我不希望这种状态在 <script> 标签之外可见。</script>

我最明显的解决方案是将其放在一个块下,因此,当离开该块时,里面的变量将在外面不可见:

<div>



<p>Para importar, no /about só precisei colocar {%include pipeline.html%} no começo do arquivo, o Jekyll se encarregou de montar tudo certo.</p>

<p>Ok, vamos por o script para detectar se deveria ou não exibir a tag:<br>
</p>

<pre class="brush:php;toolbar:false"><script>
    const queryParams = new URLSearchParams(window.location.search.substring(1));

    if (queryParams.get("status") === "true") {
        console.log("oba, vamos exibir o pipeline!")
    } else {
        console.log("nops, não vamos exibir nada")
    }
</script>
<div>



<p>So far, so good. Agora, vamos mudar a exibição para display: block caso seja para exibir o pipeline, ou sumir logo de uma vez com a <div>. Pelo console da web, bastaria fazer algo nesse esquema:<br>
</p>

<pre class="brush:php;toolbar:false">const pipeline = document.getElementById("pipeline")

if (...) {
    pipeline.style.display = "block"
} else {
    pipeline.remove()
}
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

好的,但是如何让函数可见呢?好吧,经过实验,我找到了一种方法:函数转义作用域。由于局部变量没有超出块的限制,我仍然可以在块内部放置一些辅助函数,这样它们在外部就没有任何意义。像这样的东西:

<script>
    const queryParams = new URLSearchParams(window.location.search.substring(1));
    const pipeline = document.getElementById("pipeline")

    if (queryParams.get("status") === "true") {
        pipeline.style.display = "block"
    } else {
        pipeline.remove()
    }
</script>
<div>



<p>E... falhou. Por quê? Porque no momento que a função rodar ainda não tem definido quem é o elemento com id pipeline. Então preciso mudar o ciclo de vida para rodar o script apenas quando a página for carregada. Basta colocar o <script defer>, certo? Bem, não. Porque defer não funciona bem com inline, apenas com arquivo de source explícito. Veja a documentação.

<p>Ou seja, precisei colocar o arquivo JavaScript explicitamente para o Computaria. Como a priori tudo que está solto na pasta do blog é colocado como asset disponível para o Jekyll publicar, criei o js/pipeline-loader.js:<br>
</p>

<pre class="brush:php;toolbar:false"><script src="{{ "/js/pipeline-loader.js" | prepend: site.baseurl }}" defer>
</script>
<div>



<p>E no script:<br>
</p>

<pre class="brush:php;toolbar:false">const queryParams = new URLSearchParams(window.location.search.substring(1));
const pipeline = document.getElementById("pipeline")

if (queryParams.get("status") === "true") {
    pipeline.style.display = "block"
} else {
    pipeline.remove()
}
登录后复制
登录后复制
登录后复制
登录后复制

好的,现在我需要处理超时调用。我的想法是执行一个步骤,当该步骤完成时,注册下一个超时,调用相同的步骤。只是为了避免将此步骤永远限制为几次。

那么,如果没有超时问题,会是什么样子呢?递归调用:

const queryParams = new URLSearchParams(window.location.search.substring(1));
const pipeline = document.getElementById("pipeline")

if (queryParams.get("status") === "true") {
    pipeline.style.display = "block"

    const pipelineImg = document.createElement("img")
    pipelineImg.src = "{{site.repository.base}}/badges/master/pipeline.svg"

    pipeline.appendChild(pipelineImg)
} else {
    pipeline.remove()
}
登录后复制
登录后复制
登录后复制

看起来不错,加个超时怎么样?嗯,在步骤的主体内部,所以调用步骤就是设置超时。为了一个好的暂停,我需要时间:

GET http://localhost:4000/blog/about/{{site.repository.base}}/badges/master/pipeline.svg [HTTP/1.1 404 Not Found 4ms]
登录后复制
登录后复制

好的,剩下要做的就是保存超时标识符,我们就准备好了。我将这一步放在公开的公共函数中,我们就准备好了:

---
# frontmatter vazio para fazer o parse do liquid
---

const queryParams = new URLSearchParams(window.location.search.substring(1));
const pipeline = document.getElementById("pipeline")

if (queryParams.get("status") === "true") {
    pipeline.style.display = "block"

    const pipelineImg = document.createElement("img")
    pipelineImg.src = "{{site.repository.base}}/badges/master/pipeline.svg"

    pipeline.appendChild(pipelineImg)
} else {
    pipeline.remove()
}
登录后复制

好的,我们现在有好玩的地方了:

参见小提琴 https://jsfiddle.net/jeffque/5Lrasyqk/

使用setInterval

setInterval 的使用非常相似,但“召回”步骤是隐式的。如果我想停止循环,我需要显式取消注册的setInterval。

那么,像上面的例子一样开始怎么样?但具有不同的暂存区 ID:

---
# frontmatter vazio para fazer o parse do liquid
---
;

const queryParams = new URLSearchParams(window.location.search.substring(1));
const pipeline = document.getElementById("pipeline")

if (queryParams.get("status") === "true") {
    pipeline.style.display = "block"

    const pipelineImg = document.createElement("img")
    pipelineImg.src = "{{site.repository.base}}/badges/master/pipeline.svg"

    pipeline.appendChild(pipelineImg)
} else {
    pipeline.remove()
}
登录后复制

参见小提琴 https://jsfiddle.net/jeffque/5Lrasyqk/

重新加载尝试

定义了重复计时机制后,现在的问题是定义如何重新加载图像。首先,分析 GitLab 在搜索徽章时返回的标头:https://gitlab.com/computaria/blog//badges/master/pipeline.svg:

const queryParams = new URLSearchParams(window.location.search.substring(1));

if (queryParams.get("status") === "true") {
    console.log("oba, vamos exibir o pipeline!")
} else {
    console.log("nops, não vamos exibir nada")
}
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

比较来自不同请求的多个 etag,以防万一:

<div>



<p>Para importar, no /about só precisei colocar {%include pipeline.html%} no começo do arquivo, o Jekyll se encarregou de montar tudo certo.</p>

<p>Ok, vamos por o script para detectar se deveria ou não exibir a tag:<br>
</p>

<pre class="brush:php;toolbar:false"><script>
    const queryParams = new URLSearchParams(window.location.search.substring(1));

    if (queryParams.get("status") === "true") {
        console.log("oba, vamos exibir o pipeline!")
    } else {
        console.log("nops, não vamos exibir nada")
    }
</script>
<div>



<p>So far, so good. Agora, vamos mudar a exibição para display: block caso seja para exibir o pipeline, ou sumir logo de uma vez com a <div>. Pelo console da web, bastaria fazer algo nesse esquema:<br>
</p>

<pre class="brush:php;toolbar:false">const pipeline = document.getElementById("pipeline")

if (...) {
    pipeline.style.display = "block"
} else {
    pipeline.remove()
}
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

嗯,etag 总是相同的,表明它是相同的资源。 cache-control: no-store 强烈告诉我它不是用于存储缓存的。指向过去的过期时间强烈表明它的目的是表明不应考虑对该资源进行缓存。除非另有证明,cf-cache-status: MISS 仅表示它没有命中 Cloudflare 的缓存。

最后,严格的运输安全。这意味着什么?这和资源本身有什么关系?

嗯,这与正在访问的资源无关。但这表明该网站只能通过 HTTPS 访问。

好的,所有这些都表明图像不应该是卷曲的。正如预期的那样,F5 总是会导致它被再次下载。对我来说,这是一个非常有力的指标,如果我的缓存出现问题,它不会出现在服务器或网络上,而是出现在浏览器级别。

第一次尝试:创建一个新的 img 元素并扔掉之前的元素。

为了方便起见,没有什么比拥有一个返回元素的函数更好的了:

<script>
    const queryParams = new URLSearchParams(window.location.search.substring(1));
    const pipeline = document.getElementById("pipeline")

    if (queryParams.get("status") === "true") {
        pipeline.style.display = "block"
    } else {
        pipeline.remove()
    }
</script>
<div>



<p>E... falhou. Por quê? Porque no momento que a função rodar ainda não tem definido quem é o elemento com id pipeline. Então preciso mudar o ciclo de vida para rodar o script apenas quando a página for carregada. Basta colocar o <script defer>, certo? Bem, não. Porque defer não funciona bem com inline, apenas com arquivo de source explícito. Veja a documentação.

<p>Ou seja, precisei colocar o arquivo JavaScript explicitamente para o Computaria. Como a priori tudo que está solto na pasta do blog é colocado como asset disponível para o Jekyll publicar, criei o js/pipeline-loader.js:<br>
</p>

<pre class="brush:php;toolbar:false"><script src="{{ "/js/pipeline-loader.js" | prepend: site.baseurl }}" defer>
</script>
<div>



<p>E no script:<br>
</p>

<pre class="brush:php;toolbar:false">const queryParams = new URLSearchParams(window.location.search.substring(1));
const pipeline = document.getElementById("pipeline")

if (queryParams.get("status") === "true") {
    pipeline.style.display = "block"
} else {
    pipeline.remove()
}
登录后复制
登录后复制
登录后复制
登录后复制

在 setTimeout 中,我需要删除 #pipeline 子级并插入新图像。我通过父亲的行动找到的选项是:

  • 删除子项
  • 替换子项
  • 替换子项

嗯,removeChild 和replaceChild 涉及了解如何保存旧元素以请求将其删除。另一方面,ReplaceChildren 没有任何戏剧性,它只是传递了新元素,这很好:

const queryParams = new URLSearchParams(window.location.search.substring(1));
const pipeline = document.getElementById("pipeline")

if (queryParams.get("status") === "true") {
    pipeline.style.display = "block"

    const pipelineImg = document.createElement("img")
    pipelineImg.src = "{{site.repository.base}}/badges/master/pipeline.svg"

    pipeline.appendChild(pipelineImg)
} else {
    pipeline.remove()
}
登录后复制
登录后复制
登录后复制

仅此一点就产生了魔力。那么,它的表现如何?

Deixando a pipeline visível para acompanhar deploy do blog

创建新的 img 还不够。

我发现的另一个替代方法是再次设置变量值。这样,就不再需要生成相同元素的函数,我只需“修改”img 指向的 URL 即可。而且,这就是我如何发现在同一页面上的多个位置使用相同资源可能会遭受某种缓存的情况......

好吧,如果每次重复都在 URL 末尾添加一个“ ”来尝试欺骗 GitLab 会怎样?好吧,gitlab 意识到我不怀好意...

如果它是一个带有参数或其迭代器传递的查询参数怎么办?

Deixando a pipeline visível para acompanhar deploy do blog

但是,代价是什么?

好的,这不可能,因为这是一种解决方法,让我们尝试获取它?然后给出fetch之后,想想如何替换图片?

const queryParams = new URLSearchParams(window.location.search.substring(1));

if (queryParams.get("status") === "true") {
    console.log("oba, vamos exibir o pipeline!")
} else {
    console.log("nops, não vamos exibir nada")
}
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

嗯,错误,来自 CORS。既然我无法控制 GitLab,我还能做什么?

使管道可见以监控博客部署;没有重新加载,但

好的,新实验:简单地使用 img 标签创建 /assets/pipeline.html 并从 iframe 指向它。对于强制重新加载操作,我使用了与 Stack Overflow 答案相同的内容:

<div>



<p>Para importar, no /about só precisei colocar {%include pipeline.html%} no começo do arquivo, o Jekyll se encarregou de montar tudo certo.</p>

<p>Ok, vamos por o script para detectar se deveria ou não exibir a tag:<br>
</p>

<pre class="brush:php;toolbar:false"><script>
    const queryParams = new URLSearchParams(window.location.search.substring(1));

    if (queryParams.get("status") === "true") {
        console.log("oba, vamos exibir o pipeline!")
    } else {
        console.log("nops, não vamos exibir nada")
    }
</script>
<div>



<p>So far, so good. Agora, vamos mudar a exibição para display: block caso seja para exibir o pipeline, ou sumir logo de uma vez com a <div>. Pelo console da web, bastaria fazer algo nesse esquema:<br>
</p>

<pre class="brush:php;toolbar:false">const pipeline = document.getElementById("pipeline")

if (...) {
    pipeline.style.display = "block"
} else {
    pipeline.remove()
}
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

转为 HTML

然后,去那里!成功了!

Deixando a pipeline visível para acompanhar deploy do blog

现在,进行调整以使其适合:

  • 徽章充电的停止/重启控制
  • 在 iframe 中:按照手动编辑 SVG 的提示点咖啡来处理 iframe
  • 文档内部:删除正文的边距,只为徽章留出空间

通过进行这些调整,您可以摆脱困境

Deixando a pipeline visível para acompanhar deploy do blog

为此

Deixando a pipeline visível para acompanhar deploy do blog

您可以检查这里使用的文件:

  • /about.md
  • /_includes/pipeline.html
  • /assets/pipeline-badge.html
  • /js/pipeline-loader.js

以上是使管道可见以监控博客部署的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板