0%

概述

通过 Workers 在边缘运行 JavaScript、Rust、C 和 C++ 等。

1.安装

设置代理

set http_proxy=http://127.0.0.1:10809
set https_proxy=http://127.0.0.1:10809

测试是否成功(别用 ping):

curl https://www.google.com

后来项目生成不了,只能曲线救国,安装了个Rust

从本质上讲,Workers应用包含两部分:

  1. 一个事件监听器,用于监听FetchEvents
  2. 一个事件处理程序,返回一个Response对象,该对象传递给事件的.respondWith()方法。

当在Cloudflare的一台边缘服务器上接收到一个与Workers脚本匹配的URL的请求时,它将请求传递给Workers运行时,该运行时又在运行脚本的隔离发出“获取”事件。

When a request is received on one of Cloudflare’s edge servers for a URL matching a Workers script, it passes the request in to the Workers runtime, which in turn emits a “fetch” event in the isolate where the script is running.

  1. 事件监听器FetchEvent告知脚本,以侦听任何传入到您的Worker的请求。向事件处理程序传递了event对象,该对象包括event.requestRequest它是触发FetchEvent的HTTP请求的表示。
  2. 通过调用,.respondWith()我们可以拦截请求,以便发送回自定义响应(在这种情况下,为纯文本“ Hello worker!”)。
    • FetchEvent处理器通常会在达到高潮的呼叫的方法.respondWith()与无论是ResponsePromise<Response>一个确定响应。
    • FetchEvent对象还提供了其他两种方法来处理意外的异常和返回响应后可能完成的操作。

路由和过滤请求

既然我们已经对所有请求运行了非常基本的脚本,那么您通常希望做的下一件事是根据Worker脚本正在接收的请求生成动态响应。这通常称为路由或过滤。

选项1:手动过滤请求

~/my-worker/index.js
addEventListener("fetch", event => {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
  let response
  if (request.method === "POST") {
    response = await generate(request)
  } else {
    response = new Response("Expected POST", { status: 500 })
  }
  // ...
}}

通常基于以下条件过滤请求:

  • request.method—例如GETPOST
  • request.url —例如,根据查询参数或路径名进行过滤。
  • request.headers —根据特定的标头进行过滤。

查看对象所有属性的Request列表。

除标准请求属性外,Workers平台还使用一个cf对象填充请求,该对象包含许多有用的属性,例如regiontimezone

选项2:使用模板在URL上进行路由

对于更复杂的路由,使用库可能会有所帮助。该工人路由器启动打开外部链接 模板提供了类似于ExpressJS的API,用于基于HTTP方法和路径来处理请求:

wrangler generate my-worker-with-router https://github.com/cloudflare/worker-template-router

本教程中使用此启动程序来构建Slack Bot

5c.利用运行时API

本指南中概述的示例只是一个起点。有许多Workers运行时API可用于操纵请求和生成响应。例如,您可以使用HTMLRewriter API即时分析和转换HTML,使用Cache APICloudflare缓存中检索数据并将数据放入Cloudflare缓存中,从边缘计算自定义响应,将请求重定向到另一个服务,还有更多。

要获取灵感,请访问“与工人共建”打开外部链接 展示项目。


6.预览您的项目

准备预览代码时,请运行Wrangler的preview命令:

wrangler preview --watch

此命令将构建您的项目,将其上传到唯一的URL,然后在浏览器中打开一个选项卡以进行查看。这使您可以快速测试在实际Workers运行时上运行的项目,还可以选择甚至与其他人共享该项目。

--watch标志告诉Wrangler注意您的Workers项目目录是否有更改,并将自动使用最新的URL实时更新预览选项卡。

关于建筑的注意事项

正在运行wrangler previewwrangler publishwrangler build自动自动运行,但是build单独运行以检查错误可能很有用。运行wrangler build将为您的项目安装必要的依赖项,并将其编译以使其可以预览或部署。了解有关牧马人的更多信息


7.配置项目以进行部署

为了在Cloudflare的全球云网络上发布Workers项目,您需要wrangler.toml使用Account ID配置您的项目。

如果在免费的worker.dev子域上进行部署,就是这样。如果要部署到自己的域中,则还需要使用Zone ID配置项目。

7a.获取您的帐户ID(和区域ID)

worker.dev

对于worker.dev域,您只需要帐户ID。获得它的最简单方法是运行wrangler whoami

但是,您还可以通过以下步骤在Cloudflare仪表板中找到您的帐户ID:

  1. 登录到您的Cloudflare帐户打开外部链接然后选择工人
  2. 在右侧,查找“帐户ID”,然后单击“输入”下面的“单击以复制”。

注册移民

对于您在Cloudflare上注册的域,您需要两个ID:

  1. 登录到您的Cloudflare帐户打开外部链接 并选择所需的域。
  2. 选择导航栏上的概述选项卡。
  3. 向下滚动,直到在右侧看到区域ID和*帐户ID
  4. 单击单击以将输入复制到每个输入下方。

7b.配置项目

要配置您的项目,我们需要wrangler.toml在生成的项目根目录的文件中填写一些缺少的字段。该文件包含Wrangler连接到Cloudflare Workers API并发布代码所需的信息。

现在,仅account_id使用在运行wrangler whoami或从仪表板中查找到的值填写该字段。

wrangler.toml
name = "my-worker"
account_id = "$yourAccountId"

我们还要配置typeto "webpack",以告知Wrangler使用Webpack打包项目以进行部署。(了解有关type配置的更多信息。)

wrangler.toml
name = "my-worker"
account_id = "$yourAccountId"
type = "webpack"

最后,我们需要告诉Wrangler我们要将项目部署到哪里。

配置用于部署到worker.dev

workers_dev密钥wrangler.toml设置true为时,Wrangler会将您的项目发布到您的workers.dev子域中。

wrangler.toml
name = "my-worker"
account_id = "$yourAccountId"
type = "webpack"
workers_dev = true

部署到worker.dev子域时,名称字段将用作已部署脚本的辅助子域,例如my-worker.my-subdomain.workers.dev

(可选)配置为部署到注册域

要将应用程序发布到您拥有的域(而不是worker.dev子域)上,可以将route密钥添加到wrangler.toml

牧马人的环境功能允许我们为应用程序指定多个不同的部署目标。让我们添加一个production环境,传入zone_idroute

wrangler.toml
name = "my-worker"
account_id = "$yourAccountId"
type = "webpack"
workers_dev = true

[env.production]
# The ID of the domain to deploying to
zone_id = "$yourZoneId"

# The route pattern your Workers application will be served at
route = "example.com/*"

route这里的关键是路由模式,例如可以包含通配符。

如果将路由配置为主机名,则需要向Cloudflare添加DNS记录,以确保可以从外部解析该主机名。如果您的工作人员是您的来源(响应直接来自工作人员),则应输入一个指向的占位符(虚拟)AAAA记录100::,该记录是保留的IPv6丢弃前缀。打开外部链接


8.发布您的项目

配置好我们的项目后,就该发布它了。我们配置它的方式有两个可以发布到的部署目标。

要部署到我们的worker.dev子域,我们可以运行:

wrangler publish

注意:首次推送到worker.dev项目时,DNS传播时最初可能会看到523错误。一分钟左右后,它应该可以工作了。

要部署到我们在设置的“生产”环境中wrangler.toml,可以将--env标志传递给命令:

wrangler publish --env production

有关环境的更多信息,请查看Wrangler文档

您还可以配置GitHub存储库以在每次时自动部署git push。您可以使用Workers GitHub操作来执行此操作打开外部链接,或者编写您自己的GitHub操作并手动配置必要的GitHub机密打开外部链接

2.官方示例

返回HTML

直接从Worker脚本内部的HTML字符串传递HTML页面。

const html = `<!DOCTYPE html>
<body>
  <h1>Hello World</h1>
  <p>This markup was generated by a Cloudflare Worker.</p>
</body>`

async function handleRequest(request) {
  return new Response(html, {
    headers: {
      "content-type": "text/html;charset=UTF-8",
    },
  })
}

addEventListener("fetch", event => {
  return event.respondWith(handleRequest(event.request))
})

返回JSON

