0%

RSC(React Server Component)
ISR(Incremental Static Regeneration)

Server Component

可以在服务器上渲染和缓存的 UI, 在 next.js 中, 渲染工作进一步按路由段划分, 以实现流式和部分渲染

  • 数据获取, 将数据获取移动到更靠近数据源的服务器上, 可以减少获取渲染所需数据的时间以及客户端需要发出的请求数量来提高性能
  • 安全性, 在服务器上保留敏感数据和逻辑, 例如 token 和 API keys, 而不会将它们暴露给客户端
  • 缓存, 通过在服务器上渲染, 结果可以被缓存并在后续请求和跨用户中重用, 减少每个请求的渲染和数据获取量
  • 性能, 减少所需的客户端 javascript 的数量, 对于弱网环境或设备较弱的用户来说需要下载、解析和执行的客户端 javascript 较少
  • 初始化页面加载和首次内容绘制(FCP), 在服务器上生成 HTML, 允许用户立即查看页面而无需等客户端下载、解析和执行渲染页面所需要的 javascript
  • SEO 和 社交网络共享(SNS), 渲染的 HTML 可供搜索引擎机器人用来检索页面, 社交网络机器人可用于为页面生成社交卡预览
  • 流式传输, 服务器组件允许将渲染工作分成块, 并在准备就绪时将其流式传输到客户端. 这允许用户提前查看页面的部分内容, 而无需等待整个页面在服务器上渲染

渲染策略

  • Static Rendering, 路由在构建时渲染, 或在数据重新验证后在后台渲染, 结果被缓存. 以优化用户和服务器请求之间共享渲染的结果
  • Dynamic Redering, 在请求时渲染路由, 针对个性化的数据或只有在请求时才知道的信息
  • Streaming, 从服务器逐步渲染 UI, 并在准备就绪时流式传输给客户端, 允许用户在整个内容完整渲染之前立即看到页面的部分内容.
    有助于提高初始化页面加载性能, 以及依赖于较慢数据获取的 UI. 可以使用 Suspense 组件和 loading.tsx 开启

渲染流程:

  1. 按单个路由段和 Suspense Boundaries 拆分块
  2. 每个块使用 RSC Payload 和 客户端组件 JavaScript 指令渲染 HTML, 然后返回给客户端
  3. 客户端立即显示路由的快速非交互式页面预览, 这仅适用于初始化页面加载
  4. RSC Payload 用于协调客户端和 RSC 树并更新 DOM, JavaScript 指令用于 hydrate 客户端组件并使应用程序具有交互性

RSC Payload

渲染 RSC 树的紧凑二进制表示形式, 包含

  • RSC 的渲染结果
  • 客户端组件应渲染的占位符及其 JavaScript 文件的引用
  • 从 RSC 传递给客户端组件的任何信息

Server Action

是一个在服务器上执行的异步函数, 它们可以在服务器和客户端组件之间调用, 以处理 Next.js 应用程序中的表单和提交和数据突变

  • 不限于 <form>, 可以从 Event Handlers、useEffect、第三方库和其他表单元素 <button> 调用
  • 与 Next.js cache 和 revalidate 集成, 当调用一个 action 时, Next.js 可以在单个服务器往返中返回更新的 UI 和新数据
  • 使用 HTTP 的 POST 方法调用
  • 接收的参数 和 返回值 必须可由 React 序列化
  • 可以在应用程序的任何地方重复使用
  • 从其使用的 layout 和 page 继承 运行时
  • 从其使用的 layout 和 page 继承路由段设置, 包含 maxDuration 等字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// app/invoices/page.tsx
'use client';
import React, { useActionState } from 'react'
import { createInvoice } from './action'

export type InitialStateType = { message: string }

const initialState: InitialStateType = { message: ''}
export default function Page() {
const [state, formAction, pending] = useActionState(createInvoice, initialState)

return (
<form action={formAction}>
<input type="text" name="customerId" className='border-1 border-blue-300 rounded-2xl py-2 px-4 my-4' placeholder='customer id' /><br />
<p className='text-red-500 text-xl'>{state?.message}</p>
<input type="text" name="amount" className='border-1 border-blue-300 rounded-2xl py-2 px-4 my-4' placeholder='amount' /><br />
<input type="checkbox" name="status" className='w-4 h-6 border-blue-300 border-1 rounded-2xl' /><br />
<button type="submit" disabled={pending} className='border-1 border-blue-300 rounded-2xl py-2 px-4 text-2xl text-gray-600 enabled:hover:border-blue-700 enabled:hover:text-white cursor-pointer'>Submit</button>
<br />
</form>
)
}

