• 关于

    事件-使用发生在组件上的事件

    的搜索结果

问题

【分享】WeX5的正确打开方式(7)——数据组件详解

小太阳1号 2019-12-01 21:38:53 5902 浏览量 回答数 1

回答

OOS的常见应用场景有:事件驱动、批量操作、更新镜像、需要审批的运维场景、定时任务、跨地域、多地域的运维等应用场景,且您也可根据自身实际场景自定义诸多灵活多样的模版。 事件驱动 当一个事件发生时,触发一个运维动作。例如,当某ECS实例的CPU使用量达到了85%时,为了防止业务中断,自动重启。事件驱动场景可以提供主动运维支持,免去中间的人为因素,提高运维效率。 批量操作 批量地执行运维命令,即需要针对多个目标(如ECS实例)进行常规操作,以确保业务的正常和平滑运行,并保持业务的健康状态。 例如,批量检查ECS实例中的硬盘剩余空间。首先选择需要检查的实例列表。(多种选择方式,如名字匹配、标签分组、资源组分组等),然后通过云助手命令执行硬盘检查,最终统一查看结果。 相关的公共模板 模板名称 用途 链接 ACS-ECS-BulkyDeleteInstances 批量删除ECS按量付费实例。 YAML JSON ACS-ECS-BulkyDeleteInstancesWithApproval 通过审批后删除ECS按量付费实例。 YAML JSON ACS-ECS-BulkyDeletePrepaidInstances 批量删除ECS预付费实例。指定的ECS实例必须处于已停止状态。 YAML JSON ACS-ECS-BulkyInstallLogAgent 批量在ECS实例上安装SLS代理。 YAML JSON ACS-ECS-BulkyRebootInstances 批量重启ECS实例。 YAML JSON ACS-ECS-BulkyRunCommand 批量在多台ECS实例上运行云助手命令。 YAML JSON ACS-ECS-BulkyStartInstances 批量启动ECS实例。 YAML JSON ACS-ECS-BulkyStopInstances 批量停止ECS实例。 YAML JSON ACS-ECS-BulkyTagInstanceByLinuxKernelVersion 批量通过指定实例IDs将Linux内核版本用作标记值标记ECS实例。 YAML JSON ACS-ECS-BulkyTagInstanceByOSType 批量通过指定实例IDs将操作系统类型用作标记值标记ECS实例。 YAML JSON ACS-ECS-BulkyTagInstanceByRunCommandResult 批量根据运行云助手命令的结果标记实例。 YAML JSON ACS-ECS-BulkyUpgradeInternetBandwidth 批量升级ECS实例公网带宽。 YAML JSON ACS-ECS-CloneInstancesAcrossAZ 跨可用区克隆ECS实例。 YAML JSON ACS-ECS-CloneInstancesAcrossRegion 跨地域克隆ECS实例。 YAML JSON 更新镜像 为了保证ECS实例的运行环境始终是安全的,包括安装最新补丁,或者更新所依赖的组件等,您可以使用OOS更新镜像,从一个源镜像开始逐步更新,最终生成一个新镜像,然后用于测试和生产。 相关的公共模板 模板名称 用途 链接 ACS-ECS-UpdateImage 通过运行命令更新一个已存在的ECS镜像后创建一个新ECS镜像。 YAML JSON 需要审批的运维场景 在很多场景下都需要使用审批来确保操作是安全并符合预期的。通过在模板中增加审批动作(ACS::Approve)可以在运维动作实际执行前进行人工审批,以确保运维动作执行的必要性,避免浪费和误操作。 相关的公共模板 模板名称 用途 链接 ACS-ECS-BulkyDeleteInstancesWithApproval 通过审批后删除ECS按量付费实例。 YAML JSON ACS-ECS-RunInstancesWithApproval 通过审批后创建ECS实例。 YAML JSON 定时任务 定时执行所定义的运维动作。例如,在某测试场景中,需要清除某账号下因为测试所产生的对象存储OSS文件,则可以创建一个模板,每天凌晨运行,以确保测试环境是一个全新的环境,避免干扰下一次的测试结果。 相关的公共模板 模板名称 用途 链接 ACS-ECS-ScheduleToRebootInstances 定时重启ECS实例。 YAML JSON ACS-ECS-ScheduleToStartInstances 定时启动ECS实例。 YAML JSON ACS-ECS-ScheduleToStopInstances 定时停止ECS实例。 YAML JSON ACS-ECS-ScheduleToUpgradeInternetBandwidth 定时升级ECS实例临时带宽。 YAML JSON 跨地域的运维场景 多地域部署已经成为了高可用(HA)架构的通用架构。随之而来的跨地域运维却变得越来越复杂,使用OOS可以帮助您解决跨地域的运维难题。您可以将不同地域的运维操作定义在一个模板内,即可实现跨地域的运维场景。 相关的公共模板 模板名称 用途 链接 ACS-ECS-CloneInstancesAcrossAZ 跨可用区克隆ECS实例。 YAML JSON ACS-ECS-CloneInstancesAcrossRegion 跨地域克隆ECS实例。 YAML JSON 多地域的运维场景 当您在使用多个地域资源时,经常需要对它们进行同步操作,以便保持它们的一致性。例如,将对象存储(OSS)的多个地域的存储空间(Bucket)进行相同的操作——开启日志功能。

保持可爱mmm 2020-04-15 23:13:31 0 浏览量 回答数 0

回答

OOS的常见应用场景有:事件驱动、批量操作、更新镜像、需要审批的运维场景、定时任务、跨地域、多地域的运维等应用场景,且您也可根据自身实际场景自定义诸多灵活多样的模版。 事件驱动 当一个事件发生时,触发一个运维动作。例如,当某ECS实例的CPU使用量达到了85%时,为了防止业务中断,自动重启。事件驱动场景可以提供主动运维支持,免去中间的人为因素,提高运维效率。 批量操作 批量地执行运维命令,即需要针对多个目标(如ECS实例)进行常规操作,以确保业务的正常和平滑运行,并保持业务的健康状态。 例如,批量检查ECS实例中的硬盘剩余空间。首先选择需要检查的实例列表。(多种选择方式,如名字匹配、标签分组、资源组分组等),然后通过云助手命令执行硬盘检查,最终统一查看结果。 相关的公共模板 模板名称 用途 链接 ACS-ECS-BulkyDeleteInstances 批量删除ECS按量付费实例。 YAML JSON ACS-ECS-BulkyDeleteInstancesWithApproval 通过审批后删除ECS按量付费实例。 YAML JSON ACS-ECS-BulkyDeletePrepaidInstances 批量删除ECS预付费实例。指定的ECS实例必须处于已停止状态。 YAML JSON ACS-ECS-BulkyInstallLogAgent 批量在ECS实例上安装SLS代理。 YAML JSON ACS-ECS-BulkyRebootInstances 批量重启ECS实例。 YAML JSON ACS-ECS-BulkyRunCommand 批量在多台ECS实例上运行云助手命令。 YAML JSON ACS-ECS-BulkyStartInstances 批量启动ECS实例。 YAML JSON ACS-ECS-BulkyStopInstances 批量停止ECS实例。 YAML JSON ACS-ECS-BulkyTagInstanceByLinuxKernelVersion 批量通过指定实例IDs将Linux内核版本用作标记值标记ECS实例。 YAML JSON ACS-ECS-BulkyTagInstanceByOSType 批量通过指定实例IDs将操作系统类型用作标记值标记ECS实例。 YAML JSON ACS-ECS-BulkyTagInstanceByRunCommandResult 批量根据运行云助手命令的结果标记实例。 YAML JSON ACS-ECS-BulkyUpgradeInternetBandwidth 批量升级ECS实例公网带宽。 YAML JSON ACS-ECS-CloneInstancesAcrossAZ 跨可用区克隆ECS实例。 YAML JSON ACS-ECS-CloneInstancesAcrossRegion 跨地域克隆ECS实例。 YAML JSON 更新镜像 为了保证ECS实例的运行环境始终是安全的,包括安装最新补丁,或者更新所依赖的组件等,您可以使用OOS更新镜像,从一个源镜像开始逐步更新,最终生成一个新镜像,然后用于测试和生产。 相关的公共模板 模板名称 用途 链接 ACS-ECS-UpdateImage 通过运行命令更新一个已存在的ECS镜像后创建一个新ECS镜像。 YAML JSON 需要审批的运维场景 在很多场景下都需要使用审批来确保操作是安全并符合预期的。通过在模板中增加审批动作(ACS::Approve)可以在运维动作实际执行前进行人工审批,以确保运维动作执行的必要性,避免浪费和误操作。 相关的公共模板 模板名称 用途 链接 ACS-ECS-BulkyDeleteInstancesWithApproval 通过审批后删除ECS按量付费实例。 YAML JSON ACS-ECS-RunInstancesWithApproval 通过审批后创建ECS实例。 YAML JSON 定时任务 定时执行所定义的运维动作。例如,在某测试场景中,需要清除某账号下因为测试所产生的对象存储OSS文件,则可以创建一个模板,每天凌晨运行,以确保测试环境是一个全新的环境,避免干扰下一次的测试结果。 相关的公共模板 模板名称 用途 链接 ACS-ECS-ScheduleToRebootInstances 定时重启ECS实例。 YAML JSON ACS-ECS-ScheduleToStartInstances 定时启动ECS实例。 YAML JSON ACS-ECS-ScheduleToStopInstances 定时停止ECS实例。 YAML JSON ACS-ECS-ScheduleToUpgradeInternetBandwidth 定时升级ECS实例临时带宽。 YAML JSON 跨地域的运维场景 多地域部署已经成为了高可用(HA)架构的通用架构。随之而来的跨地域运维却变得越来越复杂,使用OOS可以帮助您解决跨地域的运维难题。您可以将不同地域的运维操作定义在一个模板内,即可实现跨地域的运维场景。 相关的公共模板 模板名称 用途 链接 ACS-ECS-CloneInstancesAcrossAZ 跨可用区克隆ECS实例。 YAML JSON ACS-ECS-CloneInstancesAcrossRegion 跨地域克隆ECS实例。 YAML JSON 多地域的运维场景 当您在使用多个地域资源时,经常需要对它们进行同步操作,以便保持它们的一致性。例如,将对象存储(OSS)的多个地域的存储空间(Bucket)进行相同的操作——开启日志功能。

1934890530796658 2020-03-24 11:20:41 0 浏览量 回答数 0

阿里云试用中心,为您提供0门槛上云实践机会!

0元试用32+款产品,最高免费12个月!拨打95187-1,咨询专业上云建议!

问题

Vue面试题汇总【精品问答】

问问小秘 2020-05-25 18:02:28 20475 浏览量 回答数 4

回答

一、清理磁盘和整理磁盘碎片。 1、在我的电脑窗口,右击要清理的盘符—“属性”—“清理磁盘”--勾选要删除的文件--确定--是。 2、清除临时文件,开始—运行—输入 %temp% --确定。3、用优化大师或超级兔子清理注册表和垃圾文件。 4、关闭一些启动程序, 开始-运行-msconfig---启动 ,除杀毒软件、输入法外一般的程序都可以关掉。 5、删除不用的程序软件。 6、整理磁盘碎片--开始 --所有程序--附件--系统工具--磁盘碎片整理程序--选定C--分析--碎片整理。 二、用优化大师或超级兔子优化你的计算机 ,再挑选以下办法进行再优化。 1、禁用闲置的IDE通道 右键点击“我的电脑-属性”,然后点击“硬件”接着点击“设备管理器”,在其中打开“IDE ATA/PATA控制器”然后分别进入主要和次要IDE通道,选择“高级设置”,在这里找到“当前传送模式”为“不适用”的一项,将此项的“设备类型”设置为“无”。 2、优化视觉效果 右键单击“我的电脑”--“属性”—“高级”,在“性能”栏中,点击“设置”--“视觉效果”,调整为最佳性能,或只保留一些必要的项目。 3、启动和故障恢复 "我的电脑"--"属性"--"高级"--"启动和故障修复"中点击“设置”,去掉"将事件写入系统日志","发送管理警报","自动重新启动"选项;将"写入调试信息"设置为"无";点击"编辑",在弹出记事本文件中: [Operating Systems] timeout=30 把 30 秒改为 0 秒。 4、禁用错误报告 "我的电脑"--"属性”--"高级”--点"错误报告”,点选“禁用错误汇报”,勾选"但在发生严重错误时通知我”--确定。 5、设置系统还原 单击“开始”--“所有程序”--“附件”--“系统工具”—“系统还原”,在系统还原界面,去掉“在所有驱动器上关闭系统工程还原”前边的勾,在“可用的驱动器”区中,选系统盘所在的分区,单击“设置”进入“系统还原设置”窗口,将“要使用的磁盘空间”调到5%或更小,“确定”返回,按上述方法将其它分区设禁用。 6、关闭自动更新 关闭自动更新具体操作为:右键单击“我的电脑”--“属性”--“自动更新”,在“通知设置”一栏选择“关闭自动更新。选出“我将手动更新计算机”一项。 7、关闭远程桌面 右键单击“我的电脑”--“属性”--“远程”,把“远程桌面”里的“允许用户远程连接到这台计算机”勾去掉。 8、禁用休眠功能 单击“开始”--“控制面板”--“电源管理”--“休眠”,将“启用休眠”前的勾去掉。 9、关闭“Internet时间同步”功能 依次单击“开始”--“控制面板”--“日期、时间、语言和区域选项”,然后单击“Internet时间”,取消“自动与Internet时间服务同步”前的勾。 10、关闭磁盘索引 打开我的电脑 ,右击驱动器,选“属性”,取消“使用索引以便快速查找文件”前面的勾。 11、禁用多余的服务组件 右键单击“我的电脑”--“管理”--“服务和应用程序”--“服务”,在右窗格将不需要的服务设为禁用或手动。 12、关闭华生医生Dr.Watson 单击“开始”--“运行”--输入“drwtsn32”命令,打开华生医生Dr.Watson 窗口,只保留“转储全部线程上下文”选项,取消其选项前边勾。 13、设置虚拟内存 虚拟内存最小值物理内存1.5—2倍,最大值为物理内存的2—3倍。 虚拟内存设置方法是: 右击我的电脑—属性--高级--性能设置--高级--虚拟内存更改--在驱动器列表中选中系统盘符--自定义大小--在“初始大小”和“最大值”中设定数值,然后单击“设置”按钮,最后点击“确定”按钮退出。 14、修改注册表 (1)加快开机及关机速度 在[开始]-->[运行]-->键入[Regedit]-->[HKEY_CURRENT_USER]-->[Control Panel]-->[Desktop],将字符串值[Hung要先删除,后再设置才行。 1、右击我的电脑—属性--高级--性能设置--高级--虚拟内存更改--在驱动器列表中选中你设有页面文件的盘符(D)--选“无页面文件”--“设置”,将这个盘的页面文件设置删除; 2、在驱动器列表中选中你要设有页面文件的盘符(C)--选“自定义大小”--在“初始大小”和“最大值”中输入数值--“设置”--“确定”。 AppTimeout]的数值数据更改为[200],将字符串值[WaitToKillAppTimeout]的数值数据更改为1000.另外在[HKEY_LOCAL_MACHINE]-->[System]-->[CurrentControlSet]-->[Control],将字符串值[HungAppTimeout]的数值数据更改为[200],将字符串值[WaitToKillServiceTimeout]的数值数据更改1000. (2)加快预读能力改善开机速度 在[开始]-->[运行]-->键入[Regedit]-->[HKEY_LOCAL_MACHINE]-->[SYSTEM]-->[CurrentControlSet]-->[Control]-->[SessionManager]-->[MemoryManagement],在[PrefetchParameters]右边窗口,将[EnablePrefetcher]的数值数据如下更改,如使用PIII 800MHz CPU以上的建议将数值数据更改为4或5。 (3)加快宽带接入速度 家用版本:打开注册表编辑器,找到HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows,增加一个名为Psched的项,在Psched右面窗口增加一个Dword值NonBestEffortLimit数值数据为0。 (4)加快宽带网速 打开注册表编辑器,找到 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip \ parameters, 右击parameters在右边空白处,选择左“新建”—>DWORD值边,将其命名为“DefaultTTL”,右击鼠标选出择“修改”,值改为 80(十六进制),或128(十进制)。 再点选左边窗格里 Tcpip 将 GlobalMaxTcpWindowSize 的值改为7FFF。 不要怕麻烦哦!!! “答案来源于网络,供您参考” 希望以上信息可以帮到您!