直接从Worker脚本返回JSON,可用于构建API和中间件。

addEventListener("fetch", event => {
  const data = {
    hello: "world"
  }

  const json = JSON.stringify(data, null, 2)

  return event.respondWith(
    new Response(json, {
      headers: {
        "content-type": "application/json;charset=UTF-8"
      }
    })
  )
})

获取HTML

Send a request to a remote server, read HTML from the response, and serve that HTML.

向远程服务器发送请求,从响应中读取 HTML,并提供该 HTML。

/**
 * Example someHost at url is set up to respond with HTML
 * Replace url with the host you wish to send requests to
 */
const someHost = "https://examples.cloudflareworkers.com/demos"
const url = someHost + "/static/html"
/**
 * gatherResponse awaits and returns a response body as a string.
 * Use await gatherResponse(..) in an async function to get the response body
 * @param {Response} response
 */
async function gatherResponse(response) {
  const { headers } = response
  const contentType = headers.get("content-type") || ""
  if (contentType.includes("application/json")) {
    return JSON.stringify(await response.json())
  }
  else if (contentType.includes("application/text")) {
    return await response.text()
  }
  else if (contentType.includes("text/html")) {
    return await response.text()
  }
  else {
    return await response.text()
  }
}

async function handleRequest() {
  const init = {
    headers: {
      "content-type": "text/html;charset=UTF-8",
    },
  }
  const response = await fetch(url, init)
  const results = await gatherResponse(response)
  return new Response(results, init)
}

addEventListener("fetch", event => {
  return event.respondWith(handleRequest())
})

获取JSON

Send a GET request and read in JSON from the response. Use to fetch external data.

发送一个 GET 请求并从响应中读取 JSON。用于获取外部数据。

/**
 * 示例 someHost被设置为接受JSON请求。
 * 用您希望发送请求的主机替换为url。
 * @param {string} someHost 要发送请求的主机。
 * @param {string} url 发送请求的URL。
 */
const someHost = "https://examples.cloudflareworkers.com/demos"
const url = someHost + "/static/json"
/**
 * gatherResponse等待并返回一个字符串形式的响应体。
 * 在异步函数中使用 await gatherResponse(...)来获取响应体。
 * @param {Response}响应。
 */
async function gatherResponse(response) {
  const { headers } = response
  const contentType = headers.get("content-type") || ""
  if (contentType.includes("application/json")) {
    return JSON.stringify(await response.json())
  }
  else if (contentType.includes("application/text")) {
    return await response.text()
  }
  else if (contentType.includes("text/html")) {
    return await response.text()
  }
  else {
    return await response.text()
  }
}

async function handleRequest() {
  const init = {
    headers: {
      "content-type": "application/json;charset=UTF-8",
    },
  }
  const response = await fetch(url, init)
  const results = await gatherResponse(response)
  return new Response(results, init)
}

addEventListener("fetch", event => {
  return event.respondWith(handleRequest())
})
var requestURL = 'https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json';
var request = new XMLHttpRequest();
request.open('GET', requestURL);
request.responseType = 'json';
request.send();
request.onload = function() {
  var superHeroes = request.response;
  populateHeader(superHeroes);
  showHeroes(superHeroes);
}

访问 Cloudflare 对象

Access custom Cloudflare properties and control how Cloudflare features are applied to every request.

访问自定义 Cloudflare 属性并控制如何将 Cloudflare 特性应用于每个请求。

addEventListener("fetch", event => {
  const data =
    event.request.cf !== undefined ?
      event.request.cf :
      { error: "The `cf` object is not available inside the preview." }

  return event.respondWith(
    new Response(JSON.stringify(data, null, 2), {
      headers: {
        "content-type": "application/json;charset=UTF-8"
      }
    })
  )
})

Redirect/重定向

将请求从一个 URL 重定向到另一个 URL,或从一组 URL 重定向到另一组 URL。

1.Redirect all requests to one URL 将所有请求重定向到一个 URL

const destinationURL = "https://example.com"
const statusCode = 301

async function handleRequest(request) {
  return Response.redirect(destinationURL, statusCode)
}

addEventListener("fetch", async event => {
  event.respondWith(handleRequest(event.request))
})

2.Redirect requests from one domain to another 将请求从一个域重定向到另一个域

const base = "https://example.com"
const statusCode = 301

async function handleRequest(request) {
  const url = new URL(request.url)
  const { pathname, search, hash } = url

  const destinationURL = base + pathname + search + hash

  return Response.redirect(destinationURL, statusCode)
}

addEventListener("fetch", async event => {
  event.respondWith(handleRequest(event.request))
})

回复另一个网站

Respond to the Worker request with the response from another website (example.com in this example).

用另一个网站的响应来响应工人的请求(例如本例中的.com)。

addEventListener("fetch", event => {
  return event.respondWith(
    fetch("https://example.com")
  )
})

A/B测试

Set up an A/B test by controlling what response is served based on cookies.

通过控制基于 cookie 提供的响应来设置 a/b 测试。

function handleRequest(request) {
  const NAME = "experiment-0"

  // The Responses below are placeholders. You can set up a custom path for each test (e.g. /control/somepath ).
  const TEST_RESPONSE = new Response("Test group") // e.g. await fetch("/test/sompath", request)
  const CONTROL_RESPONSE = new Response("Control group") // e.g. await fetch("/control/sompath", request)

  // Determine which group this requester is in.
  const cookie = request.headers.get("cookie")
  if (cookie && cookie.includes(`${NAME}=control`)) {
    return CONTROL_RESPONSE
  }
  else if (cookie && cookie.includes(`${NAME}=test`)) {
    return TEST_RESPONSE
  }
  else {
    // If there is no cookie, this is a new client. Choose a group and set the cookie.
    const group = Math.random() < 0.5 ? "test" : "control" // 50/50 split
    const response = group === "control" ? CONTROL_RESPONSE : TEST_RESPONSE
    response.headers.append("Set-Cookie", `${NAME}=${group}; path=/`)

    return response
  }
}

addEventListener("fetch", event => {
  event.respondWith(handleRequest(event.request))
})

修改headers

Change the headers sent in a request or returned in a response.

更改请求中发送的标头或响应中返回的标头。

async function handleRequest(request) {
  // Make the headers mutable by re-constructing the Request.
  request = new Request(request)
  request.headers.set("x-my-header", "custom value")
  const URL = "https://examples.cloudflareworkers.com/demos/static/html"

  // URL is set up to respond with dummy HTML
  let response = await fetch(URL, request)

  // Make the headers mutable by re-constructing the Response.
  response = new Response(response.body, response)
  response.headers.set("x-my-header", "custom value")
  return response
}

addEventListener("fetch", event => {
  event.respondWith(handleRequest(event.request))
})

汇总requests

向两个网址发送两个GET请求,并将响应汇总为一个响应。

/**
 * someHost is set up to return JSON responses
 * Replace url1 and url2 with the hosts you wish to send requests to
 * @param {string} url the URL to send the request to
 */
const someHost = "https://examples.cloudflareworkers.com/demos"
const url1 = someHost + "/requests/json"
const url2 = someHost + "/requests/json"
const type = "application/json;charset=UTF-8"
/**
 * gatherResponse awaits and returns a response body as a string.
 * Use await gatherResponse(..) in an async function to get the response body
 * @param {Response} response
 */
async function gatherResponse(response) {
  const { headers } = response
  const contentType = headers.get("content-type") || ""
  if (contentType.includes("application/json")) {
    return JSON.stringify(await response.json())
  }
  else if (contentType.includes("application/text")) {
    return await response.text()
  }
  else if (contentType.includes("text/html")) {
    return await response.text()
  }
  else {
    return await response.text()
  }
}

async function handleRequest() {
  const init = {
    headers: {
      "content-type": type,
    },
  }
  const responses = await Promise.all([fetch(url1, init), fetch(url2, init)])
  const results = await Promise.all([
    gatherResponse(responses[0]),
    gatherResponse(responses[1]),
  ])
  return new Response(results.join(), init)
}

addEventListener("fetch", event => {
  return event.respondWith(handleRequest())
})

Auth with headers