// app/invoices/action.ts
'use server';
import { redirect } from "next/navigation";
import type { InitialStateType } from './page'

export async function createInvoice(prevState: InitialStateType, formData: FormData) {
const customerId = formData.get('customerId');
const amount = formData.get('amount');
const status = formData.get('status');
console.log(customerId, amount, status);
// mutate data
// revaliate cache
await new Promise((resolve) => setTimeout(resolve, 3000));
// 如果 customerId 不符合条件则返回提示信息
if (!customerId || Number(customerId) > 100) {
return { ...prevState, message: 'Please enter a valid customer ID' };
}
redirect('/');
}
阅读全文 »

Redux

官方推荐使用封装了 Redux 核心的 @reduxjs/toolkit(RTK) 包, 包含了构建 Redux 应用所必须的 API 方法和常用依赖, 简化了大部分 Redux 任务, 阻止了常见错误, 并让编写 Redux 应用程序变得更容易

@reduxjs/toolkit/query

独立可选的入口, 允许定义端点(REST, GraphQL或任何异步函数)并生成 reducer 和中间件来完整管理数据获取, 加载状态更新和结果缓存, 还可以自动生成 React Hooks, 可用于组件获取数据

@reduxjs/toolkit(RTK)

  • 通过单一清晰的函数调用简化 store 设置, 同时保留完全配置 store 选项的能力
  • 消除意外的 mutations
  • 消除手写任何 actionCreator 或 actionType 的需求
  • 消除编写容易出错的手动不可变更新逻辑的需求, createSlice 使用 Immer 库来编写 reducer, 可以直接修改 state 状态而不需要使用解构语法
  • 允许将相关的代码放在一个文件中, 而不是分布在多个独立文件中
  • 提供优秀的 TypeScript 支持, 其 API 被设计成很好的安全性, 同时减少代码中需要定义的类型数量
  • RTK Query 可以消除编写任何 thunk, reducer, actionCreator 或者副作用狗子来管理数据获取和跟踪加载状态的需求

configureStore

特点

  • slice reducers 自动传递给 combineReducers
  • 自动添加了 redux-thunk 中间件
  • 添加了 Devtools 中间件来捕获更多意外的变更
  • 自动设置了 Redux Devtools Extension
  • 中间件和 Devtools 增强器被组合在一起添加到了 store 中
阅读全文 »

React Router

React Router v7 示例

工作模式

  • Declareative 声明式, 使用路由组件匹配 url, 导航路由
  • Data 在 React rendering 外配置路由, 支持 data APIs
  • Framework 包含 Data mode 模式的全栈框架
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// npx create-react-router@latest my-react-router-app
// Declareative
createRoot(root).render(
<BrowserRouter>
<Routes>
<Route path="/" element={ <Root /> } />
</Routes>
</BrowserRouter>
);

// Data
const router = createBrowserRouter([
{path: '/', Component: Root, loader: rootLoader}
]);
createRoot(root).render(<RouterProvider router={router}/>);

// Framework
// routes.ts
export default [
index('./home.tsx'), // 默认渲染组件
route('about', './about.tsx'),
route('dashboard', './dashboard.tsx', [ // 嵌套路由, 添加路由段
index('./home.tsx'),
route('settings', './setting.tsx')
]),
layout('./auth/layout.tsx', [ // 使用 布局, 但不会添加路由段
route('login', './auth/login.tsx'),
route('register', './auth/register.tsx')
]),
...prefix("projects", [ // 路由前缀
index("./projects/home.tsx"),
layout("./projects/project-layout.tsx", [
route(":pid", "./projects/project.tsx"),
route(":pid/edit", "./projects/edit-project.tsx"),
]),
])
]
  • 自动将 loaderData, actionData, params, matches 作为 props 传递给组件
1
2
3
4
5
6
7
8
9
10
11
12
export async function loader(){} // 路由组件渲染之前执行
export async function clientLoader({params}){} // 客户端获取数据
export async function action(){}
export async function clientAction(){}
export function ErrorBoundary(){} // 路由发生错误时渲染
export function headers(){} // 定义响应头
export function links(){} // 定义页面 head 中的 <link> 元素信息
export function meta(){} // 定义页面 head 中的 meta 信息
// 允许在 useMatches 中向 路由 匹配 中添加任意内容, 像面包屑
export const handle = {}
// 是否允许弹出路由重新验证不影响其数据的操作, 默认所有路由在操作后都会重新验证
export function shouldRevalidate(){}

