整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

迁移电商网站代价惨重?电子商务网站迁移的关键五大步骤详解

电商网站迁移到一个新的设计、平台或领域意味着你必须非常严谨地执行该操作,否则你的SEO就会受到影响,从而降低流量、转化率和收入。如果在没有任何操作策略下执行该工作,甚至会使网站受到攻击。在电子商务网站迁移之前做好准备是确保其持续成功的关键,虽然在迁移站点时SEO通常会出现下降,但如果启动之前修复错误实际上可以帮助你保持稳定的流量。

为此,本文将分享五个网站阶段迁移检查步骤,以尽量减少网站迁移所带来的任何潜在的SEO负面影响。

为什么迁移?

·网站设计过时

·你的业务更改了服务提供,因此需要添加新信息或更改现有信息以使业务更具有时效性。

·重新命名(当业务名称更改时,这可能涉及迁移到一个新域)。

·CMS(内部管理系统)平台存在一些问题,你想要切换到一个具有更多或不同的强大功能或成本更低的平台。

·网站没有转换,你希望从头开始创建一个新域

接下来将深入讨论成功迁移的具体细节,从涉及的风险开始。

不同迁移的类型具有不同的风险

低SEO风险:网站重新设计

当迁移范围仅限于简单地更改网站的设计时,风险相对较低。原因很简单,因为URL并没有更改,因此你也没有必要从外部代理机构来寻求帮助以保护SEO不受影响。

中等SEO风险:URL /更改(平台未更改)

当域发生更改时或者需要具有丰富重定向的新URL结构的更改时,你就需要进行仔细地监视。因为任何URL更改都会给你的SEO带来风险,因此网站的新URL需要被索引,这一目标是通过使URL易于抓取,从而使它们在发布后被谷歌快速索引。无论实际的设计是否发生了变化,新的URL路径都是网站SEO的基本改变,应该加以监控。

中等SEO风险:电商平台的变化

通常,平台更改意味着站点的URL也会更改。这是因为各种电子商务平台的url结构不同。例如作为指向Shopify“collections”中的产品的路径,URL结构必须是domain.com/products/productname或domain.com/collections/collectionname/products/productname

与此同时,WordPress和其他CMS(内容管理系统)都有自己的URL结构,且需求不同。这就是为什么在很多情况下,维护原始URL是不可能的的原因。平台的变化也会带来核心功能丢失的风险,而这些核心功能正是SEO(或其他营销渠道)目前所受益的。在迁移平台时,评估和最小化这样的风险才是关键。

高SEO风险:域迁移

无论URL或设计更改如何,迁移电子商务网站的域都存在很大的风险。问题是,域名在谷歌中遗留了很多的历史。因此当你从零开始时,必然会有所损失。在这种情况下,就要做更多的工作来减轻SEO的影响和流量损失。

现在你已经了解了其中的风险,以下是迁移网站需要验证的所有因素的详细列表。

阶段一:评估活动网站和改进的机会,收集基准数据

由于这是一个从一个平台到另一个平台的数据迁移过程,所以你将使用旧站点的数据作为基准,以便在稍后的步骤中参考和比较新站点。目标是在将开发站点的功能与活动站点进行比较时,以尽可能少的差异启动开发网站。

步骤1:使用DeepCrawl或Screaming Frog抓取当前域

Screaming Frog是一种应用广泛的工具,其数据输出适合于该过程。

通将DeepCrawl用于较大的站点,而Screaming Frog是作为较小站点的更便宜和更容易的选择。也就是说,你可以使用你选择的抓取工具。

抓取当前活动域可以提供网站上的URL列表,以及关于这些URL的各种数据,从而使用这些数据进行检查和基准测试。

步骤2:执行 Google Search Console(谷歌站长工具)分析

审查网站的谷歌相关数据非常重要。数据在GSC中以仪表板格式显示,你将考虑:

·导出站点的CSV数据

·是否需要将结构化数据转移到新网站?

·hreflang标签需要转到新站点吗?

·是否需要在启动前处理404错误?

步骤3:抓取并分析旧网站

现在你已经有了抓取数据和谷歌的数据,你将记录下以下几点,以便能够将它们与新站点进行比较:

·在源代码中查找Meta robots nofollow标签

·查找带有noindex(禁止索引)标记的页面

·审查规范标签

·审查标题标签

·审查元描述标签

·审查标题(h1, h2等)的用法

· 在一个文件中收集所有已知的实时URL(根据网站抓取、serp抓取、GSC/Google Search Console、GA/Google Analytics等)。

阶段二:评估模拟网站和改进的技术机会

接下来你就要进行技术工作,检查可能的错误,并采取必要的步骤来测试和解决它们。

步骤1:检查服务器更改

要求开发人员在新服务器上托管开发网站以识别潜在的问题,不要忘记要求开发人员使用robots.txt文件阻止搜索引擎机器人访问开发站点。

步骤2:建立301重定向策略

要获得新的URL索引,实现重定向的系统方法是至关重要的一步。

·使用网站抓取数据制定301重定向策略;

·记录如何实现重定向策略,以及由谁负责,每个人都应该在如何构建和实现重定向的问题上保持一致;

·是否存在需要更新的旧版重定向;

此外你还将在启动或迁移时检查重定向是否成功。

步骤3:进行技术开发网站优化

进行技术审核是成功的关键:

·查看新站点的网站地图和线框图;

·确保站点被阻止索引;

·验证自定义404错误页面是否存在&正确地发出404标题响应。404页面标题应该始终为“页面未找到”(或类似的),这样就可以在Google Analytics中找到错误(也就是用户访问的错误);

·抓取开发站点;修正404和soft 404;

·确保你不希望在开发网站上编制索引的链接是Nofollowed(无法追踪)的;

·测试重定向;

·使用多个设备浏览开发网站;

·计划更快的索引;创建一个包含所有旧URL的XML站点地图(如有必要),并计划在网站启动后保留它;

·测试和提高网站速度,尽可能多的开发网站。

由于开发网站的速度只是一个估计值,所以你需要让开发团队帮助你了解哪些地方还存在不足,以及你可以做些什么来改进它。

步骤4:确定分析迁移策略

·将分析代码添加到开发站点。(阻止访问此站点的团队成员的IP);

·目标:编制一个URL列表,以便在新站点上线时更新分析目标;

·检查网站上不同类型网页的移动设备友好性;

·检查参数是否按预期运行(并且确保在页面重新加载或重定向期间,不会从URL中删除关键跟踪参数);

·检查谷歌站长工具中的参数报告是否存在重复内容问题(旧谷歌站长工具的此功能可能不会长时间保留);

·检查谷歌分析以确保本网站不能成为其自己的引用页;

·社交媒体共享:编制社交次数最多的网址列表。保持旧的嵌入代码以保持社交计数。如果没有值得重视的页面,请不要浪费开发工作来实行这一点。

阶段三:比较活动站点和测试站点,以达到持平或更好

这个阶段的主要步骤如下:

·将旧网站与新网站相比较;

·为将要修复的内容列一个清单,目的是在迁移期间使流量尽可能接近持平。

内容

·网站是否存在重复内容或URL?

·将所有内容迁移到开发站点,尽可能保持不变;

·确保标题标签和元描述被延续,并且与当前站点相同或更好;

·检查页面内容、规范标签、内部链接,H1s/H2s,IMG ALT标签的使用。

移动站点特定任务

·在Screaming Frog中抓取移动谷歌机器人,验证页面之间的移动对等:标题、描述、标题、内容、链接、图像、指令等;

·将meta =“viewport”标签履行到网站的

部分的。

Javascript站点特定任务

·可视化地审计所有主要页面类型;

·审核缺少内容的HTML源代码;

·使用inspect元素检查缺少的内容;

·比较HTML源代码和检查元素是否矛盾;

·根据用户交互来识别内容。

AMP页面特定任务

·每个非AMP页面(即桌面、手机)都应该有一个指向相应AMP URL的标签;

·每个AMP页面应该有一个rel= “canonical ”标签指向相应的桌面页面;

·任何没有相应桌面URL的AMP页面应该有一个自引用的规范标记。

电子商务特定任务

·类别页面是否包含指向产品的可索引链接;

·检查分面导航、分页以获得最佳实践;