牧明 2019-12-02 02:17:19 0 浏览量 回答数 0

问题

【分享】html5 开发工具——WeX5中的各种绑定方式

小太阳1号 2019-12-01 21:25:48 4942 浏览量 回答数 3

问题

使用阿里云服务器进行开发的小结

gameover 2019-12-01 21:06:23 15827 浏览量 回答数 23

问题

【分享】WeX5的正确打开方式(3)——绑定机制

小太阳1号 2019-12-01 21:23:54 5393 浏览量 回答数 3

问题

对症下药:Tomcat停机过程分析与线程处理方法

驻云科技 2019-12-01 21:36:46 4001 浏览量 回答数 0

回答

本文为您介绍如何通过资源编排服务(ROS)创建资源栈。 前提条件 进行操作前,请确保您已经注册了阿里云账号。如还未注册,请先完成账号注册。 背景信息 如果您已建好模板,请在资源编排控制台我的模板页面,选择已建好的模板,单击创建栈进入创建流程。此外,您也可以根据模板样例快速创建资源栈,详情请参见通过模板样例创建资源栈。 操作步骤 登录资源编排控制台。 在页面左上角的地域下拉列表,选择资源栈的所在地域。 在左侧导航栏选择资源栈。 在资源栈列表页面,单击创建资源栈。 在创建资源栈向导的选择模板页面,根据所需选择模板,单击下一步。 您可以选择已有模板,也可以使用示例模板。 模板为JSON格式的文本文件,使用UTF-8编码。有关模板的详情,请参见模板结构说明。您也可以使用可视化编辑器编辑模板,详情请参见可视化编辑器示例。 在创建资源栈向导的配置模板参数页面,根据控制台提示,配置资源栈名称和参数录入,单击下一步。 在创建资源栈向导的配置资源栈页面,根据控制台提示,配置资源栈策略、失败时回滚、超时设置和标签,单击下一步。 资源的创建或更新未在超时设置的时间内完成,系统自动判断该操作失败,再根据失败时回滚设置,判断是否回滚到创建或更新资源之前的状态。 在创建资源栈向导的确认页面,单击创建资源栈。 您可以在资源栈管理页面,查看当前创建的资源栈状态和信息。 本文为您介绍如何通过资源编排服务(ROS)查看资源栈。 前提条件 请确保您已创建资源栈,操作方法请参见创建资源栈。 操作步骤 登录资源编排控制台。 在页面左上角的地域下拉列表,选择资源栈的所在地域。 在左侧导航栏选择资源栈。 单击资源栈名称下面的资源栈ID。 在资源栈管理页面,您可以执行以下操作: 单击资源栈信息,查看基本信息、标签和资源栈策略。 单击事件,查看资源栈生命周期中发生的每一个事件。 单击资源,查看资源栈所包括的每一个资源的信息。 单击输出,查看创建资源栈时,您申明的需要输出的实例信息。 单击参数,查看创建资源栈时,您指定的参数,包括ROS提供的以ALIYUN::开始的内部参数。 单击模板,查看资源栈所对应的模板信息。 单击更改集,管理该资源栈下的更改集。 本文为您介绍如何通过资源编排服务(ROS)更新资源栈。 前提条件 请确保您已创建资源栈,操作方法请参见创建资源栈。 背景信息 如果您只需要修改当前的资源栈模板、资源栈配置,不需要修改资源栈的所属地域,请选择更新资源栈。 如果您需要修改当前的资源栈模板、资源栈配置、资源栈的所属地域,请选择重新创建资源栈,详情请参见重建资源栈。 操作步骤 登录资源编排控制台。 在页面左上角的地域下拉列表,选择资源栈的所在地域。 在左侧导航栏选择资源栈。 单击资源栈名称对应的右侧操作栏中的更新。 在编辑资源栈向导的配置模板参数页面,根据控制台提示,重新配置参数录入。 说明 当您更新资源栈内资源的参数信息时,请确保相关参数是允许更新的,详情请参见对应的资源类型文档中各参数说明。 如果您只修改模板参数,无需修改资源栈策略,则可以单击确认修改,结束更新资源栈操作。 单击下一步。 在创建资源栈向导的配置资源栈页面,根据控制台提示,重新配置资源栈策略、失败时回滚、超时设置和标签。 单击确认修改。 如果您需要确认更新配置,则可以单击下一步,确认配置无误后,再单击确认修改,完成更新资源栈操作。 本文为您介绍如何通过资源编排服务(ROS)重建资源栈。 前提条件 请确保您已创建资源栈,操作方法请参见创建资源栈。 背景信息 如果您只需要修改当前的资源栈模板、资源栈配置,不需要修改资源栈的所属地域,请选择更新资源栈,详情请参见更新资源栈 。 如果您需要修改当前的资源栈模板、资源栈配置、资源栈的所属地域,请选择重新创建资源栈。 操作步骤 登录资源编排控制台。 在页面左上角的地域下拉列表,选择资源栈的所在地域。 在左侧导航栏选择资源栈。 单击资源栈名称对应的右侧操作栏中的1,选择重新创建。 单击上一步。 在重新创建向导的选择模板页面,您可以重新选择已有模板或示例模板,单击下一步。 在重新创建向导的配置模板参数页面,您可以重新配置资源栈名称和参数录入,单击下一步。 在重新创建向导的配置资源栈页面,您可以重新配置资源栈策略、失败时回滚、超时设置和标签,单击下一步。 资源的创建或更新未在超时设置的时间内完成,系统自动判断该操作失败,再根据失败时回滚设置,判断是否回滚到创建或更新资源之前的状态。 在重新创建向导的确认页面,单击创建资源栈。 您可以在资源栈管理页面,查看当前重新创建的资源栈状态和信息。 本文为您介绍如何通过资源编排服务(ROS)删除资源栈。 前提条件 请确保您已创建资源栈,操作方法请参见创建资源栈。 操作步骤 登录资源编排控制台。 在页面左上角的地域下拉列表,选择资源栈的所在地域。 在左侧导航栏选择资源栈。 单击资源栈名称对应的右侧操作栏中的删除。 在确认删除资源栈页面,选择删除方式。 资源栈删除方式如下: 保留资源:如果删除,当前资源栈创建的资源将会被保留。 释放资源:如果删除,当前资源栈创建的资源将会被释放,请您谨慎操作。 单击确认。 通过本文您可以了解嵌套资源栈的结构、最佳实践、常见模板、更新行为和输出值,并了解查看嵌套资源栈及其所属的父资源栈的操作方法。 嵌套资源栈是作为其他资源栈的一部分来创建的资源栈。您可以在另一个资源栈中使用ALIYUN::ROS::Stack资源创建嵌套资源栈。 随着基础设施的发展,常见模式可合并,以便在多个模板中声明相同的组件。您可以分离这些常见组件并为其创建专用模板。然后使用模板中的资源来引用其他模板,也就是创建嵌套资源栈。 例如,您有用于大多数资源栈的负载均衡器配置。您可以为负载均衡器创建专用模板,而不是将相同的配置复制并粘贴到您的模板中。您只需使用资源从其他模板中引用该模板。 嵌套资源栈的结构 嵌套资源栈本身可以包含其他嵌套资源栈,构成一个资源栈层次结构,如下图所示。根资源栈是所有嵌套资源栈最终归属的父资源栈。此外,每个嵌套资源栈都有一个直属父资源栈。对于第一级的嵌套资源栈而言,根资源栈也是父资源栈。 资源栈A是该层次结构中所有其他嵌套资源栈的根资源栈。 对于资源栈B来说,资源栈A既是父资源栈,也是根资源栈。 对于资源栈D,资源栈C是父资源栈;而对于资源栈C来说,资源栈B是父资源栈。 嵌套资源栈 使用嵌套资源栈来声明常见组件被视为最佳做法。 某些资源栈操作(如资源栈更新等)应从根资源栈启动,而不是直接在嵌套资源栈上执行。此外,在某些情况下,嵌套资源栈会影响资源栈操作的执行。 最佳实践 资源编排之嵌套资源栈 使用嵌套资源栈来重复使用常见模板 随着您的基础设施的发展,常见模板模式可合并以便声明每个模板中的相同组件。您可以分离这些常见组件并为其创建专用模板。这样一来,您可以混合和匹配不同的模板,但使用嵌套资源栈来创建单个统一资源栈。嵌套资源栈是可创建其他资源栈的资源栈。要创建嵌套资源栈,可使用您的模板中的ALIYUN::ROS::Stack 资源来引用其他模板。 例如,您有用于大多数资源栈的负载均衡器配置。您可以为负载均衡器创建专用模板,而不是将相同的配置复制并粘贴到您的模板中。然后,您只需使用ALIYUN::ROS::Stack资源从其他模板中引用该模板。如果更新负载均衡器模板,引用该模板的任何资源栈将使用更新过的负载均衡器(仅当您更新该资源栈后)。除了简化更新之外,该方法还允许您使用专家来创建和维护您不熟悉的组件。您只需引用其模板即可。 嵌套资源栈资源的更新行为 如果模板包括一个或多个嵌套资源栈,则ROS也会为每个嵌套资源栈启动更新。这对于确定嵌套资源栈是否已修改是必要的。ROS只更新嵌套资源栈中那些在相应模板中指定了更改的资源。 使用嵌套资源栈的输出值 嵌套资源栈是您使用ALIYUN::ROS::Stack资源在其他资源栈中创建的资源栈。利用嵌套资源栈,您可从一个资源栈部署和管理所有资源。您可将来自嵌套资源栈组中的一个资源栈的输出用作该组中的另一个资源栈的输入。 查看属于父资源栈的嵌套资源栈 登录资源编排控制台。 在左侧导航栏选择资源栈。 在资源栈列表页面,单击要查看其嵌套资源栈的父资源栈的名称。 说明 如果父资源栈也是嵌套资源栈,需要勾选显示嵌套资源栈。 单击资源页签。 查找类型为ALIYUN::ROS::Stack的资源。 查看嵌套资源栈的父资源栈 登录资源编排控制台。 在左侧导航栏选择资源栈。 在资源栈列表页面,勾选显示嵌套资源栈,查看资源栈列表。 单击要查看其父资源栈的嵌套资源栈的名称。 单击资源栈信息,查看父资源栈ID。 通过本文您可以了解资源栈策略的定义,以及设置、更新和修改资源栈策略的方法。 在创建资源栈时,允许对所有资源执行所有更新操作。默认情况下,具有资源栈更新权限的任何人均可更新资源栈中的所有资源。在更新期间,一些资源可能需要中断。使用资源栈策略可以防止资源栈资源在资源栈更新过程中被意外更新或删除。资源栈策略是一个JSON/YAML文档,该文档定义可对指定资源执行的更新操作。 设置资源栈策略后,默认情况下将保护资源栈中的所有资源。要允许对特定资源进行更新,您可在资源栈策略中为这些资源指定明确的Allow语句。您只能为每个资源栈定义一个资源栈策略,但在一个策略中可以保护多个资源。资源栈策略适用于所有尝试更新资源栈的ROS用户。您不能将不同的资源栈策略与不同的用户关联。 资源栈策略仅在资源栈更新过程中适用。与RAM策略不同,它不提供访问控制。仅将资源栈策略用作故障保护功能来防止意外更新特定资源栈资源。 主题 示例资源栈策略 定义资源栈策略 设置资源栈策略 更新受保护资源 修改资源栈策略 资源栈策略示例 示例资源栈策略 如下示例资源栈策略阻止更新WebServers资源: { "Statement" : [ { "Effect" : "Allow", "Action" : "Update:", "Principal": "", "Resource" : "" }, { "Effect" : "Deny", "Action" : "Update:", "Principal": "*", "Resource" : "LogicalResourceId/WebServers" } ] } 当您设置资源栈策略时,将默认保护所有资源。为了允许对所有资源进行更新,我们添加了一个Allow语句来允许对所有资源执行的所有操作。虽然Allow语句指定所有资源,但显式Deny语句将为具有WebServers逻辑 ID的资源覆盖前者。此Deny语句阻止对WebServers资源进行的所有更新操作。 需要Principal元素,但仅支持通配符*,这意味着语句适用于所有委托人(用户或服务等)。 说明 在资源栈更新期间,ROS自动更新依赖其他更新的资源。例如,ROS自动更新引用更新的资源。但如果这些资源与资源栈策略关联,您必须具有权限才能更新。 定义资源栈策略 定义资源栈策略在创建资源栈时,未设置资源栈策略,因此允许对所有资源执行所有更新操作。要阻止对资源栈资源执行更新操作,可定义一个资源栈策略,然后对资源栈设置该策略。资源栈策略是一个JSON/YAML文档,它定义ROS用户可以执行的ROS资源栈更新操作以及这些操作应用到的资源。在创建资源栈时,可通过指定一个包含资源栈策略的文本文件或键入该策略来设置资源栈策略。在资源栈上设置资源栈策略时,默认情况下会拒绝未显式允许的任何更新。 您可定义一个带5个元素的资源栈策略:Effect、Action、Principal、Resource 和 Condition。下面的伪代码显示了资源栈策略语法。 { "Statement" : [ { "Effect" : "Deny_or_Allow", "Action" : "update_actions", "Principal" : "*", "Resource" : "LogicalResourceId/resource_logical_ID", "Condition" : { "StringEquals_or_StringLike" : { "ResourceType" : [resource_type, ...] } } } ] } Effect 确定是拒绝还是允许对指定资源执行指定的操作。您只能指定Deny或Allow,例如: "Effect" : "Deny" 说明 如果资源栈策略包含重叠语句(同时允许和拒绝对资源进行更新),则Deny语句始终将覆盖Allow语句。要确保某一资源受到保护,请对该资源使用Deny语句。 Action 指定拒绝或允许的更新操作: Update:Modify 指定在对资源应用更改期间不会中断或有某些中断的更新操作。 Update:Delete 指定删除资源的更新操作。从资源栈模板中完全删除资源的更新都需要此操作。 Update:* 指定所有更新操作。星号是通配符,代表所有更新操作。 说明 Action还可以指定Update:Replace作为保留功能。但替换功能,目前尚未支持。 以下示例说明如何只指定修改和删除操作: "Action" : ["Update:Modify", "Update:Delete"] 要允许除某个更新操作之外的所有更新操作,请使用 NotAction。例如,要允许除Update:Delete之外的所有更新操作,请使用 NotAction,如本示例中所示: { "Statement" : [ { "Effect" : "Allow", "NotAction" : "Update:Delete", "Principal": "", "Resource" : "" } ] } Principal 指定策略应用于的实体。需要此元素,但仅支持通配符*,这意味着策略应用于所有主体。 Resource 指定将应用策略的资源的逻辑ID。要指定资源类型,请使用Condition元素。 要指定一个资源,请使用其逻辑ID。例如: "Resource" : ["LogicalResourceId/myECS"] 您可以对逻辑ID使用通配符。例如,如果您对所有相关资源使用一个通用逻辑ID前缀,则可使用通配符指定所有资源: "Resource" : ["LogicalResourceId/Prefix*"] 您还可以对资源使用Not元素。例如,要允许对所有资源执行除某个更新之外的所有更新,请使用NotResource元素保护该资源: { "Statement" : [ { "Effect" : "Allow", "Action" : "Update:", "Principal": "", "NotResource" : "LogicalResourceId/WebServers" } ] } 设置资源栈策略时,会拒绝未显式允许的任何更新。通过允许更新WebServers资源之外的所有资源,会拒绝更新WebServers资源。 Condition 指定应用策略的资源类型。要指定特定资源的逻辑ID,请使用Resource元素。 您可以指定资源类型(如所有ECS和RDS数据库实例),如以下示例所示: { "Statement" : [ { "Effect" : "Deny", "Principal" : "", "Action" : "Update:", "Resource" : "", "Condition" : { "StringEquals" : { "ResourceType" : ["ALIYUN::ECS::Instance", "ALIYUN::RDS::DBInstance"] } } }, { "Effect" : "Allow", "Principal" : "", "Action" : "Update:", "Resource" : "" } ] } Allow语句授予对所有资源的更新权限,而Deny语句拒绝对ECS和RDS数据库实例的更新。Deny 语句始终覆盖允许操作。 您可以对资源类型使用通配符。例如,您可以使用通配符拒绝所有ALIYUN ECS资源(如实例、安全组和子网)的更新权限,示例如下: "Condition" : { "StringLike" : { "ResourceType" : ["ALIYUN::ECS::*"] } } 使用通配符时,必须使用StringLike条件。 设置资源栈策略 您可以使用控制台或ALIYUN ROS CLI在创建资源栈时应用资源栈策略。您也可以使用ALIYUN ROS CLI将资源栈策略应用于现有资源栈。应用资源栈策略后,无法将其从资源栈中删除,但您可以使用ALIUN ROS CLI修改该策略。 资源栈策略适用于所有尝试更新资源栈的ROS用户。您不能将不同的资源栈策略与不同的用户关联。 有关如何编写资源栈策略的信息,请参见定义资源栈策略。 在创建资源栈时设置资源栈策略(控制台) 登录资源编排控制台。 在左侧导航栏选择资源栈。 在资源栈列表页面,选择创建资源栈。 在创建资源栈向导的配置资源栈页面,选择资源栈策略为输入资源栈策略或上传文件。配置资源栈 配置资源栈策略。 在控制台中直接编写策略,请选择输入资源栈策略,在文本字段中直接输入资源栈策略。 在单独文件中定义策略,请选择上传文件,上传包含资源栈策略的文件。 按照创建资源栈向导提示继续配置,完成资源栈创建。 在创建资源栈时设置资源栈策略(CLI) 将aliyun ros CreateStack命令与--StackPolicyBody选项结合使用可键入修改的策略,或将此命令与--StackPolicyURL选项结合使用可指定包含策略的文件。 将aliyun ros CreateChangeSet命令与--StackPolicyBody选项结合使用可键入修改的策略,或将此命令与--StackPolicyURL选项结合使用可指定包含策略的文件。 在现有资源栈上设置资源栈策略(仅限 CLI) 将aliyun ros SetStackPolicy命令与--StackPolicyBody 选项结合使用可键入修改的策略,或将此命令与--StackPolicyURL选项结合使用可指定包含策略的文件。 说明 要将策略添加到现有资源栈中,您必须有权执行ROS SetStackPolicy 操作。 更新受保护资源 要更新受保护的资源,可创建一个覆盖资源栈策略并允许对这些资源进行更新的临时策略。在更新资源栈时指定覆盖策略。覆盖策略不会永久更改资源栈策略。 要更新保护的资源,您必须具有操作ROS接口SetStackPolicy的权限。设置ROS权限的操作方法,请参见RAM控制访问。 说明 在资源栈更新期间,ROS自动更新依赖其他更新的资源。例如,ROS自动更新引用更新的资源。如果这些资源与资源栈策略关联,则您必须具有权限才能更新。 更新受保护的资源(控制台) 登录资源编排控制台。 在左侧导航栏选择资源栈。 在资源栈列表页面,选择需要更新的资源栈,单击更新。 按照编辑资源栈向导提示进行配置,在配置资源栈页面,选择输入资源栈策略或上传文件。配置资源栈 配置临时资源栈策略。 指定临时的资源栈策略,仅本次更新生效。覆盖策略必须为您要更新的受保护资源指定Allow语句。例如,要更新所有受保护资源,可以指定允许所有更新的临时覆盖策略: { "Statement" : [ { "Effect" : "Allow", "Action" : "Update:", "Principal": "", "Resource" : "*" } ] } 说明 ROS仅在此更新期间应用覆盖策略。覆盖策略不会永久更改资源栈策略。如果您需要修改资源栈策略,请参见修改资源栈策略 。 按照编辑资源栈向导提示继续配置,完成资源栈更新。 更新受保护资源(CLI) 将aliyun ros UpdateStack命令与--StackPolicyDuringUpdateBody选项结合使用可键入修改的策略,或将此命令与 --StackPolicyDuringUpdateURL 选项结合使用可指定包含策略的文件。 将aliyun ros CreateChangeSet命令与--StackPolicyDuringUpdateBody选项结合使用可键入修改的策略,或将此命令与--StackPolicyDuringUpdateURL选项结合使用可指定包含策略的文件。 说明 ROS仅在此更新期间应用覆盖策略。覆盖策略不会永久更改资源栈策略。要修改资源栈策略,请参见修改资源栈策略。 修改资源栈策略 要保护其他资源或从资源中删除保护,请修改资源栈策略。例如,当您将要保护的数据库添加到资源栈时,会将该数据库的Deny语句添加到资源栈策略。要修改策略,您必须有权使用SetStackPolicy操作。 修改资源栈策略(控制台) 登录资源编排控制台。 在左侧导航栏选择资源栈。 在资源栈列表中,单击需要修改的资源栈名称。 在资源栈信息页面的资源栈策略区域,单击编辑。 在修改资源栈策略页面,输入资源栈策略。修改资源栈策略 单击确认。 修改资源栈策略(CLI) 将aliyun ros SetStackPolicy命令与--StackPolicyBody选项结合使用可键入修改的策略,或将此命令与 --StackPolicyURL 选项结合使用可指定包含策略的文件。 您无法删除资源栈策略。要从所有资源删除全部保护,您可修改策略以明确允许对所有资源执行全部操作。以下策略允许对所有资源进行全部更新: { "Statement" : [ { "Effect" : "Allow", "Action" : "Update:", "Principal": "", "Resource" : "*" } ] } 在更新资源栈时修改资源栈策略(CLI) 将aliyun ros UpdateStack命令与--StackPolicyBody选项结合使用可键入修改的策略,或将此命令与--StackPolicyURL选项结合使用可指定包含策略的文件。 将aliyun ros CreateChangeSet命令与--StackPolicyBody选项结合使用可键入修改的策略,或将此命令与--StackPolicyURL选项结合使用可指定包含策略的文件。 资源栈策略示例 以下示例策略说明如何阻止对所有资源栈资源和特定资源进行更新,并阻止特定类型的更新。 阻止对所有资源栈资源的更新 要阻止对所有资源栈资源的更新,以下策略为所有资源的所有更新操作指定 Deny 语句。 { "Statement" : [ { "Effect" : "Deny", "Action" : "Update:", "Principal": "", "Resource" : "*" } ] } 阻止对单个资源的更新 { "Statement" : [ { "Effect" : "Deny", "Action" : "Update:", "Principal": "", "Resource" : "LogicalResourceId/WebServers" }, { "Effect" : "Allow", "Action" : "Update:", "Principal": "", "Resource" : "*" } ] } 您可以使用默认拒绝来获得与上一示例相同的结果。设置资源栈策略时,ROS会拒绝未显式允许的任何更新。以下策略允许对除WebServers资源(默认情况下,拒绝更新此资源)之外的所有资源进行的更新。 { "Statement" : [ { "Effect" : "Allow", "Action" : "Update:", "Principal": "", "NotResource" : "LogicalResourceId/WebServers" } ] } 说明 使用默认拒绝存在风险。如果您策略中的其他位置具有Allow语句 (例如,使用通配符的 Allow 语句),则可能意外授予(原本不打算授予)对资源的更新权限。由于显示拒绝将覆盖任何允许操作,因此可以使用Deny语句确保保护资源。 阻止对资源类型的所有实例进行更新 以下策略拒绝针对RDS数据库实例资源类型的所有更新操作。使用Allow语句允许对所有其他资源栈资源进行全部更新操作。Allow语句不应用于RDS数据库实例资源,因为Deny语句始终覆盖允许操作。 { "Statement" : [ { "Effect" : "Deny", "Action" : "Update:", "Principal": "", "Resource" : "", "Condition" : { "StringEquals" : { "ResourceType" : ["ALIYUN::RDS::DBInstance"] } } }, { "Effect" : "Allow", "Action" : "Update:", "Principal": "", "Resource" : "" } ] } 阻止对嵌套资源栈进行更新 以下策略拒绝针对ROS资源栈资源类型(嵌套资源栈)的所有更新操作。使用Allow语句允许对所有其他资源栈资源进行全部更新操作。Allow语句不会应用于ROS资源栈资源,因为Deny语句始终覆盖Allow操作。 { "Statement" : [ { "Effect" : "Deny", "Action" : "Update:", "Principal": "", "Resource" : "", "Condition" : { "StringEquals" : { "ResourceType" : ["ALIYUN::ROS::Stack"] } } }, { "Effect" : "Allow", "Action" : "Update:", "Principal": "", "Resource" : "" } ] }