以下部分为 React Router v6

阅读全文 »

React

React 18.3.1

设计思想

  • 变换, 设计 React 的核心前提是认为 UI 只是把数据通过映射关系变换成另一种形式的数据。同样的输入必会有同样的输出。这恰好就是纯函数。
  • 抽象, 需要把 UI 抽象成多个隐藏内部细节,又可复用的函数。通过在一个函数中调用另一个函数来实现复杂的 UI
  • 组合, 将两个或者多个不同的抽象通过组合再次抽象成一个抽象

不变性, 不直接操作数据源, 副本替换

  • 可以保持以前版本的数据完好无损, 并在以后重用它们
  • 使组件比较其数据是否已更改的成本非常低, 提高子组件的渲染性能

状态

  • 只有当在组件树中 相同的 位置渲染 相同的 组件时, React 才会一直保留着组件的 state

  • 在一般安全的情况下采用批处理方式处理 state 更新

  • 使用不可变的数据模型, 把可以改变 state 的函数串联起来作为原点放置在顶层

  • 状态不存在于组件内, 状态是由 React 保存的, React 通过组件在渲染树中的位置将它保存的每个状态与正确的组件关联起来

    • 这个变量是否通过 props 从父组件中获取,如果是,则不是一个状态
    • 这个变量是否在组件的整个生命周期中都保持不变,如果是,则不是一个状态
    • 这个变量是否可以通过其他状态(state)或者属性(props)计算得到,如果是,则不是一个状态
    • 这个变量是否在组件的 render 方法中使用,如果不在,则不是一个状态

不能直接声明 async 函数式组件

  • 返回类型不匹配, React 组件需要返回 React 元素(JSX), 而 async 函数默认返回一个 Promise, 导致 React 无法正确渲染组件
  • 生命周期和状态管理, 异步操作通常涉及到副作用, 这些不应该在组件的渲染阶段进行, 它们应该在组件的生命周期方法或者特定的钩子中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// state
let count = 0;
let prevCount = count;
// events
window.addEventListener("click", () => { count++ }, false);
// render
const render = () => {
document.body.innerHTML = count;
}
// diff
const reconcile = () => {
if (prevCount !== count) {
render();
prevCount = count;
}
}
// scheduler
const workloop = () => {
reconcile()
// window.scheduler.postTask(() => { //... })
// 为什么 react 官方不使用这个 API
// 因为 requestIdleCallback, scheduler.postTack 无法满足优先级调用,
// 有了优先级, 就可以知道当下什么任务最紧急
window.requestIdleCallback(() => {
workloop();
})
}
workloop();

Actions

React 19 支持

一个异步函数, 执行数据变更然后响应更新状态, useTransition, useActionState, useOptimistic

阅读全文 »

敲黑板

隐私协议

  • 2.32.3 开始支持

  • 2023-09-15 之后, 隐私相关功能默认开始, 对于未声明的处理用户信息的接口或组件直接禁用

  • wx.getPrivacySetting 查询微信侧记录的用户是否有待同意的隐私政策信息, 通过返回结果 res 中的 needAuthorization 字段获取, true 表示还没同意过

  • wx.openPrivacyContract 此接口打开 wx.getPrivacySetting 获取到的开发者在小程序管理后台配置的 《小程序用户隐私保护指引》名称信息的页面

  • wx.requirePrivacyAuthorize 模拟隐私接口调用, 并触发隐私弹窗逻辑

  • wx.onNeedPrivacyAuthorization 监听隐私接口需要用户授权事件, 当需要用户进行隐私授权时会触发. 触发事件时, 开发者需要弹出隐私协议说明,
    并在用户同意或拒绝授权后调用回调接口 resolve 触发原隐私接口或组件继续执行.

    • resolve 是一个函数, 主动调用触发原隐私接口或组件继续执行
    • eventInfo 表示触发本次事件的关联信息
1
2
3
4
5
6
7
8
<!-- 用户触发此组件时, 微信会同步收到同意信息 -->
<!-- 事件回调表示用户已同意隐私政策后的处理逻辑 -->
<button
open-type="agreePrivacyAuthorization"
bind:agreeprivacyauthorization="handleAgreePrivacyAuthorization"
>
同意
</button>
阅读全文 »