Allow or deny a request based on a known pre-shared key in a header. This is not meant to replace the WebCrypto API.

允许或拒绝基于标头中已知的预共享密钥的请求。这并不意味着要取代 WebCrypto API。

/**
 * @param {string} PRESHARED_AUTH_HEADER_KEY Custom header to check for key
 * @param {string} PRESHARED_AUTH_HEADER_VALUE Hard coded key value
 */
const PRESHARED_AUTH_HEADER_KEY = "X-Custom-PSK"
const PRESHARED_AUTH_HEADER_VALUE = "mypresharedkey"

async function handleRequest(request) {
  const psk = request.headers.get(PRESHARED_AUTH_HEADER_KEY)

  if (psk === PRESHARED_AUTH_HEADER_VALUE) {
    // Correct preshared header key supplied. Fetch request from origin.
    return fetch(request)
  }

  // Incorrect key supplied. Reject the request.
  return new Response("Sorry, you have supplied an invalid key.", {
    status: 403,
  })
}

addEventListener("fetch", event => {
  event.respondWith(handleRequest(event.request))
})

3.

双引号,替换为逗号

论正则的作用

image-20201225171059641

4.

5.实战——做个反代

Cloudflare Workers反代实战(上)》这个人的博客美化可以学一下,感觉还不错,还是个高中生

Cloudflare 新玩法利用 Workers 反向代理

https://github.com/xiaoyang-liu-cs/booster.js

const config = {
  basic: {
    upstream: 'https://en.wikipedia.org/',
    mobileRedirect: 'https://en.m.wikipedia.org/',
  },

  firewall: {
    blockedRegion: ['CN', 'KP', 'SY', 'PK', 'CU'],
    blockedIPAddress: [],
    scrapeShield: true,
  },

  routes: {
    TW: 'https://zh.wikipedia.org/',
    HK: 'https://zh.wikipedia.org/',
    FR: 'https://fr.wikipedia.org/',
  },

  optimization: {
    cacheEverything: false,
    cacheTtl: 5,
    mirage: true,
    polish: 'off',
    minify: {
      javascript: true,
      css: true,
      html: true,
    },
  },
};

async function isMobile(userAgent) {
  const agents = ['Android', 'iPhone', 'SymbianOS', 'Windows Phone', 'iPad', 'iPod'];
  return agents.any((agent) => userAgent.indexOf(agent) > 0);
}

async function fetchAndApply(request) {
  const region = request.headers.get('cf-ipcountry') || '';
  const ipAddress = request.headers.get('cf-connecting-ip') || '';
  const userAgent = request.headers.get('user-agent') || '';

  if (region !== '' && config.firewall.blockedRegion.includes(region.toUpperCase())) {
    return new Response(
      'Access denied: booster.js is not available in your region.',
      {
        status: 403,
      },
    );
  } if (ipAddress !== '' && config.firewall.blockedIPAddress.includes(ipAddress)) {
    return new Response(
      'Access denied: Your IP address is blocked by booster.js.',
      {
        status: 403,
      },
    );
  }

  const requestURL = new URL(request.url);
  let upstreamURL = null;

  if (userAgent && isMobile(userAgent) === true) {
    upstreamURL = new URL(config.basic.mobileRedirect);
  } else if (region && region.toUpperCase() in config.routes) {
    upstreamURL = new URL(config.routes[region.toUpperCase()]);
  } else {
    upstreamURL = new URL(config.basic.upstream);
  }

  requestURL.protocol = upstreamURL.protocol;
  requestURL.host = upstreamURL.host;
  requestURL.pathname = upstreamURL.pathname + requestURL.pathname;

  let newRequest;
  if (request.method === 'GET' || request.method === 'HEAD') {
    newRequest = new Request(requestURL, {
      cf: {
        cacheEverything: config.optimization.cacheEverything,
        cacheTtl: config.optimization.cacheTtl,
        mirage: config.optimization.mirage,
        polish: config.optimization.polish,
        minify: config.optimization.minify,
        scrapeShield: config.firewall.scrapeShield,
      },
      method: request.method,
      headers: request.headers,
    });
  } else {
    const requestBody = await request.text();
    newRequest = new Request(requestURL, {
      cf: {
        cacheEverything: config.optimization.cacheEverything,
        cacheTtl: config.optimization.cacheTtl,
        mirage: config.optimization.mirage,
        polish: config.optimization.polish,
        minify: config.optimization.minify,
        scrapeShield: config.firewall.scrapeShield,
      },
      method: request.method,
      headers: request.headers,
      body: requestBody,
    });
  }

  const fetchedResponse = await fetch(newRequest);

  const modifiedResponseHeaders = new Headers(fetchedResponse.headers);
  if (modifiedResponseHeaders.has('x-pjax-url')) {
    const pjaxURL = new URL(modifiedResponseHeaders.get('x-pjax-url'));
    pjaxURL.protocol = requestURL.protocol;
    pjaxURL.host = requestURL.host;
    pjaxURL.pathname = pjaxURL.path.replace(requestURL.pathname, '/');

    modifiedResponseHeaders.set(
      'x-pjax-url',
      pjaxURL.href,
    );
  }

  return new Response(
    fetchedResponse.body,
    {
      headers: modifiedResponseHeaders,
      status: fetchedResponse.status,
      statusText: fetchedResponse.statusText,
    },
  );
}

// eslint-disable-next-line no-restricted-globals
addEventListener('fetch', (event) => {
  event.respondWith(fetchAndApply(event.request));
});
参考链接:

概述

转自:这里

环境

  • node v8.5.0
  • hexo v3.7.1
  • hexo-cli v1.1.0

前言

Hexo对Markdown解析的不错,唯一不太完美的就是无法添加视频标签,不过其实也是可以的,如下

这是一篇博文

...

{% raw %}
<!-- 这里可以写原生的HTML代码 -->
{% endraw %}
...

但是如果我又想用第三方库把播放器装扮的好看一点呢,总归不可能写一次视频就写一大堆script,link,div标签吧,这样代码的重用性非常的不好,比较希望是如下效果

这是一篇博文

...

{% mdVideo video.mp4 %}{% endmdVideo %}
...

那就自己写一个插件呗。

ps:

1.这里的video.mp4是默认存在执行hexo new post时创建的文件夹中的,只要把配置文件_config.yml中的post_asset_folder设为true就可以了。这样子静态资源对应的路径就是http:// ... /文章名/video.mp4

  1. 需要对javascript的语法有一定的了解

准备工作

在博客的根目录的node_modules中创建一个文件夹,就把它叫做hexo-simple-video-tag好了,注意,文件夹一定要是以hexo-开头的,请参考官网文档:

First, create a folder in the node_modules folder.
The name of this folder must begin with hexo- or Hexo will ignore it.

到这个文件夹的根目录(/node_modules/hexo-simple-video-tag)下执行npm init命令,使其生成一个package.json文件,同时创建一个index.js文件目录如下

.
├── index.js
└── package.json

package.json(/node_modules/hexo-simple-video-tag/package.json)的文件内容如下