1934890530796658 2020-03-24 18:33:53 0 浏览量 回答数 0

回答

概述 App() 代表顶层应用,管理所有页面和全局数据,以及提供生命周期回调等。它 也是一个构造方法,生成 App 实例。 一个小程序就是一个 App 实例。 每个小程序顶层一般包含三个文件。  app.json:应用配置  app.js:应用逻辑  app.acss:应用样式(可选) 简单示例 一个简单的 app.json 代码如下: "pages": [ "pages/index/index", "pages/logs/logs" ], "window": { "defaultTitle": "Demo" } } 这段代码配置指定小程序包含两个页面(index 和 logs),以及应用窗口的默认 标题设置为 “Demo”。 一个简单的 app.js 代码如下: onLaunch(options) { // 第一次打开 }, onShow(options) { // 小程序启动,或从后台被重新打开 }, onHide() { // 小程序从前台进入后台 }, onError(msg) { // 小程序发生脚本错误或 API 调用出现报错 console.log(msg); }, globalData: { // 全局数据 name: 'alipay', }, }); 26 app.json 全局配置 app.json 用于对小程序进行全局配置,设置页面文件的路径、窗口表现、多 tab 等。 以下是一个基本配置示例: "pages": [ "pages/index/index", "pages/logs/index" ], "window": { "defaultTitle": "Demo" } } 完整配置项如下: 属性 类型 是否必填 描述 pages Array 是 设置页面路径 window Object 否 设置默认页面的窗 口表现 tabBar Object 否 设置底部 tabbar 的表现 pages app.json 中的 pages 为数组属性,数组中每一项都是字符串,用于指定小程序 的页面。在小程序中新增或删除页面,都需要对 pages 数组进行修改。 pages 数组的每一项代表对应页面的路径信息,其中,第一项代表小程序的首 页。 页面路径不需要写任何后缀,框架会自动去加载同名的 .json、.js、.axml、.acss 文件。举例来说,如果开发目录为: │ ├──index │ │ ├── index.json │ │ ├── index.js │ │ ├── index.axml │ │ └── index.acss │ ├──logs │ │ ├── logs.json │ │ ├── logs.js │ │ └── logs.axml ├── app.json ├── app.js └── app.acss app.json 中应当如下配置: { "pages":[ "pages/index/index", "pages/logs/logs" ] } window window 用于设置小程序的状态栏、导航条、标题、窗口背景色等。 示例代码: { "window":{ "defaultTitle": "支付宝接口功能演示" } } 属性 类型 是否必 填 描述 最低版本 defaultTitle String 否 页面默认标题 - pullRefresh String 否 是否允许下拉刷新。默认 NO, 备注:下拉刷新生效的 前提是 allowsBounceVertical 值 为 YES allowsBounceV ertical String 否 是否允许向下拉拽。默认 YES, 支持 YES / NO 28 transparentTitl e String 否 导航栏透明设置。默认 none,支持 always 一直透 明 / auto 滑动自适应 / none 不透明 titlePenetrate String 否 是否允许导航栏点击穿透。 默认 NO,支持 YES / NO showTitleLoadi ng String 否 是否进入时显示导航栏的 loading。默认 NO,支持 YES / NO titleImage String 否 导航栏图片地址 - titleBarColor HexCol or 否 导航栏背景色,十六进制颜 色值(0-255) - backgroundCol or HexCol or 否 页面的背景色,十六进制颜 色值(0-255) - backgroundIm ageColor HexCol or 否 下拉露出显示的背景图底 色,十六进制颜色值(0- 255) - backgroundIm ageUrl String 否 下拉露出显示的背景图链接 - gestureBack String 否 iOS 用,是否支持手势返 回。默认 NO,支持 YES / NO enableScrollBa r Boolea n 否 Android 用,是否显示 WebView 滚动条。默认 YES,支持 YES / NO onReachBotto mDistance Number 否 页面上拉触底时触发时距离 页面底部的距离,单位为 px。相关文档页面事件处理 函数 1.19.0 ,目前 iOS 在 page.json 下设 置无效,只能全 局设置。 29 tabBar 如果你的小程序是一个多 tab 应用(客户端窗口的底部栏可以切换页面),那么 可以通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页 面。 注意:  通过页面跳转(my.navigateTo)或者页面重定向(my.redirectTo)所到达的页面,即使 它是定义在 tabBar 配置中的页面,也不会显示底部的 tab 栏。  tabBar 的第一个页面必须是首页。 tabBar 配置项有以下: 属性 类型 是否必填 描述 textColor HexColor 否 文字颜色 selectedColor HexColor 否 选中文字颜色 backgroundColor HexColor 否 背景色 items Array 是 每个 tab 配置 每个 item 配置: 属性 类型 是否必填 描述 pagePath String 是 设置页面路径 name String 是 名称 icon String 否 平常图标路径 activeIcon String 否 高亮图标路径 icon 图标推荐大小为 60×60 px 大小,系统会对传入的非推荐尺寸的图片进行非 等比拉伸或缩放。 示例代码: "tabBar": { 30 "textColor": "#dddddd", "selectedColor": "#49a9ee", "backgroundColor": "#ffffff", "items": [ { "pagePath": "pages/index/index", "name": "首页" }, { "pagePath": "pages/logs/logs", "name": "日志" } ] } } app.acss 全局样式 app.acss 作为全局样式,作用于当前小程序的所有页面。 ACSS 是一套样式语言,用于描述 AXML 的组件样式,决定 AXML 的组件的显 示效果。 为适应广大前端开发者,ACSS 和 CSS 规则完全一致,100% 可以用。同时为更 适合开发小程序,对 CSS 进行了扩充。 ACSS 支持 px,rpx,vh,vw 等单位。 rpx rpx(responsive pixel)可以根据屏幕宽度进行自适应,规定屏幕宽为 750rpx。以 Apple iPhone6 为例,屏幕宽度为 375px,共有 750 个物理像 素,则 750rpx = 375px = 750 物理像素,1rpx = 0.5px = 1 物理像素。 设备 rpx 换算 px(屏幕宽度 / 750) px 换算 rpx(750 / 屏幕宽 度) iPhone5 1rpx = 0.42px 1px = 2.34rpx iPhone6 1rpx = 0.5px 1px = 2rpx iPhone6 Plus 1rpx = 0.552px 1px = 1.81rpx 样式导入 使用 @import 语句可以导入外联样式表,@import 后需要加上外联样式表相对 路径,用;表示结束。 示例代码: .sm-button { padding: 5px; } /** app.acss **/ @import "./button.acss"; .md-button { padding: 15px; } 导入路径支持从 node_modules 目录载入第三方模块,例如 page.acss: @import "./button.acss"; /相对路径/ 32 @import "/button.acss"; /项目绝对路径/ @import "third-party/page.acss"; /第三方 npm 包路径/ 内联样式 组件上支持使用 style、class 属性来控制样式。 style 属性 用于接收动态样式,样式在运行时会进行解析。行内样式不支持!important 优先 级规则。 class 属性 用于接收静态样式,属性值是样式规则中类选择器名(样式类名)的集合,样式类 名不需要带上.,多个类名间以空格分隔。请静态样式写进 class 中,避免将静态 样式写进 style 中,以免影响渲染速度。 选择器 同 CSS3 保持一致。 注意:  .a-, .am- 开头的类选择器为系统组件占用,不可使用。  不支持属性选择器。 全局样式与局部样式  app.acss 中的样式为全局样式,作用于每一个页面。  页面文件夹内的 .acss 文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖 app.acss 中相同的选择器。 本地资源引用 ACSS 文件里的本地资源引用请使用绝对路径的方式,不支持相对路径引用。例 如: /* 支持 / background-image: url('/images/ant.png'); / 不支持 */ background-image: url('./images/ant.png'); 33 app.js 注册小程序 App(object: Object) App() 用于注册小程序,接受一个 Object 作为属性,用来配置小程序的生命周 期等。 App() 必须在 app.js 中调用,必须调用且只能调用一次。 object 属性说明 属性 类型 描述 触发时机 onLaunch Function 生命周期回调:监 听小程序初始化 当小程序初始化完 成时触发,全局只 触发一次 onShow Function 生命周期回调:监 听小程序显示 当小程序启动,或 从后台进入前台显 示时触发 onHide Function 生命周期回调:监 听小程序隐藏 当当前页面被隐藏 时触发,例如跳 转、按下设备 Home 键离开 onError Function 监听小程序错误 当小程序发生 js 错误时触发 onShareAppMessage Function 全局分享配置 - 前台/后台定义:  小程序用户点击右上角关闭,或者按下设备 Home 键离开支付宝时,小程序并不会直接销 毁,而是进入后台。  当用户再次进入支付宝或再次打开小程序时,小程序会从后台进入前台。  只有当小程序进入后台 5 分钟后,或占用系统资源过高,才会被真正销毁。 onLaunch(object: Object) 及 onShow(object: Object) object 属性说明: 属性 类型 描述 34 query Object 当前小程序的 query,从启动参数的 query 字段解析而来 scene number 启动小程序的 场景值 path string 当前小程序的页面地址,从启动参数 page 字段解析而来,page 忽略时默认为首页 referrerInfo Object 来源信息 比如,启动小程序的 schema url 如下: alipays://platformapi/startapp?appId=1999&query=number%3D1&page=x%2Fy%2 Fz  小程序首次启动时,onLaunch 方法可获取 query、path 属性值。  小程序在后台被用 schema 打开,也可从 onShow 方法中获取 query、path 属性值。 App({ onLaunch(options) { // 第一次打开 console.log(options.query); // {number:1} console.log(options.path); // x/y/z }, onShow(options) { // 从后台被 schema 重新打开 console.log(options.query); // {number:1} console.log(options.path); // x/y/z }, }); referrerInfo 子属性说明: 属性 类型 描述 最低版本 appId string 来源小程序 - sourceServiceId string 来源插件,当处于插件运行模式时可见 1.11.0 35 extraData Object 来源小程序传过来的数据。 - 注意:  不要在 onShow 中进行 redirectTo 或 navigateTo 等操作页面栈的行为。  不要在 onLaunch 里调用 getCurrentPages(),因为此时 page 还未生成。 onHide() 小程序从前台进入后台时触发 onHide() 。 示例代码: App({ onHide() { // 进入后台时 console.log('app hide'); }, }); onError(error: String) 小程序发生脚本错误时触发。 示例代码: App({ onError(error) { // 小程序执行出错时 console.log(error); }, }); onShareAppMessage(object: Object) 全局分享配置。当页面未设置 page.onShareAppMessage 时,调用分享会执行 全局的分享设置,具体见 分享 。 globalData 全局数据 App() 中可以设置全局数据 globalData。 示例代码: // app.js App({ globalData: 1 }); getApp 方法 小程序提供了全局的 getApp() 方法,可获取当前小程序实例,一般用于在子页 面中获取顶层应用。 var app = getApp(); console.log(app.globalData); // 获取 globalData 使用过程中,请注意以下几点:  App() 函数中不可以调用 getApp(),可使用 this 可以获取当前小程序实例。  通过 getApp() 获取实例后,请勿私自调用生命周期回调函数。  请区分全局变量及页面局部变量,比如: // app.js App({ //定义全局变量 globalData,在整个 App 中有效 globalData: 1 }); // a.js // 定义页面局部变量 localValue,只在 a.js 有效 var localValue = 'a'; // 获取 app 实例 var app = getApp(); // 拿到全局数据,并改变它 app.globalData++; // b.js // 定义页面局部变量 localValue,只在 b.js 有效 var localValue = 'b'; // 如果 a.js 先运行,globalData 会返回 2 console.log(getApp().globalData); a.js 和 b.js 两个文件中都声明了变量 localValue,但并不会互相影响,因为各 个文件声明的局部变量和函数只在当前文件下有效。 内容来源:https://developer.aliyun.com/article/756818?spm=a2c6h.12873581.0.dArticle756818.26162b70Su1GZy&groupCode=tech_library

KaFei 2020-04-27 13:54:36 0 浏览量 回答数 0

问题

【精品问答】130+大数据面试汇总

问问小秘 2019-12-01 21:52:42 1644 浏览量 回答数 2

问题

10+年程序员总结的20+条经验教训

雅蕾 2019-12-01 21:56:26 7714 浏览量 回答数 0

回答

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension ,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。 什么是“状态管理模式”? 让我们从一个简单的 Vue 计数应用开始: new Vue({ // state data () { return { count: 0 } }, // view template: <div>{{ count }}</div>, // actions methods: { increment () { this.count++ } } }) 这个状态自管理应用包含以下几个部分: state,驱动应用的数据源; view,以声明方式将 state 映射到视图; actions,响应在 view 上的用户输入导致的状态变化。 以下是一个表示“单向数据流”理念的简单示意: 但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏: 多个视图依赖于同一状态。 来自不同视图的行为需要变更同一状态。 对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。 因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为! 通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。 这就是 Vuex 背后的基本思想,借鉴了 Flux 、Redux 和 The Elm Architecture 。与其他模式不同的是,Vuex 是专门为 Vue.js 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。 如果你想交互式地学习 Vuex,可以看这个 Scrimba 上的 Vuex 课程 ,它将录屏和代码试验场混合在了一起,你可以随时暂停并尝试。 vuex 什么情况下我应该使用 Vuex? Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。 如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式 就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。引用 Redux 的作者 Dan Abramov 的话说就是: Flux 架构就像眼镜:您自会知道什么时候需要它。

景凌凯 2020-03-25 22:32:44 0 浏览量 回答数 0

回答

一 系统介绍 Android 是Google开发的基于Linux平台的、开源的、智能手机操作系统。Android包括操作系统、中间件和应用程序,由于源代码开放,Android可以被移植到不同的硬件平台上。 围绕在Google的Android系统中,形成了移植开发和上层应用程序开发两个不同的开发方面。手机厂商从事移植开发工作,上层的应用程序开发可以由任何单位和个人完成,开发的过程可以基于真实的硬件系统,还可以基于仿真器环境。 作为一个手机平台,Android在技术上的优势主要有以下几点: - 全开放智能手机平台 - 多硬件平台的支持 - 使用众多的标准化技术 - 核心技术完整,统一 - 完善的SDK和文档 - 完善的辅助开发工具 Android的开发者可以在完备的开发环境中进行开发,Android的官方网站也提供了丰富的文档、资料。这样有利于Android系统的开发和运行在一个良好的生态环境中。 https://developer.android.com/about安卓开发者官方网站 从宏观的角度来看,Android是一个开放的软件系统,它包含了众多的源代码。从下至上,Android系统分成4个层次: 第1层次:Linux操作系统及驱动; 第2层次:本地代码(C/C++)框架; 第3层次:Java框架; 第4层次:Java应用程序。 Android系统的架构如图所示: 由于Android系统需要支持Java代码的运行,这部分内容是Android的运行环境(Runtime),由虚拟机和Java基本类组成。 对于Android应用程序的开发,主要关注第3层次和第4层次之间的接口。 二 学习路线 基础学习——JavaSE: 基础学习扩展——JavaEE: 基础学习扩展——Linux基础: Android开发学习——基础理论:系统架构分析: Android系统从底向上一共分了4层,每一层都把底层实现封装,并暴露调用接口给上一层。 Linux内核(Linux Kernel) Android运行在linux kernel 2.6之上,但是把linux内受GNU协议约束的部分做了取代,这样在Android的程序可以用于商业目的。 Linux 内核是硬件和软件层之间的抽象层。 中间件 中间件包括两部分: 核心库和运行时(libraries & Android runtime) 核心库包括,SurfaceManager 显示系统管理库,负责把2D或3D内容显示到屏幕;Media Framework 媒体库,负责支持图像,支持多种视频和音频的录制和回放;SQlite 数据库,一个功能强大的轻量级嵌入式关系数据库;WebKit 浏览器引擎等。 Dalvik虚拟机: 区别于Java虚拟机的是,每一个Android 应用程序都在它自己的进程中运行,都有一个属于自己的Dalvik 虚拟机,这一点可以让系统在运行时可以达到优化,程序间的影响大大降低。Dalvik虚拟机并非运行Java字节码,而是运行自己的字节码。 应用程序框架(Application Framework) 丰富而又可扩展性的视图(Views),可以用来构建应用程序, 它包括列表(lists),网格(grids), 文本框(text boxes),按钮( buttons), 可嵌入的web 浏览器。内容提供者(Content Providers)使得应用程序可以访问另一个应用程序的数据(如联系人数据库), 或者共享它们自己的数据。资源管理器(Resource Manager)提供非代码资源的访问,如本地字符串,图形,和布局文件( layoutfiles )。通知管理器(Notification Manager) 使得应用程序可以在状态栏中显示自定义的提示信息。活动管理器( Activity Manager) 用来管理应用程序生命周期并提供常用的导航回退功能。 三 基础知识 掌握java部分之后,可以使用开发工具进入android世界 您可以使用 Kotlin、Java 和 C++ 语言编写 Android 应用。Android SDK 工具会将您的代码连同任何数据和资源文件编译成一个 APK(Android 软件包),即带有 .apk 后缀的归档文件。一个 APK 文件包含 Android 应用的所有内容,它也是 Android 设备用来安装应用的文件。 每个 Android 应用都处于各自的安全沙盒中,并受以下 Android 安全功能的保护: • Android 操作系统是一种多用户 Linux 系统,其中的每个应用都是一个不同的用户; • 默认情况下,系统会为每个应用分配一个唯一的 Linux 用户 ID(该 ID 仅由系统使用,应用并不知晓)。系统会为应用中的所有文件设置权限,使得只有分配给该应用的用户 ID 才能访问这些文件; • 每个进程都拥有自己的虚拟机 (VM),因此应用代码独立于其他应用而运行。 • 默认情况下,每个应用都在其自己的 Linux 进程内运行。Android 系统会在需要执行任何应用组件时启动该进程,然后当不再需要该进程或系统必须为其他应用恢复内存时,其便会关闭该进程。 Android 系统实现了最小权限原则。换言之,默认情况下,每个应用只能访问执行其工作所需的组件,而不能访问其他组件。这样便能创建非常安全的环境,在此环境中,应用无法访问其未获得权限的系统部分。不过,应用仍可通过一些途径与其他应用共享数据以及访问系统服务: • 可以安排两个应用共享同一 Linux 用户 ID,在此情况下,二者便能访问彼此的文件。为节省系统资源,也可安排拥有相同用户 ID 的应用在同一 Linux 进程中运行,并共享同一 VM。应用还必须使用相同的证书进行签名。 • 应用可以请求访问设备数据(如用户的联系人、短信消息、可装载存储装置(SD 卡)、相机、蓝牙等)的权限。用户必须明确授予这些权限。如需了解详细信息,请参阅使用系统权限。 本文档的其余部分将介绍以下概念: • 用于定义应用的核心框架组件 • 用来声明组件和应用必需设备功能的清单文件。 • 与应用代码分离并允许应用针对各种设备配置适当优化其行为的资源。 应用组件 应用组件是 Android 应用的基本构建块。每个组件都是一个入口点,系统或用户可通过该入口点进入您的应用。有些组件会依赖于其他组件。 共有四种不同的应用组件类型: • Activity • 服务 • 广播接收器 • 内容提供程序 每种类型都有不同的用途和生命周期,后者会定义如何创建和销毁组件。以下部分将介绍应用组件的四种类型。 Activity Activity 是与用户交互的入口点。它表示拥有界面的单个屏幕。例如,电子邮件应用可能有一个显示新电子邮件列表的 Activity、一个用于撰写电子邮件的 Activity 以及一个用于阅读电子邮件的 Activity。尽管这些 Activity 通过协作在电子邮件应用中形成一种紧密结合的用户体验,但每个 Activity 都独立于其他 Activity 而存在。因此,其他应用可以启动其中任何一个 Activity(如果电子邮件应用允许)。例如,相机应用可以启动电子邮件应用内用于撰写新电子邮件的 Activity,以便用户共享图片。Activity 有助于完成系统和应用程序之间的以下重要交互: • 追踪用户当前关心的内容(屏幕上显示的内容),以确保系统继续运行托管 Activity 的进程。 • 了解先前使用的进程包含用户可能返回的内容(已停止的 Activity),从而更优先保留这些进程。 • 帮助应用处理终止其进程的情况,以便用户可以返回已恢复其先前状态的 Activity。 • 提供一种途径,让应用实现彼此之间的用户流,并让系统协调这些用户流。(此处最经典的示例是共享。) 您需将 Activity 作为 Activity 类的子类来实现。如需了解有关 Activity 类的更多信息,请参阅 Activity 开发者指南。 服务 服务是一个通用入口点,用于因各种原因使应用在后台保持运行状态。它是一种在后台运行的组件,用于执行长时间运行的操作或为远程进程执行作业。服务不提供界面。例如,当用户使用其他应用时,服务可能会在后台播放音乐或通过网络获取数据,但这不会阻断用户与 Activity 的交互。诸如 Activity 等其他组件可以启动服务,使该服务运行或绑定到该服务,以便与其进行交互。事实上,有两种截然不同的语义服务可以告知系统如何管理应用:已启动服务会告知系统使其运行至工作完毕。此类工作可以是在后台同步一些数据,或者在用户离开应用后继续播放音乐。在后台同步数据或播放音乐也代表了两种不同类型的已启动服务,而这些服务可以修改系统处理它们的方式: • 音乐播放是用户可直接感知的服务,因此,应用会向用户发送通知,表明其希望成为前台,从而告诉系统此消息;在此情况下,系统明白它应尽全力维持该服务进程运行,因为进程消失会令用户感到不快。 • 通常,用户不会意识到常规后台服务正处于运行状态,因此系统可以更自由地管理其进程。如果系统需要使用 RAM 来处理用户更迫切关注的内容,则其可能允许终止服务(然后在稍后的某个时刻重启服务)。 绑定服务之所以能运行,原因是某些其他应用(或系统)已表示希望使用该服务。从根本上讲,这是为另一个进程提供 API 的服务。因此,系统会知晓这些进程之间存在依赖关系,所以如果进程 A 绑定到进程 B 中的服务,系统便知道自己需使进程 B(及其服务)为进程 A 保持运行状态。此外,如果进程 A 是用户关心的内容,系统随即也知道将进程 B 视为用户关心的内容。由于存在灵活性(无论好坏),服务已成为非常有用的构建块,并且可实现各种高级系统概念。动态壁纸、通知侦听器、屏幕保护程序、输入方法、无障碍功能服务以及众多其他核心系统功能均可构建为在其运行时由应用实现、系统绑定的服务。 您需将服务作为 Service 的子类来实现。如需了解有关 Service 类的更多信息,请参阅服务开发者指南。 注意:如果您的应用面向 Android 5.0(API 级别 21)或更高版本,请使用 JobScheduler 类来调度操作。JobScheduler 的优势在于,它能通过优化作业调度来降低功耗,以及使用 Doze API,从而达到省电目的。如需了解有关使用此类的更多信息,请参阅 JobScheduler 参考文档。 广播接收器 借助广播接收器组件,系统能够在常规用户流之外向应用传递事件,从而允许应用响应系统范围内的广播通知。由于广播接收器是另一个明确定义的应用入口,因此系统甚至可以向当前未运行的应用传递广播。例如,应用可通过调度提醒来发布通知,以告知用户即将发生的事件。而且,通过将该提醒传递给应用的广播接收器,应用在提醒响起之前即无需继续运行。 许多广播均由系统发起,例如,通知屏幕已关闭、电池电量不足或已拍摄照片的广播。应用也可发起广播,例如,通知其他应用某些数据已下载至设备,并且可供其使用。尽管广播接收器不会显示界面,但其可以创建状态栏通知,在发生广播事件时提醒用户。但广播接收器更常见的用途只是作为通向其他组件的通道,旨在执行极少量的工作。例如,它可能会根据带 JobScheduler 的事件调度 JobService 来执行某项工作 广播接收器作为 BroadcastReceiver 的子类实现,并且每条广播都作为 Intent 对象进行传递。如需了解详细信息,请参阅 BroadcastReceiver 类。 内容提供程序 内容提供程序管理一组共享的应用数据,您可以将这些数据存储在文件系统、SQLite 数据库、网络中或者您的应用可访问的任何其他持久化存储位置。其他应用可通过内容提供程序查询或修改数据(如果内容提供程序允许)。例如,Android 系统可提供管理用户联系人信息的内容提供程序。 因此,任何拥有适当权限的应用均可查询内容提供程序(如 ContactsContract.Data),以读取和写入特定人员的相关信息。我们很容易将内容提供程序看作数据库上的抽象,因为其内置的大量 API 和支持时常适用于这一情况。但从系统设计的角度看,二者的核心目的不同。对系统而言,内容提供程序是应用的入口点,用于发布由 URI 架构识别的已命名数据项。因此,应用可以决定如何将其包含的数据映射到 URI 命名空间,进而将这些 URI 分发给其他实体。反之,这些实体也可使用分发的 URI 来访问数据。在管理应用的过程中,系统可以执行以下特殊操作: • 分配 URI 无需应用保持运行状态,因此 URI 可在其所属的应用退出后继续保留。当系统必须从相应的 URI 检索应用数据时,系统只需确保所属应用仍处于运行状态。 • 这些 URI 还会提供重要的细粒度安全模型。例如,应用可将其所拥有图像的 URI 放到剪贴板上,但将其内容提供程序锁定,以便其他应用程序无法随意访问它。当第二个应用尝试访问剪贴板上的 URI 时,系统可允许该应用通过临时的 URI 授权来访问数据,这样便只能访问 URI 后面的数据,而非第二个应用中的其他任何内容。 内容提供程序也适用于读取和写入您的应用不共享的私有数据。 内容提供程序作为 ContentProvider 的子类实现,并且其必须实现一组标准 API,以便其他应用能够执行事务。如需了解详细信息,请参阅内容提供程序开发者指南。 Android 系统设计的独特之处在于,任何应用都可启动其他应用的组件。例如,当您想让用户使用设备相机拍摄照片时,另一个应用可能也可执行该操作,因而您的应用便可使用该应用,而非自行产生一个 Activity 来拍摄照片。您无需加入甚至链接到该相机应用的代码。只需启动拍摄照片的相机应用中的 Activity 即可。完成拍摄时,系统甚至会将照片返回您的应用,以便您使用。对用户而言,这就如同相机是您应用的一部分。 当系统启动某个组件时,它会启动该应用的进程(如果尚未运行),并实例化该组件所需的类。例如,如果您的应用启动相机应用中拍摄照片的 Activity,则该 Activity 会在属于相机应用的进程(而非您的应用进程)中运行。因此,与大多数其他系统上的应用不同,Android 应用并没有单个入口点(即没有 main() 函数)。 由于系统在单独的进程中运行每个应用,且其文件权限会限制对其他应用的访问,因此您的应用无法直接启动其他应用中的组件,但 Android 系统可以。如要启动其他应用中的组件,请向系统传递一条消息,说明启动特定组件的 Intent。系统随后便会为您启动该组件。 启动组件 在四种组件类型中,有三种(Activity、服务和广播接收器)均通过异步消息 Intent 进行启动。Intent 会在运行时对各个组件进行互相绑定。您可以将 Intent 视为从其他组件(无论该组件是属于您的应用还是其他应用)请求操作的信使。 您需使用 Intent 对象创建 Intent,该对象通过定义消息来启动特定组件(显式 Intent)或特定的组件类型(隐式 Intent)。 对于 Activity 和服务,Intent 会定义要执行的操作(例如,查看或发送某内容),并且可指定待操作数据的 URI,以及正在启动的组件可能需要了解的信息。例如,Intent 可能会传达对 Activity 的请求,以便显示图像或打开网页。在某些情况下,您可以通过启动 Activity 来接收结果,这样 Activity 还会返回 Intent 中的结果。例如,您可以发出一个 Intent,让用户选取某位联系人并将其返回给您。返回 Intent 包含指向所选联系人的 URI。 对于广播接收器,Intent 只会定义待广播的通知。例如,指示设备电池电量不足的广播只包含指示“电池电量不足”的已知操作字符串。 与 Activity、服务和广播接收器不同,内容提供程序并非由 Intent 启动。相反,它们会在成为 ContentResolver 的请求目标时启动。内容解析程序会通过内容提供程序处理所有直接事务,因此通过提供程序执行事务的组件便无需执行事务,而是改为在 ContentResolver 对象上调用方法。这会在内容提供程序与请求信息的组件之间留出一个抽象层(以确保安全)。 每种组件都有不同的启动方法: • 如要启动 Activity,您可以向 startActivity() 或 startActivityForResult() 传递 Intent(当您想让 Activity 返回结果时),或者为其安排新任务。 • 在 Android 5.0(API 级别 21)及更高版本中,您可以使用 JobScheduler 类来调度操作。对于早期 Android 版本,您可以通过向 startService() 传递 Intent 来启动服务(或对执行中的服务下达新指令)。您也可通过向将 bindService() 传递 Intent 来绑定到该服务。 • 您可以通过向 sendBroadcast()、sendOrderedBroadcast() 或 sendStickyBroadcast() 等方法传递 Intent 来发起广播。 • 您可以通过在 ContentResolver 上调用 query(),对内容提供程序执行查询。 如需了解有关 Intent 用法的详细信息,请参阅 Intent 和 Intent 过滤器文档。以下文档将为您详细介绍如何启动特定组件:Activity、服务、BroadcastReceiver 和内容提供程序。

问问小秘 2020-03-03 09:47:38 0 浏览量 回答数 0

问题

【精品问答】大数据计算技术1000问

问问小秘 2019-12-01 21:57:13 6895 浏览量 回答数 2

问题

程序员报错QA大分享(1)

问问小秘 2020-06-18 15:46:14 8 浏览量 回答数 1

问题

你需要的是持续的服务改进

sunny夏筱 2019-12-01 21:41:32 7450 浏览量 回答数 3

回答

浅谈Flutter框架原理及其生态圈 Flutter的锋芒 跨平台高性能的渲染引擎逐渐成为移动端、大前端领域的一个热点,作为其中的明星框架Flutter,经过近几年来的迅速发展,由极大的可能成为下一代跨端终端解决方案。自从2017 年 5 月,谷歌公司发布的了 Alpha 版本的 Flutter; 2018 年底 Flutter Live 发布的 1.0 版本;2019年7月发布1.5版本,截止今日(2020年2月)已经发布了v1.14.6 Beta版本。 在Flutter诞生之前,已经有许多跨平台UI框架的方案如Cordova、ReactNative、weex、uni-app、Hippy等,常见的需要处理兼容的终端平台也包括android、ios、web、Iot等,但是在大前端的浪潮下,对于企业和开发者来说开发效率和使用体验都十分重要,传统的做法莫过于分不同的团队开发不同的终端项目,如果还要继续向其他平台,拓展的话,我们需要付出的成本和时间将成倍增长。正因为如此,在这样的背景下,Flutter等跨端框架的兴起,从本质上讲,帮助开发者增加业务代码的复用率,减少因为要适配多个平台带来的工作量,从而降低开发成本、提高开发效率。 纵观已有的跨端方案,可以分为三类:Web 容器、泛 Web 容器、自绘引擎框架。 基于web容器即基于浏览器的跨平台也做得越来越好,自然管线也越来越短,与native的一些技术手段来实现性能上的相互补充。比如Egret、Cocos、Laya这些游戏引擎,它们在跨平台方面的做法多以Typescript编写,在iOS和安卓平台的各种浏览器中轻松的运行HTML5游戏,并在不同平台浏览器里提供近乎一致的用户体验,比如Egret还会提供高效的 JS-C Binding 编译机制,以满足游戏编译为原生格式的需求,不过大多数HTML游戏引擎也属于web容器这个范畴内。web容器框架也有一个明显的致命(在对体验&性能有较高要求的情况下)的缺点,那就是WebView的渲染效率和JavaScript执行性能太差。再加上Android各个系统版本和设备厂商的定制,很难保证所在所有设备上都能提供一致的体验。 泛 Web 容器框架比如ReactNative和Weex,即上层通过面向前端友好的UI,下层通过native的渲染形式,虽然同样使用类HTML+JS的UI构建逻辑,但是最终会生成对应的自定义原生控件,以充分利用原生控件相对于WebView的较高的绘制效率,同时H5与native相互补充来达到更好的用户体验,这也是一种很好的解决方案。缺陷也很明显,随着系统版本变化和API的变化,开发者可能也需要处理不同平台的差异,甚至有些特性只能在部分平台上实现,这样框架的跨平台特性就会大打折扣。 自绘引擎框架这里专指Flutter框架,从底层就承担跨端的任务和渲染方式,从目前来看,从技术的实现和方案的成熟度、产品的性能方面比较,Flutter有很大可能成为下一代主流跨平台框架。 Flutter与其他跨端框架的不同点之一就是自带渲染引擎,Flutter渲染引擎依靠跨平台的Skia图形库来实现,Skia引擎会将使用Dart语言构建的抽象的视图结构数据加工成GPU数据,交由 OpenGL 最终提供给 GPU 渲染,至此完成渲染闭环,因此可以在最大程度上保证一款应用在不同平台、不同设备上的体验一致性。 而开发语言选用的是同时支持 JIT和 AOT的 Dart语言,Dart本身提供了三种运行方式,应对web环境,用Dart2js编译成JavaScript代码,运行在常规浏览器中;使用DartVM直接在命令行中运行Dart代码;AOT方式编译成机器码,例如Flutter App框架。而且Dart 避免了抢占式调度和共享内存,可以在没有锁的情况下进行对象分配和垃圾回收,在性能方面表现相当不错,不仅保证了开发效率,代码性能和用户体验也更卓越。因此,Flutter在各类跨平台移动开发方案中脱颖而出。同时在去年2019的Google IO大会上,备受关注的Fuchsia系统虽然并没有发布,但是宣布了 Flutter除了支持开发 Android 和 iOS 程序之外,现在还支持开发Web程序了,在 I/O 大会上,谷歌发布了 Web 版 Flutter 的首个技术预览版,宣布 Flutter 将为包括 Google Home Hub 在内的 Google Smart Display 平台提供技术支持,并迈出利用 Chrome 操作系统支持桌面级应用的第一步。 很多JS开发者会思考Google Flutter团队至于为啥选择Dart而不是JS,其实Google 公司给出的原因很简单也很直接:Dart 语言开发组就在隔壁,对于 Flutter 需要的一些语言新特性,能够快速在语法层面落地实现;而如果选择了 JavaScript,就必须经过各种委员会(TC39等)和浏览器提供商漫长的决议。 Flutter绘制原理 在计算机系统中,图像的显示需要 CPU、GPU 和显示器一起配合完成:CPU 负责图像数据计算,GPU 负责图像数据渲染,而显示器则负责最终图像显示。 CPU 把计算好的、需要显示的内容交给 GPU,由 GPU 完成渲染后放入帧缓冲区,随后视频控制器根据垂直同步信号(VSync)以每秒 60 次的速度,从帧缓冲区读取帧数据交由显示器完成图像显示。 操作系统在呈现图像时遵循了这种机制,而 Flutter 作为跨平台开发框架也采用了这种底层方案。下面有一张更为详尽的示意图来解释 Flutter 的绘制原理。可以看到,Flutter 关注如何尽可能快地在两个硬件时钟的 VSync 信号之间计算并合成视图数据,然后通过 Skia 交给 GPU 渲染:UI 线程使用 Dart 来构建视图结构数据,这些数据会在 GPU 线程进行图层合成,随后交给 Skia 引擎加工成 GPU 数据,而这些数据会通过 OpenGL 最终提供给 GPU 渲染。 Skia原理 Skia 是一款用由C++ 开发的2D 图像绘制引擎。在2005 年被 Google 公司收购后被广泛应用在 Android和其他等核心产品上,Skia 目前是Android 官方的图像渲染引擎,因此 Flutter Android SDK 无需内嵌 Skia 引擎就可以获得天然的 Skia 支持;而对于 iOS 平台来说,由于 Skia 是跨平台的,因此它作为 Flutter iOS 渲染引擎被嵌入到 Flutter 的 iOS SDK 中,替代了 iOS 闭源的 Core Graphics/Core Animation/Core Text,这也正是 Flutter iOS SDK 打包的 App 包体积比 Android 要大一些的原因。 底层渲染能力统一了,上层开发接口和功能体验也就随即统一了,开发者再也不用操心平台相关的渲染特性了。也就是说,Skia 保证了同一套代码调用在 Android 和 iOS 平台上的渲染效果是完全一致的。 Flutter架构 Framework底层是Flutter引擎,引擎主要负责图形绘制(Skia)、文字排版(libtxt)和提供Dart运行时,引擎全部使用C++实现,Framework层使我们可以用Dart语言调用引擎的强大能力。Flutter 架构采用分层设计,从下到上分为三层,依次为:Embedder、Engine、Framework。 Embedder 是操作系统适配层,实现了渲染 Surface 设置,线程设置,以及平台插件等平台相关特性的适配。从这里我们可以看到,Flutter 平台相关特性并不多,这就使得从框架层面保持跨端一致性的成本相对较低。 Engine 层主要包含 Skia、Dart 和 Text,实现了 Flutter 的渲染引擎、文字排版、事件处理和 Dart 运行时等功能。Skia 和 Text 为上层接口提供了调用底层渲染和排版的能力,Dart 则为 Flutter 提供了运行时调用 Dart 和渲染引擎的能力。而 Engine 层的作用,则是将它们组合起来,从它们生成的数据中实现视图渲染。 Framework 层则是一个用 Dart 实现的 UI SDK,包含了动画、图形绘制和手势识别等功能。为了在绘制控件等固定样式的图形时提供更直观、更方便的接口,Flutter 还基于这些基础能力,根据 Material 和 Cupertino 两种视觉设计风格封装了一套 UI 组件库,开发者可以直接使用这些组件库。 Flutter运行流程 页面中的各界面元素(Widget)以树的形式组织,即控件树。Flutter 通过控件树中的每个控件创建不同类型的渲染对象,组成渲染对象树。在Flutter界面渲染过程分为三个阶段:布局、绘制、合成,布局和绘制在Flutter框架中完成,合成则交由引擎负责。 Flutter 采用深度优先机制遍历渲染对象树,决定渲染对象树中各渲染对象在屏幕上的位置和尺寸。在布局过程中,渲染对象树中的每个渲染对象都会接收父对象的布局约束参数,决定自己的大小,然后父对象按照控件逻辑决定各个子对象的位置,最终完成布局过程。这里只需要注意一点,无论布局还是绘制,都是父子间的遍历关系:父Widget的布局需要依赖子Widget的布局结果;而绘制则反过来(子Widget需要盖在父Widget上),布局是后续遍历,绘制是前序遍历,他们都是深度优先遍历。 Flutter生命周期 可以看到,Flutter中State 的生命周期可以分为 3 个阶段:创建(插入视图树)、更新(在视图树中存在)、销毁(从视图树中移除)。接下来,我们一起看看每一个阶段的具体流程。 第一步创建 State 初始化时会依次执行 :构造方法 -> initState -> didChangeDependencies -> build,随后完成页面渲染。构造方法是 State 生命周期的起点,Flutter 会通过调用StatefulWidget.createState() 来创建一个 State。我们可以通过构造方法,来接收父 Widget 传递的初始化 UI 配置数据。这些配置数据,决定了 Widget 最初的呈现效果。 initState,会在 State 对象被插入视图树的时候调用。这个函数在 State 的生命周期中只会被调用一次,所以我们可以在这里做一些初始化工作,比如为状态变量设定默认值。 didChangeDependencies 则用来专门处理 State 对象依赖关系变化,会在 initState() 调用结束后,被 Flutter 调用。 build,作用是构建视图。经过以上步骤,Framework 认为 State 已经准备好了,于是调用 build。我们需要在这个函数中,根据父 Widget 传递过来的初始化配置数据,以及 State 的当前状态,创建一个 Widget 然后返回。 第二步更新 Widget 的状态更新,主要由个方法触发:setState、didchangeDependencies、didUpdateWidget。 setState:我们最熟悉的方法之一。当状态数据发生变化时,我们总是通过调用这个方法告诉 Flutter:“我这儿的数据变啦,请使用更新后的数据重建 UI!” didChangeDependencies:State 对象的依赖关系发生变化后,Flutter 会回调这个方法,随后触发组件构建。哪些情况下 State 对象的依赖关系会发生变化呢?典型的场景是,系统语言 Locale 或应用主题改变时,系统会通知 State 执行 didChangeDependencies 回调方法。 didUpdateWidget:当 Widget 的配置发生变化时,比如,父 Widget 触发重建(即父 Widget 的状态发生变化时),热重载时,系统会调用这个函数。一旦这三个方法被调用,Flutter 随后就会销毁老 Widget,并调用 build 方法重建 Widget。 第三步销毁 比如组件被移除,或是页面销毁的时候,系统会调用 deactivate 和 dispose 这两个方法,来移除或销毁组件。 Flutter生态圈及其常用框架 一项技术一个框架是否流行,最直观的体现就是它的生态圈是否活跃,下面列举了一些Flutter开发中常用的库工具。 参考文献 1、[Flutter原理与实践](https://tech.meituan.com/2018/08/09/waimai-flutter-practice.html) 少杰 2、[Flutter框架技术概览](https://flutter.dev/docs/resources/technical-overview) 3、[Flutter中文官网](https://pub.dartlang.org/flutter/) 4、[Flutter插件仓库](https://pub.dev/flutter/packages)

罗思雨 2020-02-27 11:47:50 0 浏览量 回答数 0

问题

UI自动化体系建设的创新实践

云效平台 2019-12-01 20:57:08 2984 浏览量 回答数 0

问题

內建安全的软件开发

技术小菜鸟 2019-12-01 21:48:18 2077 浏览量 回答数 1

回答

为什么你的代码是一个单体? 除了已经实现了微前端的应用之外,所有前端应用本质上都是单一的应用。原因是如果您正在使用 React 库进行开发,并且如果您有两个团队,则两个团队都应该使用相同的React 库,并且两个团队应该在部署时保持同步,并且在代码合并期间始终会发生冲突。它们没有完全分离,很可能它们维护着相同的仓库并具有相同的构建系统。单体应用的退出被标志为微服务的出现。但是它适用于后端! 什么是微服务? 对于微服务,一般而言最简单的解释是,它是一种开发技术,允许开发人员为平台的不同部分进行独立部署,而不会损害其他部分。独立部署的能力允许他们构建孤立或松散耦合的服务。为了使这个体系结构更稳定,有一些规则要遵循,可以总结如下:每个服务应该只有一个任务,它应该很小。所以负责这项服务的团队应该很小。关于团队和项目的规模,James Lewis 和 Martin Fowler 在互联网上做出的最酷解释之一如下: 在我们与微服务从业者的对话中,我们看到了一系列服务规模。报道的最大规模遵循亚马逊关于Two Pizza Team的概念(即整个团队可以由两个比萨饼供给),意味着不超过十几个人。在规模较小的规模上,我们已经看到了一个由六人组成的团队支持六项服务的设置。 我画了一个简单的草图,为整体和微服务提供了直观的解释: 从上图可以理解,微服务中的每个服务都是一个独立的应用,除了UI。UI仍然是一体的!当一个团队处理所有服务并且公司正在扩展时,前端团队将开始苦苦挣扎并且无法跟上它,这是这种架构的瓶颈。 除了瓶颈之外,这种架构也会导致一些组织问题。假设公司正在发展并将采用需要 跨职能 小团队的敏捷开发方法。在这个常见的例子中,产品所有者自然会开始将故事定义为前端和后端任务,而 跨职能 团队将永远不会成为真正的 跨职能 部门。这将是一个浅薄的泡沫,看起来像一个敏捷的团队,但它将在内部分开。关于管理这种团队的更多信息将是一项非常重要的工作。在每个计划中,如果有足够的前端任务或者sprint中有足够的后端任务,则会有一个问题。为了解决这里描述的所有问题和许多其他问题,几年前出现了微前端的想法并且开始迅速普及。 解决微服务中的瓶颈问题:Micro Frontends 解决方案实际上非常明显,采用了多年来为后端服务工作的相同原则:将前端整体划分为小的UI片段。但UI与服务并不十分相似,它是最终用户与产品之间的接口,应该是一致且无缝的。更重要的是,在单页面应用时代,整个应用在客户端的浏览器上运行。它们不再是简单的HTML文件,相反,它们是复杂的软件,达到了非常复杂的水平。现在我觉得微型前端的定义是必要的: Micro Frontends背后的想法是将网站或Web应用视为独立团队拥有的功能组合。每个团队都有一个独特的业务或任务领域,做他们关注和专注的事情。团队是跨职能的,从数据库到用户界面开发端到端的功能。(micro-frontends.org) 根据我迄今为止的经验,对于许多公司来说,直接采用上面提出的架构真的很难。许多其他人都有巨大的遗留负担,这使他们无法迁移到新的架构。出于这个原因,更柔软的中间解决方案更加灵活,易于采用和安全迁移至关重要。在更详细地概述了体系结构后,我将尝试提供一些体系结构的洞察,该体系结构确认了上述提议并允许更灵活的方式。在深入了解细节之前,我需要建立一些术语。 整体结构和一些术语 让我们假设我们通过业务功能垂直划分整体应用结构。我们最终会得到几个较小的应用,它们与单体应用具有相同的结构。但是如果我们在所有这些小型单体应用之上添加一个特殊应用,用户将与这个新应用进行通信,它将把每个小应用的旧单体UI组合成一个。这个新图层可以命名为拼接图层,因为它从每个微服务中获取生成的UI部件,并为最终用户组合成一个无缝 UI,这将是微前端的最直接实现朗 为了更好地理解,我将每个小型单体应用称为微应用,因为它们都是独立的应用,而不仅仅是微服务,它们都有UI部件,每个都代表端到端的业务功能。 众所周知,今天的前端生态系统功能多样,而且非常复杂。因此,当实现真正的产品时,这种直接的解决方案还不够。 要解决的问题 虽然这篇文章只是一个想法,但我开始使用Reddit讨论这个想法。感谢社区和他们的回复,我可以列出一些需要解决的问题,我将尝试逐一描述。 当我们拥有一个完全独立的独立微应用时,如何创建无缝且一致的UI体验? 好吧,这个问题没有灵丹妙药的答案,但其中一个想法是创建一个共享的UI库,它也是一个独立的微应用。通过这种方式,所有其他微应用将依赖于共享的UI库微应用。在这种情况下,我们刚刚创建了一个共享依赖项, 我们就杀死了独立微应用的想法。 另一个想法是在根级共享CSS自定义变量( CSS custom variables )。此解决方案的优势在于应用之间的全局可配置主题。 或者我们可以简单地在应用团队之间共享一些SASS变量和混合。这种方法的缺点是UI元素的重复实现,并且应该对所有微应用始终检查和验证类似元素的设计的完整性。 我们如何确保一个团队不会覆盖另一个团队编写的CSS? 一种解决方案是通过CSS选择器名称进行CSS定义,这些名称由微应用名称精心选择。通过将该范围任务放在拼接层上将减少开发开销,但会增加拼接层的责任。 另一种解决方案可以是强制每个微应用成为自定义Web组件(custom web component)。这个解决方案的优点是浏览器完成了范围设计,但需要付出代价:使用shadow DOM进行服务器端渲染几乎是不可能的。此外,自定义元素没有100%的浏览器支持,特别是IE。 我们应该如何在微应用之间共享全局信息? 这个问题指出了关于这个主题的最关注的问题之一,但解决方案非常简单:HTML 5具有相当强大的功能,大多数前端开发人员都不知道。例如,自定义事件(custom events) 就是其中之一,它是在微应用中共享信息的解决方案。 或者,任何共享的pub-sub实现或T39可观察的实现都可以实现。如果我们想要一个更复杂的全局状态处理程序,我们可以实现共享的微型Redux,通过这种方式我们可以实现更多的相应式架构。 如果所有微应用都是独立应用,我们如何进行客户端路由? 这个问题取决于设计的每个实现, 所有主要的现代框架都通过使用浏览器历史状态在客户端提供强大的路由机制, 问题在于哪个应用负责路由以及何时。 我目前的实用方法是创建一个共享客户端路由器,它只负责顶级路由,其余路由器属于相应的微应用。假设我们有 /content/:id 路由定义。共享路由器将解析 /content,已解析的路由将传递到ContentMicroApp。ContentMicroApp是一个独立的服务器,它将仅使用 /:id 进行调用。 我们必须是服务器端渲染,但是有可能使用微前端吗? 服务器端呈现是一个棘手的问题。如果你正在考虑iframes缝合微应用然后忘记服务器端渲染。同样,拼接任务的Web组件也不比iframe强大。但是,如果每个微应用能够在服务器端呈现其内容,那么拼接层将仅负责连接服务器端的HTML片段。 与传统环境集成至关重要!但是怎么样? 为了整合遗留系统,我想描述我自己的策略,我称之为“ 渐进式入侵 ”。 首先,我们必须实现拼接层,它应该具有透明代理的功能。然后我们可以通过声明一个通配符路径将遗留系统定义为微应用:LegacyMicroApp 。因此,所有流量都将到达拼接层,并将透明地代理到旧系统,因为我们还没有任何其他微应用。 下一步将是我们的 第一次逐步入侵 :我们将从LegacyMicroApp中删除主要导航并用依赖项替换它。这种依赖关系将是一个使用闪亮的新技术实现的微应用:NavigationMicroApp 。 现在,拼接层将每个路径解析为 Legacy Micro App ,它将依赖关系解析为 Navigation MicroApp ,并通过连接这两个来为它们提供服务。 然后通过主导航遵循相同的模式来为引导下一步。 然后我们将继续从Legacy MicroApp中获取逐步重复以上操作,直到没有任何遗漏。 如何编排客户端,这样我们每次都不需要重新加载页面? 拼接层解决了服务器端的问题,但没有解决客户端问题。在客户端,在将已粘贴的片段作为无缝HTML加载后,我们不需要每次在URL更改时加载所有部分。因此,我们必须有一些异步加载片段的机制。但问题是,这些片段可能有一些依赖关系,这些依赖关系需要在客户端解决。这意味着微前端解决方案应提供加载微应用的机制,以及依赖注入的一些机制。 根据上述问题和可能的解决方案,我可以总结以下主题下的所有内容: 客户端 编排路由隔离微应用应用之间通信微应用UI之间的一致性 服务端 服务端渲染路由依赖管理 灵活、强大而简单的架构 所以,这篇文章还是很值得期待的!微前端架构的基本要素和要求终于显现! 在这些要求和关注的指导下,我开始开发一种名为microfe的解决方案。在这里,我将通过抽象的方式强调其主要组件来描述该项目的架构目标。 它很容易从客户端开始,它有三个独立的主干结构:AppsManager, Loader, Router 和一个额外的MicroAppStore。 AppsManager AppsManager 是客户端微应用编排的核心。AppsManager的主要功能是创建依赖关系树。当解决了微应用的所有依赖关系时,它会实例化微应用。 Loader 客户端微应用编排的另一个重要部分是Loader。加载器的责任是从服务器端获取未解析的微应用。 Router 为了解决客户端路由问题,我将 Router 引入了 microfe。与常见的客户端路由器不同,microf 的功能有限,它不解析页面而是微应用。假设我们有一个URL /content/detail/13 和一个ContentMicroApp。在这种情况下,microfe 将URL解析为 /content/,它将调用ContentMicroApp /detail/13 URL部分。 MicroAppStore 为了解决微应用到微应用客户端的通信,我将MicroAppStore引入了 microfe。它具有与Redux库类似的功能,区别在于:它对异步数据结构更改和reducer 声明更灵活。 服务器端部分在实现上可能稍微复杂一些,但结构更简单。它只包含两个主要部分 StitchingServer 和许多MicroAppServer。 MicroAppServer MicroAppServer 的最小功能可以概括为 init 和 serve。 虽然 MicroAppServer 首先启动它应该做的是使用 微应用声明 调用 SticthingServer 注册端点,该声明定义了 MicroAppServer 的微应用 依赖关系, 类型 和 URL架构。我认为没有必要提及服务功能,因为没有什么特别之处。 StitchingServer StitchingServer 为 MicroAppServers 提供注册端点。当 MicroAppServer 将自己注册到 StichingServer 时,StichingServer 会记录MicroAppServer 的声明。 稍后,StitchingServer 使用声明从请求的URL解析 MicroAppServers。 解析M icroAppServer 及其所有依赖项后,CSS,JS和HTML中的所有相对路径都将以相关的 MicroAppServer 公共URL为前缀。另外一步是为CSS选择器添加一个唯一的 MicroAppServer 标识符,以防止客户端的微应用之间发生冲突。 然后 StitchingServer 的主要职责就是:从所有收集的部分组成并返回一个无缝的HTML页面。 其他实现一览 甚至在2016年被称为微前端之前,许多大公司都试图通过 BigPipe 来解决Facebook等类似问题。如今这个想法正在获得验证。不同规模的公司对该主题感兴趣并投入时间和金钱。例如,Zalando开源了其名为Project Mosaic的解决方案。我可以说,微型和 Project Mosaic.遵循类似的方法,但有一些重要的区别。虽然microfe采用完全分散的路由定义来增强每个微应用的独立性,但Project Mosaic更喜欢每条路径的集中路由定义和布局定义。通过这种方式,Project Mosaic可以实现轻松的A/B测试和动态布局生成。 对于该主题还有一些其他方法,例如使用iframe作为拼接层,这显然不是在服务器端而是在客户端。这是一个非常简单的解决方案,不需要太多的服务器结构和DevOps参与。这项工作只能由前端团队完成,因此可以减轻公司的组织负担,同时降低成本。 已经有一个框架叫做 single-spa。该项目依赖于每个应用的命名约定来解析和加载微应用。容易掌握想法并遵循模式。因此,在您自己的本地环境中尝试该想法可能是一个很好的初步介绍。但是项目的缺点是你必须以特定的方式构建每个微应用,以便他们可以很好地使用框架。 最后的想法 我相信微前端话题会更频繁地讨论。如果该主题能够引起越来越多公司的关注,它将成为大型团队的事实发展方式。在不久的将来,任何前端开发人员都可以在这个架构上掌握一些见解和经验,这真的很有用。 关于本文 译者:@Vincent.W 译文:https://zhuanlan.zhihu.com/p/82965940 作者:@onerzafer 原文:https://hackernoon.com/understanding-micro-frontends-b1c11585a297 加入阿里云钉钉群享福利:每周技术直播,定期群内有奖活动、大咖问答 阿里云开发者社区

茶什i 2020-01-06 17:57:24 0 浏览量 回答数 0

问题

Nginx性能为什么如此吊

小柒2012 2019-12-01 21:20:47 15038 浏览量 回答数 3

问题

在 berserkJS 中无缝使用 Wind.js:报错

kun坤 2020-06-07 14:00:40 0 浏览量 回答数 1

回答

Kafka 是目前主流的分布式消息引擎及流处理平台,经常用做企业的消息总线、实时数据管道,本文挑选了 Kafka 的几个核心话题,帮助大家快速掌握 Kafka,包括: Kafka 体系架构 Kafka 消息发送机制 Kafka 副本机制 Kafka 控制器 Kafka Rebalance 机制 因为涉及内容较多,本文尽量做到深入浅出,全面的介绍 Kafka 原理及核心组件,不怕你不懂 Kafka。 1. Kafka 快速入门 Kafka 是一个分布式消息引擎与流处理平台,经常用做企业的消息总线、实时数据管道,有的还把它当做存储系统来使用。早期 Kafka 的定位是一个高吞吐的分布式消息系统,目前则演变成了一个成熟的分布式消息引擎,以及流处理平台。 1.1 Kafka 体系架构 Kafka 的设计遵循生产者消费者模式,生产者发送消息到 broker 中某一个 topic 的具体分区里,消费者从一个或多个分区中拉取数据进行消费。拓扑图如下: 目前,Kafka 依靠 Zookeeper 做分布式协调服务,负责存储和管理 Kafka 集群中的元数据信息,包括集群中的 broker 信息、topic 信息、topic 的分区与副本信息等。 ** 1.2 Kafka 术语** 这里整理了 Kafka 的一些关键术语: Producer:生产者,消息产生和发送端。 Broker:Kafka 实例,多个 broker 组成一个 Kafka 集群,通常一台机器部署一个 Kafka 实例,一个实例挂了不影响其他实例。 Consumer:消费者,拉取消息进行消费。 一个 topic 可以让若干个消费者进行消费,若干个消费者组成一个 Consumer Group 即消费组,一条消息只能被消费组中一个 Consumer 消费。 Topic:主题,服务端消息的逻辑存储单元。一个 topic 通常包含若干个 Partition 分区。 Partition:topic 的分区,分布式存储在各个 broker 中, 实现发布与订阅的负载均衡。若干个分区可以被若干个 Consumer 同时消费,达到消费者高吞吐量。一个分区拥有多个副本(Replica),这是Kafka在可靠性和可用性方面的设计,后面会重点介绍。 message:消息,或称日志消息,是 Kafka 服务端实际存储的数据,每一条消息都由一个 key、一个 value 以及消息时间戳 timestamp 组成。 offset:偏移量,分区中的消息位置,由 Kafka 自身维护,Consumer 消费时也要保存一份 offset 以维护消费过的消息位置。 1.3 Kafka 作用与特点 Kafka 主要起到削峰填谷(缓冲)、系统解构以及冗余的作用,主要特点有: 高吞吐、低延时:这是 Kafka 显著的特点,Kafka 能够达到百万级的消息吞吐量,延迟可达毫秒级; 持久化存储:Kafka 的消息最终持久化保存在磁盘之上,提供了顺序读写以保证性能,并且通过 Kafka 的副本机制提高了数据可靠性。 分布式可扩展:Kafka 的数据是分布式存储在不同 broker 节点的,以 topic 组织数据并且按 partition 进行分布式存储,整体的扩展性都非常好。 高容错性:集群中任意一个 broker 节点宕机,Kafka 仍能对外提供服务。 2. Kafka 消息发送机制 Kafka 生产端发送消息的机制非常重要,这也是 Kafka 高吞吐的基础,生产端的基本流程如下图所示: 主要有以下方面的设计: 2.1 异步发送 Kafka 自从 0.8.2 版本就引入了新版本 Producer API,新版 Producer 完全是采用异步方式发送消息。生产端构建的 ProducerRecord 先是经过 keySerializer、valueSerializer 序列化后,再是经过 Partition 分区器处理,决定消息落到 topic 具体某个分区中,最后把消息发送到客户端的消息缓冲池 accumulator 中,交由一个叫作 Sender 的线程发送到 broker 端。 这里缓冲池 accumulator 的最大大小由参数 buffer.memory 控制,默认是 32M,当生产消息的速度过快导致 buffer 满了的时候,将阻塞 max.block.ms 时间,超时抛异常,所以 buffer 的大小可以根据实际的业务情况进行适当调整。 2.2 批量发送 发送到缓冲 buffer 中消息将会被分为一个一个的 batch,分批次的发送到 broker 端,批次大小由参数 batch.size 控制,默认16KB。这就意味着正常情况下消息会攒够 16KB 时才会批量发送到 broker 端,所以一般减小 batch 大小有利于降低消息延时,增加 batch 大小有利于提升吞吐量。 那么生成端消息是不是必须要达到一个 batch 大小时,才会批量发送到服务端呢?答案是否定的,Kafka 生产端提供了另一个重要参数 linger.ms,该参数控制了 batch 最大的空闲时间,超过该时间的 batch 也会被发送到 broker 端。 2.3 消息重试 此外,Kafka 生产端支持重试机制,对于某些原因导致消息发送失败的,比如网络抖动,开启重试后 Producer 会尝试再次发送消息。该功能由参数 retries 控制,参数含义代表重试次数,默认值为 0 表示不重试,建议设置大于 0 比如 3。 3. Kafka 副本机制 前面提及了 Kafka 分区副本(Replica)的概念,副本机制也称 Replication 机制是 Kafka 实现高可靠、高可用的基础。Kafka 中有 leader 和 follower 两类副本。 3.1 Kafka 副本作用 Kafka 默认只会给分区设置一个副本,由 broker 端参数 default.replication.factor 控制,默认值为 1,通常我们会修改该默认值,或者命令行创建 topic 时指定 replication-factor 参数,生产建议设置 3 副本。副本作用主要有两方面: 消息冗余存储,提高 Kafka 数据的可靠性; 提高 Kafka 服务的可用性,follower 副本能够在 leader 副本挂掉或者 broker 宕机的时候参与 leader 选举,继续对外提供读写服务。 3.2 关于读写分离 这里要说明的是 Kafka 并不支持读写分区,生产消费端所有的读写请求都是由 leader 副本处理的,follower 副本的主要工作就是从 leader 副本处异步拉取消息,进行消息数据的同步,并不对外提供读写服务。 Kafka 之所以这样设计,主要是为了保证读写一致性,因为副本同步是一个异步的过程,如果当 follower 副本还没完全和 leader 同步时,从 follower 副本读取数据可能会读不到最新的消息。 3.3 ISR 副本集合 Kafka 为了维护分区副本的同步,引入 ISR(In-Sync Replicas)副本集合的概念,ISR 是分区中正在与 leader 副本进行同步的 replica 列表,且必定包含 leader 副本。 ISR 列表是持久化在 Zookeeper 中的,任何在 ISR 列表中的副本都有资格参与 leader 选举。 ISR 列表是动态变化的,并不是所有的分区副本都在 ISR 列表中,哪些副本会被包含在 ISR 列表中呢?副本被包含在 ISR 列表中的条件是由参数 replica.lag.time.max.ms 控制的,参数含义是副本同步落后于 leader 的最大时间间隔,默认10s,意思就是说如果某一 follower 副本中的消息比 leader 延时超过10s,就会被从 ISR 中排除。Kafka 之所以这样设计,主要是为了减少消息丢失,只有与 leader 副本进行实时同步的 follower 副本才有资格参与 leader 选举,这里指相对实时。 3.4 Unclean leader 选举 既然 ISR 是动态变化的,所以 ISR 列表就有为空的时候,ISR 为空说明 leader 副本也“挂掉”了,此时 Kafka 就要重新选举出新的 leader。但 ISR 为空,怎么进行 leader 选举呢? Kafka 把不在 ISR 列表中的存活副本称为“非同步副本”,这些副本中的消息远远落后于 leader,如果选举这种副本作为 leader 的话就可能造成数据丢失。Kafka broker 端提供了一个参数 unclean.leader.election.enable,用于控制是否允许非同步副本参与 leader 选举;如果开启,则当 ISR 为空时就会从这些副本中选举新的 leader,这个过程称为 Unclean leader 选举。 前面也提及了,如果开启 Unclean leader 选举,可能会造成数据丢失,但保证了始终有一个 leader 副本对外提供服务;如果禁用 Unclean leader 选举,就会避免数据丢失,但这时分区就会不可用。这就是典型的 CAP 理论,即一个系统不可能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition Tolerance)中的两个。所以在这个问题上,Kafka 赋予了我们选择 C 或 A 的权利。 我们可以根据实际的业务场景选择是否开启 Unclean leader选举,这里建议关闭 Unclean leader 选举,因为通常数据的一致性要比可用性重要的多。 4. Kafka 控制器 控制器(Controller)是 Kafka 的核心组件,它的主要作用是在 Zookeeper 的帮助下管理和协调整个 Kafka 集群。集群中任意一个 broker 都能充当控制器的角色,但在运行过程中,只能有一个 broker 成为控制器。 这里先介绍下 Zookeeper,因为控制器的产生依赖于 Zookeeper 的 ZNode 模型和 Watcher 机制。Zookeeper 的数据模型是类似 Unix 操作系统的 ZNode Tree 即 ZNode 树,ZNode 是 Zookeeper 中的数据节点,是 Zookeeper 存储数据的最小单元,每个 ZNode 可以保存数据,也可以挂载子节点,根节点是 /。基本的拓扑图如下: Zookeeper 有两类 ZNode 节点,分别是持久性节点和临时节点。持久性节点是指客户端与 Zookeeper 断开会话后,该节点依旧存在,直到执行删除操作才会清除节点。临时节点的生命周期是和客户端的会话绑定在一起,客户端与 Zookeeper 断开会话后,临时节点就会被自动删除。 Watcher 机制是 Zookeeper 非常重要的特性,它可以在 ZNode 节点上绑定监听事件,比如可以监听节点数据变更、节点删除、子节点状态变更等事件,通过这个事件机制,可以基于 ZooKeeper 实现分布式锁、集群管理等功能。 4.1 控制器选举 当集群中的任意 broker 启动时,都会尝试去 Zookeeper 中创建 /controller 节点,第一个成功创建 /controller 节点的 broker 则会被指定为控制器,其他 broker 则会监听该节点的变化。当运行中的控制器突然宕机或意外终止时,其他 broker 能够快速地感知到,然后再次尝试创建 /controller 节点,创建成功的 broker 会成为新的控制器。 4.2 控制器功能 前面我们也说了,控制器主要作用是管理和协调 Kafka 集群,那么 Kafka 控制器都做了哪些事情呢,具体如下: 主题管理:创建、删除 topic,以及增加 topic 分区等操作都是由控制器执行。 分区重分配:执行 Kafka 的 reassign 脚本对 topic 分区重分配的操作,也是由控制器实现。 Preferred leader 选举:这里有一个概念叫 Preferred replica 即优先副本,表示的是分配副本中的第一个副本。Preferred leader 选举就是指 Kafka 在某些情况下出现 leader 负载不均衡时,会选择 preferred 副本作为新 leader 的一种方案。这也是控制器的职责范围。 集群成员管理:控制器能够监控新 broker 的增加,broker 的主动关闭与被动宕机,进而做其他工作。这里也是利用前面所说的 Zookeeper 的 ZNode 模型和 Watcher 机制,控制器会监听 Zookeeper 中 /brokers/ids 下临时节点的变化。 数据服务:控制器上保存了最全的集群元数据信息,其他所有 broker 会定期接收控制器发来的元数据更新请求,从而更新其内存中的缓存数据。 从上面内容我们大概知道,控制器可以说是 Kafka 的心脏,管理和协调着整个 Kafka 集群,因此控制器自身的性能和稳定性就变得至关重要。 社区在这方面做了大量工作,特别是在 0.11 版本中对控制器进行了重构,其中最大的改进把控制器内部多线程的设计改成了单线程加事件队列的方案,消除了多线程的资源消耗和线程安全问题,另外一个改进是把之前同步操作 Zookeeper 改为了异步操作,消除了 Zookeeper 端的性能瓶颈,大大提升了控制器的稳定性。 5. Kafka 消费端 Rebalance 机制 前面介绍消费者术语时,提到了消费组的概念,一个 topic 可以让若干个消费者进行消费,若干个消费者组成一个 Consumer Group 即消费组 ,一条消息只能被消费组中的一个消费者进行消费。我们用下图表示Kafka的消费模型。 5.1 Rebalance 概念 就 Kafka 消费端而言,有一个难以避免的问题就是消费者的重平衡即 Rebalance。Rebalance 是让一个消费组的所有消费者就如何消费订阅 topic 的所有分区达成共识的过程,在 Rebalance 过程中,所有 Consumer 实例都会停止消费,等待 Rebalance 的完成。因为要停止消费等待重平衡完成,因此 Rebalance 会严重影响消费端的 TPS,是应当尽量避免的。 5.2 Rebalance 发生条件 关于何时会发生 Rebalance,总结起来有三种情况: 消费组的消费者成员数量发生变化 消费主题的数量发生变化 消费主题的分区数量发生变化 其中后两种情况一般是计划内的,比如为了提高消息吞吐量增加 topic 分区数,这些情况一般是不可避免的,后面我们会重点讨论如何避免因为组内消费者成员数发生变化导致的 Rebalance。 5.3 Kafka 协调器 在介绍如何避免 Rebalance 问题之前,先来认识下 Kafka 的协调器 Coordinator,和之前 Kafka 控制器类似,Coordinator 也是 Kafka 的核心组件。 主要有两类 Kafka 协调器: 组协调器(Group Coordinator) 消费者协调器(Consumer Coordinator) Kafka 为了更好的实现消费组成员管理、位移管理,以及 Rebalance 等,broker 服务端引入了组协调器(Group Coordinator),消费端引入了消费者协调器(Consumer Coordinator)。每个 broker 启动的时候,都会创建一个 GroupCoordinator 实例,负责消费组注册、消费者成员记录、offset 等元数据操作,这里也可以看出每个 broker 都有自己的 Coordinator 组件。另外,每个 Consumer 实例化时,同时会创建一个 ConsumerCoordinator 实例,负责消费组下各个消费者和服务端组协调器之前的通信。可以用下图表示协调器原理: 客户端的消费者协调器 Consumer Coordinator 和服务端的组协调器 Group Coordinator 会通过心跳不断保持通信。 5.4 如何避免消费组 Rebalance 接下来我们讨论下如何避免组内消费者成员发生变化导致的 Rebalance。组内成员发生变化无非就两种情况,一种是有新的消费者加入,通常是我们为了提高消费速度增加了消费者数量,比如增加了消费线程或者多部署了一份消费程序,这种情况可以认为是正常的;另一种是有消费者退出,这种情况多是和我们消费端代码有关,是我们要重点避免的。 正常情况下,每个消费者都会定期向组协调器 Group Coordinator 发送心跳,表明自己还在存活,如果消费者不能及时的发送心跳,组协调器会认为该消费者已经“死”了,就会导致消费者离组引发 Rebalance 问题。这里涉及两个消费端参数:session.timeout.ms 和 heartbeat.interval.ms,含义分别是组协调器认为消费组存活的期限,和消费者发送心跳的时间间隔,其中 heartbeat.interval.ms 默认值是3s,session.timeout.ms 在 0.10.1 版本之前默认 30s,之后默认 10s。另外,0.10.1 版本还有两个值得注意的地方: 从该版本开始,Kafka 维护了单独的心跳线程,之前版本中 Kafka 是使用业务主线程发送的心跳。 增加了一个重要的参数 max.poll.interval.ms,表示 Consumer 两次调用 poll 方法拉取数据的最大时间间隔,默认值 5min,对于那些忙于业务逻辑处理导致超过 max.poll.interval.ms 时间的消费者将会离开消费组,此时将发生一次 Rebalance。 此外,如果 Consumer 端频繁 FullGC 也可能会导致消费端长时间停顿,从而引发 Rebalance。因此,我们总结如何避免消费组 Rebalance 问题,主要从以下几方面入手: 合理配置 session.timeout.ms 和 heartbeat.interval.ms,建议 0.10.1 之前适当调大 session 超时时间尽量规避 Rebalance。 根据实际业务调整 max.poll.interval.ms,通常建议调大避免 Rebalance,但注意 0.10.1 版本之前没有该参数。 监控消费端的 GC 情况,避免由于频繁 FullGC 导致线程长时间停顿引发 Rebalance。 合理调整以上参数,可以减少生产环境中 Rebalance 发生的几率,提升 Consumer 端的 TPS 和稳定性。 6.总结 本文总结了 Kafka 体系架构、Kafka 消息发送机制、副本机制,Kafka 控制器、消费端 Rebalance 机制等各方面核心原理,通过本文的介绍,相信你已经对 Kafka 的内核知识有了一定的掌握,更多的 Kafka 原理实践后面有时间再介绍。

剑曼红尘 2020-04-16 18:15:45 0 浏览量 回答数 0

回答

先说结论: 不要对接!不要对接!不要对接! 开个玩笑,以上仅代表个人观点,大家也知道这种“三体式警告”根本没有用的,我自己也研究如何对接,说不定做完后就觉得“真香”了。 为什么要对接? 首先讨论一下为什么要把 Flutter 对接到 Web 生态。 Flutter 现在是一个炙手可热的跨平台技术,能够一套代码运行在 Android、iOS、PC、IoT 以及浏览器上,被认为是下一代跨平台技术。相比于 Weex 和 React Native 可以很好地解决多平台一致性问题,原生渲染性能相近,上层没有 JS 那么厚的封装层次,整体性能会略好一些。 但是大部分兴冲冲去学 Flutter 的人疑惑的第一个问题就是:为什么 Flutter 要用 Dart?一个全新的语言意味着新的学习成本,难道 JS 不香吗?JS 不香不是还有 TypeScript 吗!事实上 Flutter 抛弃的岂止是 JS 这门语言,也抛弃了 HTML 和 CSS,设计了一套解耦得更好的 Widget 体系,Flutter 抛弃的是整个 Web,致力于打造一个新的生态,但是这个生态无法复用 Web 生态的代码和解决方案。尤其是之前所有跨平台方案 Hybrid、React Native、Weex 都是对接 Web 生态的,这让 Flutter 显得有些格格不入,也让大部分前端开发者望而却步。 下面是我整理出来的,前端开发者使用 Flutter 的各方面成本: 因为 Flutter 的开发模式和前端框架比较像(可以说就是抄的 React),所以框架的学习成本并不高,稍微高一些的是 Dart 语言的学习成本,另外还要学习如何用 Widget 组装 UI,虽然很多布局 Widget 设计得和 CSS 很像,灵活度还是差了很多。要想在真实项目中用起来,还要改造整个工具链,以“Native First”的视角做开发,开发 Flutter 和开发原生应用的链路是比较像的,和开发前端页面有较大差异。最高的还是生态成本,前端生态的积累无论是代码还是技术方案都很难复用,这是最痛的一点,生态也是 Flutter 最弱的一环。 无论是为了先进的技术理念还是出于商业私心,先不管 Flutter 为什么抛弃 Web 生态,现实问题是最大的 UI 开发者群体是前端,最丰富的生态是 Web 生态,我觉得 Web 技术也是开发 UI 最高效的方式。如果能在上层使用 Web 技术栈开发,在底层使用 Flutter 实现跨平台渲染,不是可以很好的兼顾开发效率、性能和跨平台一致性吗?还能复用 Web 技术栈大量的技术积累。 可能这些理由也不够充分,暂且先照着这个假设继续分析,最后再重新讨论到底该不该对接。 关于 Flutter 和 Web 生态的对接涉及两个方面: 从 Web 到 Flutter。就是使用 Web 技术栈来开发,然后对接到 Flutter 上实现跨平台渲染。对 Web 来说是解决性能和跨平台一致性问题,对 Flutter 来说是解决生态复用问题。从 Flutter 到 Web。就是官方已经实现的 Web support for Flutter,把已经用 Dart 开发好的 App 编译成 HTML/JS/CSS 然后运行在浏览器上,可以用于降级和外投场景。 如何实现“从 Web 到 Flutter”? 首先分析一下 Flutter 的架构图,看看可以从哪里下手。 Flutter 可以分为 Framework 和 Engine 两部分,Engine 部分比较底层也比较稳定了,最好不要动,需要改的是用 Dart 实现的 Framework。要想对接 Web 生态的话,JS 引擎肯定是要引入的,至于是否保留 Dart VM 有待讨论。图中最上面 Material 和 Cupertino 两个 UI 库前端是不需要的,前端有自己的。关键是 Widget 这部分,是替换成 HTML/CSS 的方式写 UI,还是继续保留 Widget 但是把语言换成 JS,不同方案给出的解法也不一样。 有不少方案可以实现对接,业界有挺多尝试的,我总结了下面三种方式: - TS 魔改:用 JS 引擎替换掉 Dart VM,用 JS/TS 重新实现 Flutter Framework(或者直接 dart2js 编译过来)。 - JS 对接:引入 JS 引擎同时保留 Dart VM,用前端框架对接 Flutter Framework。 - C++ 魔改:用 JS 引擎替换掉 Dart VM,用 C++ 重新实现 Flutter Framework。 TS 魔改 TS 魔改就是完全抛弃掉 Dart VM,用 TypeScript 重新实现一遍用 Dart 写的 Flutter Framework。 为啥是 TS 而不是 JS?这不是因为 TS 是个大热门嘛,而且向下兼容 JS,现在几乎所有时髦的框架都要用 TS 重写了。 这种方案的出发点是“如果能把 Flutter 的 Dart 换成 JS 就好了”,最容易想到的路就是把 Dart 翻译成 TS,或者直接用 dart2js 把代码编译成 js,但是编译出来的代码包含很多 dart:ui 之类的库的封装,生成的包也挺大的,也比较难定制需要导出的接口,不如干脆用 TS 重写一遍,工具链更熟悉一些,还可以加一些定制。 理论上讲翻译之后 Flutter 绝大部分功能都依然支持,可以复用各种 npm 包,还可以动态化,但是丧失了 AOT 能力,JS 语言的执行性能应该是不如 Dart 的。而且所有节点的布局运算都发生在 JS,底层只需要提供基础的图形能力就好了,就好像是基于 Canvas API 写了一套 UI 框架,性能未必有现存前端框架的性能高。 此外最大的问题是如何与官方 Flutter 保持一致,假如现在是从 v1.13 版本翻译过来的,以后官方升级到了 v1.15 要不要同步更新?这个过程没啥技术含量,而且需要持续投入,做起来比较恶心。 另外还需要考虑上层是用 Widget 的方式写 UI,还是用前端熟悉的 HTML+CSS。如果依然用 Widget 的话,那大部分前端组件还是用不了的,UI 还是得重写一遍。反正要重写的话,成本也没降下来,那就用 Dart 重写呗…… 直接用官方原版 Flutter 也避免每次更新都要翻译一遍 Dart 代码。所以既然选择了对接前端生态,那就要对接 CSS,不然就没有足够的价值。然而 CSS 和 Widget 的对接也是很繁琐的过程,而且存在完备性问题。 JS 对接 翻译代码的方式不够优雅,那就保留 Dart,把 JS/CSS 对接到 Widget 上面不就好了? 当然可以,这种方式是仅把 Flutter 当做了底层的渲染引擎,上层保持前端框架的写法,仅把渲染部分对接到 Flutter。现存的很多前端框架都把底层渲染能力做了抽象,可以对接到不同渲染引擎上,如 Vue/Rax 同时支持浏览器和 Weex,用同样的方式,可以再支持一个 Flutter。 这种方式对前端框架的兼容性比较好,但是链路太长了,业务代码调用前端框架接口做渲染,一顿操作之后发出了渲染指令,这个渲染指令要基于通信的方式传给 Flutter Framework,这中间涉及一次 JS 到 C++ 再到 Dart 的跨语言转换,然后再接收到渲染指令之后还要转成相应的 Widget 树,从 CSS 到 Widget 的转换依然很繁琐。而且 Widget 本身是可以带有状态的,本身就是响应式更新的,在更新时会重新生成 widget 并 diff,如果在前端更新 UI 的话,前端框架在 js 里 diff 一次 vdom,传到 Flutter 之后又 diff 一次 widget。 如果要绕过 Widget 直接对接图中的 Rendering 这一层,可以绕过 widget diff 但是得改 Flutter Framework 的渲染链路,既然要改 Flutter Framework 那为什么不直接用 TS 魔改呢,还绕过了 JS 到 Dart 的通信,又回到了第一种方案。 总结来说,这个方案的优点是:实现简单、能最大化保留前端开发体验,缺点是:渲染链路长、通信成本高、响应式逻辑冲突、CSS 转 Widget 不完备等。 C++ 魔改 想要干掉 Dart VM,就需要用其他语言重新实现用 Dart 开发的 Framework,用 JS/TS 可以,用 C++ 当然可以,最硬核的方式就是用 C++ 重新实现 Flutter 的 Framework,然后接入 JS 引擎,通过 binding 把 C++ 接口透出到 JS 环境,上层应用还是用 JS 做开发。 把 Framework 层下沉到 C++ 之后,不仅会有更好的性能,也能支持更多语言。原本 Flutter Framework 是在 Dart VM 之上的,必须依赖 Dart VM 才能运行,所以对 Dart 有强依赖;用 C++ 重新实现之后,JS 引擎是在 C++ 版 Framework 之上的,框架本身并不依赖 JS 引擎,还可以对接其他各种语言,如对接了 JVM 之后可以支持 Java 和 Kotlin,对接回 Dart VM 可以继续支持 Dart。 这个方案可以增强性能,也能保持和 Flutter 的一致性,但是改造成本和维护成本都相当高。C++ 的开发效率肯定不如 Dart,当 Flutter 快速迭代之后如何跟进是很大的问题,如果跟进不及时或者实现不一致那很可能就分化了。从 CSS 到 Widget 的转换也是不得不面对的问题。 几种方案对比 把上面几种方案画在同一张图里是这个样子的: 图中实线部分表示了跨语言的通信,太过频繁会影响性能,虚线部分表示了其他对接可能性。 从下到上,Flutter Engine 是不需要动的,这一层是跨平台的关键。Framework 则有三种语言版本,JS/TS、Dart、C++,性能是 C++ 版本最好,成本是 Dart 版本最低。然后还需要向上处理 HTML/CSS 和 Widget 的问题,可以直接对接一个前端框架,也可以直接在 C++ 层实现(不然需要透出的 binding 接口就太多了,用通信的方式也太过频繁了)。 如何实现“从 Flutter 到 Web”? 这个功能官方已经实现了,可以把使用 Dart 开发的 App 编译成 Web App 运行在浏览器上,官方文档以介绍用法和 API 为主,我这里简单分析一下内部具体的实现方案。 实现原理 结合 Flutter 的架构图来看,要实现 Web 到 Flutter 需要改造的是上层 Framework,要实现 Flutter 到 Web 需要改造的则是底层 Engine。 Framework 对 Engine 的核心依赖是 dart:ui,这是库是在 Engine 里实现的,抽象出了绘制 UI 图层的接口,底层对接 skia 的实现,向上透出 Dart 语言的接口。这样来看,对接方式就比较简单了: 使用 dart2js 把 Framework 编译成 JS 代码。基于浏览器的 API 重新实现 dart:ui,即 dart:web_ui。 把 Dart 编译成 JS 没什么问题,性能可能会有一点影响,功能都是可以完全保留的,关键是 dart:web_ui 的实现。在原生 Engine 中,dart:ui 依赖 skia 透出的 SkCanvas 实现绘制,这是一套很底层的图形接口,只定义了画线、画多边形、贴图之类的底层能力,用浏览器接口实现这一套接口还是很有挑战的。上图可以看到 Web 版 Engine 是基于 DOM 和 Canvas 实现的,底层定义了 DomCanvas 和 BitmapCanvas 两种图形接口,会把传来的 layer tree 渲染成浏览器的 Element tree,但是节点上仅包含了 position, transform, opacity 之类的样式,只用到 CSS 很小的一个子集,一些更复杂的绘制直接用 2D canvas 实现。 存在的问题 我编译了一个还算复杂的 demo 试了一下,性能很不理想,滑动不流畅,有时候图片还会闪动。生成出来的 js 代码有 1.1MB (minify 之后,未 gzip),节点层次也比较深,我评估这个页面用前端写不会超过 300KB,节点数可以少一半以上。 另外再看一下 Flutter 仓库的 issue,过滤出 platfrom-web 相关的,可以看到大量:文字编辑失效、找不到光标、ListView 在 ios 上不可滚动、checkbox/button 行为不正常、安卓滚动卡顿图片闪烁、字体失效、某些机型视频无法播放、文字选中后无法复制、无法调试…… 感觉 flutter for web 已经陷入泥潭,让人回想起前端当年处理各种浏览器兼容性的噩梦。 这些性能和兼容性问题,核心原因是浏览器未暴露足够的底层能力,以及浏览器处理手势、用户输入和方式和 Flutter 差异巨大。 实现 Flutter Engine 需要的是底层的图形接口和系统能力,虽然canvas 提供了相似的图形接口,如果全部用 canvas 实现的话很难处理可访问性、文本选择、手势、表单等问题,也会存在很多兼容性问题。所以真实方案里用的是 Canvas + DOM 混合的方式,封装层次太高了,渲染链路太长。就好像 Flutter Framework 里进行了一顿猛如虎的操作之后,节点生成好了、布局算好了、绘制属性也处理好了,就差一个画布画出来了,然后交到浏览器手里,又生成一遍 Element,再算一遍布局,在处理一遍绘制,最终才交给了底层的图形库画出来。 再比如长页面的滚动,浏览器里只要一条 CSS (overflow:scroll) 就可以让元素可滚动,手势的监听以及页面的滚动以及滚动动画都是浏览器原生实现的,不需要与 JS 交互,甚至不需要重新 layout 和 paint,只需要 compositing。如上图所示,在 Flutter 中 Animation 和 Gesture 是用 Dart 实现的,编译过来就是 JS 实现的,浏览器本身并不知道这个元素是否可滚,只是不断派发 touchmove 事件,JS 根据事件属性计算节点偏移,然后运算动画,然后把 transform 或者新的 position 作用到节点上,然后浏览器再来一遍完整的渲染流程…… 优化方案 性能和兼容性的问题还是要解决的,短期内先把 issue 解掉,长线的优化方案,官方有两种尝试: 使用 CSS Painting API 做绘制。 a, 这是还处于提案状态的新标准,可以用 JS 实现一些绘制功能,自定义 CSS 属性。 b. 目前还未实现,需要等浏览器先把 CSS Houdini 支持好。 使用 WebAssembly 版本的 Skia 做绘制 https://skia.org/user/modules/canvaskit a, 这样可以发挥 wasm 的性能优势,并且保持 skia 功能的一致。但是目前 wasm 在浏览器环境里未必有性能优势,这里不展开讨论了。 b. 已经部分实现,参考这里的配置启用功能: https://github.com/flutter/flutter/issues/41062#issuecomment-533952994 这两个方案都是想更多的利用到浏览器的底层能力,只有浏览器暴露了更多底层能力,才能更好的实现 Flutter 的 Web Engine。不过这个要等挺久的时间,我们也参与不了,现阶段想要使用 flutter for web,还是得保持现有架构,一起参与进去把 issue 解决掉,优先保障功能,其次优化性能。 一种适应性更好的架构 如果理想化一点,能不能从架构角度让 Flutter 和 Web 生态融合的更好一些呢? 回顾文章最开始的官方架构图,上面是 Framework(Dart),下面是 Engine(C++),切分在 Foundation 这一层,双方之间的交互是几何图形信息。如果还保持这个架构,把切分层次划分的更靠上一些,如下图所示,划分在 Widgets 和 Rendering 这一层,理论上讲对 Flutter 的开发者来说是无感知的,因为上层的开发语言和 Widget 接口都是不变的。 切分在这一层,Framework 和 Engine 之间的交互就不再是几何图形而是节点信息,Widget 的组合、setState 响应式更新、Widget diff 都还在 Dart 中,展开后的 RenderObject 的布局、绘制、裁剪、动画全都在 C++ 中,不仅有更好的性能,还可以与 Engine 有更好的结合。 或者说,还原本保留 Engine 的设计,把下沉的这部分逻辑上划分成 Renderer,就有了如下三层的结构: 这样划分出来的每一层都有明确的定位: Framework: 开发框架。为开发者提供可编程 API,实现响应式的开发模式,提供细粒度 Widget 供开发者自由封装和组合。Renderer: 渲染引擎。专门实现布局、绘制、动画、手势的的处理,这部分功能相对独立,是可以与开发框架解耦的,也不必与特定语言绑定。Engine: 图形引擎。实现跨平台一致的图形接口,合成输入的层并绘制到屏幕上,处理好平台力的接入和适配。 这样切分除了有性能优势以外,也使得渲染引擎摆脱了对 Dart 的依赖,能够支持多种语言,也能支持多种开发模式。对接到 Dart VM 就可以用 Dart 写代码,对接到 JS 引擎就可以用 JS 写代码,对接到 JVM 还可以写 Java,但是无论怎么写,底层的渲染能力是一样的,一套统一的布局算法,动画和手势的处理行为也是一致的。 在这样的架构下,对接 Web 生态就更容易了。Dart 和 Widget 是前端不想要的,希望能换成 JS 和 CSS,但是又想要底层的跨平台一致渲染引擎,那从 Renderer 层开始对接就好了,绕过了所有不想要的,也保留了所有想要的。 要实现 Flutter for Web 也更简单了一些。在 Engine 层做对接,一直苦于浏览器透出的底层能力不够,如果是在 Renderer 之上做对接就更容易一些,基于 JS/CSS/DOM/Canvas 的能力封装出一套 Rendering 接口,供 Widget 调用就好了,这样可以使渲染链路更短一些,但是依然要处理 Widget 和 DOM/CSS 之间的兼容性问题。 再讨论一遍:为什么要对接? 技术上已经分析完了,要想搞定 Flutter 生态和 Web 生态的对接,需要投入很大的成本,所以真正决定做之前,要先讨论清楚为什么要做对接?到底要不要做对接? 首先 Google 官方对 Flutter 的定位就是个问题。Flutter 设计之初就是不考虑 Web 生态的,甚至在刻意回避,倡导的是更贴近原生的开发方式。我之所以在开头说不要对接,原因也很简单:两种技术设计理念不同,不是朝着一个方向发展的,生态不通,技术方案不通,强行融合很可能让彼此都丧失了优势。但是业界又有很多团队在做这种尝试,说明需求是存在的,如果 Google 抵制这个方向,那就不好做了。不过现在官方已经支持了 Flutter for Web,已经向 Web 生态迈了一步,未来是否进一步与 Web 融合,也是有可能的。 另外就是跨平台技术本身的问题,浏览器发展了二三十年,已经是个很强大的跨平台产品了,几乎是 Web 的代名词了,这一点无人能敌。但是也臃肿不堪,有大量历史包袱,性能和体验不够好,和 Native 的结合度差,尤其在移动和 IoT 平台。虽然硬件性能在不断提升,但这是所有软件共享的,浏览器的性能和体验总会比 Native 差一些,差的这一些很可能就是新业务和新场景的发挥空间。观察一下近几年新诞生的业务场景,很多都是利用到了 Native 新提供的能力才火爆起来的,如 AI/AR/ 视频 / 直播 等,有因为新的 Web API 而孵化生出来的商业模式吗? 原文链接: https://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650405725&idx=1&sn=0b7476f7c7c01df7fdafda578f9ceb98&chksm=83953345b4e2ba53917ac30b709c07be15bd1c2fd5ae2a8ecfbb129b3813f771621b8fac95ca&scene=27#wechat_redirect

剑曼红尘 2020-03-10 09:54:40 0 浏览量 回答数 0
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 企业信息查询 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 企业建站模板