敲黑板

  • 开启 charles 无法代理 https, 检查电脑和手机上的 charles 证书是否过期

电脑删除证书

  1. 使用 win + r 键调起系统运行窗口, 在输入框中输入 mmc 命令后回车
    charles-11
  2. 在弹出的对话框选择 “文件” -> “添加或删除管理单元”
    charles-12
阅读全文 »

@babel/cli

install

1
npm i -D @babel/cli @babel/core @babel/preset-env

运行

参数

  • --watch | -w 监听文件改变自动编译
  • --out-file | -o 输出指定文件名
  • --out-dir | -d 编译整个目录
    • 编译目录下所有文件输出合并为一个文件
1
2
3
4
5
npx babel index.js -w # 编译并监听 index.js

npx babel index.js -o index.min.js # 编译 index.js 文件输出到 index.min.js

npx babel src -d dist # 编译 src 目录下文件输出到 dist 下
阅读全文 »

全局 API

全局 API 应用实例

  • Vue.config -> app.config
  • Vue.config.productionTip -> 移除
  • Vue.config.ignoreElements -> app.config.compilerOptions.isCustomElement
  • Vue.component -> app.component
  • Vue.directive -> app.directive
  • Vue.mixin -> app.mixin
  • Vue.use -> app.use
  • Vue.prototype -> app.config.globalProperties
  • Vue.extend -> 移除

全局 API Treeshaking

  • Vue.nextTick() -> nextTick()
  • Vue.observable -> reactive()
  • Vue.version -> version
  • Vue.compile(仅完整构建版本)
  • Vue.set(仅兼容构建版本)
  • Vue.delete(仅兼容构建版本)

模板指令

v-model

参见 vue3.md 的 v-model 指令部分

key 使用改变

对于 v-if, v-else-if, v-else 的各分支项 key 将不再是必须的, Vue 会自动生成唯一的 key

v-if 和 v-for 优先级

两者作用于同一个元素上时,v-if 会拥有比 v-for 更高的优先级

v-bind 合并行为

v-bind 的绑定顺序会影响渲染结果

  • vue 2.x 独立绑定的 attribute 会覆盖 v-bind 中的 attribute
  • vue 3.x 根据声明顺序决定如何被合并
1
2
3
4
5
6
7
8
9
<!-- 模板 -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- 结果 -->
<div id="red"></div>

<!-- 模板 -->
<div v-bind="{ id: 'blue' }" id="red"></div>
<!-- 结果 -->
<div id="red"></div>
阅读全文 »

状态码

  • 100 Continue 请求继续

  • 101 Switching Protocols 协议切换,通过 upgrade 消息头切换协议

  • 102 Processing 继续执行

  • 200 OK 请求成功

  • 204 No Content 没有响应实体

  • 206 Partial Content 服务器处理部分 GET 请求

  • 301 Moved Permanently 资源永久移动

  • 302 Move Temporarily 资源临时移动

  • 303 See Other 参见其他

  • 304 Not Modified 资源没有改动

  • 305 Use Proxy 使用代理

  • 307 Temporary Redirect 资源临时从不同的 URI 响应

  • 308 Permanent Redirect 永久重定向

  • 400 Bad Request 错误的请求

  • 401 Unauthorized 需要验证

  • 403 Forbidden 服务器拒绝执行

  • 404 Not Found 资源未找到

  • 405 Method Not Allowed 请求方法不能用于相应资源

  • 408 Request Timeout 请求超时

  • 413 Request Entity Too Large 请求体超长

  • 415 Unsupported Media Type 不支持的媒体类型

  • 500 Internal Server Error 服务器错误

  • 502 Bad Gateway 网关错误

  • 504 Gateway Timeout 网关超时

  • 505 HTTP Version Not Supported 服务器不支持的使用的 HTTP 版本

URL 编码常用

  • %21 !
  • %22 “
  • %23 #
  • %24 $
  • %25 %
  • %26 &
  • %27 ‘
  • %28 (
  • %29 )
  • %2F /
  • %30-9 0..9
  • %3A :
  • %3B ;
  • %3C <
  • %3D =
  • %3E >
  • %3F ?
  • %40 @
  • %41 A

HSTS

HTTP Strict-Transport-Security, HTTP 严格传输安全 是一种安全策略机制, 旨在强制客户端仅通过 HTTPS 协议与服务器进行通信, 并且在未来的一段时间内, 所有对该站点的请求都必须使用 HTTPS.