{
  "name": "hexo-simple-video-tag",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

nameversion的信息添加到博客根目录的package.json文件中

....
  "dependencies": {
    ...

    "hexo-simple-video-tag": "1.0.0"
  },
....

使用npm script简化命令的输入,还是在博客根目录的package.json文件中

....
  "scripts": {
    "local": "hexo clean && hexo g && hexo s"
  }
....

这样直接在命令行中执行npm run local就可以启动本地预览服务器了

好了,打开文件夹hexo-simple-video-tag中的index.js文件开始编写代码

关于 hexo.extend.tag.register 的说明请查看官方文档

hexo.extend.tag.register('mdVideo', function(args, content){
  return `<div>Hello ${args[0]} tag! ${content}</div>`
  // ends为true的话标签是这样的
  // {%tag arg0 arg1 arg2%} content {%endtag%}
  // false的话是这样
  // {%tag%}
  // 回调函数的 args参数是[arg0, arg1, arg2 ...] 组成的数组
  //          content是夹在标签之间的内容
}, {ends: true})

创建一篇新博文,就是本文的标题吧教程向:自己写一个hexo插件,写入如下内容

{% mdVideo hexo %}
<p>I'm content</p>
{%endmdVideo%}

在博客根目录下执行hexo clean && hexo g && hexo s看效果

依赖(dependencies)

underscore.js

作用大至如下:

_.cat();                    // 0-args
//=> []

_.cat([]);                  // 1-arg, empty array
//=> []

_.cat([1,2,3]);             // 1-arg
//=> [1,2,3]

_.cat([1,2,3],[4,5,6]);     // 2-args
//=> [1,2,3,4,5,6]

_.cat([1,2,3],[4,5,6],[7]); // 3+ args
//=> [1,2,3,4,5,6,7]

lodash.js

通过使用数组、数字、对象、字符串等,使得 JavaScript 变得更加简单。Lodash 的模块化方法非常适用于:

迭代数组、对象和字符串操作和测试值创建复合函数

作用大至如下:

_.defaults({ 'a': 1 }, { 'a': 3, 'b': 2 });
// → { 'a': 1, 'b': 2 }
_.partition([1, 2, 3, 4], n => n % 2);
// → [[1, 3], [2, 4]]
参考链接:

概述

1.picgo-plugin-web-uploader

图床配置

  • url: 图床上传API地址
  • paramName: POST参数名
  • jsonPath: 图片URL所在返回值的JsonPath(eg:data.url)
  • customHeader: 自定义请求头 标准JSON(eg: {“key”:”value”}
  • customBody: 自定义Body 标准JSON(eg: {“key”:”value”})

服务端配置

  1. 参照 PicUploader 配置同时上传多个免费图床
  2. 可使用已打包的pic-upload docker镜像
version: '3'
services:
  pic-uploader:
    image: zqiannnn/pic-uploader:1.0
    ports:
      - 8080:80
    volumes:
      - ./server.conf:/etc/nginx/conf.d/server.conf
    environment:
      - STORAGE_TYPE=Qiniu,Netease,Aliyun,Ucloud,Qingcloud
#     10G 10G
      - QINIU_AK=xx
      - QINIU_SK=xx
      - QINIU_BUCKET=xx
      - QINIU_DOMAIN=http://xx.bkt.clouddn.com
#     50G 20G
      - NETEASE_AK=xx
      - NETEASE_AS=xx
      - NETEASE_BUCKET=xx
      - NETEASE_ENDPOINT=xx
      - NETEASE_DOMAIN=https://xx.nos-eastchina1.126.net
#     无
      - ALIYUN_AK=xx
      - ALIYUN_AS=xx
      - ALIYUN_BUCKET=xx
      - ALIYUN_ENDPOINT=xx
      - ALIYUN_DOMAIN=https://xx.aliyuncs.com
#     20G 20G
      - UCLOUD_PUBLIC_KEY=xx
      - UCLOUD_PRIVATE_KEY=xx
      - UCLOUD_PROXY_SUFFIX=xx
      - UCLOUD_BUCKET=xx
      - UCLOUD_ENDPOINT=ufile.ucloud.com.cn
      - UCLOUD_DOMAIN=http://xx.ufileos.com
#     30G 11G
      - QINGCLOUD_AK=xx
      - QINGCLOUD_SK=xx
      - QINGCLOUD_BUCKET=xx
      - QINGCLOUD_ZONE=xx
#     最后一个domain可设置为步骤3中Nginx域名
      - QINGCLOUD_DOMAIN=https://xx.qingstor.com

#     自定义返回
      - CUSTOM_FORMAT={"url":"{{url}}"}
      - WATERMARK=水印字符
  1. 使用Nginx代理多个免费图床nginx

2.

3.

4.

5.

参考链接:

概述

网上推荐看的教程

菜鸟教程-JS
Mozilla
廖雪峰-JS

1.

2.

3.

4.

5.

JavaScript库/框架

JavaScript 高级程序设计(特别是对浏览器差异的复杂处理),通常很困难也很耗时。

为了应对这些调整,许多的 JavaScript (helper) 库应运而生。

这些 JavaScript 库常被称为 JavaScript 框架

在本教程中,我们将了解到一些广受欢迎的 JavaScript 框架:

  • jQuery
  • Prototype
  • MooTools

所有这些框架都提供针对常见 JavaScript 任务的函数,包括动画、DOM 操作以及 Ajax 处理。

在本教程中,您将学习到如何开始使用它们,来使得 JavaScript 编程更容易、更安全且更有乐趣。

jQuery

jQuery 是目前最受欢迎的 JavaScript 框架。

它使用 CSS 选择器来访问和操作网页上的 HTML 元素(DOM 对象)。

jQuery 同时提供 companion UI(用户界面)和插件。

许多大公司在网站上使用 jQuery:

  • Google
  • Microsoft
  • IBM
  • Netflix

如需更深入地学习 jQuery,请访问我们的 jQuery 教程


Prototype

Prototype 是一种库,提供用于执行常见 web 任务的简单 API。

API 是应用程序编程接口(Application Programming Interface)的缩写。它是包含属性和方法的库,用于操作 HTML DOM。

Prototype 通过提供类和继承,实现了对 JavaScript 的增强。


MooTools

MooTools 也是一个框架,提供了可使常见的 JavaScript 编程更为简单的 API。

MooTools 也含有一些轻量级的效果和动画函数。


其他框架

下面是其他一些在上面未涉及的框架:

YUI - Yahoo! User Interface Framework,涵盖大量函数的大型库,从简单的 JavaScript 功能到完整的 internet widget。

Ext JS - 可定制的 widget,用于构建富因特网应用程序(rich Internet applications)。

Dojo - 用于 DOM 操作、事件、widget 等的工具包。

script.aculo.us - 开源的 JavaScript 框架,针对可视效果和界面行为。

UIZE - Widget、AJAX、DOM、模板等等。


CDN -内容分发网络

您总是希望网页可以尽可能地快。您希望页面的容量尽可能地小,同时您希望浏览器尽可能多地进行缓存。

如果许多不同的网站使用相同的 JavaScript 框架,那么把框架库存放在一个通用的位置供每个网页分享就变得很有意义了。

CDN (Content Delivery Network) 解决了这个问题。CDN 是包含可分享代码库的服务器网络。

Google 为一系列 JavaScript 库提供了免费的 CDN,包括:

  • jQuery
  • Prototype
  • MooTools
  • Dojo
  • Yahoo! YUI

但是由于 Google 在中国经常被GFW(防火长城,英文名称Great Firewall of China,简写为Great Firewall,缩写GFW)屏蔽,造成访问不稳定,所以建议使用百度静态资源公共库。

如需在您的网页中使用 JavaScript 框架库,只需在


使用框架

在您决定为网页使用 JavaScript 框架之前,首先对框架进行测试是明智的。

JavaScript 框架很容易进行测试。您无需在计算机上安装它们,同时也没有安装程序。

通常您只需从网页中引用一个库文件。

参考链接:

概述

利用git上传文件至github是特别常用的,总结以下内容供参考使用。
第一步:下载git工具,[这](https://git-scm.com/downloads),选择适合自己的版本进行安装。

image-20210604093018378

第二步:安装完成后,找到Git bash,双击打开。

image-20210604093120264

第三步:输入自己的用户名和邮箱(为注册GITHUB账号时的用户名和邮箱)

git config --global user.name "coliyin@163.com"
git config --global user.email "coliyin@163.com"

image-20210604093209976

第四步:设置SSH key

  众所周知ssh是加密传输。加密传输的算法有好多,git可使用rsa,rsa要解决的一个核心问题是,如何使用一对特定的数字,使其中一个数字可以用来加密,而另外一个数字可以用来解密。这两个数字就是你在使用git和github的时候所遇到的public key也就是公钥以及private key私钥。

  其中,公钥就是那个用来加密的数字,这也就是为什么你在本机生成了公钥之后,要上传到github的原因。从github发回来的,用那公钥加密过的数据,可以用你本地的私钥来还原。

  如果你的key丢失了,不管是公钥还是私钥,丢失一个都不能用了,解决方法也很简单,重新再生成一次,然后在github.com里再设置一次就行。

言归正传,我们首先检验本机是否生成密钥,执行命令:

cd ~/.ssh
ls

若结果如下,则说明密钥已存在。

这里写图片描述

如果没有密钥,则执行以下命令来生成密钥:

ssh-keygen -t rsa -C "coliyin@163.com"

生成过程中按3次回车键就好(默认路径,默认没有密码登录),生成成功后,去对应默认路径里用记事本打开id_rsa.pub,得到ssh key公钥。

第五步:为github账号配置SSH key

  接下来,切换到个人github账号里,点击右上角用户头像下的小三角,找到setting,在右侧菜单栏中找到SSH and GPG keys,选择new SSH key,输入title,下面key的内容就是本机ssh key 公钥,直接将id_rsa.pub中的内容粘贴过来就可以,然后点击下面的add SSH key即可完成。

第六步:上传本地文件

1、创建一个本地文件

这里写图片描述

2、建立本地仓库

(1)首先进入layout文件夹:

这里写图片描述

(2)执行指令进行初始化,会在原始文件夹中生成一个隐藏的文件夹.git:

git init

这里写图片描述

(3)执行指令将文件添加到本地仓库:

git add .         //添加当前文件夹下的所有文件
git add **.cpp    //添加当前文件夹下的**.cpp这个文件12

(4)输入本次的提交说明,准备提交暂存区中的更改的已跟踪文件,单引号内为说明内容:

git commit -m "layout"  //引号中的内容为对该文件的描述1

这里写图片描述

3、关联github仓库

在github中新建一个repository,复制仓库地址:

这里写图片描述

执行命令:

   //新建一个repository时会出现下面的代码,直接复制即可
git remote add origin https://github.com/CongliYin/CSS.git

注意:

如果出现错误:fatal: remote origin already exists,则执行以下语句:

git remote rm origin1

再重新执行:

git remote add origin https://github.com/CongliYin/CSS.git

即可成功。

最后执行命令:

git push origin master

如果出现错误failed to push som refs to…….,则执行以下语句,先把远程服务器github上面的文件拉先来,再push 上去。:

git pull origin master

这里写图片描述

刷新github,即可看到上传的文件夹。

另一个

前提:

①确定本机(windows环境)已经安装git(https://git-scm.com/downloads)

②建立好远程Git仓库

1、在你想推送的文件夹下:右键→选择Git Bath Here 弹出如下命令框:

image-20210604085652093

2、输入:git init (在当前文件夹下初始化一个git仓库)

image-20210604085527216

3、输入:git add . (将当前工作区的所有文件存放到暂存区)

4、输入:git commit -m “cuihan测试”(引号里面是备注信息;此命令作用:将暂存区中存放的文件提交到git仓库;其中 “ae8e179”就是你所要提交的ID)

5、输入:git remote add origin https://github.com/cuihanObject/cmdTest.git (将本地代码库的当前分支与远程的git代码库相关联,此处你可改为自己的git仓库)

image-20210604090249284

6、输入:git push -u origin master (把当前分支推送到远程的代码库;下面标红的100% 提示你进度。)

image-20210604091141809

7、刷新git仓库,便可以看到你上传的文件了

image-20210604091205709

参考链接:

概述

1.

2.id 和 class 选择器

css选择器的格式,规定不带空格的选择条件之间是“且”关系,带空格的是“父子”关系,并且可以是非直接的“父子”关系

类名的第一个字符不能使用数字!它无法在 Mozilla 或 Firefox 中起作用。

3.CSS 的继承性

当创建的样式表越来越复杂时,一个标签的样式将会受到越来越多的影响,这种影响可能来自周围的标签,也可能来自其自身。下面我们从这两方面去看看 CSS 样式的优先级。

CSS 的继承特性指的是应用在一个标签上的那些 CSS 属性被传到其子标签上。看下面的 HTML 结构:

<div>
    <p></p>
</div>

如果

有个属性 color: red,则这个属性将被

继承,即

也拥有属性 color: red

由上可见,当网页比较复杂, HTML 结构嵌套较深时,一个标签的样式将深受其祖先标签样式的影响。影响的规则是:

CSS 优先规则1: 最近的祖先样式比其他祖先样式优先级高。

例1:

<!-- 类名为 son 的 div 的 color 为 blue -->
<div style="color: red">
    <div style="color: blue">
        <div class="son"></div>
    </div>
</div>

如果我们把一个标签从祖先那里继承来的而自身没有的属性叫做”祖先样式”,那么”直接样式”就是一个标签直接拥有的属性。又有如下规则:

CSS 优先规则2:“直接样式”比”祖先样式”优先级高。

例2:

<!-- 类名为 son 的 div 的 color 为 blue -->
<div style="color: red">
    <div class="son" style="color: blue"></div>
</div>

选择器的优先级

上面讨论了一个标签从祖先继承来的属性,现在讨论标签自有的属性。在讨论 CSS 优先级之前,先说说 CSS 7 种基础的选择器:

  • ID 选择器, 如 #id{}
  • 类选择器, 如 .class{}
  • 属性选择器, 如 a[href=”segmentfault.com”]{}
  • 伪类选择器, 如 :hover{}
  • 伪元素选择器, 如 ::before{}
  • 标签选择器, 如 span{}
  • 通配选择器, 如 *{}

CSS 优先规则3:优先级关系:内联样式 > ID 选择器 > 类选择器 = 属性选择器 = 伪类选择器 > 标签选择器 = 伪元素选择器

例3:

// HTML
<div class="content-class" id="content-id" style="color: black"></div>

// CSS
#content-id {
    color: red;
}
.content-class {
    color: blue;
}
div {
    color: grey;
}

最终的 color 为 black,因为内联样式比其他选择器的优先级高。

所有 CSS 的选择符由上述 7 种基础的选择器或者组合而成,组合的方式有 3 种:

  • 后代选择符: .father .child{}
  • 子选择符: .father > .child{}
  • 相邻选择符: .bro1 + .bro2{}

当一个标签同时被多个选择符选中,我们便需要确定这些选择符的优先级。我们有如下规则:

CSS 优先规则4:计算选择符中 ID 选择器的个数(a),计算选择符中类选择器、属性选择器以及伪类选择器的个数之和(b),计算选择符中标签选择器和伪元素选择器的个数之和(c)。按 a、b、c 的顺序依次比较大小,大的则优先级高,相等则比较下一个。若最后两个的选择符中 a、b、c 都相等,则按照”就近原则”来判断。

例4:

// HTML
<div id="con-id">
    <span class="con-span"></span>
</div>

// CSS
#con-id span {
    color: red;
}
div .con-span {
    color: blue;
}

由规则 4 可见, 的 color 为 red。

如果外部样式表和内部样式表中的样式发生冲突会出现什么情况呢?这与样式表在 HTML 文件中所处的位置有关。样式被应用的位置越在下面则优先级越高,其实这仍然可以用规则 4 来解释。

例5:

// HTML
<link rel="stylesheet" type="text/css" href="style-link.css">
<style type="text/css">
@import url(style-import.css); 
div {
    background: blue;
}
</style>

<div></div>

// style-link.css
div {
    background: lime;
}

// style-import.css
div {
    background: grey;
}

从顺序上看,内部样式在最下面,被最晚引用,所以

的背景色为 blue。

上面代码中,**@import** 语句必须出现在内部样式之前,否则文件引入无效。当然不推荐使用 @import 的方式引用外部样式文件,原因见另一篇博客:CSS 引入方式

CSS 还提供了一种可以完全忽略以上规则的方法,当你一定、必须确保某一个特定的属性要显示时,可以使用这个技术。

CSS 优先规则5:属性后插有 !important 的属性拥有最高优先级。若同时插有 !important,则再利用规则 3、4 判断优先级。

例6:

// HTML
<div class="father">
    <p class="son"></p>
</div>

// CSS
p {
    background: red !important;
}
.father .son {
    background: blue;
}

虽然 .father .son 拥有更高的权值,但选择器 p 中的 background 属性被插入了 !important, 所以

的 background 为 red。

错误的说法

在学习过程中,你可能发现给选择器加权值的说法,即 ID 选择器权值为 100,类选择器权值为 10,标签选择器权值为 1,当一个选择器由多个 ID 选择器、类选择器或标签选择器组成时,则将所有权值相加,然后再比较权值。这种说法其实是有问题的。比如一个由 11 个类选择器组成的选择器和一个由 1 个 ID 选择器组成的选择器指向同一个标签,按理说 110 > 100,应该应用前者的样式,然而事实是应用后者的样式。错误的原因是:权重的进制是并不是十进制,CSS 权重进制在 IE6 为 256,后来扩大到了 65536,现代浏览器则采用更大的数量。。还是拿刚刚的例子说明。11 个类选择器组成的选择器的总权值为 110,但因为 11 个均为类选择器,所以其实总权值最多不能超过 100, 你可以理解为 99.99,所以最终应用后者样式。

5.

参考链接:

概述

EJS 是什么?

“E” 代表什么?可以表示 “可嵌入(Embedded)”,也可以是“高效(Effective)”、“优雅(Elegant)”或者是“简单(Easy)”。EJS 是一套简单的模板语言,帮你利用普通的 JavaScript 代码生成 HTML 页面。

实例

<% if (user) { %>
  <h2><%= user.name %></h2>
<% } %>

用法

let template = ejs.compile(str, options);
template(data);
// => 输出渲染后的 HTML 字符串

ejs.render(str, data, options);
// => 输出渲染后的 HTML 字符串

ejs.renderFile(filename, data, options, function(err, str){
    // str => 输出渲染后的 HTML 字符串
});

参数

  • cache 缓存编译后的函数,需要指定 filename
  • filenamecache 参数用做键值,同时也用于 include 语句
  • context 函数执行时的上下文环境
  • compileDebug 当值为 false 时不编译调试语句
  • client 返回独立的编译后的函数
  • delimiter 放在角括号中的字符,用于标记标签的开与闭
  • debug 将生成的函数体输出
  • _with 是否使用 with() {} 结构。如果值为 false,所有局部数据将存储在 locals 对象上。
  • localsName 如果不使用 with ,localsName 将作为存储局部变量的对象的名称。默认名称是 locals
  • rmWhitespace 删除所有可安全删除的空白字符,包括开始与结尾处的空格。对于所有标签来说,它提供了一个更安全版本的 -%> 标签(在一行的中间并不会剔除标签后面的换行符)。
  • escape<%= 结构设置对应的转义(escape)函数。它被用于输出结果以及在生成的客户端函数中通过 .toString() 输出。(默认转义 XML)。
  • outputFunctionName 设置为代表函数名的字符串(例如 'echo''print')时,将输出脚本标签之间应该输出的内容。
  • async 当值为 true 时,EJS 将使用异步函数进行渲染。(依赖于 JS 运行环境对 async/await 是否支持)

标签含义

  • <% ‘脚本’ 标签,用于流程控制,无输出。
  • <%_ 删除其前面的空格符
  • <%= 输出数据到模板(输出是转义 HTML 标签)
  • <%- 输出非转义的数据到模板
  • <%# 注释标签,不执行、不输出内容
  • <%% 输出字符串 ‘<%’
  • %> 一般结束标签
  • -%> 删除紧随其后的换行符
  • _%> 将结束标签后面的空格符删除

包含(include)

通过 include 指令将相对于模板路径中的模板片段包含进来。(需要提供 ‘filename’ 参数。) 例如,如果存在 “./views/users.ejs” 和 “./views/user/show.ejs” 两个模板文件,你可以通过 <%- include('user/show'); %> 代码包含后者。

你可能需要能够输出原始内容的标签 (<%-) 用于 include 指令,避免对输出的 HTML 代码做转义处理。

<ul>
  <% users.forEach(function(user){ %>
    <%- include('user/show', {user: user}); %>
  <% }); %>
</ul>

自定义分隔符

可针对单个模板或全局使用自定义分隔符:

let ejs = require('ejs'),
    users = ['geddy', 'neil', 'alex'];

// 单个模板文件
ejs.render('<?= users.join(" | "); ?>', {users: users},
    {delimiter: '?'});
// => 'geddy | neil | alex'

// 全局
ejs.delimiter = '$';
ejs.render('<$= users.join(" | "); $>', {users: users});
// => 'geddy | neil | alex'

缓存

EJS 附带了一个基本的进程内缓存,用于缓在渲染模板过程中所生成的临时 JavaScript 函数。 通过 Node 的 lru-cache 库可以很容易地加入 LRU 缓存:

let ejs = require('ejs'),
    LRU = require('lru-cache');
ejs.cache = LRU(100); // 具有 100 条内容限制的 LRU 缓存

如果要清除 EJS 缓存,调用 ejs.clearCache 即可。如果你正在使用的是 LRU 缓存并且需要设置不同的限额,则只需要将 ejs.cache 重置为 一个新的 LRU 实例即可。

自定义文件加载器

默认的文件加载器是 fs.readFileSync,如果你想要的自定义它, 设置ejs.fileLoader 即可。

let ejs = require('ejs');
let myFileLoader = function (filePath) {
  return 'myFileLoader: ' + fs.readFileSync(filePath);
};

ejs.fileLoader = myFileLoad;

使用此功能,您可以在读取模板之前对其进行预处理。

布局(Layouts)

EJS 并未对块(blocks)提供专门的支持,但是可以通过 包含页眉和页脚来实现布局,如下所示:

<%- include('header'); -%>
<h1>
  Title
</h1>
<p>
  My page
</p>
<%- include('footer'); -%>

客户端支持

latest release 链接下载 ./ejs.js./ejs.min.js 文件。或者,你可以 clone 这个仓库并 通过执行 jake build 自己编译(或者执行 $(npm bin)/jake build,如果 jake 不是安装在全局环境的话)。

在页面中包含上面的任意一个文件,然后 ejs 就全局可用了

示例

<div id="output"></div>
<script src="ejs.min.js"></script>
<script>
  let people = ['geddy', 'neil', 'alex'],
      html = ejs.render('<%= people.join(", "); %>', {people: people});
  // With jQuery:
  $('#output').html(html);
  // Vanilla JS:
  document.getElementById('output').innerHTML = html;
</script>

注意事项

大多数情况下,EJS 将会按照我们的预期运行; 但是, 仍然需要注意:

  1. 显然, 如果你没有文件系统的访问权限, ejs.renderFile 将无法正常工作。
  2. 相同的原因, 除非为 include 设置一个回调函数,否则 include 无法正常工作。如下所示:
let str = "Hello <%= include('file', {person: 'John'}); %>",
      fn = ejs.compile(str, {client: true});

fn(data, null, function(path, d){ // include callback
  // path -> 'file'
  // d -> {person: 'John'}
  // Put your code here
  // Return the contents of file as a string
}); // returns rendered string
参考链接:

概述

Hexo是什么?

GitHub Pages是什么?

GitHub Pages 是由 GitHub 官方提供的一种免费的静态站点托管服务,让我们可以在 GitHub 仓库里托管和发布自己的静态网站页面。我把它理解为动态网站的主机。功能都很类似。

1.安装

**1.**Hexo基于Node.js,搭建中还需要npm(Node.js自带)和git,因此我们需要首先搭建本地环境。安装Node.js和Git。

Node.js安装:

Node.js官网:https://nodejs.org/zh-cn/

进入官网后下载Windows(x64)长期支持版。

安装Git:

Git官网下载网址:https://git-scm.com/downloads

选择自己的平台,我选择Windows。点击下载。

下面验证Node.js和Git是否安装成功,也就是本地环境是否搭建成功。Win+R打开运行。输入cmd,进入命令提示符。

依次输入:(注意输入法在英文状态下)

node -v
npm -v
git --version
20201209090437

若如上图所示,则表明安装成功!

2.注册github

下来在桌面或者哪儿都可以,鼠标右键,点击【Git Bash Here】。

依次输入:

git config --global user.name "GitHub用户名"
git config --global user.email "GitHub邮箱"

3.生成ssh并连接

(如果想在多平台部署请忽略这步,直接看2,3)

输入:

ssh-keygen -t rsa -C "GitHub邮箱" 

然后一路回车。创建SSH密匙。

进入下面路径,如果不显示的记得选择显示隐藏的项目。

路径:C:\用户\用户名\ .ssh

用记事本打开id_rsa.pub文件。复制文件中的内容。

登陆GitHub,进入setting。

选择左边SSH and GPG Keys选项。进行添加密匙。

点击New SSH Key进行添加。

title自己随便取,把刚才复制的密匙粘贴到Key中,点击Add SSH Key添加完成。

打开Git Bash,输入:

ssh -T git@github.com

出现Are you sure……的时候输入yes回车。

最后显示You’ve successfully……的时候表示连接成功。

点击右上角加号,点击New repository新建GitHub Pages仓库。

在Repository name中输入:用户名.github.io

勾选“Initialize this repository with a README”

Description选填。

填好后点击Create repository。创建后默认启用HTTPS。

博客的地址为:https://用户名.github.io

首先安装hexo-deployer-git。输入:

npm install hexo-deployer-git --save

耐心等待

2.多SSH连接

ssh-keygen -t rsa -C "xxxxxx@xxx.com" -f "id_rsa_gitee"
ssh-keygen -t rsa -C "xxxxxx@xxx.com" -f "id_rsa_github"

在.ssh目录下创建config文件,添加如下内容:

# github
Host github.com
HostName github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/id_rsa_github

# gitee
Host gitee.com
HostName gitee.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/id_rsa_gitee

3.多平台部署

github

vercel

部署效果

套了cf后,链接1,链接2

netlify

部署效果

coding

部署效果

gitee

部署效果

csdn

目前仅支持企业版,可以用来当个备份!

扩展

Hexo 配置文件中的 theme_config 的优先级最高,其次是 _config.[theme].yml 文件,最后是位于主题目录下的 _config.yml 文件。

概述

1.

https://blog.mimvp.com/article/23401.html

1、双引号 (百度、谷歌)

把搜索关键词放在双引号中,例如:”米扑科技”,表示完全匹配搜索,不会被拆词分割,搜索结果返回的页面包含双引号中出现完整的关键词,连顺序也必须完全匹配。百度和谷歌都支持这个指令。例如搜索 “米扑科技” : https://www.baidu.com/s?wd="米扑科技"

可以看到,返回结果都是完整而且按顺序出现的 “米扑科技”,使用双引号扫完可以更准确地找到特定关键词,可以当做用于查询外链(反链),但并不准确。

2、加号、减号 (百度、谷歌)

加号(**+**)表示搜索结果一定要包含加号后面词的页面,示例:[米扑科技 +代理](https://www.baidu.com/s?wd=米扑科技 +代理)

减号(**-**)表示搜索结果不包含减号后面词的页面,示例:[米扑科技 -代理](https://www.baidu.com/s?wd=米扑科技 -代理)

使用这个指令时,加号/减号前面必须是空格,加号/减号后面没有空格,紧跟着要出现或排除的词,谷歌和百度都支持这个词。

例如搜索 “[米扑科技 +代理](https://www.baidu.com/s?wd=米扑科技 +代理)”:[https://www.baidu.com/s?wd=](https://www.baidu.com/s?wd=米扑科技 +代理)[米扑科技 +代理](https://www.baidu.com/s?wd=米扑科技 +代理)

返回结果页面就一定要有关键词“代理”,实际上百度支持的并不好。

3、星号 (谷歌)

星号常用作通配符,百度不支持*号搜索指令,谷歌支持。

例如:在Google中搜索 “米扑技”,其中的号代表任何文字。

返回的结果就不仅包含“米扑科技”,还包含“米扑学技”,“米扑口技”等内容。

4、inurl: (百度、谷歌)

inurl: 指令用于搜索查询词出现在URL中的页面,inurl:支持中文和英文,简单来说就是url中包含“你要搜索词”的页面,谷歌和百度都支持inurl:指令。

例如:搜索“inurl:米扑科技”,返回的结果中,网址url中必须包含“米扑科技”关键词的页面。

由于关键词出现在 URL中对排名有一定影响,因此使用 inurl:关键词,可以更准确地找到竟争对手。

5、inanchor: (谷歌)

inanchor: 指令返回的结果是导入链接锚文字中包含搜索词的页面。(百度不支持)

比如在谷歌搜索 “inanchor:米扑科技”,返回结果页面本身并不一定包含 “米扑科技” 这4个字,而是指向这些页面的链接锚文字中出现了“米扑科技” 这4个字。

6、intitle: (百度、谷歌)

intitle: 指返回的页面title中包含关键词的页面。谷歌和百度都支持intitle:指令。

例如:搜索 “intitle:米扑”,就会返回页面Title包含关键词 “米扑” 的页面。

title是目前页面优化的最重要因素。做SEO的人无论要做哪个词的排名,都会把关键词放进title中。

使用intitle: 指令找到的文件才是更准确的竟争页面,如果关键词只出现在页面可见文字中,而没有出现在title中,大部分情况是并没有针对关键词进行优化,也不是有力的竟争对手。

7、intext: (谷歌)

intext: 指返回的页面正文中包含关键词的页面。(百度不支持)

例如:搜索“intext:米扑科技”,就会返回页面正文中包含 “米扑科技” 的页面。

8、allintitle: (谷歌)

allintitle:搜索返回的是页面title中包含多组关键词的页面。(百度不支持)

例如:[allintitle:米扑科技 米扑代理](https://www.baidu.com/s?wd=allintitle:米扑科技 米扑代理)

就相当于:

intitle:米扑科技 intitle:米扑代理

返回的是title标题中既包含“米扑科技”,也包含 “米扑代理” 的页面。

9、allintext: (谷歌)

allintext:搜索返回的是页面正文中包含多组关键词的页面。(百度不支持)

例如:[allintext:米扑科技 米扑代理](https://www.baidu.com/s?wd=allintext:米扑科技 米扑代理)

就相当于:

intext:米扑科技 intext:米扑代理

返回的是正文中既包含“米扑科技”,也包含 “米扑代理” 的页面。

10、allinurl: (谷歌)

与allintitle类似,搜索结果页面的url中包含多个关键词的页面。(百度不支持)

例如:[allinurl:米扑科技 米扑代理](https://www.baidu.com/s?wd=allinurl:米扑科技 米扑代理)

就相当于:inurl:米扑科技 inurl:米扑代理

11、filetype: (百度、谷歌)

filetype: 用于搜索特定文件格式,Google和百度都支持filetype:指令。

例如:搜索 “filetype:pdf 米扑” 返回的就是包含关键词“米扑”的所有 PDF文件。

百度支持的文件格式有:pdf、doc、xls、ppt、rtf、all,其中“all” 表示所有百度支持的文件类型。 Google则支持所有能索引的文件格式,包括HTML、PHP等。

filetype:指令用来搜索特定的资源,比如PDF电子书、Word文件等非常有用。

12、site: (百度、谷歌)

site:是用来搜索某个域名下的所有文件,一般用于查询网站收录页面索引量的最直接方法。

例如: site:mimvp.com

site指令也可用于子域名。

例如: site:proxy.mimvp.com

13、link: (谷歌)

link:用来搜索某个url的反向链接,既包含内部链接,也包含外部链接。

比如:link:mimvp.com

返回的就是米扑科技域名 mimvp.com 的反向链接。

不过可惜的是,Google的link:指令返回的链接只是google索引库中的一部分,而且是近乎随机的一部分,所以用link:指令查反向链接几乎没有用。

百度则不支持link:指令。

14、related: (谷歌)

仅Google适用,返回结果是与某个网站有关联的页面。

例如搜索:related:mimvp.com

这种关联到到底指的是什么,Google并没有明确说明,一般认为指的是有共同外部链接的网站。

综合使用高级搜索命令

上面介绍的一些高级搜索指令,单独使用可以找到不少资源,或者可以更精确地定位竞争对手。

如果把这些高级指令混合起来使用则更强大。

高级指令混合应用的几个例子,仅供参考:

1、[inurl:gov 减肥](https://www.baidu.com/s?wd=inurl:gov 减肥)

返回的就是URL中包含“gov”,页面中有“减肥”这个词的页面。

很多SEO人员认为政府和学校网站有比较高的权重,找到相关的政府和学校网站,就找到了最好的链接资源。

2、[inurl:blog 交换链接](https://www.baidu.com/s?wd=inurl:blog 交换链接)

返回的URL中包含blog,网页正文包含 “交换链接” 这个词的页面。从中SEO人员可以找到愿意交换链接的博客网站。

或者使用一个更精确的搜索:[inurl:blog 交换链接 -163 -sina -csdn -网易 -新浪 -搜狐 -和讯](https://www.baidu.com/s?wd=inurl:blog 交换链接 -163 -sina -csdn -网易 -新浪 -搜狐 -和讯)

返回的结果,则是URL中包含blog,网页正文包含“交换链接”这四个字,并且排除了网易、新浪、搜狐、和讯等博客,返回的结果大部分是个人博客站点。

3、[友情链接 inurl:link](https://www.baidu.com/s?wd=友情链接 inurl:link)

很多站长把友情链接的页面命名为 link.html 或 links.html 等,所以这个指令返回的就是与“友情链接”主题相关的交换链接页面。

4、allinurl:gov.cn+links

返回的是url中包含“gov.cn” 以及“links” 的页面,也就是政府域名上的交换链接页面。

图形化的高级搜索指令

上面介绍的高级搜索指令,都是一些常用的,但估计很多人不容易记住

最后,推荐百度的高级搜索,它具有图形化的界面

百度高级搜索:https://www.baidu.com/gaoji/advanced.html

2.

3.

4.

5.

参考链接:

概述

在使用了市面上大多数流行的编译器后,发现还是jetbrains家的IDE深得我心,特别是VS和VS code给我留下了极其糟糕的记忆,原因是不管是VS还是VS code的C++对于中文的支持着实糟糕,博主愣是折腾了好几天都没有解决输出乱码的问题,一气之下把软件卸载的干干净净,决定再也不碰了,太垃圾了。

(jetbrains家在我的电脑会出现闪烁的问题,这个也挺让人难受的,不知道是驱动还是什么问题。。。)

接下来说说我用过的几个jetbrains的产品吧

前奏

jetbrains家的产品很多需要付费的,学生可以用教育邮箱免费使用。(网上也有各种破解版和注册码,这就只能看大家各显神通啦

设置中文

由于官方没有提供中文,所以需要去github下载汉化包

image-20201207161456048

选择对应IDE的汉化包,放在IDE的安装目录lib目录下,例如“D:\Program Files\JetBrains\CLion 2020.3\lib”,重新打开软件即可。

(不过汉化包挺久没更新了,有很多还是没汉化的,所以,英语还是非常重要滴)

设置背景图片

1.打开IDE,按两次shift输入set background image或者用快捷键Ctrl+shift+A,选择set background image

20201208090651

2.进入如下页面

image-20201208090845087

Opacity:透明度。

1.Pycharm

官方文档

2.Intellij

官方文档

导入jar包

1.在文件->项目结构

image-20201207161016548

2.选择Modules

image-20201207161305922

3.点击右边的加号“+”,选择第一个,导入目录和一个jar文件都可,最后点确定。

20201207162334

(directories:目录,Scope: 范围,Compile: 编译,Provided: 提供.)

3.CLion

官方文档

4.Rider

官方文档

5.DataGrip

官方文档

参考链接:

概述

SQLite是一个进程内库,它实现了一个自足的、无服务器的、零配置的、事务性的SQL数据库引擎。SQLite的代码在公共领域,因此可以免费用于任何目的,无论是商业还是私人。SQLite是世界上部署最广泛的数据库,其应用数量之多,我们无法统计,其中包括几个备受瞩目的项目。

(这个数据库非常小,只有几百kb,且易于安装,非常适合练习和做小项目使用)

1.安装SQLite

官方页面下载SQLite,需要下载 sqlite-tools-win32-*.zipsqlite-dll-win32-*.zip 压缩文件。(dll的64位也可以用,推荐下载64位)

20201208085105

博主现在使用的是3.34的版本

并在此文件夹下解压上面两个压缩文件,将得到 sqlite3.def、sqlite3.dll 和 sqlite3.exe 文件,把对应文件的位置添加到环境变量Path。

打开cmd,使用 sqlite3 命令,将显示如下结果

image-20201208085422762

注:红色的英文的意思是:连接到一个内存中的临时数据库

2.连接SQLite

在 Java 程序中使用 SQLite 之前,我们需要确保机器上已经有 SQLite JDBC Driver 驱动程序和 Java。可以查看 Java 教程了解如何在计算机上安装 Java。现在,我们来看看如何在机器上安装 SQLite JDBC 驱动程序。

下载对应版本的jar,并把它添加到对应IDE的jar路径,如果是intellij可以参考这篇文档,如果是使用cmd的可以参考此教程

jar下载链接

新建Sample.java,输入如下代码

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class Sample
{
  public static void main(String[] args)
  {
    Connection connection = null;
    try
    {
      // create a database connection
      connection = DriverManager.getConnection("jdbc:sqlite:sample.db");
      Statement statement = connection.createStatement();
      statement.setQueryTimeout(30);  // set timeout to 30 sec.

      statement.executeUpdate("drop table if exists person");
      statement.executeUpdate("create table person (id integer, name string)");
      statement.executeUpdate("insert into person values(1, 'leo')");
      statement.executeUpdate("insert into person values(2, 'yui')");
      ResultSet rs = statement.executeQuery("select * from person");
      while(rs.next())
      {
        // read the result set
        System.out.println("name = " + rs.getString("name"));
        System.out.println("id = " + rs.getInt("id"));
      }
    }
    catch(SQLException e)
    {
      // if the error message is "out of memory",
      // it probably means no database file is found
      System.err.println(e.getMessage());
    }
    finally
    {
      try
      {
        if(connection != null)
          connection.close();
      }
      catch(SQLException e)
      {
        // connection close failed.
        System.err.println(e.getMessage());
      }
    }
  }
}

运行后,如果有如下输出,这说明配置成功:

image-20201208091432594

参考链接:

菜鸟

Emby概述

为了照顾小白,我就从最基础的开始吧。

首先,我们需要知道Emby是什么?

简单的说,Emby是观看家庭影视资源的流媒体服务器,我们可以通过安装Emby到自己的NAS/VPS/电脑中,在任意的地方观看视频。(需要有公网IP,不然只能在局域网观看)

具体功能有:

image-20201128194320062

还有个比较重要的信息是Emby目前支持的视频格式:

使用后的效果如图:

未搜刮的:

搜刮后:

image-20201128201212504

看起来是非常不错和舒服的,如果是NAS用户在局域网观看

同类型的软件有:

Jellyfin(类似于Emby,是一个免费的开源程序。和Emby相比最大的优点是支持硬解)

20201128195125

Plex(由于服务器在国外,首次连接时间较长,)

效果如图:

20201128194958 20201128201713

Infuse(仅支持ios,功能也极其强大,ios用户的首选,不过传闻Emby的ios似乎对不太友好,不过我没用过ios,具体情况就不清楚了)

效果如图:

20201128202120

有兴趣了解其中的不同的朋友可以看看这个英文的文章《Plex vs. Emby vs. Jellyfin — which is the best option?

最后,给出Emby官方链接

Emby入门

注:博主主要是用的是VPS挂载gd(因为没钱买nas.jpg),所以待会主要讲的是linux的搭建技巧,windows/NAS那些只会稍微提及,有需求的小伙伴建议自行谷歌/百度,或者看看推荐阅读那里有没有符合自己需求的文章。

安装

建议:结合官方文档食用本教程,味道更佳哦!

下面给出一些常见系统的安装指南:

Windows下安装:

下载地址:安装版 便携版(推荐)

Linux下安装

推荐阅读:

题目 介绍

一些槽点

Emby的搜索功能

Emby进阶

使用CDN进行加速

一般是用的免费CDN是:cloudfare,国内合作商:

缺点:对视频播放速度加速效果不明显,封面加速效果较明显

(如果土豪,请随意腾讯或百度等国内大厂的cdn)

推荐的插件

推荐阅读

题目 介绍
利用预刮削和自动整理快速更新EMBY动画库
EMBY媒体库推荐抓取设置

可能会遇到的问题

1.不兼容的流

如图