·如果图片链接出现在锚文本链接之前,是否使用了关键词丰富的ALT文本。

WordPress特定任务

·使用Simple 301 Redirects Plugin重定向开发站点WordPress中的URL;

·在WordPress站点和CMS(内部管理系统)上安装Google Tag Manager Plugin (谷歌标签管理器插件)并进行配置;

·设置Yoast WordPress SEO & Yoast Analytics插件;

·如果你使用像HubSpot这样的CMS ,安装并设置CMS插件;

·建立Yoast Analytics。

阶段四:启动检查以确保一切顺利

步骤1:站点抓取/分析检查

在这个步骤中,你将检查活动站点和测试站点之间的匹配数据:

1、验证301重定向都已正确实施;

2、抓取新站点以确定技术问题和可访问性;;

3、确保新的活动站点没有被阻止、被抓取和索引

4、验证是否将“Nofollow”标记添加到不希望索引的页面。这些应该在开发站点上进行标识,例如在分面导航链接上;

5、确保每个页面上都有“index、follow”元标记;

6、寻找不应该出现的404页面;

7、检查内部链接:查找断开的链接,以及指向开发站点的链接;

8、验证规范标签的项目执行;

9、检查标准和重复URL;

10、检查标题标签(与旧数据匹配);

11、检查元描述(与旧数据匹配);

12、检查H1、H2使用情况(与旧数据匹配);

13、检查IMG ALT属性(与旧数据匹配);

14、检查字数(与旧数据匹配,解释

标签内的所有模板文本);

15、按页面检查内部链接计数(与旧数据匹配,是否有关键页面丢失链接)。

步骤2: Google Analytics(谷歌分析) / Search Console Checks(谷歌站长工具)

现在你可以观察谷歌为新站点注册了哪些数据:

1、确保其准确性:验证所有页面上的分析代码,以及实施正确的代码;

2、更新分析目标,检查它们是否能够正常工作;

3、使用网站启动日期对分析进行注释。

步骤3:网站速度检查

1、观察通过站点速度工具运行几个页面,将旧站点和开发站点上的站点速度进行相比;

2、如果可以的话,做好改进的记录。

步骤4:其他任务

1、站点是否更改了服务器?确保没有与服务器相关的问题;

2、比较顶部登录页面,前后(标题、元描述、页眉、页面内容等);

3、验证404页面是否返回404状态;

4、验证旧的XML站点地图是否在新站点上,重新提交到Google Search Console (谷歌站长工具)和 Bing Webmaster Tools(必应网站管理员工具);

5、如果域正在更改,请在GSC中声明新的域变体并提交地址更改请求。

阶段五:1-2个月以上的网站监测

1、发布后一周

·监控GSC谷歌站长工具(抓取统计数据、排名、流量、索引页面等);

·检查GSC是否有新的索引页面(并检查任何没有索引的页面);

·检查旧的GSC配置文件,以确保旧页面被取消索引;

·保留旧的XML站点地图,让谷歌重新抓取。

2、发布后2 - 3周内

·监控谷歌站长工具(抓取统计数据、排名、流量、索引页面等);

·删除旧的XML站点地图,用新的XML站点地图替换它们;

·检查sitemap.xml文件,是否有正确的url(无多余的URL、没有Dev URL等);

·提交新的XML站点地图到谷歌站长工具&必应网站管理员工具。

3、发布后一个月

·监控谷歌站长工具(抓取统计数据、排名、流量、索引页面等);

·检查流量损失分析,观察哪些页面丢失了流量以及原因。

4、发布后2个月

·继续监控谷歌站长工具(抓取统计数据、排名、流量、索引页面等);

·大多数迁移通常会在启动后出现流量的下降和上升。也就是说,每个地点和迁移都是不同的,所以实际的影响很难预测;

· 较好的办法就是遵循这个过程。这样做可以确保新站点的数据仍然被谷歌索引,从而对潜在客户可见。

(编译/雨果网 宋淑湲)

【特别声明】未经许可同意,任何个人或组织不得复制、转载、或以其他方式使用本网站内容。转载请联系:editor@cifnews.com