通过在 HTTP 响应头中包含一个特殊的指令: Strict-Transport-Security 来实现.

浏览器接收到这个响应头, 会将该站点标记为 HSTS 站点, 并在指定的时间内自动将所有的 HTTP 请求转换为 HTTPS 请求。即使是手动输入的 http url 或者点击的一个 http 链接也会被自动升级为 HTTPS 请求.

部分主流浏览器维护了一个预加载的 HSTS 站点列表, 这些站点默认被强制使用 HTTPS, 无需等待首次访问后的响应头设置.

1
Strict-Transport-Security: max-age=<expire-time>; includeSubDomains; preload
  • max-age, 浏览器记录的只能使用 HTTPS 访问站点的最大时间(单位秒)
  • includeSubDomains, 可选, 表示该策略适用于主域名及其所有子域名
  • preload, 可选, 表示该站点希望被列入浏览器的 HSTS 预加载列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ curl -I https://www.douyin.com
HTTP/1.1 404 Not Found
Server: Tengine
Content-Type: text/plain; charset=utf-8
Connection: keep-alive
Date: Wed, 11 Dec 2024 09:11:02 GMT
cache-control: no-store
Vary: Accept-Encoding, Accept-Encoding
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
X-Download-Options: noopen
X-Content-Type-Options: nosniff
content-security-policy: upgrade-insecure-requests ;frame-ancestors https://pc.xgo.bytedance.net https://tcs.bytedance.net https://*.douyin.com https://aidp.bytedance.com https://aidp.bytedance.net https://www.aidp-cqc.com;script-src 'report-sample' 'strict-dynamic' 'nonce-4QE2wZ2rc-ztbxrA3IWHy' 'wasm-unsafe-eval' 'unsafe-eval' 'self' *.bytedance.com *.bytedance.net *.pstatp.com *.bytednsdoc.com *.bytescm.com *.douyin.com *.bytegoofy.com *.snssdk.com *.byted-static.com *.huoshanstatic.com *.douyinstatic.com *.ibytedapm.com *.zijieapi.com *.bytetos.com *.yhgfb-cn-static.com *.byteimg.com;report-uri https://i.snssdk.com/log/sentry/v2/api/slardar/main/?ev_type=csp&bid=douyin_web;report-to main-endpoint
Reporting-Endpoints: main-endpoint="https://mon.zijieapi.com/monitor_browser/collect/batch/security/?bid=douyin_web", default="https://mon.zijieapi.com/monitor_browser/collect/batch/security/?bid=douyin_web"
X-Tt-Logid: 20241211171102BFBC2AC0A24F4C05B24E
X-Agw-Info: N1yG692xcEzUcPRkqur4q1w8MlUT0hGFq0kNWMMV07yBP0rl8Ro_MQ8j_XpZ93UiGF_yYjjOLU0-LJkSol7AQbN5g-cwiWw79Mh3Nn_6upuBUoABSTZXiw5xsf5QXhFwSLwtY4xBUmCBdBQIjt7NUQ==
Server-Timing: inner; dur=276,tt_agw; dur=264
Set-Cookie: ttwid=1%7Ct5T1yUApy2aoqf8tihUlKDs27fKNe5akRSMvf5zx5nk%7C1733908262%7C209a31899a105d3c878e20feea33411d21a34e256860ec7250eec309baab71e4; Domain=.douyin.com; Path=/; Expires=Sat, 06 Dec 2025 09:11:02 GMT; HttpOnly
Set-Cookie: UIFID_TEMP=92559570181449f7274d01beb9fdcc50ede300fee49b9150f96a9b11ec20d282f33b1ce26fee42ba9461631433a50fa5108eb52097c37c530c81c602f7edb85602265a215753110a5ddc46f4e991edf4; path=/; expires=Sat, 25 Apr 2026 09:11:02 GMT; domain=douyin.com; samesite=none; secure
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Access-Control-Allow-Credentials: true
x-tt-trace-host: 011ff03de9066d57c184f3d11219de2a9150664283b7d9b81d900f399ddf465413a8c909a66ed35f5f7f27db6607d80ca8d1103636a7e5a90d5b92105a23ba0862ae5cdac345035fa6422cb605c9b9dddb594f35c29470ce5c159141f9bdd32f9a
x-tt-trace-tag: id=03;cdn-cache=miss;type=dyn
server-timing: cdn-cache;desc=MISS,edge;dur=0,origin;dur=303
x-alicdn-da-ups-status: endOs,0,404
Via: live5.cn2073[303,0]
Timing-Allow-Origin: *
EagleId: 3db6831717339082625713776e

