Contents

杂记

Q: source map 是什么?如何使用?

A: source map 一个保存转换前、后文件内容位置对应信息的 json 格式的文件。位置关系保存在 mapping 字段的值中,通过这个值和特定的算法可以从转化后的文件中的任意位置找到源文件中与之对应的位置,源文件内容存在于 sourcesContent 字段中。浏览器通过处理后的文件结尾的 //# sourceMappingURL=url 来加载对应的 map 文件,进而借之还原转换前的 js 文件,以便为开发者提供友好的调试体验。url 可以是一个指向某处 map 文件的资源连接,也可以是一个由对应内容转换的 base64 链接。像 rollup、webpack、esbuild 等 bundler 工具都支持 inline(对应打包后文件末尾注释的 base64) 和 external(对应独立的 map 文件)的生成方式。当一个支持 source map 的浏览器加载 map 文件后,当打包后文件报错时,浏览器可以从打包文件中的报错位置定位到源文件中对应的位置。

Q: CSP(Content Security Policy) 是什么?

A:

  • CSP 是一系列检测(上报)和减轻特定类型的 Web 攻击(主要是 XSS)的策略,通过配置返回 html 文件时的 Content-Security-Policy: ${policy-token} header 或在返回的 html 文件中设置 <meta http-equiv="Content-Security-Policy" content="${policy-token}"> 来生效
  • 上述 policy-token 由一系列 directives 组成,每个 directives 通过 ; 分隔,directives 又由为 directive-name 和 directive-value 组成,ANBF 定义如下:
policy-token    = [ directive-token *( ";" [ directive-token ] ) ]
directive-token = *WSP [ directive-name [ WSP directive-value ] ]
directive-name  = 1*( ALPHA / DIGIT / "-" )
directive-value = *( WSP / <VCHAR except ";" and ","> )
  • 不支持 CSP 的浏览器默认使用同源策略。对于 web server 不返回 CSP header 的情况,浏览器也默认使用同源策略

  • CSP 主要通过为浏览器指定从特定的域加载的 script 是有效可执行的,使得服务器管理员可以减少或消除 XSS 的发生。兼容 CSP 的浏览器只会在从允许域收到的源文件中加载的脚本执行,忽略所有其他脚本(包括内联脚本和事件处理 HTML 属性),除了限定加载特定资源的域以外,CSP 还可以限定加载的协议比如,https:、blob: 等

  • 常见的限定资源加载方式的 directive-name:

    • default-src
    • font-src
    • script-src
    • style-src
    • img-src
    • media-src
    • connect-src
    • object-src
    • child-src
    • manifest-src (CSP v3 引入)
    • worker-src (CSP v3 引入)
    • frame-src (CSP v3 引入)
  • 常见的 directive-value:

    • ‘none’
    • ‘self’
    • 某个特定的 host 值,比如 http://example.com
    • 某个特定的协议值,比如 https:、blob:
    • ‘unsafe-inline’
    • ‘unsafe-eval’
  • default-src 的值为所有其它未显式指定的 *-src 的默认值,若某个 *-src 显示指定值后,即覆盖 default-src 给它提供的默认值

  • 违反 policy 则上报的功能通过 report-uri directive 配置一个能处理上报的 json 数据的地址来开启,当发生违反配置的 policy 加载资源时,相关的错误信息会被以 json 的格式 POST 到处理地址

  • 当 CSP 是通过给 html 配置 meta 标签的方式来开启时,report-uri 被忽略

  • 可以通过设置 script-src 和 object-src 值排除 ‘unsafe-inline’ 和 data: 来保护 web 应用免受 XSS 攻击

  • CSP 不应该被视为抵御 XSS 的第一道防线,第一道防线应该是 web server 谨慎的处理它们的输入和输出

  • 详情:

Q: data:blob: 是什么?

A:

  • data: 是一种行内引入「小」资源的 scheme,资源的内容就包含在 url 本身,无需再外部加载其他资源,其定义为 data:[<mediatype>][;base64],<data>
  • blob: 一种 scheme,其对应的 url 称 Blob URLs,是一种只能由浏览器内部通过 URL.createObjectURL 方法创建的 url,这个 url 指向某个 Blob(Binary large object)对象或者 File 对象,url 只在当前 session 中有效。url 可以作为 img 标签的 src 以用来展示图片、下载链接(前提是下载时对应的 Blob 对象未被 URL.revokeObjectURL 释放)等

cjs 和 esm 相关

Q: 经常从某个库里导入的代码 log 中看到 __esModule: true 的信息,这是做什么的?

A: 这是 babel 将 esm 转换为 cjs 时添加的,以便与并非由 esm 转换而来的原始 cjs 的作区分。因为 cjs 无所谓什么 default export 和 named export 只分,所有的 import 都是 module.exports 的属性,babel 将 esm 转换为 cjs 时会为 module.exports 添加一个 __esModule: true 的属性,同时将此 esm 的 default export 转化为 module.exports['default'] 属性。如此一来,当通过 webpack 等工具打包 js 时,若某个 cjs 通过 const m = require('xx') 引用了另一个 cjs,我们便可以通过 __esModule 属性来决定我们要的 mrequire('xx') 还是 require('xx')['default'],这通过下面的方法完成:

function _interopRequireDefault(obj) {
  return obj && obj.__esModule ? obj : { default: obj }
}

// 然后对应的 m 就会是:
const m = _interopRequireDefault(require('xx'))['default']

凡是支持将 esm 转译(打包)成 cjs 的 bundler 工具都有类似的机制,比如 rollup 为了兼容 babel 也会为转化后的 cjs 添加 __esModule: true,但它自身用的是:

function _interopDefaultLegacy(e) {
  return e && typeof e === 'object' && 'default' in e ? e : { default: e }
}

Q: tree-shaking 的意义?

A: 它只在使用 rollup 和 webpack 等工具为浏览器端将开发中各个 module 打包成一个 bundle 的时候才有意义,而且其意义并不如其名声那般大,因为当你 bundle 时使用 tree-shaking 跟不使用时差别很大的话,那往往意味着你的应用的代码,或者你依赖的库的代码该优化了。

Q: cjs 和 esm 在 node 中的互操作性?

A:

  • cjs import cjs
  • cjs import esm ❌(原因之一是 esm 支持 Top-level await,但 cjs 不支持)
  • esm import esm
  • esm import cjs(当 cjs 导出语句是 module.exports = { a: 1 } 时,esm 无法通过 import { a } from 'cjs' 导入 a

Q: cjs 和 esm 有区别的地方? A:

  • cjs 动态加载,esm 动态加载
  • 通过 const obj = require('./module.js') 导入的 obj 是 module.js 中 module.exports 的一个引用,通过 obj.xx 修改对应的值会影响原来的值,而 esm 不允许修改 import 对象的属性
  • 对各个 bundler 工具的 tree-shaking 来讲,esm 更友好