eta 版的 Execute Program 是用 Ruby 和 JavaScript 编写的。之后,我们分几步将整个应用完全移植到了 TypeScript 上。本文介绍的是移植的第一步,也就是前端的部分。

在 Execute Program 的原始 JavaScript 前端中,我经常会犯一些小错误。例如,我会将错误的 prop 名称传递给 React 组件,或者遗漏某个 prop,抑或传递错误的数据类型。(Prop 是作为参数发送到 React 组件的数据。组件将一些 props 传递给自己的某个子组件,以此类推,这是很常见的。)

对于像 JavaScript 和 Ruby 这样的动态语言来说,这不是个小问题。过去 15 年来我一直在学习该如何应对这种错误问题。我在之前谈论的是 2011 年代的情况,其中讨论的缓解措施确实有些用途,但它们无法随着系统的发展顺利地扩展下去,而且我们忘掉它们时也没有安全网可用。

我觉得 15 年时间已经够长了。我想回到静态类型系统的怀抱,毕竟这种系统中根本不会出现这类错误。彼时我们有几个选项:Elm、Reason、Flow、TypeScript 和 PureScript。(这里举了一部分例子。)最后我决定使用 TypeScript 是因为:

  1. TypeScript 是 JavaScript 的超集,因此移植起来很容易。移植回来也更容易些:只需删除类型定义即可,然后我们又回到了 JavaScript。
  2. TypeScript 编译器使用 TypeScript 编写,并作为已编译的 JavaScript 代码分发,因此我们可以在自己的 Web 应用中运行它。我们的 TypeScript 课程正是这样做的:在浏览器中评估用户的 TypeScript 代码,以避免网络延迟。
  3. 这一条是我们的业务特有的:TypeScript 比其他选项更受欢迎。这意味着更多的人希望从像我们这样的课程中学习 TypeScript。用 TypeScript 编写 Execute Program,让我们可以制作出更好的 TypeScript 课程。

在 2018 年 10 月,我们用了大约两天时间将前端 JavaScript 代码移植到了 TypeScript。下面的图表显示了在移植前和移植后每种语言拥有的代码量。

当时我们还是 pre-beta 版本,所以系统还很小,只有大约 6,000 行。这张图上没有涉及移植后的情况;我们将在以后的文章中具体介绍相关内容。

在这次移植之后,React prop 问题消失了。下面我们会看几个示例,首先是一个简单的例子。以下是渲染“Continue”按钮的代码,这个按钮出现在我们课程的每个文本段落之后:

复制代码

<Button  autofocus={true}  icon="arrowRight"  onClick={continue}  primary>  Continue</Button>

这个 Button 组件的 props 的类型如下所示。当读取诸如 autofocus?: boolean 之类的属性类型时:“autofocus”是属性的名称;“?”表示它是可选的;“:”将属性名称与其类型分开;而“boolean”是类型。最后一个属性类型 onClick 表示“一个不带参数且不返回任何内容的函数”。如果你不熟悉 TypeScript 的函数类型语法,可以在我们的课程中全面了解 TypeScript 的函数类型。

复制代码

type ButtonProps = {  autofocus?: boolean  icon?: IconName  primary?: boolean  onClick: () => void}

如果将“autofocus”prop 从 true 更改为 1,会发生什么?现在,我们在类型系统期望一个布尔值的地方传递了一个数字值。不到一秒钟后,编译器将在下面显示错误。(这里删除了一些不相关的细节;本系列文章中所有涉及到错误的地方都会这样处理。)

复制代码

src/client/components/explanation.tsx(13,27):  error: Type 'number' is not assignable to type 'boolean | undefined'.

有害代码在 vim 中也变成了红色。修好它后,红色消失了。解决错误只需要几秒钟。在 Ruby 或 JavaScript 中,我可能会花几分钟的时间手动测试应用程序,并反复浏览它的状态才能知道到底发生了什么事情。我也可以依靠自动化测试,但是我们在另一篇文章中介绍了测试 vs 类型的问题。

这个整数到布尔的更改是对类型系统的一次简单而低风险的测试。Button 的 icon 属性显示了更高级的用法。下面还是 Button 调用:

复制代码