CSP

内容安全策略 是一个额外的安全层, 用于检测并消弱某些特定类型的攻击, 包括 跨站脚本(XSS) 和数据注入攻击等. 它通过定义哪些资源可以被加载和执行来限制网页的攻击面.

CSP 通过 HTTP 响应头或 HTML <meta> 标签传递给浏览器, 浏览器接收到这些策略后根据规则阻止不符合条件的资源加载或执行.

1
2
3
4
Content-Security-Policy: default-src 'self'; img-src *; 
media-src media1.com media2.com;
script-src userscripts.example.com;
report-uri /_/csp_reports
1
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';" />
  • default-src, 默认策略, 适用于所有未明确指定的资源类型, 如果指定了其他指令将覆盖此值
  • script-src, 允许加载的 js 源, 可以指定域名、协议或路径, 或者使用关键字 self,unsafe-inline,unsafe-eval
  • style-src, 允许加载的 CSS 样式表源
  • img-src, 允许加载的图像源
  • connect-src, 允许通过脚本接口加载的 URL
  • font-src, 允许加载的字体源
  • object-src, 允许加载的插件对象源, 如 flash
  • frame-src, 允许嵌入的框架源
  • media-src, 允许加载的音频和视频源
  • child-src, (使用 frame-src 和 worker-src 代替)允许加载的子资源, 如 iframe 或 worker
  • form-action, 允许提交表单的目的URL
  • base-uri, 允许使用的 <base> 标签URI
  • sandbox, 启用类似沙盒的限制
  • prefetch-src, 允许预加载或预渲染的资源源
  • report-uri, 指定违反 CSP 时发送报告的 URL
  • report-to, 指定一个或多个组名, 用于接收 CSP 违规报告, 需要配合 Report-To HTTP 头使用

CSP 仅报告模式, 对任何违规行为将会报告一个指定的 URI 地址, 但不具有强制性

1
2
3
4
5
Content-Security-Policy-Report-Only: default-src 'self'; 
img-src *;
media-src media1.com media2.com;
script-src userscripts.example.com;
report-uri /_/csp-reports

HTTP cache mode

  • force-cache, 自己先在缓存中查找资源, 如果有不管是否过期直接返回
  • default, 自己先在缓存中查找资源, 然后验证资源是否过期, 如果过期再询问服务器资源是否过期
  • no-cache, 自己先在缓存中查找资源, 然后再询问服务器资源是否过期
  • reload, 不查看缓存, 直接从服务器获取资源, 然后使用下载的资源更新缓存
  • no-store, 不查看缓存, 直接从服务器获取资源, 并且不会更新缓存资源

MutationObserver

MutationObserver 接口提供监视对 DOM 树所做更改的能力, 用于替代 Mutation Events 的新 API, 与 Events 不同的是: 事件是同步触发, 即 DOM 发生变动会立刻触发相应事件, MutationObserver 则是异步触发, DOM 发生变动以后并不会马上触发, 而是要等到当前所有 DOM 操作都结束后才触发, 所有监听操作以及相应的处理都是在其他任务执行完成之后异步执行的, 并且在 DOM 更改触发之后,将更改记录存储在数组之中, 统一进行回调通知

构造函数

创建并返回一个新的 MutationObserver 实例, 会在指定的 DOM 发生变化时被调用

  • 参数 callback

    当被指定的节点或子树以及配置项有 Dom 变化时会被调用, 回调函数有两个参数:

    • MutationRecord 描述所有被触发改动的记录对象数组
    • MutationObserver 调用该函数的 MutationObserver 对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 创建一个观察器并传入回调函数
const observer = new MutationObserver(function (MutationRecord, observer) {
console.log(MutationRecord, observer);
// [{
// addedNodes: NodeList []
// attributeName: ""
// attributeNamespace: null
// nextSibling: null
// oldValue: ""
// previousSibling: null
// removedNodes: NodeList []
// target:
// type: "attributes"
// }]
});
// 指定观察变动的 DOM 节点和配置项
observer.observe(document.querySelector('#someElement'), {
subtree: true,
childList: true,
attributes: true,
});
// 停止观察器
observer.disconnect();
  • 返回值 MutationObserver 对象
阅读全文 »