@ decocms/compat-v7 (英语)
v7 的纯帮助站点移出 @decocms/blocks 7点到8点 每个都给出了v7的输出,字节表示字节,所以一个迁移的站点从这里导入,而不是保存自己的副本. 测试将每个帮助者标注在v7自己的代码上(最新7.x).
这不是一个v7运行时间。 没有 ctx,没有引用,没有请求范围,也没有缓存的加载器:v8没有它们,这个包没有带回它们. 它没有读取环境变量.
端点 : @decocms/blocks (同一个版本). 框架子路径还使用网站绑定: /tanstack 带 @decocms/tanstack 联合国 @tanstack/react-start 财务报告和审定财务报表 react, (中文(简体) ), /nextjs 带 @decocms/nextjs 联合国 next 财务报告和审定财务报表 react) (中文(简体) ). 这些是可选的同伴;一个网站只安装自己的.
类型
import type { ImageWidget, RichText, SectionProps } from "@decocms/compat-v7/types";
export interface Props {
image: ImageWidget;
body: RichText;
}
export const loader = async (props: Props, req: Request) => ({ ...props, url: req.url });
export default function Banner(props: SectionProps<typeof loader>) {
// ...
}
SectionProps<typeof loader>是装载器的返回值,等待。 v7 还被打开aPromise;对于同步加载器,v7给出了加载器自己的类型,在这里给出了值.Resolved<T>, (中文(简体) ),LoadingFallbackProps财务报告和审定财务报表Flag这是v7的.- 部件的别名( N)
ImageWidget, (中文(简体) ),VideoWidget, (中文(简体) ),HTMLWidget, (中文(简体) ),RichText, (中文(简体) ),TextArea, (中文(简体) ),Color, (中文(简体) ),TextWidget, (中文(简体) ),ButtonWidget, (中文(简体) ),Secret)为弦.deco schema读取它们的名字,所以一个字段会像在v7上所做的那样获得网站编辑器的部件.Secret是 v7 的被遮盖的文本框 : 一个在内容中保存的平面字符串, 所以将真实的证书保存在您的部署平台的秘密中, 由网站的代码读取并作为参数通过 .@decocms/compat-v7/types/widgets只有一个别名.
7号 AppContext 财务报告和审定财务报表 FnContext 不在这里,他们描述 ctx 参数,v8不会通过.
设备检测
import { detectDevice, isMobileUA } from "@decocms/compat-v7/device";
const device = detectDevice(request.headers.get("user-agent") ?? ""); // "mobile" | "tablet" | "desktop"
这些是v7的用户代理模式,在v7的顺序(tablet first)中检查. isMobileUA 电话和平板电脑都是真的.
内置脚本
import { inlineScript, useScript } from "@decocms/compat-v7/script";
<script {...inlineScript(`document.body.dataset.ready = "true"`)} />
<script dangerouslySetInnerHTML={{ __html: useScript(onLoad, "banner-1") }} />
useScript, (中文(简体) ), useScriptAsDataURI 财务报告和审定财务报表 inlineScript 给 v7 输出。 v7 在开发中警告 fn.toString(),因为服务器和客户端构建可以以不同的方式序列化一个函数并打破水分. 警告不见了,因为它读到 NODE_ENV,但建议仍然有效: inlineScript 带一弦.
链接
import { clx } from "@decocms/compat-v7/clx";
clx("btn", isActive && "btn-active", undefined); // "btn btn-active"
它会降低发作值,加入其余部分,并像 v7 那样崩溃白空间运行. 像v7,它不会修剪.
匹配者
import { type DeviceProps, device, type RandomProps, random } from "@decocms/compat-v7/matchers";
export const blocks = {
"website/matchers/device.ts": (props: DeviceProps) => device(props, matcherContext()),
"website/matchers/random.ts": (props: RandomProps) => random(props, matcherContext()),
};
device, (中文(简体) ), cookie, (中文(简体) ), host, (中文(简体) ), pathname, (中文(简体) ), queryString, (中文(简体) ), location, (中文(简体) ), multi 财务报告和审定财务报表 random 给v7的结果。 v8 块函数只获得其输入, 因此每个匹配者将请求作为其第二个参数: a Request,或 { request, url, flags }时 url 当请求是客户端导航服务器功能调用时, 是页面的 URL,以及 flags 对 A/B 决定,每个请求一个阵列. matcherContext() 然而,该站点符合所要满足的要求;框架的子路径提供了这种要求. always, (中文(简体) ), never 财务报告和审定财务报表 date 是v8内置,所以他们不在这里;v8's date 命名已保存块的运动 campaigns,并移动每个 v7 日期窗口.
random是 v7 的粘度随机( 上 7. x) :deco_segmentcookie),当流量变化时再次被绘制出. 饼干是按键测试的 组名, 已保存的匹配者块名一个v8区块不知道自己的名字 所以这个区块把它带进cohort; 随机规则内置使用website/matchers/random.ts和v7一样 改变流量份额时保持组别:重新命名让每个访客都参加新的测试. v7 – v8 迁移不注册: 它报告每个随机匹配者, 将移动到保存的块中experiments(类型)split-traffic)在同名下,所以访客保留他们的团体,装订者拥有deco_segment。不要运行两者在一个网站: 饼干的两位作者 撤销彼此的决定.- 页面解决后,
segmentSetCookie(request, flags)给Set-Cookie发送,或null当访客的饼干 已经说了同样的话.decideRandom返回一个决定与饼干。 不要缓存一个回应 设置给其他访客. multi获得规则的结果:v8先解决. 内在randomv7在每一个请求中都重新画出来.environment抛出:它读取没有 v8 软件包的流程环境。 登记自己的配对者的名字.location读取cf-*头,然后工人request.cf,然后是__cf_geo_*饼干,像v7一样.
区装填器
import { compose, runSectionLoaders, withDevice, withSearchParam } from "@decocms/compat-v7/sections";
const loaders = {
"site/sections/Product/ProductShelf.tsx": withDevice(),
"site/sections/Header/Header.tsx": compose(withDevice(), withSearchParam()),
"site/sections/Category/Banner.tsx": async (props, req) => ({ ...props, url: req.url }),
};
// After the page's sections resolved, once per page, globals included:
const { sections, degraded } = await runSectionLoaders(page.sections, { loaders, request, url: pageUrl });
runSectionLoaders 运行 v7's (props, req) 区域加载器 :
- 每个区段运行它的装载器,然后道具中的区段运行,无论它们在哪里(最晚为7.x). 其v7基线为7.77.12或更早通过的地点
nested: "direct":这些释放只运行直接以道具或列表道具保存的部分. - 一个装填器得到道具
__pageUrl财务报告和审定财务报表__pagePath添加(在道具中已经获得数值)和 aGET请求页 :url当请求是客户端导航的服务器功能呼叫时, 否则request.url,用请求的页眉. - 投出道具的加载器是保存的道具. 该节列于
degraded除非是在nonCritical(v7级)registerNonCriticalSections; 将 v7 的布局区段也放在那里, 因为 v7 从未标记它们退化). v7 已发送X-Deco-Degraded当列表不是空的,所以页面没有被边缘标记. runSectionLoader某一节(推迟部分)也是如此.
v7 的缓存( layout 区段, 可缓存区段) 及其注册不在此: 加载器是一个参数 。 v7的第三个论点, ctx不见了 一个读取它的加载器扔出一个错误,说出该使用什么来取而代之,而其部分保留了被保存的道具.
withDevice, (中文(简体) ), withMobile, (中文(简体) ), withSearchParam, (中文(简体) ), compose 财务报告和审定财务报表 withSectionLoader 是v7的混音器,有v7的输出. withSectionLoader 保留 v7 的捕获量:一个会扔回道具的区段模块装载器,而区段没有退化.
v8节为 { component, props }. v7 的解析器将嵌入部分写成 { Component, props };这里的嵌入区段形状与页面区段相同,并保留它拥有的任何其他密钥.
商业类型和帮助者
import type { Product } from "@decocms/compat-v7/commerce/types";
import { formatPrice } from "@decocms/compat-v7/commerce/sdk/formatPrice";
import { useOffer } from "@decocms/compat-v7/commerce/sdk/useOffer";
import { relative } from "@decocms/compat-v7/commerce/sdk/url";
import { mapProductToAnalyticsItem } from "@decocms/compat-v7/commerce/utils/productToAnalyticsItem";
7号 @decocms/apps-commerce下页 /commerce 使用自己的子路径布局: 替换 @decocms/apps-commerce/ 与 @decocms/compat-v7/commerce/ 在导入中,没有其他变化.
/commerce/types(和/commerce: schema.org 商业类型(Product, (中文(简体) ),Offer, (中文(简体) ),AggregateOffer, (中文(简体) ),BreadcrumbList, (中文(简体) ),ProductListingPage,分析事件,... ),与车型的再出口./commerce/types/cart单凭手推车类型和区段预设./commerce/sdk/*数字 :formatPrice编号 :formatPriceRange, (中文(简体) ),relative, (中文(简体) ),useOffer编号 :bestInstallment, (中文(简体) ),useVariantPossibilities和分析绘图器analytics) (中文(简体) )./commerce/utils/*数字 :canonicalFromBreadcrumblist, (中文(简体) ),DEFAULT_IMAGE, (中文(简体) ),parseRange编号 :formatRange, (中文(简体) ),mapProductToAnalyticsItem,以及getStateFromZip(默认导出stateByZip) (中文(简体) ).
每个文件都是v7's,格式化一旁,测试运行v7's文件旁边. 无使用反应( N)useOffer 财务报告和审定财务报表 useVariantPossibilities 是普通函数,尽管名称不同)。 v7 的应用程序运行时间( e)app-types, (中文(简体) ), manifest-utils, (中文(简体) ), registry, (中文(简体) ), resolve) 这里没有: v8 配置有参数的客户端.
TanStack 开始(@decocms/tanstack(中文(简体) )
// the block map
import { random, type RandomProps } from "@decocms/compat-v7/matchers";
import { matcherContext } from "@decocms/compat-v7/tanstack/server";
"website/matchers/random.ts": (props: RandomProps) => random(props, matcherContext()),
// the page's server function (it gets the page's href)
const page = await openPage(data.href); // resolves the page, so the matchers run
commitSegmentCookie(); // deco_segment, as v7's page route set it
return { ...page, device: requestDevice() };
// the page component
import { DeviceProvider } from "@decocms/compat-v7/tanstack";
<DeviceProvider value={page.device}>{sections}</DeviceProvider>
// src/worker-entry.ts: the segment of the site's own page cache
const segment = (request: Request) => ({ device: …, ...cohortSegment(request) });
matcherContext(pageUrl?)给每个匹配者所服务的请求( TanStack Start )getRequest(),页面的 URL 和一个flags每个请求的阵列。 在客户端导航上,请求是服务器功能呼叫,因此通过该页面的URL,因为v7匹配正在导航的页面;没有它,该呼叫会丢出.commitSegmentCookie()套装deco_segment将客人们的饼干合并:deco_segment=<value>; Max-Age=31536000; Path=/; SameSite=Lax,v7的字节. 当饼干已经说一样的话时,就没什么了. 在页面解析后在服务器函数中调用它 。 一个网站的页面缓存从不存储设置它的响应,因为v7的工人没有: 保留deco_segment从饼干中取出它的缓存条和商店(工人进入处方)@decocms/tanstackREADME 存储的响应中,没有设置 cookie 以外的响应SAFE_COOKIES) (中文(简体) ).cohortSegment(request)是 v7 边缘缓存的 A/B 拆分({ abf: <token> }在一个考验中,{}因此,每个组保留自己的缓存页面,其他人共享一个.requestDevice()为 v7'sdetectDevice关于请求的用户代理:默认情况下工人-输入食谱的缓存密钥用途。 与页面一起返回,所以浏览器的水合值是相同的.useDevice()读取提供者;没有读取浏览器的用户代理("desktop"和v7一样.
A/B与遗产来源分离
7号 withABTesting (由 KV 驱动在 TanStack 站点与其取代站点之间的分割) 是 abTestingRoutes,为 createDecoWorker是因为 routes数字 :
// src/worker-entry.ts
import { abTestingRoutes } from "@decocms/compat-v7/tanstack/worker";
const abTesting = abTestingRoutes({
worker: () => worker,
kvBinding: "SITES_KV",
shouldBypassAB: (_request, url) => isCommercePath(url.pathname),
});
const worker = createDecoWorker({
// …
routes: async (request, env, ctx) => (await abTesting(request, env, ctx)) ?? siteRoutes(request),
});
export default worker;
- 相同的 KV 条目( E)
{ workerName, fallbackOrigin, abTest: { ratio } }在请求的主机名下,同一桶(?_deco_bucket=覆盖,然后是_deco_bucket=<bucket>:<ratio %>在目前比例下设定了饼干,然后是客户IP的散列,同样的饼干和x-deco-bucket头,断路器,shouldBypassAB, (中文(简体) ),preHandler和倒置代理(主机名在Set-Cookie, (中文(简体) ),Location和2xx文本机构,流出)。 测试运行 v7 的文件旁边. - v7包了整个工人。 操作器运行在工人运行的地方
routes:在CMS重定向后,/deco/_liveness和缓存清洗。 对于"工人"的桶 它叫工人回来(worker)),因此页面得到桶饼干,一个工人出错会回落到遗留来源,如v7中;内调会跳过处理器并去站点的其他路线和边缘缓存,这已经让_deco_bucket通过缓存页面. - 留下的遗迹的回音 就像工人一样
routes回应 :CDN-Cache-Control: no-store当它没有设置, 工地的安全头 当它没有设置preload提示( 它们为新站点命名 ; v7 发送了 无) 。 "工人"桶的一页载着他们一次,从制造它的内部呼叫.
下册.js@decocms/nextjs(中文(简体) )
// proxy.ts: forward the visitor's URL (with the draft headers, if any)
import { pageUrlRequestHeaders } from "@decocms/compat-v7/nextjs/proxy";
const headers = pageUrlRequestHeaders(request, draftRequestHeaders(request, decision));
return applyDraft(NextResponse.next({ request: { headers } }), decision);
// the block map
import { matcherContext } from "@decocms/compat-v7/nextjs/server";
"website/matchers/random.ts": async (props: RandomProps) => random(props, await matcherContext()),
// the page (a server component)
import { SegmentCookie } from "@decocms/compat-v7/nextjs/server";
const sections = await resolvePage(...); // the matchers run here
return <>{sections}<SegmentCookie nonce={nonce} /></>;
// app/layout.tsx
import { DeviceProvider } from "@decocms/compat-v7/nextjs";
<DeviceProvider value={await requestDevice()}>{children}</DeviceProvider>
- 一个服务器组件看到请求的页眉,但不看到其URL,代理机可能会重写一个页面所绘制的路径. 代理转发访问者要求的 URL
x-deco-page-url(取出任何外部副本),以及matcherContext()匹配; 通过 URL 到matcherContext(url)而不是当页面知道它. - 这些决定是按React的要求作出的
cache。 。 。 . - 服务器组件不能设置饼干,代理在页面决定前运行. 这么说吧
SegmentCookie制作一行脚本,设置同样的 cookie v7 的 TanStack 路由发送(deco_segment=<value>; Max-Age=31536000; Path=/; SameSite=Lax)),或者当访客的饼干已经说了同样的话就什么都没有了. 在客户端导航 React 使页面本身不运行脚本之后,它不会生成脚本,所以一个效果就设置了相同的饼干. 内容解决后再渲染. 阅读请求的一页是动态的,所以Next永远不要在访客之间分享;让一个CDN在前面也不要缓存. v7's Next.js 捆绑从未写过这个饼干: 在那里,随机再次从每个请求中抽取.
设备上下文是两个框架相同的模块: DeviceContext。 。 。 .
包根导出类型,设备,脚本和 Clx 帮助. 选手,选手, /commerce/* 而框架的子路径 只在自己的子路径.