<Button  autofocus={true}  icon="arrowRight"  onClick={continue}  primary>  Continue</Button>

看起来 icon prop 只是一个字符串:“arrowRight”。在运行时,在已编译的 JavaScript 代码中,它将是一个字符串。但是在上面显示的 ButtonProps 类型中,我们将其定义为 IconName,后者是在其他地方定义的。在查看其定义之前,让我们先看看这个类型的作用。假设我们将“icon”prop 更改为“banana”。我们实际上没有名为“banana”的图标。

复制代码

<Button  autofocus={true}  icon="banana"  onClick={continue}  primary>  Continue</Button>

不到一秒钟后,TypeScript 编译器拒绝了这一更改:

复制代码

src/client/components/explanation.tsx(13,44):  error: Type '"banana"' is not assignable to type    '"menu" | "arrowDown" | "arrowLeft" | ... 21 more ... | undefined'.

编译器说“icon”不能是任意字符串。它必须是我们定义为 icon 名称的 24 个字符串之一。编译器将拒绝任何使我们引用不存在图标的更改;这不是有效的程序,甚至无法开始执行。

有多种方法可以实现 IconName 类型。一种是编写一种类型,该类型显式列出所有可能的 icon 名称。然后,我们必须使 icon 名称与其在磁盘上的图像文件保持同步。这种类型可能是这样的:

复制代码

type IconName =  "menu" |  "arrowDown" |  "arrowLeft" |  "arrowRight" |  ...

翻译成中文:“这里会静态地保证 IconName 类型的一个值是此处指定的字符串之一,但不能是其他任何字符串。”(这个类型是我们两堂课程涵盖的两个主题的组合:字面量类型和类型联合)

我们的 IconName 未被定义为字面量类型的简单联合。让图标名称列表与文件列表保持同步是很无聊的工作,我们可以让计算机来完成它!相反,我们的 icon.tsx 文件如下所示:

复制代码

export const icons = {  arrowDown: {    label: "Down Arrow",    data() {      return <path ... />    }  },  arrowLeft: {    label: "Left Arrow",    data() {      return <path ... />    }  },  ...}

实际的 SVG < path/> 标签就在源代码中,在以 icon 名称为键的对象中。(也可以在不将 SVG 内联到源文件中的情况下执行此操作。例如,我们可以使用一些 Webpack 技巧将图像保存在它们自己的文件中,但仍然可以确保列表中的每个图标也都存在于磁盘上。到目前为止,这种简单的解决方案对我们来说是很好用的。)

通过这种方式定义 icon 后,我们可以使用一行代码自动提取其名称的联合类型(union type):

复制代码

export type IconName = keyof typeof icons

(这里的意思是,你可以认为该类型表示“每当某物的类型为”IconName”时,它必须是与 icons 对象的键之一匹配的字符串。)

这样就搞定了;并不需要其他类型层面的工作。剩下的代码只是一个简单的 Icon React 组件,它在列表中查找图标并返回其 SVG 路径。这个函数中没有明确的 TypeScript 类型。它看起来像是纯粹的 JavaScript 代码,但它也经过了类型检查。这是一个最小版本,其中删除了所有无关的细节:

复制代码

export function Icon(props: {  name: IconName}) {  return <svg>    {icons[props.name].data()}  </svg>}

现在,我们可以将 SVG 标签放入这个源文件中,并将新 icon 拖放到“icons”列表中。当我们这样做时,这个 icon 就可以在 Button 组件,以及系统内接受 icon 名称的其他任何部分中使用。如果我们从列表中删除一个 icon,则系统中引用该 icon 的所有部分都将立即无法编译,从而确保没有过时的 icon 引用在运行时导致错误。

这些示例按照静态类型标准来说是很简单的,但我认为它们证明了 Web 应用程序中有多少可以轻松实现的改进之处。一个应用程序中的大多数代码都不涉及高级类型系统功能;多数需求仅仅是“确保我们传递正确的 props”和“确保我们的图标确实存在”之类的简单事情。

我们在整个系统中都做了这种事情。其他的一些示例:

  1. 我们在整个系统中使用了一个 Note 组件。它具有一个 tone prop 来确定提示的样式:“info”“warning”“error”等。如果我们不再使用其中某个 tone 选项,则我们将从联合类型中将其删除,并且所有引用这个 tone 的 Note 将出错,直到我们更新它们。
  2. 我们链接到的每个 URL 都将静态保证存在。当我们重命名或删除 URL 时,链接到它的每个组件都无法编译,直到我们对其进行更新以匹配为止。
  3. 当我们链接到这些 URL 时,类型系统可确保我们填充 URL 中的所有空缺。例如,路径“/courses/:courseId/lessons/:lessonId”具有两个 hole,“courseId”和“lessonId”。如果我们尝试链接到该路径,但忘记提供“courseId”,则代码将无法编译。
  4. 我们在客户端上发出的每个 API 请求都会被静态确保与相应服务端 API 端点的负载结构匹配。如果我们在端点中重命名一个属性,哪怕属性在嵌套的 API 对象的内部深处,引用该端点属性的任何代码也都将无法编译,直到我们对其更新以匹配为止。我们在另一篇文章中介绍了细节。

诸如此类的问题经常会出现在编程工作中,尤其是在动态语言中非常常见;但我们无需编写任何自动测试,也用不着什么手动测试,就可以从静态上避免这些问题。有些问题解决起来需要费些功夫。我们的 API 路由器验证写起来很麻烦。但是写多了就顺手了。上面的单行“IconName”类型实际上是问题的完整解决方案。如果将其复制到 TypeScript 文件中,它就能起作用。

将我们的前端代码移植到 TypeScript 仅仅是个开始。那之后,我们又将后端从 Ruby 移植到了 TypeScript,然后在移植后的 9 个月内对其进行了扩展和维护。

oogle Docs宣布将会把HTML迁移到基于Canvas渲染,这一消息的出现再次把几年前随HTML5诞生的标签重新推到了人们视线之中。Canvas在刚推出时主打的优势就是更快的渲染速度,堪称HTML届的“小飞人”,刷新了人们对Web页面元素绘制速度的印象。但Canvas的优势仅限于此吗?

(图片来源于网络)

HTML绘图届的前辈:SVG

Canvas是HTML5时代引入的“新”标签。与很多标签不同,Canvas不具有自己的行为,只将一组API 展现给客户端 JavaScript ,让开发者使用脚本把想绘制的东西画到一张画布上。

在HTML5之前,人们通常使用SVG来在页面上绘制出图形。SVG使用XML来定义图形,就像使用HTML标签和样式定义DIV一样,我们也可以将一个空白的DIV想象为长方形的SVG,两者的设计思想是相通的,SVG的本质就是一个DOM元素。而Canvas则不同,Canvas提供的是 JavaScript 的绘图 API,而不是像 SVG那样使用XML 描述绘图,通过JavaScript API直接完成绘制,比起修改XML来说要更简便、更直接。

除了定义的方式不同,Canvas和DOM(当然也包含SVG)的差异更多的体现在浏览器的渲染方式上。

浏览器在做页面渲染时,Dom元素是作为矢量图进行渲染的。每一个元素的边距都需要单独处理,浏览器需要将它们全都处理成像素才能输出到屏幕上,计算量十分庞大。当页面上内容非常多,存在大量DOM元素的时候,这些内容的渲染速度就会变得很慢。

而Canvas与DOM的区别则是Canvas的本质就是一张位图,类似img标签,或者一个div加了一张背景图(background-image)。所以,DOM那种矢量图在渲染中存在的问题换到Canvas身上就完全不同了。在渲染Canvas时,浏览器只需要在JavaScript引擎中执行绘制逻辑,在内存中构建出画布,然后遍历整个画布里所有像素点的颜色,直接输出到屏幕就可以了。不管Canvas里面的元素有多少个,浏览器在渲染阶段也仅需要处理一张画布。

然而这样更加强大的功能,不可避免的让使用canvas渲染有很高的门槛。Google Docs在构建Canvas的过程中重新定义了往常已经被人们所熟悉的内容,例如精确定位、文本选择、拼写检查、重画调优等。为什么更多开发者还是选择了接纳Canvas这个门槛更高的技术路线呢?这就得回到Canvas的最大优势:渲染性能。

Canvas的渲染模式

这里的渲染是指浏览器将页面的代码呈现为屏幕上内容的过程。Canvas和Dom的渲染模式完全不同,搞清楚这个差异对理解Canvas的性能优势至关重要。

Dom:驻留模式

驻留模式(Retained Mode)是Dom在浏览器中的渲染模式。下图粗略展示了这一过程的工作流程。

DOM的核心是标签,一种文本标记型语言,多样性很强且多个标签之间存在各种关联(如在同一个DIV下设置为float的子DIV)。浏览器为了更好的处理这些DOM元素,减少对绘制API的调用,就设计了一套将中间结果存放于内存的“驻留模式”。首先,浏览器会将解析DOM相关的全部内容(包含HTML标签、样式和JavaScript),将其转化为场景(scene)和模型(model)存储到内存中,然后再调用系统的绘制API(如Windows程序员熟悉的GDI/GDI+),把这些中间产物绘制到屏幕。

驻留模式通过场景和模型缓存减少了对绘制API的调用频次,将性能压力转移到场景和模型生成阶段,即浏览器需要根据DOM上下文和BOM中的尺寸数据,“自行判断”每一个元素的绘制结果。

Canvas:快速模式

Canvas采用了和DOM不同的快速模式(Immediate Mode),让我们先来看看快速模式是如工作的:

与驻留模式相比,快速模式将场景和模型的生成从浏览器移交给了开发者。开发者在设计页面时,就通过Canvas的JavaScript API定义了画布内所有元素的绘制方式。浏览器只需要简单的执行这些脚本即可,而不需要像渲染DOM一样逐个处理子元素了。

在快速模式中,页面的绘制性能得到了大幅提升。但开发者不仅需要指定什么需要画,还要创建和维护一个模型。此外,开发者还需要管理好当前场景重绘时带来的改变,以及响应用户的点击或输入操作等。

Canvas的应用优点

上面介绍的两种不同的模式直接造成了Dom和Canvas的性能差异。对于使用快速模式渲染的Canvas而言,浏览器的每次重绘都是基于代码的,不存在能让处理流程变慢的多层解析,所以它真的很快。除了快之外,Canvas的灵活性也大大超出DOM。我们可以通过代码精确的控制如何、何时绘制出我们想要的效果。

在资源消耗上,DOM的驻留模式意味着场景中每增加一点东西就需要额外消耗一些内存,而Canvas并没有这个问题。这个差异会随着页面元素的数量增多而愈加明显。以B端的企业应用场景为例,表单那种数据量比较小的场景,不同渲染模式带来的效果差异并不明显;但在工业制造、金融财会等类Excel电子表格操作的场景下,单元格数量动辄便是上百万(5万行x 20列)甚至上亿个,浏览器需要对表格所有单元格本身内容进行渲染,同时还涉及到丰富的数据处理,情况就完全不同了。

(Web页面上的电子表格,包含1百万个单元格)

在Canvas出现之前,在前端渲染表格时只能通过构建复杂的DOM来实现。这种方式下,浏览器的性能成为了Web应用瓶颈,让很多开发者放弃了在浏览器上实现电子表格的想法。

在Canvas出现后,快速模式带来的性能优势无疑是一个巨大的亮点,大量、复杂的DOM渲染处理带来的性能问题终于有了解决途径。

回到电子表格的应用场景,业内已经出现了使用Canvas绘制画布的表格组件,这类组件在渲染数据层时不仅无需重复创建和销毁DOM元素,在画布的绘制过程中,也比Dom元素渲染的限制更少。除了表格之外,Canvas也为数字孪生可视化大屏、页面游戏等场景带来了变革。

(数字孪生大屏,精确控制各种形状、样式)

总结

总结一下,在渲染模式上,Canvas站在了DOM的对面,浏览器对其内容一无所知,一切渲染的权利回到了开发者的手上,这个改变带来了显著的性能优势。此外,我们可以使用Canvas绘制种类更为丰富的UI元素,如线形、特殊图形等,通过画法逻辑,还可以实现更加精准的UI界面渲染,解决了浏览器差异造成的样式误差,让更多应用场景可以顺利迁移到Web平台上来。