关于chrome-devtools:chrome网络面板的4种过滤方式
关键字totalValue只显示蕴含totalValue的申请 排除-totalValue只显示不蕴含totalValue的申请 正则/26$/只显示以26结尾的申请 正则也能够组合排除应用-/26$/只显示不以totalValue结尾的申请 查看特定域domain:*dida365.com只显示域名中蕴含dida365.com的申请
关键字totalValue只显示蕴含totalValue的申请 排除-totalValue只显示不蕴含totalValue的申请 正则/26$/只显示以26结尾的申请 正则也能够组合排除应用-/26$/只显示不以totalValue结尾的申请 查看特定域domain:*dida365.com只显示域名中蕴含dida365.com的申请
The combination of "IPQ5018" and "QCN6122" refers to two different components used in networking devices.IPQ5018: IPQ5018 is a system-on-a-chip (SoC) designed by Qualcomm Technologies. It is specifically designed for networking devices, such as routers, gateways, and access points. The IPQ5018 SoC provides high-performance processing capabilities and features to support various networking functions.QCN6122: QCN6122 is a wireless networking chip developed by Qualcomm. It is designed to provide Wi-Fi connectivity in devices such as routers and access points. The QCN6122 chip supports the latest Wi-Fi 6E standard.Wi-Fi 6E: Wi-Fi 6E is an extension of the Wi-Fi 6 (802.11ax) standard. The "E" stands for "extended" and refers to the use of additional frequency bands in the 6 GHz spectrum. Wi-Fi 6E offers faster speeds, lower latency, and higher capacity compared to previous Wi-Fi standards. It allows for less interference and more available channels for devices to communicate, resulting in improved performance and better user experiences.IPQ5018:https://www.wallystech.com/Routerboard/DR5018-wifi6-Qualcomm-...The IPQ5018 is a system-on-chip (SoC) designed specifically for high-performance Wi-Fi routers. It is based on a quad-core ARM Cortex-A53 processor, running at speeds up to 1.8GHz. The chip also includes an Adreno 506 GPU for improved graphics performance.One of the key features of the IPQ5018 is its support for the latest Wi-Fi 6E standard. This standard operates on the 6GHz frequency band, providing faster speeds and less interference than previous standards. It also supports up to 8 spatial streams, which means it can handle more devices simultaneously without slowing down.The IPQ5018 also includes advanced security features such as WPA3 and enhanced Open. ...
以后社会正在进入万物互联、万物数字化时代,目前急需一个兼具翻新硬件、终端平安、互联协同、人机交互、智能撑持的新一代操作系统。而 openKylin 也始终在致力于打造一款具备万物互联能力的操作系统,在通过数个月的致力研发,汇合 49 个 SIG 组的力量之后,openKylin 首款真正意义上具备万物互联能力的操作系统 - openKylin 0.9,于明天正式公布! 本次 openKylin 0.9 版本基于 5.15 内核构建,新合并了 1720 + 内核补丁,新增了互联互通利用、零碎管家利用、openKylin 通用集成开发环境等;同时降级优化了零碎装置、系统升级、声音服务框架等多个模块;提供全新的 openSDK 2.0 反对,并修复零碎多个已知问题,适配 UKUI 桌面环境在平板模式下的显示和交互,为用户带来更好的平板操作体验。欢送大家下载体验!装置形式1. 通过官网下载全新装置:https://www.openkylin.top/dow... 已装置 openKylin 0.7.5 版本的用户通过以下形式降级:$ sudo apt update$ sudo apt full-upgrade零碎新增个性01内核优化集成 Intel 12 代 CPU 的最新反对补丁,提供对 12 代 CPU 的大小核调度反对集成 Intel 显示补丁,实现对 Intel 支流显卡的适配工作集成支流外设驱动及适配补丁,提供对支流蓝牙,无线网卡,声卡等设施的反对集成中文字体补丁,提供虚构终端中文字体显示反对02新增互联互通利用支持系统设施之间便捷互联,共享屏幕、文件同步治理、跨端搜寻文件等性能反对 Android 设施与零碎设施便捷互联,共享屏幕、文件同步治理、跨端搜寻文件等性能03新增零碎管家利用反对垃圾清理性能反对故障修复性能反对百宝箱性能04全新 openSDK 2.0欠缺 SDK 控件反对款式欠缺获取零碎信息接口欠缺窗口属性设置 / 获取接口05新增 openKylin 通用集成开发环境新增通用集成开发环境 kylin-code(软件商店获取)06声音框架服务选型openKylin 零碎采纳 pipewire 声音框架服务07优化零碎装置优化分区计划,反对 20~50G 小硬盘装置08全新系统升级服务提供全新系统升级性能提供零碎更新历史查看性能09新增麒麟虚构键盘反对输入法框架在平板模式应用虚构键盘10UKUI 桌面环境优化平板模式体验开始菜单优化开始菜单工具提醒内容(珍藏 & 最近区域)任务栏平板桌面任务栏新增日历入口平板桌面新增首屏预装利用默认分组多任务视图优化多任务视图布局新增 PC 多桌面切换至平板交互逻辑新增键盘导航反对主题优化和补充寻光主题局部图标定时关机新增帮忙、对于、退出 3 项在定时关机窗口右上角看图新增反对拖拽窗口图片查看打印机优化增加打印机、批改驱动流程优化界面风格扫描适配平板界面显示优化平板模式下界面显示闹钟 新增闹钟设置选项框适配零碎主题款式对立遗留重大问题修复修复虚拟机联网装置零碎时会装置失败的问题修复 0.7 版本升级到 0.7.5 后软件商店挪动利用界面提醒网络异样问题修复虚拟机下 win+D 最小化后无奈关上侧边栏问题修复无奈敞开夜间模式问题修复任务栏每次调整任务栏尺寸或者地位时都会弹出电源界面的问题修复在多任务视图敞开利用后,利用在任务栏约 8s 后才会敞开的问题修复软件商店的搜寻框无奈切换输入法的问题修复局部快捷键如 Ctrl+C、Ctrl+V、Ctrl+X 理论与预期不同的问题修复看图在 PC 模式下最大化按钮不失效的问题修复 0.7 更新降级到 0.9 版本时声音模块局部性能无奈应用的问题修复登录及重启平板时麒麟虚构键盘闪现的问题修复连贯蓝牙音箱后无奈应用蓝牙音箱播放声音的问题修复 wayland 模式下和 X 模式下刻录的最大化最小化按钮性能生效的问题修复进入平板模式关上任意利用而后敞开,再次关上利用敞开后间接跳到 PC 模式,之后除桌面利用外点击其余中央均无奈点击的问题修复新版便签无奈关上的问题修复互联互通利用在麒麟设施间建设连贯时无奈通过文件右键 - 发送至多端协同进行文件传输的问题修复在 32G 硬盘装置零碎时未依照设计分出 boot 和 swap 分区的问题修复新版本闹钟无奈关上的问题0.9 版本已知问题Issue IDIssue 题目I5VJDJ【wayland】【登录】任何账户进行重启操作,都会主动登录进入桌面(控制面板未开启主动登录)I5VJGG【X】【模式切换】X 下进行重启进入登录界面,右下角不显示模式切换图标,无奈切换登录 wayland(进入 X 后登记进入登录界面可显示图标)I5XA15【控制面板】显示器色温未减少追随日出日落的主动色温计划I5XAG9【锁屏】锁屏界面未集成软键盘I5XALN【多端协同】左近设施链接性能不可用,无奈扫描到对端设施(必现)I5XAQ8【多端协同】多端协同利用窗口 pc 模式下无最大化选项(必现)I5XC9N【控制面板】【触摸屏】控制面板减少主动切换模式入口未实现I5XCOR【多端协同】平板模式下进行 usb 链接 / 设施码连贯 / 扫码连贯 / 投屏操作等,界面须要弹出二级窗口后,点击弹窗上选项,停留在桌面,不会主动跳转至下一界面(必现)I5XCRM【多端协同】平板模式下,在麒麟设施端进行设施码连贯,无奈自动弹出软键盘,无奈输出设施码(必现)I5XCZC【多端协同】平板模式下关上多端协同利用,窗口显示在左上角(复现概率:7/10)I5XD1J【蓝牙】按 Tab 键切换可点击组件性能未失效I5XDD1【蓝牙】任务栏蓝牙界面,点击上 / 下键无奈在设施列表中切换I5XDKM【文件管理器】连贯有线网络或 wifi, 在文件管理器的网络界面没有显示局域网内的其余主机I5XEIA【VPN】未实现控制面板 VPN 界面性能I5XELE【VPN】任务栏虚构连贯界面没有详情按钮I5XER8【网络连接】增加 DNS 性能异样I5XF5R【主题框架】控件可点击区域高度不为 48pxI5XF94【多端协同】建设连贯后,麒麟 pc 设施端搜寻框无奈输出内容 (必现)I5XIIY【SDK】开发指南文档须要欠缺批改细节I5XIWI【wayland】【任务栏】没有适配跳过任务栏的性能I5XL8Y【多端协同】多端协同投屏利用不反对在 wayland 下应用,在 xwayland 下可应用(必现)I5XL9I【UKUI 需要 需要 17186】【电源治理】批改 kylin 用户的电源配置,其余用户的电源配置没有同步批改,需要预期为对立配置I5XPNS【多端协同】安卓设施投屏至麒麟设施后,手机端播放音乐,麒麟 pc 设施无声音输入(必现)I5Z2B9【锁屏】锁屏界面未显示电池图标I5Z2HC【生物特色管理工具】应用两台用户名同为 kylin 的 pc 录入同一个设施中的密钥,录入胜利后,两台 pc 均可能验证通过进入零碎I5Z3YD【平板模式】【闹钟】平板模式下关上闹钟后点击敞开按钮闹钟会间接敞开,再次点击桌面闹钟后弹出询问窗口I5Z45W【平板模式】【闹钟】平板模式关上闹钟后敞开,抉择后盾运行后无奈关上闹钟I5Z7NH【电源治理】不连电源,静置休眠,解锁进入后黑屏,拔插电源有声音,能够进 tty1,但没有图形界面I5VI7P【触摸】【多任务视图】在多任务视图应用手指滑动后,零碎触摸性能生效(无奈应用单击双击滑动等性能),重启后复原I5WZLC软件商店装置微信(网页版装置失败)I5XBMB【多端协同】麒麟设施与安卓设施或麒麟设施之间进行各种连贯或投屏操作,弹出相干二级子窗口,显示两个标题栏(必现)I5XEMN【wayland】【更新降级】0.7 版本未集成的包,更新降级到 0.9 版本没有装上I5XFFQ锁屏登入界面无键盘图标,无实体键盘时无奈输出I5XH5T【多端协同】平板模式下搜寻性能弹出的软键盘显示在界面地方(必现)I5XHI8【输入法框架】重命名新建文本文件失败I5XIIS【输入法框架】中文符号中括号无奈输出I5XM2I【影音】wayland 模式下和 X 模式下,影音的最大化最小化按钮性能生效如有更多 Bug 反馈,大家能够通过点击 “浏览原文” 返回 openKylin Gitee 仓库提交 issue 进行反馈。同时,openKylin 0.9 版本公测流动已正式开启,欢送大家积极参与~ ...
一、到github上下载 https://github.com/vuejs/devt...抉择一个版本下载,为了反对vue3,我这里抉择了最新的一个版本 二、编辑器关上下载后的我的项目,进入该我的项目,yarn install。我的项目目录构造如下,咱们须要给该我的项目装置依赖包。从目录构造最初一个文件咱们晓得,该我的项目用的是yarn,所以咱们应用yarn装置依赖包。装置后咱们多了node_modules依赖包。 三、yarn run build打包之后,shell-chrome文件夹下多了一个build文件夹,阐明咱们曾经打包胜利。 四、装置插件1.关上Chrome浏览器->更多工具->扩大程序->关上开发者模式2.点击“加载已解压的扩大程序”,抉择咱们刚刚下载的我的项目的shell-chrome文件夹。 五、完事
前言Sonic平台周边生态sib推出新性能webinspector啦!给iOS进行H5测试带来什么不一样的体验呢?往下持续查看吧 sib应用文档 iOS web测试根底原理浅谈以后的支流H5调试,基本上都是基于浏览器凋谢的debug ws服务来进行的。咱们通过连贯这些ws,而后发送对应的协定过来,即可达到debug的目标,例如iOS获取elements,则须要依照协定通过ws发送getDocument办法到webkit外面,期待ws server返回对应的element信息。iOS的webkit protocol具体可参考:WebKit/Source/JavaScriptCore/inspector/protocol at main · WebKit/WebKit · GitHub ,外面通过划分域的模式,曾经将协定分为次要的20-30个文件。 如何开启iOS web debug服务?不同于安卓只须要简略的去开发者模式里开启webview的debug模式,iOS因为其封闭性,开启web debug十分麻烦。咱们须要发送相干的DTX协定给iOS内置的com.apple.webinspector(参考:sonic-gidevice/webinspector.go at main · SonicCloudOrg/sonic-gidevice · GitHub 、sonic-ios-bridge/src/webinspector at main · SonicCloudOrg/sonic-ios-bridge · GitHub )。 大体流程如下:通过gidevice启动相干的webinspector server办法,随后DTX发送对应的connect id到webinspector, 这时候会返回对应的DTX信息,咱们会依据DTX信息的case标记(Selector参数)进行webinspector client的初始化解决。 该过程中会失去以后设施中的webkit利用pid和base page信息(依据一些技术文章,如果iOS的利用有developer证书,则能够开启H5调试,后续开发保护时会进行验证其真实性)。依据这些pid和page信息,当须要对某个webkit利用进行web debug时,创立一个senderid,并将其发送到webinspector中,让webkit开启debug服务,咱们只须要发送相干的协定信息就行。 协定兼容尽管iOS的webkit inspector是倒退最早的一个网页调试器,然而因为iOS的封闭性和其余的一些因素,后续的其余内核的浏览器调试并没有应用iOS的webkit调试协定,基于易用性思考,sonic参考google/ios-webkit-debug-proxy 、RemoteDebug/remotedebug-ios-webkit-adapter 这两个我的项目,用golang重写了一遍,只须要应用sib的webinspector adapter模式,即可通过chrome devtool简略调试iOS的safari。外围思路是sib将发送协定信息这个关键步骤做成ws服务,采纳双向代理的模式,通过SonicCloudOrg/sonic-ios-webkit-adapter 拦挡iOS webkit调试协定和Chrome DevTools Protocol协定之间的特异办法,将其转换成单方可承受的调试协定和返回后果。 案例如果咱们须要获取以后页面下导航栏中的历史信息时,Chrome DevTools Protocol的做法是ws里发送Page域中的getNavigationHistory办法到以后调试的利用中,期待返回的后果就行。比拟惋惜的是,这个办法间接发送到iOS webkit中,iOS webkit会返回信息告知并没有这个办法,不过iOS webkit能够通过曲线救国的形式达到相似的成果。首先,咱们先看Chrome DevTools Protocol中getNavigationHistory的返回信息是什么(参考:Chrome Devtools Protocol ) { "id": "TransitionType", "description": "Transition type.", "type": "string", "enum": [ "link", "typed", "address_bar", "auto_bookmark", "auto_subframe", "manual_subframe", "generated", "auto_toplevel", "form_submit", "reload", "keyword", "keyword_generated", "other" ]}{ "name": "getNavigationHistory", "description": "Returns navigation history for the current page.", "returns": [ { "name": "currentIndex", "description": "Index of the current navigation history entry.", "type": "integer" }, { "name": "entries", "description": "Array of navigation history entries.", "type": "array", "items": { "$ref": "NavigationEntry" } } ]}{ "id": "NavigationEntry", "description": "Navigation history entry.", "type": "object", "properties": [ { "name": "id", "description": "Unique id of the navigation history entry.", "type": "integer" }, { "name": "url", "description": "URL of the navigation history entry.", "type": "string" }, { "name": "userTypedURL", "description": "URL that the user typed in the url bar.", "type": "string" }, { "name": "title", "description": "Title of the navigation history entry.", "type": "string" }, { "name": "transitionType", "description": "Transition type.", "$ref": "TransitionType" } ]}由返回构造可知,最重要的是url和titile(其余信息可自定义生成),所以思路能够这样: ...
IPQ6010 (IPQ6018 FAMILY) EMBEDDED BOARD WITH ON-BOARD WIFI DUAL BAND DUAL CONCURRENT / 802.11AX MU-MIMO OFDMA / 2* GE PORTS DR6018-S QCN9074 WiFi Card IPQ6010,IPQ6018 802.11ax Support OpenWRT 2x2 2.4G&5G https://www.wallystech.com/Ro... MT7915/MT 7975/IPQ6000/IPQ6018/IPQ6010/IPQ4019/IPQ4029/ipq4018/IPQ8072/IPQ8074/QCN9074/QCN9072/QCN9024/IPQ5018/BY:Wallys Communications (Suzhou ) Co., LTDEMAIL:sales3@wallystech.com Wallys Communications (SuZhou) Co., Ltd., http://www.wallystech.com,which is a professional supplier specializing in product design, manufacturing and offering superior OEM/ODM/JDM services in wireless communications. As a specialized manufacturer and exporter for these products in China,We sincerely hope to establish business relations with your esteemed corporation. We mainly develop high power wireless products based on Quacomm chip such asIPQ6000/IPQ6018/IPQ6010/IPQ4019/IPQ4029/IPQ8072/IPQ8074/QCN9074 and so on Product Description 524wifi 6018-S based on IPQ6010 chipset is an enterprise wireless module integrated with 2×2 5G high power Radio module and 2×2 2.4G high power Radio module designed specifically to provide users with mobile access to high-bandwidth video streaming, voice, and data transmission for office and challenging RF environment in factories, warehouses establishment. Passive POE 24-48V supported, DC jack 24-48V. If you need 802.3af/bt active POE support , then please order 524wifi 6018-S HV version. ...
录了段视频放b站了,https://www.bilibili.com/vide... 1,DOM面板1,鼠标点击选中DOM元素,双击能够编辑2,拖拽、复制、剪切、粘贴(ctrl c,ctrl v,ctrl x 快捷键)3,搜寻 ctrl F (能够搜字符串、css选择器、xpath,) 比较复杂的css选择器也能够,比方: 相邻兄弟选择器 .my-button+.my-button、 属性选择器 [class^="nav"] 这个性能很实用,调试代码有时候用得着4,鼠标右键add attribute:增加属性force status:设置元素状态,能够用来调试不同状态的款式 (:hover、:active、:focus、:visited、:focus-within、:focus-visible) style面板也有一个中央能够设置元素状态break on:元素发生变化的时候,打断点 subtree modifacations: 子节点扭转 attribute modifications: 属性扭转 node removal: 节点被删除expand recursively:开展collapse children:收起capture node screenshot:截图 (留神滚动条) 比方 截b站首页https://www.bilibili.com/ 选中html标签,或者body标签,都只能截到一屏, 能够看左边的computed面板的height html标签的height只有864px。 #app高度有14048px,那就截图#app,能够截到残缺的页面。2,style 面板filter:筛选过滤:hov:设置元素状态样式表:双击可编辑,数值型的值 按高低方向键 会加1/减1,关键词的 能够看到所有能够设置的值,按高低键可切换。 3,computed 面板最初理论利用的值相对值换算成绝对值(比方em、rem、百分比、vh、vw等,换成px)filter:筛选show all: 显示全副(很多属性没有被样式表设置的,有默认值)group: 按布局、文字、外观等组合4,Event Listeners元素(和它的先人元素)的监听事件如果把Ancestors勾选上,就显示本人+先人的,如果不勾选,就只显示本人的 5,DOM Breakpointers右键 break on 打的断点会显示在这里
录了段解说视频,放b站了https://www.bilibili.com/vide... 1,打开方式F12ctrl shift i(command option i)鼠标右键 -> 查看(inspect)右上角更多按钮(三个点)-> 更多工具 -> 开发者工具2,放大放大ctrl 鼠标滚轮ctrl +/-,重置 ctrl 0,mac Command +/- , 重置 command 0 3,面板(panel) 面板罕用的有element(右边dom,左边款式),console(个别用上面那个drawer),source(打断点),network,application(本地存储,localstorage、 sessionstorage、cookie) 关上/敞开console draweresc右上角的更多按钮(三个点)里的'open console drawer'/‘hide console drawer’查看元素 切换设施(pc和挪动端)(挪动端:responsive、custom) 切换语言:设置 -> language,或者:设置 ->restore defaults and reload(复原默认值并从新加载)(会把所有的设置项都重置)禁用javascript:设置 -> disable javascript 更多:地位、hide/show console drawersearch (搜寻网站资源 (soucre面板里的))// 其余几个面板的search element、network// filter console、networkrun command // 一些内置命令,比方 capture fullsize screenshot(整个页面截图), capture node screenshot(先在elements面板选中一个节点,而后再运行这个命令,能够截图选中的节点)// elements面板选中一个节点,按鼠标右键,也有一个capture node screenshot,这个更不便// 截图要留神有滚动条的状况,下次讲elements面板的时候讲
这个扩大的名称:Night Eye - Dark mode on any website 能够间接在 Chrome extension web store 里下载。 看个应用的例子:这是我失常的网页: 这个扩大装置胜利后,在右上角扩大图标栏里能看到对应的图标,点击: 抉择“光明”模式即可: 切换成彩色模式之后的网页: 点击这个齿轮图标,能够进行自定义设置: 即便不付费,也能够永恒应用,只是只能切换最多五个网站为深色模式: 更多Jerry的原创文章,尽在:"汪子熙":
Edit files with Workspaces 本教程提供设置和应用工作区的实际练习,以便您能够在本人的我的项目中应用工作区。 工作区使您可能将在 DevTools 中所做的更改保留到存储在计算机上的源代码中。 Overview工作区使您可能将在 Devtools 中所做的更改保留到计算机上同一文件的本地正本。 例如,假如: (1) 您的桌面上有站点的源代码。(2) 您正在从源代码目录运行本地 Web 服务器,以便能够通过 localhost:8080 拜访该站点。(3) 您曾经在 Google Chrome 中关上了 localhost:8080,并且您正在应用 DevTools 来更改站点的 CSS。 启用工作区后,您在 DevTools 中所做的 CSS 更改将保留到桌面上的源代码中。 Limitations如果您应用的是古代框架,它可能会将您的源代码从易于保护的格局转换为通过优化以尽快运行的格局。 Workspaces 通常可能在源映射的帮忙下将优化后的代码映射回原始源代码。 然而框架之间在如何应用源映射方面存在很多差别。 Devtools 根本无法反对所有的变动。 比方,Workspaces 不反对 Create React App. Related feature: Local OverridesLocal Overrides 是另一个相似于 Workspaces 的 DevTools 性能。 当您想对页面的更改进行试验,并且须要跨页面加载查看这些更改时,请应用本地笼罩,但您不关怀将更改映射到页面的源代码。 只有以后被加载到 Chrome 开发者工具的 web 利用对应的文件夹色彩扭转了: 间接在 Chrome 开发者工具里批改 index.html: 加上 from Jerry 的字符串: ...
绑定本地地址是为了对于本地计算机的两个过程过程通信,而绑定端口号是为了绑定一个过程,是为了对于客户端进行发送音讯到服务器的时候,能够找到服务器。星池starpool 一个绝对独立的上下文环境,咱们能够依据入参对值加工计算,并返回新的值。从类型层面看,咱们也能够通过类型推断加工计算入参的类型,并返回新的类型,示例如下:咱们能够应用 Generator 的同名接口泛型或者 Iterator 的同名接口泛型示意返回值的类型(Generator 类型继承了 Iterator 类型),示例如下; 但如果传则肯定是string类型。也就是说调用函数时,咱们能够不显式传入参数。然而,如果咱们申明了参数类型为 xxx | undefined,https://www.starpool.cn 就示意函数参数是不可缺省且类型必须是 xxx 或者 undfined。
源端配置: 1.1装置OGG软件。 OGG软件不做要求12版本即可 配置MGR PORT 7810 DYNAMICPORTLIST 7811-7914 AUTORESTART REPLICAT dpe*, WAITMINUTES 1, RETRIES 5 AUTORESTART REPLICAT ext*, WAITMINUTES 1, RETRIES 5 PURGEOLDEXTRACTS /home/ogg/kafka_ogg/dirdat/kf*,USECHECKPOINTS, minkeephours 6 配置ext抽取过程参数: EXTRACT extkaf --setenv (NLS_LANG="AMERICAN_AMERICA.AL32UTF8") setenv (ORACLE_HOME=/u01/app/oracle/product/11.2.0/dbhome_1) userid goldengate@ogg ,password Golden_1230 --getupdatebefores GETTRUNCATES REPORTCOUNT EVERY 15 MINUTES, RATE DISCARDFILE ./dirrpt/extkaf.dsc,APPEND,MEGABYTES 1024 --THREADOPTIONS MAXCOMMITPROPAGATIONDELAY 90000 IOLATENCY 90000 DBOPTIONS ALLOWUNUSEDCOLUMN --WARNLONGTRANS 2h,CHECKINTERVAL 3m EXTTRAIL ./dirdat/kf -- TRANLOGOPTIONS CONVERTUCS2CLOBS TRANLOGOPTIONS EXCLUDEUSER goldengate TRANLOGOPTIONS DBLOGREADER -- TRANLOGOPTIONS _noReadAhead Any --DYNAMICRESOLUTION ...
在多线程中,1.5版本之前,咱们都应用同步代码块或者同步办法来解决线程平安问题 比方: 同步代码块 synchronized(锁对象){ 性能代码; } 同步办法 public synchronized void test(){ 性能代码; } //首先实现共享数据 class Person{ String name; boolean isMoney=true; //挣钱 public void zhengQian(){ synchronized(this){ while(!isMoney){ try{wait();}catch(Exception e){} } name="男人"; System.out.println(www.sangpi.comThread.currentThread().getName()+name+"---挣钱---"); isMoney=!isMoney; notifyAll();//叫醒所有 } } //花钱 public void huaQian(){ synchronized(this){ while(isMoney){ try{wait();}catch(Exception e){} } name="women。。。。人"; ...
1.整体数据库配置三个源端和一个downstream上游抽取数据库配置: alter system set enable_goldengate_replication=TRUE; Alter database add supplemental log data; Alter database add supplemental log data(primary key,unique index) columns; Alter database add supplemental log data(all) columns; Alter database force logging; select SUPPLEMENTAL_LOG_DATA_MIN,SUPPLEMENTAL_LOG_DATA_PK,SUPPLEMENTAL_LOG_DATA_UI,SUPPLEMENTAL_LOG_DATA_FK,SUPPLEMENTAL_LOG_DATA_ALL,force_logging from v$database; 2.SOURCE DB1、2、3端数据库参数调整和配置:1.db1,db2,db3 三个库创立表空间和用户 create tablespace ogg datafile ‘ xxxxx.dbf ’ size 1g autoextend on next 1g; alter tablespace ogg add datafile ‘ xxxx.dbf ’ size 1g autoextend on next 1g; create user ogg account identified by oracle ; ...
批改数组长度 应用“数组名.length”能够获取或批改数组的长度。数组长度的计算形式为数组中元素的最大索引值加1,示例代码如下。 var arr = ['a', 'b', 'c']; console.log(arr.length); //输入后果:3 在上述代码中,数组中最初一个元素是c,该元素的索引为2,因而数组长度为3。应用arr.length不仅能够获取数组长度,还能够批改数组长度,示例代码如下。 var arr1 = [1, 2]; arr1.length = 4; //大于原有长度 console.log(arr1); //输入后果:(4) [1, 2, empty x 2] var arr2 = [1, 2, 3, 4]; arr2.length = 2; //小于原有长度 console.log(arr2); //输入后果: (2) [1, 2] 在console.log()的输入后果中,后面的“(4)” 示意数组的长度为4,前面显示的是数组中的元素,emply示意空元素。若length的值大于数组中原来的元素个数,则短少的元素会占用索引游戏的地位上,成为空元素;若length的值小于数组中原来的元素个数,多余的数组元素将会被舍弃。 当拜访空元素时,返回后果为undefined,示例代码如下。 var arr = [1]; arr.length = 4; //批改数组的长度为4 console.log(arr); //输入后果: (4) [1, empty x 3] ...
相干篇章Chrome Devtools: Elements篇 概述Sources面板用于资源检索、代码逻辑调试。 演示前置示例ElementUI官网其余篇章有的是以掘金为示例演示的,而掘金是服务端渲染(SSR),资源压缩,不易演示。 环境Chrome浏览器版本 90.0.4430.93操作释义聚焦控制台鼠标在控制台范畴内点击一下,使后续操作上下文绑定在控制台中。关上控制台以ElementUI官网为示例讲述: 通过链接关上页面,通过F12或鼠标右键【查看】关上开发者工具控制台。 默认布局 资源管理器面板 该面板下又细分不同的面板,默认展现的是Page面板Page面板默认以域名分类,列出站点依赖的所有资源代码编辑面板 在资源管理器面板中选中一个文件后,该文件的内容展现在该面板代码Debugger面板 操作断点查看断点上下文Sources面板该面板下的性能紧紧依赖着资源管理面板、代码编辑面板、调试面板。 只不过细分到上层面板治理不同的资源: Page面板 治理近程站点资源Filesystem面板 将Sources面板当IDE(代码编辑器),治理本地站点资源Snippets面板 治理浏览器长久化代码资源咱们以Page面板为主体讲述,其它小面板(Filesystem、Snippets)一带而过。 Page面板面板布局1:资源管理器默认布局的1地位即Page面板的全部内容,该面板列出了以后站点页面执行的所有资源,咱们能够通过该面板获取以下站点信息: 技术栈: 能够从资源的要害代码查看相干资源: 从资源列表中一眼能看出页面加载的资源类型以后页面执行的自定义脚本,比方Snippets面板下定义的...依赖域: 从资源分类上,依赖资源所在域高深莫测浏览器扩大程序 以后页面加载的浏览器扩大程序为了洁净的调试环境,排除扩大程序的烦扰,所以,个别选用无痕模式调试 前提:没有开启扩大程序无痕模式下可用面板布局2:代码编辑面板在Page面板中点击任意资源,即可在默认布局2的编辑面板中看到资源详情。上图点击了图片资源,能够看到该图片是一张二维码。上图点击了CSS资源,在默认布局2地位的编辑面板中,能够点击左下角的 {} 按钮,进行代码丑化 —— 依据以后CSS资源的大小,丑化所须要的工夫不同。 若资源太大,浏览器可能会因为CPU占用过高卡死。 这里咱们做了一个试验,检索到顶部菜单的选择器,进行款式更新,能够实时地在页面上看到展现成果,甚至不须要保留。 然而,在这里批改的代码只是保留在内存中,刷新页面代码就还原了。 JS资源调试这里,咱们将Javascript资源独自讲述,因为在Devtools中JS资源调试的复杂度较高。 调试JS的场景在编写代码过程中,查看未知参数的构造;在编写、Bug修复过程中,运行后果与逻辑设计不符时,代码逻辑梳理;调试JS的步骤以修复Bug为例: 找出Bug复现的法则相熟代码的前提下,由法则登程,推断Bug复现的范畴在不同的范畴打(条件)断点Step by Step的调试断点依据调试的后果,一直的放大Bug范畴放大至找到确定的问题针对找到的问题,提出解决方案评估解决方案,抉择适合的计划修复Note:针对网络申请,断点工夫过长会造成申请超时。 断点 vs. 日志说到调试代码,罕用的形式有两种:断点、日志; 断点Debugger日志Console中断代码的执行不中断代码的执行查看中断代码那一时刻的上下文信息查看代码执行完结的上下文信息非侵入式侵入式,将日志代码写入业务代码中查看代码中断时刻所有的执行上下文信息只能查看指定的打印信息时效性:此时此刻的值代码执行完结时指定信息的值,除非深拷贝示例在代码编辑面板中,只有针对Javascript的断点能力拦挡执行胜利,而针对DOM的断点,须要在Elements面板增加:DOM的操作,详情参见Elements篇。 下述以Chrome浏览器提供的官网调试代码为例: 关上控制台,从Panel面板中能够看到以后页面只有两个资源:HTML页面及相干的JavaScript。其它的是我装置的Chrome扩大(没有应用无痕模式)。 从get-started.html中咱们能够看到相干的HTML构造、Style款式及引入的JavaScript代码。 页面逻辑输出Number1、Number2,点击按钮取得计算结果。 冀望后果计算获取Number1、Number2两个数字的和:1 + 1 = 2 理论后果获取到Number1、Number2字符串的拼接:1 + 1 = 11 复现率100%,阐明是逻辑谬误,而不是代码逻辑对某种边界没有笼罩的概率问题。 代码锁定Bug范畴(嫌疑犯)function updateLabel() { var addend1 = getNumber1(); var addend2 = getNumber2(); var sum = addend1 + addend2; label.textContent = addend1 + ' + ' + addend2 + ' = ' + sum;}由代码 label.textContent = addend1 + ' + ' + addend2 + ' = ' + sum; 及 1 + 1 = 11 的显示后果猜想,addend1、addend2看似没有问题,而sum看似有问题,那咱们就在sum计算的中央打断点。 ...
此示例将演示如何获取CAD控件图纸上一些罕用信息,如符号表(块表、层表、文字样式表、线型表、点样式表)、字典。 。 上面将介绍如何获取符号表的一些信息,如下图所示: 15123534486359648.png 图层操作DWG数据库中,图层寄存在图层层表中,层表类名:MxDrawLayerTable,它的具体接口阐明参考: 层表中每一条记录称为层表记录,类名:MxDrawLayerTableRecord,它的具体接口阐明参数: 每一个层,有色彩,线型,敞开/关上,解冻等属性,DWG数据库默认总是有一个"0"层,该层不能被删除。 图层操作在线演示例子: 视频演示: A. 以后层 以后层示意www.sangpi.comDWG数据库默认层,向数据库中增加一个对象时,默认放到该层上,该属性随图寄存,通过零碎变量CLAYER操作,零碎变量设置参考: 设置以后层代码JS: 1 // 新建一个图层 mxOcx.AddLayer("MyLayerName"); // 设置以后层 mxOcx.SetSysVarString("CLAYER", "MyLayerName"); // 设置控件Draw函数使得的层 mxOcx.LayerName = "MyLayerName";或者这样: 1 // 增加层 mxOcx.AddLayer("aaaa"); // 失去以后数据库 var database = mxOcx.GetDatabase(); // 设置以后层 database.CurrentlyLayerName = "aaaa";B.增加图层 调用控件wwwAddLayer办法增加图层,具体接口阐明见: 例如JS代码: 1 // 新建一个图层 mxOcx.AddLayer("MyLayerName"); // 设置控件Draw函数使得的层 mxOcx.LayerName = "MyLayerName";或应用层表Add办法来增加一个图层 1 // sNewLayerName新创建的图层名 var sNewLayerName = "TestNewLayer"; //返回控件的数据库对象 var database = mxOcx.GetDatabase(); var layerTable = database.GetLayerTable(); // 向层表中新增一个层表记录 var layerTableRec = layerTable.Add(sNewLayerName); // 设置新图层的色彩。SetRGB var color = layerTableRec.Color; color.SetRGB(255, 0, 0); layerTableRec.Color = color; // 把新建的层设置成以后层 database.CurrentlyLayerName = sNewLayerName;C.遍历所有图层 ...
WIA/WIB1002 DS Assignment (S2, 2020/21) ‘Always on Time’ Delivery1‘Always On Time’ DeliveryIntroductionYour friend’s delivery company ‘Never On Time Sdn Bhd’ is receiving tons of complaints fromcustomers as they feel that the delivery process is far too slow. Delivery men in your friend’scompany are always lost in the middle of their delivery route, don’t know where to deliver theparcel and which road they should take to shorten the delivery time. Sometimes they feel angryand exhausted when they lose their direction and they eventually take it out on the parcelswhich causes more complaints from customers. Your friend tried out many ways to solve theproblem but to no avail. Hence, you are your friend’s last hope to save his company. ...
什么是指令(directive)在Angular和Vue中都有Directive的概念,咱们通常讲Directive 翻译为“指令”。 在计算机技术中,指令是由指令集架构定义的单个的CPU操作。在更宽泛的意义上,“指令”能够是任何可执行程序的元素的表述,例如字节码。 那么在前端框架Vue中“指令”到底是什么,他有什么作用呢? 在Vue开发中咱们在模板中常常会应用v-model和v-show等以v-结尾的关键字,这些关键字就是Vue框架内置的指令。通过应用v-model,能够获取实现DOM和数据的绑定;应用v-show,能够管制DOM元素显示。简而言之通过应用这些模板上的标签,让框架对DOM元素进行了指定的解决,同时DOM扭转后框架能够同时更新指定数据。指令是Vue MVVM的根底之一。 指令的应用场景 除了应用内置的指令,Vue同样反对自定义指令,以下场景能够思考通过自定义指令实现: DOM的根底操作,当组件中的一些解决无奈用现有指令实现,能够自定义指令实现。例如组件水印,主动focus。绝对于用ref获取DOM操作,封装指令更加合乎MVVM的架构,M和V不间接交互。 < p v-highlight="'yellow'">Highlight this text bright yellow p > 多组件可用的通用操作,通过应用组件(Component)能够很好的实现复用,同样通过应用组件也能够实现性能在组件上的复用。例如拼写查看、图片懒加载。应用组件,只有在须要拼写查看的输出组件上加上标签,遍可为组件注入拼写查看的性能,无需再针对不同组件封装新的反对拼写性能呢。 Vue 3如何自定义指令Vue反对全局注册和部分注册指令。 全局注册注册通过app实例的directive办法进行注册。 let app = createApp(App) app.directive('highlight', { beforeMount(el, binding, vnode) { el.style.background = binding.value } }) 部分注册通过给组件设置directive属性注册 export default defineComponent({ name: "WebDesigner", components: { Designer, }, directives: { highlight: { beforeMount(el, binding, vnode) { el.style.background = binding.value; }, }, }, }); 注册组件蕴含组件的名字,须要惟一和组件的一个实现对象,组册后即可在任何元素上应用了。 1 < p v-highlight="'yellow'">Highlight this text bright yellow p > ...
背景:在接口测试中,时常会遇到短信验证码的获取及校验;个别的办法是:倡议与开发沟通,设置万能验证码或是屏蔽校验逻辑代码,如果想挑战一下技术,那么就须要本人入手编写代码,这里次要介绍java的Scanner类来与jmeter进行交互输出验证码。 1、例一:两个接口申请,一个申请验证码返回,一个输出验证码校验,(这种状况只需关联即可)脚本构造如下: tips:下面两种状况模仿正确和失败场景,jmeter场景设计,接口申请个别不会把验证码放在响应参数,这样是不平安的。 2、接下来例二:同样是两个接口,做的事件也一样,只有一个问题,游戏验证码从哪里来?数据库?程序发送短信性能,在测试过程中,短信会发送到手机,同样也会入库(只思考入库状况,因短信功能模块不同,不确定渠道商的短信解决形式)。先讲JMeter-通过jdbc元件从数据库获取验证的形式,脚本构造如下: tips:脚本构造与下面比拟,多了一个jdbc配置元件,json提取器换成了jdbc后置处理器;code的获取形式产生了扭转。 3、再来讲JMeter脚本通过beanshell导入Scanner类编码实现从控制台获取code来校验,脚本构造如下: tips: 通过一个beanshell取样器编写脚本,beanshell后置器会在java request之前执行(仅此①例)。以下附上www.sangpi.combeanshell代码: //beanshell中导入Scanner扫描类import java.util.Scanner;//new一个扫描对象Scanner scanner=new Scanner(System.in);//给vars应用,所以给String类。亦或int类型,应用nextInt()办法String messagecode=scanner.next();//vars字符串的赋值,如果前面的不是String类型,就须要转toString()vars.put("code",messagecode);//敞开扫描流scanner.close();复制代码4、以上是JMeter接口测试取得短信验证码的多种解决形式,为了平安起见,程序设计短信验证码是不会从接口返回的。
React Native 是FaceBook开源的一个我的项目,FaceBook心愿能够用写 Web App 的形式去写 Native App。它能够让咱们用JS和React来开发利用,应用React Native能够通吃Android 和 IOS ,以及Web,仅仅须要一份业务逻辑代码就能够来创立咱们的利用。 起源React Native 是由 React 衍生进去的,而 React 起源于 Facebook 的外部我的项目,因为FaceBook对市场上所有 JavaScript MVC 框架,都不太称心,就决定本人写一套,用来架设本人的的网站。等做进去当前,发现这套货色很不错,而且好用,就在2013年5月开源了。 而衍生的 React Native 我的项目,心愿用写 Web App 的形式去写 Native App。这样同一组人只须要写一次 UI ,就能同时运行在服务器、浏览器和手机上。FaceBook最早公布的对于React Native是针对IOS的,而React Native for Android是在去年也就是2015年9月15日开源公布的。现阶段React Native的体验尽管曾经很不错了,只能说很靠近原生利用,然而还是不能达到Native App的体验。所以它很火,未来的事,咱们将来说,然而目前曾经十分火了,多门技术,多条路。咱们也应该学学了。 环境搭建因为我用的是macbook,所以这次环境搭建是依据mac电脑来说的,不过大家释怀,针对windows电脑的环境搭建,我会分享给大家一篇文章或者视频的,不会忘了你们的。 Homebrew装置Homebrew是一款自在及凋谢源代码的软件包管理系统,用以简化Mac OS X零碎上的软件装置过程,Homebrew以Ruby语言写成,针对于Mac OS X操作系统自带Ruby的版本。默认装置在/usr/local,由一个外围git版本库形成,以使用户能更新Homebrew。是 OS X 不可或缺的套件管理器。装置之前,你能够先检查一下电脑上是否曾经装置了Homebrew,查看形式如下:在终端执行下列命令: brew -v如果曾经装置了,就像下图一样,显示版本号。图片如果没有装置,那就能够用上面这种形式,进行装置,在终端上间接输出上面的命令即可: ruby -e "$(curl -fsSL https://raw.githubusercontent...Node.js的装置Node.js®是一个基于Chrome V8 引擎的 JavaScript 运行时。 Node.js 应用高效、轻量级的事件驱动、非阻塞 I/O 模型。Node.js 之生态系统是目前最大的开源包管理系统。咱们都晓得React Native是须要应用js开发的,所以Node.js是必不可少的装置。 装置Node.js也很简略,如下: nvm install node && nvm alias default node可能你发现了,在Terminal终端上应用的命令是nvm,如果你没有装置nvm是会提醒command not found的,所以咱们还得先装置nvm。nvm 是 Node.js 的版本管理器,能够轻松装置各个版本的 Node.js 版本。装置形式如下:咱们能够通过Homebrew装置: ...
演示简介环境Chrome浏览器版本 90.0.4430.93操作释义聚焦控制台 鼠标在控制台范畴内点击一下,使后续操作上下文绑定在控制台中。关上控制台以掘金为示例讲述: 通过链接关上页面,通过F12或鼠标右键【查看】关上开发者工具控制台。 默认布局布局渲染 Render Tree的视觉体现Elements面板 DOM Tree折叠模式展现Styles面板 CSSOM汇总Console面板 默认暗藏,键盘 Esc 键切换展示状态Elements面板锚点执行步骤 抉择如图红色标记工具点击掘金的页面菜单,能够看到Elements面板聚焦到选中展现对应的DOM构造。在对应的DOM构造后,有 $0 的非凡标识,能够在 Console 面板间接应用 $0 援用以后选中元素利用场景查看动静插入的DOM树是否正确查看动静编辑的节点属性是否正确嵌套构造执行步骤 通过锚点工具选中某DOM元素在Elements面板底部,展示以后选中元素的先人选择器点击对应的选择器,Elements面板聚焦对应的DOM元素利用场景查看元素嵌套关系响应式布局执行步骤 通过按钮1开启响应视图模式自定义或应用预置视图尺寸定义网络状态横竖屏切换更多细节展现利用场景Mobile H5开发:不同手机尺寸下的布局适配调试PC Web开发:在不同断点范畴内的布局差异化展现调试PC & Mobile开发:不同视图模式下的适配调试Flex布局如果足够认真,能够看到选中页面菜单对应的DOM构造中,有一个差异化的展现模式: 执行步骤聚焦控制台, Ctrl + P 输出 > experiments 选中 Settings Show Experiments 回车 或 间接点击地位2,抉择 Experiments开启Flex调试 点击 Elements 面板中DOM构造后的 flex 标识,以后页面渲染会高亮展现Flexbox中的子项布局 点击 Styles 面板中对应款式后的标识,能够快捷批改以后Flexbox的布局属性 利用场景查看Flexbox布局是否正确疾速调试Flexbox布局Styles面板款式起源 用户代理内置的款式 浏览器默认款式站点开发者定义的款式 网站开发者内联、外联款式站点用户指定的款式 用户在控制台编写的款式用户通过抓包工具重定向的款式用户通过浏览器插件(如: Stylus)指定的款式款式申明在Styles面板展示的款式申明是默认状态下的申明款式,即页面初始化渲染后的款式,不蕴含交互状态款式。 Computed面板该面板汇总浏览器最终利用到元素上的款式。 锁定交互伪类案例场景鼠标在掘金页面菜单悬停时,能够看到菜单文字款式发生变化,如何调试悬停款式? 执行步骤通过锚点工具聚焦以后变动的DOM节点通过Styles面板的 :hov 按钮切换节点交互状态即可在Styles面板查看交互状态下的款式 利用场景调试通过伪类产生交互款式的场景动静批改元素款式类名执行步骤 点击 .cls 按钮通过点击class复选框动静增删类名能够看到删除 active 类名时,款式产生扭转在其它同级节点上增加 active 类名,验证猜测 ...
灵感的源泉来源于一直的承受陈腐事物。Chrome 89 新性能一览,性能晋升显著,大量 DevTools 新个性! 文章中的新个性,把握了对日常开发,很受害,连忙更新浏览器版本吧。 谈谈其中提到的:新的色彩对比度算法-先进感知对比度算法(APCA)。 启用该性能设置:选中 Settings > Experiments 下的 Enable new Advanced Perceptual Contrast Algorithm (APCA) replacing previous contrast ratio and AA/AAA guidelines 复选框。 能帮忙开发者验证文本是否满足倡议的对比度比率。 对比度在构建页面或应用程序时须要思考对比度,这一点很重要。对比度是页面上相邻显示的色彩之间的差别。 对比度差的页面很难浏览,并且元素也不突出。具备良好对比度的页面,不仅看起来更好,而且对用户更加敌对和具备高可拜访性。 某些视力较差的人看不到十分亮堂或十分暗的区域。所有内容往往都看起来简直雷同,这使辨别轮廓和边缘变得很艰难。 对比度比率测量文本前景和背景之间的亮度差别。如果文本的对比度较低,则这些视力较差的用户可能会以空白屏幕的模式体验您的网站。 为了帮忙开发者正确地取得对比度,WCAG(Web内容可拜访性指南)倡议最小(AA)对比度至多为 4.5:1,对于大文本,则为 3:1,或者加强(AAA)对比度为 7:1 或 4.5:1(大文本)。 最小对比度(AA): 加强对比度(AAA): 在控制台查看good bad 色彩选取器可帮忙你验证文本是否满足倡议的对比度比率: 查看拾色器的 " 对比度比率 " 局部。 一个复选标记示意该元素满足 最低倡议。 两个复选标记示意它合乎 加强的倡议。 单击 " 对比度比率 " 局部可查看详细信息。可视选取器中的色彩选取器顶部会显示一条线。如果以后色彩满足倡议,则行的同一侧的任何内容也满足倡议。如果以后色彩不合乎倡议,则同一侧的任何内容也不能满足倡议。 插件或网站有很多插件或网站能够进行查看,比方:https://contrast-ratio.com/。 感知对比度算法(APCA)感知对比度算法(APCA)正在取代色彩选择器中的 AA/AAA 对比度。 APCA 是在古代色觉钻研的根底上倒退起来的一种新的计算对比度的办法。与 AA/AAA 相比,APCA 更依赖于上下文。对比度是依据文本的空间、色彩和上下文的空间属性来计算的。 ...
简介: 无论是在前端刀耕火种的 jQuery/YUI 时代,还是到当初基于数据驱动 UI 的 React/Vue 时代,物料/组件始终是前端永恒的话题。基于大量反复逻辑的封装能够很不言而喻地晋升前端 UI 的构建效率,简略而间接,因而无论技术栈如何变动,物料工作都是排在各个前端团队的首要地位解决。 前言无论是在前端刀耕火种的 jQuery/YUI 时代,还是到当初基于数据驱动 UI 的 React/Vue 时代,物料/组件始终是前端永恒的话题。基于大量反复逻辑的封装能够很不言而喻地晋升前端 UI 的构建效率,简略而间接,因而无论技术栈如何变动,物料工作都是排在各个前端团队的首要地位解决。 在 2021 年的当初来看,基于 React/Rax 体系下的根底组件体系曾经根本欠缺,既有蚂蚁良好设计语言的 AntDesign[1],也有团体基于 DPL 疾速定制的 Fusion[2](阿里中后盾 UI 解决方案,已开源),在根底组件的层面性能日趋完善,各个业务团队之间在这个层面的低级反复建设也越来越少,这是十分好的后果。但在业务组件体系的构建上,目前还出现着百花齐放的场面,因为技术栈的一直裁减(可视化、小程序等),业务组件的开发上还存在着很多诸如工程体系凌乱,开发链路不通的问题。 来到企业智能的 5 年多的工夫里,经验了团队物料体系从最后的 Arale/kuma,到基于 react-component 的 UXCore/SaltUI,再到当初全面与 Fusion 交融。根底层面的变动也带来了业务组件畛域的工具链的一直变动。写这篇文章,一方面记录一下本人在这方面做的一些工作和两头的思考,另一方面也心愿能在社区里取得大家的一些贵重倡议,以失去一些新的启发。 一 、一个业务组件要开发几遍?1、 窘境 某天,共事 A 找到我,说 TA 的业务里须要构建一个业务组件包,涵盖了 PC 端,小程序和对应的可视化组件,过后我正在做一些对于前端业务能力构建的相干工作,所以想来问下我的倡议。这个问题看似是很简略,但理论剖析下来却发现有很多问题。 首先,PC 的业务组件过后是应用 飞冰[3](iceworks,阿里 GUI 构建工具) + deep 脚手架模板(deep 出自企业智能用户体验团队)的形式来开发的,益处是能够和 Fusion 深度买通,公布和同步物料到 Fusion 对应的 deep 站点都比拟不便。小程序/挪动端的组件,过后基于 Rax 的动态化小程序组件计划 Fusion Mobile 和 Deep Mobile 刚刚起步,业务组件的开发还没有本人的规范,惟一一套可用的是之前政务钉钉的前端团队做的 gdt-utils 来承载。 ...
引言都说 StringBuilder 在解决字符串拼接上效率要强于 String,但有时候咱们的了解可能会存在肯定的偏差。最近我在测试数据导入效率的时候就发现我以前对 StringBuilder 的局部了解是谬误的。 起初我通过实际测试 + 找原理 的形式搞清楚了这块的逻辑。当初将过程分享给大家 测试用例咱们的代码在循环中拼接字符串个别有两种状况 第一种就是每次循环将对象中的几个字段拼接成一个新字段,再赋值给对象第二种操作是在循环外创立一个字符串对象,每次循环向该字符串拼接新的内容。循环完结后失去拼接好的字符串对于这两种状况,我创立了两个对照组 第一组: 在每次 For 循环中拼接字符串,即拼即用、用完即毁。别离应用 String 和 StringBuilder 拼接 /** * 循环内 String 拼接字符串,一次循环后销毁 */ public static void useString(){ for (int i = 0; i < CYCLE_NUM_BIGGER; i++) { String str = str1 + i + str2 + i + str3 + i + str4 ; } } /** * 循环内 应用 StringBuilder 拼接字符串,一次循环后销毁 */ public static void useStringBuilder(){ for (int i = 0; i < CYCLE_NUM_BIGGER; i++) { StringBuilder sb = new StringBuilder(); String s = sb.append(str1).append(i).append(str2).append(i).append(str3).append(i).append(str4).toString(); } } 第二组: ...
CQRSCQRS 的意思是“命令-查问责任隔离”。咱们拆散了命令(写申请)和查问(读申请)之间的责任。写申请和读申请由不同的对象解决。 就是这样。咱们能够进一步宰割数据存储,应用独自的读写存储。一旦产生这种状况,可能会有许多读取存储,这些存储针对解决不同类型的查问或逾越多个边界上下文进行了优化。尽管常常探讨与 CQRS 相干的独自读写存储,但这并不是 CQRS 自身。CQRS 只是命令和查问的第一局部。 术语Command该命令是一个简略的数据结构,示意执行某些操作的申请。 Command Bus残缺源码: github.com/ThreeDotsLabs/watermill/components/cqrs/command_bus.go// ...// CommandBus 将命令(commands)传输到命令处理程序(command handlers)。type CommandBus struct {// ... Command Processor残缺源码: github.com/ThreeDotsLabs/watermill/components/cqrs/command_processor.go// ...// CommandProcessor 决定哪个 CommandHandler 应该解决这个命令received from the command bus.type CommandProcessor struct {// ... Command Handler残缺源码: github.com/ThreeDotsLabs/watermill/components/cqrs/command_processor.go// ...// CommandHandler 接管由 NewCommand 定义的命令,并应用 Handle 办法解决它。// 如果应用 DDD, CommandHandler 能够批改并长久化聚合。//// 与 EvenHandler 相同,每个命令必须只有一个 CommandHandler。//// 在解决音讯期间应用 CommandHandler 的一个实例。// 当同时发送多个命令时,Handle 办法能够同时执行屡次。// 因而,Handle 办法必须是线程平安的!type CommandHandler interface {// ... Event该事件示意曾经产生的事件。 事件是不可变的。 ...
通过组织Repository,咱们能够让多个Repository共用同一个DbContext。其实咱们还能够更进一步: 让每一次Http申请都应用而且只应用同一个DbContext —— 这就是 Context Per Request 模式。 ConnectionPerRequest出现一个残缺的页面,可能会有很屡次的数据库操作。以“内容列表页”为例,想一想: 导航栏LogonStatus:查找以后用户+更新帮帮豆(可能)列表:取得当前页的Problem分页:取得Problem总数右侧widget:……每一次查找,都要应用一次数据库链接,耗费性能。能不能每一个Request申请,都只应用一个数据库链接?在承受到HTTP申请时关上连贯,在HTTP申请完结时敞开连贯? OK,ASP.NET为咱们提供这种机制:Filter。 学习Filter首先要理解一个 带来的益处进步性能缩小了DbContext的生成:以前一次Http申请,可能须要new好几个DbContext的,当初一次就OK了。 当然,这样每一个DbConext占用的工夫会更长,好在Web我的项目中每一次Http申请耗费的工夫都不会太长,所以通常这都不是一个问题。 在Request层面实现 Unit of Work简直所有的Http申请,人造要求“事务”属性。比方用户在文章发布页面点击公布按钮,当然是心愿和文章公布相干的所有业务逻辑(比方扣帮帮币加帮帮点生成音讯等等)都实现,不可能文章公布失败但帮帮币给扣掉了啥的…… Context Per Request 就可能: 在Context生成的时候,启动事务在Request完结的时候,提交/回滚事务@想一想@:如果一个HttpRequest只是进行查问,放到事务外面会影响性能么? SELECT * BEGIN TRANSELECT * COMMIT 是一样的么? HttpContext容器第一个要解决的问题:每一次用到的DbContext,寄存在哪里? ASP.NET为咱们提供了一个Dictionary容器:HttpContext.Current.Items,它寄存在以后Http申请的上下文环境中,即:如果Http申请完结,Items也隐没;一个Http申请一个Items——这是一个绝佳的寄存DbContext的容器。 存入/读取为了便于所有Service获取DbContext实例,咱们在BaseService中增加一个dbContext只读属性: protected SqlContext dbContext { get { 而后,用咱们曾经十分相熟的形式解决: string contextKey = "dbContext"; //尝试从HttpContext.Current.Items中获取DbContext SqlContext context = HttpContext.Current.Items[contextKey] as SqlContext; //获取不到就生成一个 if (context == null) { context = new SqlContext(); //不要忘了启动事务 context.Database.BeginTransaction(); //将DbContext存入Items以便下次应用 HttpContext.Current.Items[contextKey] = context; }//else nothing return context; ...
因为在开发中常常用到json数据,而浏览器通常并不能显示出适宜浏览的json数据格式,比方如下成果:因而须要装置扩大程序JSONView,装置后成果如下: 实现步骤:1、下载JSONView扩大程序包下载地址:JSONView下载关上地址页面如上,能够抉择复制地址,在本地的文件夹中关上cmd,输出git clone https://github.com/gildas-lormeau/JSONView-for-Chrome.git进行下载,也能够间接下载zip进行解压 2、关上chrome的扩大程序 点击右上角的三个小圆点-->更多工具-->扩大程序点击加载已解压的扩大程序 如果没有这个选项,点击右上角的开发者模式 而后找到文件夹WebContent,点击抉择3、最初失去如下界面:装置实现,CTRL+R刷新一下即可
CSS概览面板(CSS Overview Panel)提供了对于你的CSS的乏味信息。你能够查看高级别的CSS指标,这些指标在重构时特地有用。 如何应用此性能启用此性能: 从“DevTools试验(Experiments pane)”窗格中启用CSS Overview(Cmd + Shift + P>Show Experiments)从“DevTools”Command Menu中抉择“Show CSS Overview”(Cmd + Shift + P)在CSS Overview面板中,您能够导航到不同的局部: 概述摘要(Overview Summary)-CSS上乏味的指标,例如元素数量,样式表,类vs ID选择器,简单选择器等等。色彩(Colors)-可视化预览背景色、文字色、填充色和边框色。色彩自身是能够点击的,所以你能够精确地查看哪些元素应用了这些色彩。字体信息(Font info) -掂量字体的应用状况以及它们在样式表中呈现的频率。包含字体分量和行高指标。能够抉择字体指标来显示受影响的元素。未应用的申明(Unused declarations)-未应用的CSS申明,能够照常单击。媒体查问(Media queries)-CSS媒体查问的细节(如最小/最大宽度值)以及它们在样式表中呈现的频率。你能够点击这些来间接跳到源面板。如果你启用了源映射,你将可能看到原始款式,例如Sass。 何时应用此性能当重构你的代码,或标准各页面的品牌格调时,请应用此性能。例如,如果你留神到一种『色彩』的轻微变动分布在你的CSS中,概览面板中的这个色彩面板(Colors pane)是辨认这种货色的好中央。 您还能够应用CSS概览面板中的媒体查问面板来查看您是否针对预期的媒体查问断点集,并确保您的页面在各种屏幕尺寸上看起来都很好。 未应用的申明面板可能会通过告知您能够删除哪些CSS来帮忙改善网络和渲染性能。 最初,你能够应用CSS概览面板向你的前端团队的其余成员,特地是新入职者传播CSS代码的状态信息,包含可能须要重点关注的畛域。 CSS概览面板能够提供对于CSS的有价值的指标,而Lighthouse面板则提供整个网站的指标,包含JavaScript。
管理软件的二次开发就是在现有软件产品的根底上,针对客户的个性化需要进行的开发,个别是由软件产品的开发厂商进行,或由厂商提供二次开发接口和源码由第三方来进行。不同于齐全的定制开发,二次开发不是从头开发,是在已有软件的根底上进行的。评估一个二开软件产品是不是合格,开发接口的成熟、欠缺是重要的标记。 一、为什么会存在管理软件的二次开发? 现有的产品性能不能满足客户的需要,或须要和其他软件进行对接、实现数据的替换和传输等。百度云二次开发个别会依据现有产品手艺和设计状况,须要提供相干的接口或源码,同时须要意识个性化的性能和需要,综合进行设计和开发。 二次开发的工作量是由现有产品的性能与客户个性化需要的差别水平,接口的难易水平、体系的设计(如:模块之间耦合水平低)、产品的扩展性(是不是适宜于二次开发)等综合因素决定的。 二、管理软件二次开发的劣势 1、绝对于齐全的定制开发,二次开发的工作量小、工夫短、危险低。 2、二次开发是在已有产品的根底上进行的,原有产品性能和业务的积攒能够很好的被继承。 3、解决了产品化个性化需要不能满足的问题。 三、管理软件二次开发存在的问题 二次开发的问题通常与现有零碎密切相关,特地是软件系统的架构和设计以及二次开发接口的难易水平。 1.二次开发最好是基于零碎提供的接口进行开发,如果间接针对源代码进行批改和开发,则将在外围源代码的根底上进行处理惩办,这不仅会导致新的谬误和现有性能的不稳固,而且在产商降级规范产品之后,不能间接进行降级,须要从新整合,这种状况是灾难性的。很多用户不清晰问题的严重性,这也是很多软件厂商不违心提供二次开发的起因之一。 2、现有产品需提供成熟和欠缺的系列接口,这是考查一个软件产品是不是成熟和标准的重要指标之一,否则二次开发只能由原厂商进行,如果厂商的服务和反对不实时、不能提供精良的服务,后续的服务和开发无奈进行。不能进行二次开发导致现有体系不能深刻的应用或只能替换,现有的投资和工夫投入都付之东流。 3、不是所有的产品都能进行二次开发,没有成熟和标准的接口,体系设计和编码十分差的体系,二次开发的工夫和老本要远远高于体系的替换和齐全定制开发,这点也是相当重要、容易被疏忽
Chrome DevTools 对前端工程师来说,几乎每天都会用到,但是有些技巧你可能不知道,让我们来了解一下吧~1. 轻松获取某个审查元素的引用打开控制台,在 Elements 界面下,对着某个 dom 标签点击右键,选择 Store as global variable。 2. 创建并钉住一个动态表达式你可以在控制台创建一个动态表达式,并且把它钉到控制台顶部,这对观察页面上某个变化的元素非常有用。 3. 模拟弱网环境在 Network 界面下可以模拟多种不同的网络环境,利用它可以观察你的应用在不同环境下的加载时间。 4. 禁用缓存和保存日志缓存有时候会造成很多难以排查的 bug,为了排除这个因素,你可以勾选Network 界面下的 Disable cache 选项(它只在 DevTools 窗口打开时生效)。 Preserve log 可以让你在切换页面的时候也能保存控制台中的打印日志。 5. 在控制台直接创建截图Chrome Devtools 有一个内置的截图工具。打开控制台,然后按下 Ctrl+Shift+P,输入 screenshot ,即可选择针对当前网页进行截图。如果选择 Capture node screenshot 还可以针对当前选中的元素创建截图。 6. 不只是 console.log除了 console.log,还有几个常用的方法: console.warn, console.errorconsole.warn 可以打印出黄颜色的警告信息,也可以配合 filter 方便查询。 console.table如果你有一个数组,用 console.table 来打印就再合适不过了。 还有更多的方法如: console.assert,console.group,你可以在这里找到:Google Developers 7. $_ 返回最近那个表达式使用 $_ 可以打印出你在控制台最后使用的表达式的值。 ...
此文主要归纳 Chrome 中所有键盘快捷键的参考信息。一些快捷键全局可用,而其他快捷键会特定于 Chrome DevTools 中的单一面板。 它就像一本字典,可以随时查看我们需要的快捷键。 访问 DevTools访问 DevToolsMacWindows / Linux打开控制台面板Command + Option + JCtrl + Shift + J打开元素面板Command + Shift + C or Command + Option + CCtrl + Shift + C打开你上次使用的面板Command + Option+IF12 or Ctrl + Shift + IDevTools 跨面板快捷键全局快捷键MacWindows / Linux显示设置? or Function+F1? or F1显示下一个面板Command + ]Ctrl + ]显示上一个面板Command + [Ctrl + [根据历史切换 DevTools 的位置Command + Opt + [Ctrl + Shift + D打开/关闭 Device ModeCommand + Shift + MCtrl + Shift + M在当前文件或面板中搜索文本(不支持 Audits、Application、和 Security 面板)Command + FCtrl + F放大Command + Shift ++Ctrl ++缩小Command + Shift --Ctrl --恢复默认文本大小Command + 0Ctrl + 0打开/关闭 Command MenuCommand + Shift + PCtrl + Shift + P在所有加载的资源中搜索文本Command + Shift + FCtrl + Shift + F打开文件Command + OCtrl + O控制台快捷键控制台快捷键MacWindows / Linux拒绝命令提示建议EscapeEscape接受命令提示建议Right Arrow or TabRight Arrow or Tab输入历史中上/下一个命令向上键/向下键向上键/向下键聚焦到控制台Command + 反引号Ctrl + 反引号清除控制台Command + K or Option + LCtrl + L多行输入Command + ReturnShift + Enter执行ReturnEnterElements 面板快捷键Elements 面板MacWindows / Linux切换为以 HTML 形式编辑Function + F2F2撤消更改Command + ZCtrl + Z重新应用更改Command + Shift + ZCtrl + Y选择当前选定元素上方/下方的元素向上键/向下键向上键/向下键展开/折叠节点向右键/向左键向右键/向左键展开/折叠节点及其所有子节点Opt + 点击箭头图标Ctrl + Alt + 点击箭头图标编辑属性Enter / 双击属性Enter / 双击属性隐藏元素(visibility)HHStyles 边栏快捷键Styles 边栏MacWindows / Linux编辑规则点击点击插入新属性点击空格点击空格前往声明属性值的行Command + 点击属性Ctrl + 点击属性循环展示颜色值的 rbga、hsla 和 hex 形式按住 shift 键,然后单击值旁边的颜色预览框按住 shift 键,然后单击值旁边的颜色预览框编辑下一个/上一个属性Tab / Shift + TabTab / Shift + Tab以 0.1 为增量增大/减小值Opt + 向上键 / Opt + 向下键Alt + 向上键 / Alt + 向下键以 1 为增量增大/减小值向上键/向下键向上键/向下键以 10 为增量增大/减小值Shift + Up / Shift + DownShift + Up / Shift + Down以 100 为增量增大/减小值Command + Up / Command + DownCtrl + Up / Ctrl + DownSources 面板快捷键Sources 面板MacWindows / Linux暂停/继续执行脚本F8 or Command + \F8 or Ctrl + \跳过下一个函数调用F10 or Command + 'F10 or Ctrl + '进入下一个函数调用F11 or Command + ;F11 or Ctrl + ;跳出当前函数Shift + F11 or Command + Shift + ;Shift + F11 or Ctrl + Shift + ;暂停时继续执行某行代码按住 Command,然后单击代码行按住 Ctrl,然后单击代码行前往当前调用栈中当前帧下面的调用帧/帧上面的调用帧Control + . / Control + ,Ctrl + . / Ctrl + ,保存本地更改Command + SCtrl + S保存所有更改Command + Opt + SCtrl + Alt + S跳转到指定行(输入格式:单个数字)Control + GCtrl + G跳转到列(输入格式:行:列)Control + GCtrl + G在资源面板下搜索文件Command + O or Command + PCtrl + O or Ctrl + P关闭活动的标签Option + WAlt + W代码编辑器快捷键代码编辑器MacWindows / Linux跳转到匹配的括号Control + MCtrl + M从光标处删除至当前所在单词结尾Option + DeleteCtrl + Delete添加/删除行断点将光标集中在行上,然后按 Command + B将光标集中在行上,然后按 Ctrl + B切换注释Command + /Ctrl + /继续选择下一个匹配/撤消上一个选择Command + D / Command + UCtrl + D / Ctrl + U性能面板快捷键性能面板MacWindows / Linux开始/停止录制Command + ECtrl + E保存录制Command + SCtrl + S打开录制Command + OCtrl + O内存面板快捷键内存面板MacWindows / Linux开始/停止录制Command + ECtrl + E标签页和窗口快捷键标签页和窗口快捷键MacWindows / Linux打开新窗口Ctrl + N⌘ + N在无痕模式下打开新窗口Ctrl + Shift + N⌘ + Shift + N打开新的标签页,并跳转到该标签页Ctrl + T⌘ + T按标签页的关闭顺序重新打开先前关闭的标签页Ctrl + Shift + T⌘ + Shift + T跳转到下一个打开的标签页Ctrl + Tab 或 Ctrl + PgDn⌘ + Option + 向右箭头键跳转到上一个打开的标签页Ctrl + Shift + Tab 或 Ctrl + PgUp⌘ + Option + 向左箭头键跳转到特定标签页Ctrl + 1 到 Ctrl + 8⌘ + 1 到 ⌘ + 8跳转到最右侧的那个标签页Ctrl + 9⌘ + 9在当前标签页中打开主页Alt + Home⌘ + Shift + H打开当前标签页浏览记录中记录的上一个页面Alt + 向左箭头键⌘ + [ 或 ⌘ + 向左箭头键打开当前标签页浏览记录中记录的下一个页面Alt + 向右箭头键⌘ + ] 或 ⌘ + 向右箭头键关闭当前标签页Ctrl + w 或 Ctrl + F4⌘ + W关闭当前窗口Ctrl + Shift + w 或 Alt + F4⌘ + Shift + W最小化当前窗口Alt + 空格键,然后按 N 键⌘ + M最大化当前窗口Alt + 空格键,然后按 X 键⌘ + H退出 Google ChromeAlt + F,然后按 X 键⌘ + QGoogle Chrome 功能快捷键Google Chrome 功能快捷键MacWindows / Linux打开 Chrome 菜单Alt + F 或 Alt + E 显示或隐藏书签栏Ctrl + Shift + B⌘ + Shift + B打开书签管理器Ctrl + Shift + O⌘ + Option + B在新标签页中打开“历史记录”页Ctrl + H⌘ + Y在新标签页中打开“下载内容”页Ctrl + J⌘ + Shift + J打开 Chrome 任务管理器Shift + Esc 将焦点放置在 Chrome 工具栏中的第一项上Shift + Alt + T 将焦点放置在 Chrome 工具栏中最右侧的那一项上F10 将焦点移到未聚焦于的对话框(如果显示)或所有工具栏F6 打开查找栏搜索当前网页Ctrl + F 或 F3 跳转到与查找栏中搜索字词相匹配的下一条内容Ctrl + G 跳转到与查找栏中搜索字词相匹配的上一条内容Ctrl + Shift + G 打开“清除浏览数据”选项Ctrl + Shift + Delete⌘ + Shift + Delete在新标签页中打开 Chrome 帮助中心F1 使用其他帐号登录或以访客身份浏览Ctrl + Shift + M⌘ + Shift + M打开反馈表单Alt + Shift + I 地址栏快捷键地址栏快捷键MacWindows / Linux使用默认搜索引擎进行搜索输入搜索字词并按 Enter 键输入搜索字词并按 Enter 键使用其他搜索引擎进行搜索输入搜索引擎名称,然后按 Tab 键输入搜索引擎名称,然后按 Tab 键为网站名称添加 www. 和 .com,并在当前标签页中打开该网站输入网站名称并按 Ctrl + Enter 键输入网站名称并按 Control + Enter 键打开新的标签页并执行 Google 搜索输入搜索字词并按 Alt + Enter 键 跳转到地址栏Ctrl + L、Alt + d 或 F6⌘ + l从页面中的任意位置搜索Ctrl + K 或 Ctrl + E从地址栏中移除联想查询内容按向下箭头键以突出显示相应内容,然后按 Shift + Delete 键按向下箭头键以突出显示相应内容,然后按 hift + FN + Delete 键,在笔记本电脑上按 Forward Delete 或 Fn-Delete 键网页快捷键网页快捷键MacWindows / Linux打开选项以打印当前网页Ctrl + p⌘ + P打开选项以保存当前网页Ctrl + S⌘ + S重新加载当前网页F5 或 Ctrl + R 重新加载当前网页(忽略缓存的内容)Shift + F5 或 Ctrl + Shift + R⌘ + Shift + R停止加载网页Esc 浏览下一个可点击项TabTab浏览上一个可点击项Shift + TabShift + Tab显示当前网页的 HTML 源代码(不可修改)Ctrl + U⌘ + Option + U将当前网页保存为书签Ctrl + D⌘ + D将所有打开的标签页以书签的形式保存在新文件夹中Ctrl + Shift + D⌘ + Shift + D开启或关闭全屏模式F11⌘ + Ctrl + F向下滚动网页,一次一个屏幕空格键或 PgDn空格键向上滚动网页,一次一个屏幕Shift + 空格键或 PgUpShift + 空格键转到网页顶部Ctrl + Home 转到网页底部Ctrl + End 在网页上水平滚动按住 Shift 键并滚动鼠标滚轮 将光标移到文本字段中的上一个字词起始处Ctrl + 向左箭头键Option + 向左箭头键将光标移到下一个字词起始处Ctrl + 向右箭头键Option + 向右箭头键删除文本字段中的上一个字词Ctrl + BackspaceOption + Delete鼠标快捷键鼠标快捷键MacWindows / Linux在当前标签页中打开链接(仅限鼠标)将链接拖到标签页中将链接拖到标签页中在新的后台标签页中打开链接按住 Ctrl 键的同时点击链接按住 ⌘ 键的同时点击链接打开链接,并跳转到该链接按住 Ctrl + Shift 键的同时点击链接按住 ⌘ + Shift 键的同时点击链接打开链接,并跳转到该链接(仅使用鼠标)将链接拖到标签栏的空白区域将链接拖到标签栏的空白区域在新窗口中打开链接按住 Shift 键的同时点击链接按住 Shift 键的同时点击链接在新窗口中打开标签页(仅使用鼠标)将标签页拖出标签栏将标签页拖出标签栏将标签页移至当前窗口(仅限鼠标)将标签页拖到现有窗口中将标签页拖到现有窗口中将标签页移回其原始位置拖动标签页的同时按 Esc拖动标签页的同时按 Esc将当前网页保存为书签将相应网址拖动到书签栏中将相应网址拖动到书签栏中在网页上水平滚动按住 Shift 键并滚动鼠标滚轮 下载链接目标按住 Alt 键的同时点击链接按住 Option 键的同时点击链接显示浏览记录右键点击左上角“后退”/“前进”箭头,或左键点击左上角“后退”/“前进”箭头不释放右键点击左上角“后退”/“前进”箭头,或左键点击左上角“后退”/“前进”箭头不释放在最大化模式和窗口模式间切换双击标签栏的空白区域 放大网页上的所有内容按住 Ctrl 键并向上滚动鼠标滚轮 缩小网页上的所有内容按住 Ctrl 键并向下滚动鼠标滚轮 其它整理不易,难得齐全。 ...
有时由于种种原因,我们不能直接使用Chrome web store进行Chrome扩展应用的安装,这时可以让一位已经安装了某Chrome扩展应用的朋友将他的应用导出到本地成为.crx文件,然后发给你,这样你就可以通过.crx文件进行离线安装了。这种方式也可以用来作为Chrome扩展应用的备份。 我们先在线安装SAP UI5 inspector这个扩展应用: 根据ui5 inspector这个关键字进行搜索,在结果列表里选中,点击Add to Chrome: 安装完毕后,打开Chrome extension即扩展应用管理器的开发者模式,抄下UI5 inspector的ID: 通过这个ID,我们通过进入如下的路径,能发现安装好的UI5 inspector本地文件: C:Usersi042416AppDataLocalGoogleChromeUser DataDefaultExtensionsbebecogbafbighhaildooiibipcnbngo0.9.5_0 这时就可以把这些文件夹进行打包导出了,点击Pack extension: 输入路径C:Usersi042416AppDataLocalGoogleChromeUser DataDefaultExtensionsbebecogbafbighhaildooiibipcnbngo0.9.5_0,点击Pack Extension: 得到打包好的.crx文件。 发给你的朋友,他就可以进行离线安装了。 要获取更多Jerry的原创文章,请关注公众号"汪子熙":
见过太多同学调试Javascript只会用简单的console.log甚至alert,看着真为他们捉鸡。。因为大多数同学追求优雅而高效地写代码,却忽略了如何优雅而高效地调试代码,不得不说是有点“偏科”了。下面我就分享一些实用且聪明的调试技巧,希望能让大家调试自己代码的时候更加从容自信。 1. 不要使用alert首先,alert只能打印出字符串,如果打印的对象不是String,则会调用toString()方法将该对象转成字符串(比如转成[object Object]这种),所以除非你打印String类型的对象,其他什么信息都获取不到。其次,alert会阻塞UI和javascript的执行,必须点击'OK'按钮才能继续,非常低效。所以,喜欢使用alert的同学可以改改这个习惯了。 2. 学会使用console.logconsole.log谁都会用,但是很多同学只知道最简单的console.log(x)这样打印一个对象,当你的代码里面console.log多了之后,会很难将某条打印结果和代码对应,所以我们可以给打印信息加上一个标签便于区分: let x = 1;console.log('aaaaaaaa', x);得到: 标签不一定要有明确的含义,视觉效果显著就可以了,当然有明确意义更好。 事实上,console.log可以接收任意多的参数,最后将这些对象拼接输出,比如: 如果打印信息过多,不容易找到目标信息的话,可以在控制台中进行过滤: 注意点 在使用console.log打印一个引用类型(比如数组和自定义对象)的对象的时候,输出结果可能并不是执行console.log方法那个时间点的值。举个例子: 可以发现两个console.log输出的结果展开后都是[1, 2, 3, 4],因为数组是引用类型,所以在展开后获取到的都是数组最新的状态。我们可以使用JSON.parse(JSON.stringify(...))来解决这个问题: 3. 学会使用console.dir我们有时候想看看一个DOM对象里面到底有什么属性和方法,但是常规的console.log打印出来的只是HTML标签,就像这样: 和直接审查元素没有什么区别。 如果我们想看到DOM对象作为JavaScript对象的结构可以使用console.dir,比如: 事实上,console.dir可以打印出任何JavaScript对象的属性列表,比如打印一个方法: 4. 学会使用console.table我们经常会遇到这样的场景:获取到一个用户列表,每个用户有很多属性,但是我们只想查看其中的某些属性,在用console.log打印出来的时候需要把每个用户对象展开一个个查看,非常麻烦。而console.table完美的解决这个问题,比如我只想获取到下列用户的id和坐标: console.log打印结果: console.table打印结果: 非常的准确和快速! 5. 学会使用console.time有时候我们想知道一段代码的性能或者一个异步方法需要运行多久,这时候需要用到定时器,JavaScript提供了现成的console.time方法,例如: 6. 使用debugger打断点有时候我们需要打断点进行单步调试,一般会选择在浏览器控制台直接打断点,但这样还需要先去Sources里面找到源码,然后再找到需要打断点的那行代码,比较费时间。使用debugger关键词,我们可以直接在源码中定义断点,方便很多,比如: 7. 查到源码文件有时候我们想在控制台的Sources中查找某个js源文件,要把文件夹逐一点开找,非常麻烦。其实Chrome提供了文件的搜索功能,只不过大部分时候我们给忽略了。。 只要按Command + P(windows的快捷键请自行查看)就能弹出搜索框搜索你想要找的文件啦: 8. 压缩JS文件的阅读有时候我们需要在Sources中阅读一段js代码,但是发现它被压缩了,Chrome也提供了和方便的格式化工具,让代码变得重新可读: 点完之后变成这样: 以上就是我个人在平时比较常用的一些调试小技巧,如果大家有其他好的调试技巧也欢迎分享,谢谢????!
写这个文章,是因为在今天早上,有两个兄弟找我要源码。有图有真相。我震惊于都 9102年了,身为了一个正(xie)经(xin)前端er,还不会看控制台? chrome 开发者工具之 Sources打开我们的 Sources 选项看,我们可以看到如下结构。分为三块功能,左(目录)中(资源展示)右(断点调试)。 Sources 工具(左边区域)我们选中 page ,在这个里面我们可以看到我们所有的资源以树状展示。我们在对应的域名下 www.lilnong.top 下,找到 https://www.lilnong.top/static/html/svg-data-background-img-download.html这个路径,点击就可以看到对应的资源 Sources 工具(中间区域)这里就可以看到对应资源详情了,我们都看到源码了,直接复制岂不美滋滋。当然这里还有其他用途,比如说调试代码。作为一个正(wai)气(men)凛(xie)然(dao)的前端er,我们就不发挥一下自己的脑回路? 不知道大家有没有遇到调试线上 Vue 文件的时候遇到 new 出来的对象没绑定到全局拿不到? 方案一 找个方法打个断点,触发一下,然后 this 绑定到 window 。这边我们就可以随心所欲。方案二 找到 el 绑定的 DOM 对象去拿 __vue__大家有玩过网页游戏吗? Console 写个代码?或者说偷偷看一下过关条件?这里也和游戏有关,有一兄弟,爱摸鱼。之前的几款游戏,比较简单,他自己就破解了。游戏一上外挂,就索然无味。 这天,一个 angular 写的游戏,他束手无策,玩了几天,身体日渐消瘦。我决定拯救他一下。 通过我上面写的本领,成功打断点,找到初始化的时候,增加了外置修改器。解救兄弟与水火之间Sources 工具(右边区域)这里是调试工具,下面我们介绍一下每个按钮的功能 当有断点的时候是个,三角,意思是放过这个断点。当无断点的时候会在下次调用的时候停住。下一行,如果是方法不会跳进去步入,如果是方法,可以跳进去步出,跳出当前方法下一步(我没用过)当前状态是捕获调试。蓝色的时候是不捕获调试,会跳过 debugger。方便你打了断点,然后又想测试效果这个是捕获错误。当前是不捕获。chrome 开发者工具之 Network如果说,上一个 Sources工具 基本都是和代码相关的。这个就比较常用了。 看接口的返回值看接口的请求头,响应头查看资源的加载速度查看资源的大小,缓存情况,响应情况(cdn、waiting 等时间) Network 之 preserve log该功能为长日志功能,正常来说看到的都是当前页面的。如果跳转页面或者说刷新之后就没了。通过打开 preserve log,我们可以长久的保留内容。那它有什么作用呢? 我们可以看到一些中间页的跳转,省去了抓包的麻烦。可以和上个页面的数据比对。Network 之 disable cache前端缓存也是比较麻烦的一个事情。经常需要强刷,清缓存一顿的操作。当我们打开 disable cache 之后,我们就不需要关了,每次都是无缓存的加载 Network 之 offline比如说在测试 PWA。或者说弱网的情况下的一种快速配置。 ...
1、什么是Immutable.js Object Formatter?Immutable.js Object Formatter 是一款格式化immutable.jsChrome插件,极大方便开发人员操作immutable.js。 2、到chrome 网上应用店安装 3、启用之后,点击设置 4、启用Enable custom formatters 5、查看代码已经被格式化 参考:1.Custom Formatters for Immutable.js2.Custom Object Formatters in Chrome DevTools3.immutable-devtools4.Immutable.js Object Formatter 1.9.2 CRX for Chrome
$0$0可以用来表示当前在Chrome DevTools中的Elements栏中查看页面信息中选中的html节点 $0 表示当前选中的节点信息$1 表示当前选中的节点的下一个节点信息$2 表示当前选中的节点的上一个节点信息 $和$$$在console控制台中是document.querySelector方法的别名【未定义$的情况下】,$$则是document.querySelectorAll的方法并将结果以数组的形式返回NodeList类型$$的作用简单表示 Array.from(document.querySelectorAll('div')) === $$('div')$_$_ 可以用在控制台中作为上一个值的引用直接使用,节省时间 使用Math.random(); // 0.2505550952725395$_ // 0.2505550952725395$i搭配插件Console Importer使用,注意:有些页面受CSP安全策略影响无法使用当需要引入某个库时,可以使用$i(npm package name); 比如:$i('lodash');提示成功后,就可以在控制台中使用lodash库提供的方法了 copy(...)DevTools中提供的copy方法可以用来将控制台Console中任何存在的东西复制到粘贴板上 使用msg = 'asdf'.repeat(3); // asdfasdfasdfcopy($_) // asdfasdfasdfconsole.assert使用console.assert断言打印自定义信息提示,如果console.assert第一个参数是falsy值则会打印自定义信息 使用value = null;console.assert(value,'Value is empty'); // VM5452:2 Assertion failed: Value is emptyconsole.table用于将数据按照表格的形式输出,视觉上更加直观 使用console.table(data);console.dir可以使用console.dir将DOM节点的详细信息和默认属性打印出来 使用console.dir(NodeType); Consle is Async在Console中,要使用async await不用手动包裹一层async,可直接使用await,因为它默认已经加了Async resp = await fetch('url');json = await fetch('url');monitor functions可以用来追踪查看某个属性或方法被调用 使用class Person { constructor(name, role) { this.name = name; this.role = role; } greet() { return this.getMessage('greeting'); } getMessage(type) { if (type === 'greeting') { return `Hello, I'm ${this.name}!`; } }}j = new Person('Json');m = new Person('Mary');monitor(j.getMessage); j.greet(); //function getMessage called with arguments: greeting// "Hello, I'm Json!"monitorEvent给某个节点添加监听事件 ...
最近粗线了不少 HTTP2 相关的帖子和讨论,感觉新一轮的潮流在形成,所以最近找了本 HTTP2 相关书籍做知识储备,刚好记成笔记以备后询 这本书本身不错,缺点就是翻译的有点蹩脚,另外因为是 2017 年出的书,所以有些内容时效性不太好,比如关于 Chrome 的部分,所以我根据 Chrome 的官方文档增加了点内容 ????1. HTTP进化史1.1 HTTP/0.9、HTTP/1.0、HTTP/1.1HTTP/0.9: 是个相当简单的协议。它只有一个方法(GET),没有首部,其设计目标也无非是获取 HTML(也就是说没有图片,只有文本)。HTTP/1.0: 多了很多功能,首部、错误码、重定向、条件请求等,但仍存在很多瑕疵,尤其是不能让多个请求共用一个连接、缺少强制的 Host 首部、缓存的选择也相当简陋,这三点影响了 Web 可扩展的方式。HTTP/1.1: 增加了缓存相关首部的扩展、OPTIONS 方法、Upgrade 首部、Range 请求、压缩和传输编码、管道化等功能。因为强制要求客户端提供 Host 首部,所以虚拟主机托管成为可能,也就是在一个 IP 上提供多个 Web 服务。另外使用了 keep-alive 之后,Web 服务器也不需要在每个响应之后关闭连接。这对于提升性能和效率而言意义重大,因为浏览器再也不用为每个请求重新发起 TCP 连接了。1.2 HTTP/2HTTP2 被希望达到以下特性:相比 HTTP/1.1,性能显著提高;解决 HTTP 中的队头阻塞问题;并行的实现机制不依赖与服务器建立多个连接,从而提升 TCP 连接的利用率,特别是在拥塞控制方面;保留 HTTP/1.1 的语义,可以利用已有的文档资源,包括(但不限于) HTTP 方法、状态码、URI 和首部字段;明确定义 HTTP/2.0 和 HTTP/1.x 交互的方法,特别是通过中介时的方法(双向);明确指出它们可以被合理使用的新的扩展点和策略。2. HTTP/2 快速入门2.1 启动并运行很多网站已经在用HTTP/2(h2)了,比如 Facebook、Instagram、Twitter 等,下面介绍以下如何自己搭建 h2 服务器。要运行 h2 服务器,主要分两步:获取并安装一个支持 h2 的 Web 服务器下载并安装一张 TLS 证书,让浏览器和服务器通过 h2 连接2.2 获取证书证书可以通过三种方式获取:使用在线资源自己创建一张证书从数字证书认证机构(CA)申请一张证书前两个方法 将创建自签名证书,仅用于测试,由于不是 CA 签发的,浏览器会报警后面关于创建 h2 服务器的步骤就不记了,可以百度下3. Web优化『黑魔法』的动机与方式3.1 当前的性能挑战3.1.1 剖析Web页面请求从用户在浏览器中点击链接到页面呈现在屏幕上,在此期间到底发生了什么?浏览器请求 Web 页面时,会执行重复流程,获取在屏幕上绘制页面需要的所有信息。为了更容易理解,我们把这一过程分成两部分:资源获取、页面解析/渲染。资源请求流程图:流程为:把待请求 URL 放入队列解析 URL 中域名的 IP 地址(A)建立与目标主机的 TCP 连接(B)如果是 HTTPS 请求,初始化并完成 TLS 握手(C)向页面对应的 URL 发送请求。资源响应流程图:接收响应如果(接收的)是主体 HTML,那么解析它,并针对页面中的资源触发优先获取机制(A)如果页面上的关键资源已经接收到,就开始渲染页面(B)接收其他资源,继续解析渲染,直到结束(C)页面上的每一次点击,都需要重复执行前面那些流程,给网络带宽和设备资源带来压力。Web 性能优化的的核心,就是加快甚至干脆去掉其中的某些步骤。3.1.2 关键性能指标下面网络级别的性能指标,它会影响整个 Web 页面加载。延迟: 指 IP 数据包从一个网络端点到另一个网络端点所花费的时间。带宽: 只要带宽没有饱和,两个网络端点之间的连接会一次处理尽可能多的数据量。DNS查询: 在客户端能够获取 Web 页面前,它需要通过域名系统(DNS)把主机名称转换成 IP 地址。建立连接时间: 在客户端和服务器之间建立连接需要三次握手。握手时间一般与客户端和服务器之间的延迟有关。TLS协商时间: 如果客户端发起 HTTPS 连接,它还需要进行传输层安全协议(TLS)协商,TLS 会造成额外的往返传输。首字节时间(TTFB): TTFB 是指客户端从开始定位到 Web 页面,至接收到主体页面响应的第一字节所耗费的时间。它包含了之前提到的各种耗时,还要加上服务器处理时间。对于主体页面上的资源,TTFB 测量的是从浏览器发起请求至收到其第一字节之间的耗时。内容下载时间: 等同于被请求资源的最后字节到达时间(TTLB)。开始渲染时间: 客户端的屏幕上什么时候开始显示内容?这个指标测量的是用户看到空白页面的时长。文档加载完成时间: 这是客户端浏览器认为页面加载完毕的时间。3.1.3 HTTP/1 的问题HTTP/1 的问题自然是 HTTP/2 要解决的核心问题1. 队头阻塞浏览器很少只从一个域名获取一份资源,一般希望能同时获取许多资源。h1 有个特性叫管道化(pipelining),允许一次发送一组请求,但是只能按照发送顺序依次接收响应。管道化备受互操作性和部署的各种问题的困扰,基本没有实用价值。在请求应答过程中,如果出现任何状况,剩下所有的工作都会被阻塞在那次请求应答之后。这就是『队头阻塞』,它会阻碍网络传输和 Web 页面渲染,直至失去响应。为了防止这种问题,现代浏览器会针对单个域名开启 6 个连接,通过各个连接分别发送请求。它实现了某种程度上的并行,但是每个连接仍会受到的影响。2. 低效的 TCP 利用传输控制协议(TCP)的设计思路是:对假设情况很保守,并能够公平对待同一网络的不同流量的应用。涉及的核心概念就是拥塞窗口(congestion window)。『拥塞窗口』是指,在接收方确认数据包之前,发送方可以发出的 TCP 包的数量。 例如,如果拥塞窗口指定为 1,那么发送方发出 1 个数据包之后,只有接收方确认了那个包,才能发送下一个。TCP 有个概念叫慢启动(Slow Start),它用来探索当前连接对应拥塞窗口的合适大小。慢启动的设计目标是为了让新连接搞清楚当前网络状况,避免给已经拥堵的网络继续添乱。它允许发送者在收到每个确认回复后额外发送 1 个未确认包。这意味着新连接在收到 1 个确认回复之后,可以发送 2 个数据包;在收到 2 个确认回复之后,可以发 4 个,以此类推。这种几何级数增长很快到达协议规定的发包数上限,这时候连接将进入拥塞避免阶段。这种机制需要几次往返数据请求才能得知最佳拥塞窗口大小。但在解决性能问题时,就这区区几次数据往返也是非常宝贵的时间成本。如果你把一个数据包设置为最大值下限 1460 字节,那么只能先发送 5840 字节(假定拥塞窗口为 4),然后就需要等待接收确认回复。理想情况下,这需要大约 9 次往返请求来传输完整个页面。除此之外,浏览器一般会针对同一个域名开启 6 个并发连接,每个连接都免不了拥塞窗口调节。传统 TCP 实现利用拥塞控制算法会根据数据包的丢失来反馈调整。如果数据包确认丢失了,算法就会缩小拥塞窗口。这就类似于我们在黑暗的房间摸索,如果腿碰到了桌子就会马上换个方向。如果遇到超时,也就是等待的回复没有按时抵达,它甚至会彻底重置拥塞窗口并重新进入慢启动阶段。新的算法会把其他因素也考虑进来,例如延迟,以提供更妥善的反馈机制。前面提到过,因为 h1 并不支持多路复用,所以浏览器一般会针对指定域名开启 6 个并发连接。这意味着拥塞窗口波动也会并行发生 6 次。TCP 协议保证那些连接都能正常工作,但是不能保证它们的性能是最优的。3. 臃肿的消息首部虽然 h1 提供了压缩被请求内容的机制,但是消息首部却无法压缩。消息首部可不能忽略,尽管它比响应资源小很多,但它可能占据请求的绝大部分(有时候可能是全部)。如果算上 cookie,就更大了。消息首部压缩的缺失也容易导致客户端到达带宽上限,对于低带宽或高拥堵的链路尤其如此。『体育馆效应』(Stadium Effect)就是一个经典例子。如果成千上万人同一时间出现在同一地点(例如重大体育赛事),会迅速耗尽无线蜂窝网络带宽。这时候,如果能压缩请求首部,把请求变得更小,就能够缓解带宽压力,降低系统的总负载。4. 受限的优先级设置如果浏览器针对指定域名开启了多个 socket(每个都会受队头阻塞问题的困扰),开始请求资源,这时候浏览器能指定优先级的方式是有限的:要么发起请求,要么不发起。然而 Web 页面上某些资源会比另一些更重要,这必然会加重资源的排队效应。这是因为浏览器为了先请求优先级高的资源,会推迟请求其他资源。但是优先级高的资源获取之后,在处理的过程中,浏览器并不会发起新的资源请求,所以服务器无法利用这段时间发送优先级低的资源,总的页面下载时间因此延长了。还会出现这样的情况:一个高优先级资源被浏览器发现,但是受制于浏览器处理的方式,它被排在了一个正在获取的低优先级资源之后。5. 第三方资源如今 Web 页面上请求的很多资源完全独立于站点服务器的控制,我们称这些为第三方资源。现代 Web 页面加载时长中往往有一半消耗在第三方资源上。虽然有很多技巧能把第三方资源对页面性能的影响降到最低,但是很多第三方资源都不在 Web 开发者的控制范围内,所以很可能其中有些资源的性能很差,会延迟甚至阻塞页面渲染。令人扫兴的是, h2 对此也束手无策。3.2 Web性能优化技术2010 年,谷歌把 Web 性能作为影响页面搜索评分的重要因素之一,性能指标开始在搜索引擎中发挥作用。对于很多 Web 页面,浏览器的大块时间并不是用于呈现来自网站的主体内容(通常是 HTML),而是在请求所有资源并渲染页面。因此,Web 开发者逐渐更多地关注通过减少客户端网络延迟和优化页面渲染性能来提升Web 性能。3.2.1 Web性能的最佳实践1. DNS 查询优化在与服务主机建立连接之前,需要先解析域名;那么,解析越快就越好。下面有一些方法:限制不同域名的数量。当然,这通常不是你能控制的保证低限度的解析延迟。了解你的 DNS 服务基础设施的结构,然后从你的最终用户分布的所有地域定期监控解析时间在 HTML 或响应中利用 DNS 预取指令。这样,在下载并处理 HTML 的同时,预取指令就能开始解析页面上指定的域名 <link rel=“dns-prefetch” href="//ajax.googleapis.com">2. 优化 TCP 连接本章前面提到过,开启新连接是一个耗时的过程。如果连接使用 TLS(也确实应该这么做),开销会更高。降低这种开销的方法如下尽早终止并响应。借助 CDN,在距离请求用户很近的边缘端点上,请求就可以获得响应,所以可以终止连接,大幅减少建立新连接的通信延迟。实施最新的 TLS 最佳实践来优化 HTTPS。利用 preconnect 指令,连接在使用之前就已经建立好了,这样处理流程的关键路径上就不必考虑连接时间了,preconnect 不光会解析 DNS,还会建立 TCP 握手连接和 TLS 协议(如果需要)<link rel=“preconnect” href="//fonts.example.com" crossorigin>如果要从同一个域名请求大量资源,浏览器将自动开启到服务器的并发连接,避免资源获取瓶颈。虽然现在大部分浏览器支持 6 个或更多的并发连接数目,但你不能直接控制浏览器针对同一域名的并发连接数。3. 避免重定向重定向通常触发与额外域名建立连接。在移动网络中,一次额外的重定向可能把延迟增加数百毫秒,这不利于用户体验,并最终会影响到网站上的业务。简单的解决方案就是彻底消灭重定向,因为对于重定向的使往往并没有合理原因。如果它们不能被直接消灭,你还有两个选择:利用 CDN 代替客户端在云端实现重定向如果是同一域名的重定向,使用 Web 服务器上的 rewrite 规则,避免重定向4. 客户端缓存没有什么比直接从本地缓存获取资源来得更快,因为它根本就不需要建立网络连接。所谓的纯静态内容,例如图片或带版本的数据,可以在客户端永久缓存。即便 TTL 被设置得很长,比如一个月,它还是会因为缓存提早回收或清理而过期,这时客户端可能不得不从源头再次获取。CSS/JS 和个性化资源,缓存时间大约是会话(交互)平均时间的两倍。这段时间足够长,保证大多数用户在浏览网站时能够从本地拉取资源;同时也足够短,几乎能保证下次会话时从网络上拉取最新内容。可以通过 HTTP 首部指定 cache control 以及键 max-age(以秒为单位),或者 expires 首部。5. 网络边缘的缓存个人信息(用户偏好、财务数据等)绝对不能在网络边缘缓存,因为它们不能共享。时间敏感的资源也不应该缓存,例如实时交易系统上的股票报价。这就是说,除此之外其他一切都是可以缓存的,即使仅仅缓存几秒或几分钟。对于那些不是经常更新,然而一旦有变化就必须立刻更新的资源,例如重大新闻,可以利用各大 CDN 厂商提供的缓存清理(purging)机制处理。这种模式被称为『一直保留,直到被通知』(Hold til Told),意思是永久缓存这些资源,等收到通知后才删除。6. 条件缓存如果缓存 TTL 过期,客户端会向服务器发起请求。在多数情况下,收到的响应其实和缓存的版本是一样的,重新下载已经在缓存里的内容也是一种浪费。HTTP 提供条件请求机制,客户端能以有效方式询问服务器:『如果内容变了,请返回内容本身;否则,直接告诉我内容没变。』当资源不经常变化时,使用条件请求可以显著节省带宽和性能;但是,保证资源的最新版迅速可用也是非常重要的。使用条件缓存可以通过以下方法。在请求中包含 HTTP 首部 Last-Modified-Since。仅当最新内容在首部中指定的日期之后被更新过,服务器才返回完整内容;否则只返回 304 响应码,并在响应首部中附带上新的时间戳 Date 字段。在请求体中包含实体校验码,或者叫 ETag;它唯一标识所请求的资源。ETag 由服务器 提供,内嵌于资源的响应首部中。服务器会比较当前 ETag 与请求首部中收到的 ETag,如果一致,就只返回 304 响应码;否则返回完整内容。一般来说,大多数 Web 服务器会对图片和 CSS/JS 使用这些技术,但你也可以将其用到其他资源。7. 压缩和代码极简化所有的文本内容(HTML、JS、CSS、SVG、XML、JSON、字体等),都可以压缩和极简化。这两种方法组合起来,可以显著减少资源大小。更少字节数对应着更少的请求与应答,也就意味着更短的请求时间。极简化(minification, 混淆)是指从文本资源中剥离所有非核心内容的过程。通常,要考虑方便人类阅读和维护,而浏览器并不关心可读性,放弃代码可读性反而能节省空间。在极简化的基础上,压缩可以进一步减少字节数。它通过可无损还原的算法减少资源大小。在发送资源之前,如果服务器进行压缩处理,可以节省 90% 的大小。8. 避免阻塞 CSS/JS在屏幕上绘制第一个像素之前,浏览器必须确保 CSS 已经下载完整。尽管浏览器的预处理器很智能,会尽早请求整个页面所需要的 CSS,但是把 CSS 资源请求放在页面靠前仍然是种最佳实践,具体位置是在文档的 head 标签里,而且要在任何 JS 或图片被请求和处理之前。默认情况下,如果在 HTML 中定位了 JS,它就会被请求、解析,然后执行。在浏览器处理完这个 JS 之前,会阻止其后任何资源的下载渲染。然而大多数时候,这种默认的阻塞行为导致了不必要的延迟,甚至会造成单点故障。为了减轻 JS 阻塞带来的潜在影响,下面针对己方资源(你能控制的)和第三方资源(你不能控制的)推荐了不同的策略定期校验这些资源的使用情况。随着时间的变迁,Web 页面可能会持续下载一些不再需要的 JS,这时最好去掉它。如果 JS 执行顺序无关紧要,并且必须在 onload 事件触发之前运行,那么可以设置 async 属性,像这样:<script async src="/js/myfile.js">只需做到下载 JS 与解析 HTML 并行,就能极大地提升整体用户体验。慎用 document.write 指令,因为很可能中断页面执行,所以需要仔细测试。如果 JS 执行顺序很重要,并且你也能承受脚本在 DOM 加载完之后运行,那么请使用 defer 属性。像这样<script defer src="/js/myjs.js">对不会影响到页面初次展示的 JS 脚本,必须在 onload 事件触发之后请求(处理)它。如果你不想延迟主页面的 onload 事件,可以考虑通过 iframe 获取 JS,因为它的处理独立于主页面。但是,通过 iframe 下载的 JS 访问不了主页面上的元素。9. 图片优化对大多数网站而言,图片的重要性和比重在不断增加。既然图片主导了多数现代网站,优化它们就能够获得最大的性能回报。所有图片优化手段的目标都是在达到指定视觉质量的前提下传输最少的字节。图片元信息,例如题材地理位置信息、时间戳、尺寸和像素信息,通常包含在二进制数据里,应该在发送给客户端之前去掉(务必保留版权和色彩描述信息)。这种无损处理能够在图片生成时完成。对于 PNG 图片,一般会节省大概 10% 的空间。图片过载(image overloading)是指,图片最终被浏览器自动缩小,要么因为原始尺寸超过了浏览器可视区中的占位大小,要么因为像素超过设备的显示能力。这不仅浪费带宽,消耗的 CPU 资源也很可观,这些计算资源有时在手持设备上相当宝贵。想要解决图片过载,可以使用技术手段,针对用户的设备、网络状况和预期的视觉质量,提供裁剪过的图片(就尺寸和质量而言)。3.2.2 反模式HTTP/2 对每个域名只会开启一个连接,所以 HTTP/1.1 下的一些诀窍对它来说只会适得其反。详细看 6.7 节3.3 小结HTTP/1.1 孕育了各种性能优化手段与诀窍,可以帮助我们深入理解 Web 及其内部实现。HTTP/2 的目标之一就是淘汰掉众多(并不是全部)此类诀窍。4. HTTP/2 迁移在升级到 HTTP/2 之前,你应该考虑:浏览器对 h2 的支持情况迁移到 TLS(HTTPS)的可能性对你的网站做基于 h2 的优化(可能对 h1 有反作用)网站上的第三方资源保持对低版本客户端的兼容4.1 浏览器的支持情况任何不支持 h2 的客户端都将简单地退回到 h1,并仍然可以访问你的站点基础设施。4.2 迁移到 TLS所有主流浏览器只能访问基于 TLS(即 HTTPS 请求)的 h2。4.3 撤销针对 HTTP/1.1 的优化Web 开发者之前花费了大量心血来充分使用 h1,并且已经总结了一些诀窍,例如资源合并、域名拆分、极简化、禁用 cookie 的域名、生成精灵图,等等。所以,当得知这些实践中有些在 h2 下变成反模式时,你可能会感到吃惊。例如,资源合并(把很多 CSS/JS 文件拼合成一个)能避免浏览器发出多个请求。对 h1 而言这很重要,因为发起请求的代价很高;但是在 h2 的世界里,这部分已经做了深度优化。放弃资源合并的结果可能是,针对单个资源发起请求的代价很低,但浏览器端可以进行更细粒度的缓存。详细看 6.7 节5. HTTP/2 协议本章将全面探讨 HTTP/2 的底层工作原理,深入到数据层传输的帧及其通信方式。5.1 HTTP/2 分层HTTP/2 大致可以分为两部分分帧层 即 h2 多路复用能力的核心部分,主要目的还是传输 HTTP数据或 http 层 其中包含传统上被认为是 HTTP 及其关联数据的部分,向后兼容 HTTP/1.1h2 有些特点需要关注一下:二进制协议 :h2 的分帧层是基于帧的二进制协议,这方便了机器解析,但是肉眼识别比较困难首部压缩 :仅使用二进制协议还不够,h2 的首部还会被深度压缩。这将显著减少传输中的冗余字节多路复用 :在调试工具里查看基于 h2 传输的连接的时候,你会发现请求和响应交织在一起加密传输 :线上传输的绝大部分数据是加密过的,所以在中途读取会更加困难5.2 连接与完全无状态的 h1 不同的是,h2 把它所承载的帧(frame)和流(stream)共同依赖的连接层元素捆绑在一起,其中既包含连接层设置也包含首部表。也就是说,与之前的 HTTP 版本不同,每个 h2 连接都有一定的开销。之所以这么设计,是考虑到收益远远超过其开销。在连接的时候,HTTP/2 提供两种协议发现的机制:在连接不加密的情况下,客户端会利用 Upgrade 首部来表明期望使用 h2。如果服务器也可以支持 h2,它会返回一个101 Switching Protocols(协议转换)响应。这增加了一轮完整的请求-响应通信如果连接基于 TLS,情况就不同了。客户端在 ClientHello 消息中设置 ALPN (Application-Layer Protocol Negotiation,应用层协议协商)扩展来表明期望使用 h2 协议,服务器用同样的方式回复。如果使用这种方式,那么 h2 在创建 TLS 握手的过程中完成协商,不需要多余的网络通信。在协议制定过程中,很早就把小数点去掉了,这表明未来的 HTTP 版本不能保证语义的向后兼容,也就是说只有 HTTP/2 没有 HTTP/2.0、HTTP/2.25.3 帧HTTP/2 是基于帧(frame)的协议,采用分帧是为了将重要信息都封装起来,让协议的解析方可以轻松阅读、解析并还原信息。 相比之下,h1 不是基于帧的,而是以文本分隔。所以解析 h1 的请求或响应可能会出现以下问题:一次只能处理一个请求或响应,完成之前不能停止解析无法预判解析需要多少内存。这会带来一系列问题:你要把一行读到多大的缓冲区里;如果行太长会发生什么;应该增加并重新分配内存,还是返回 400 错误从另一方面来说,有了帧,处理协议的程序就能预先知道会收到什么。下图是一个 HTTP/2 帧的结构前 9 个字节对于每个帧是一致的。解析时只需要读取这些字节,就可以准确地知道在整个帧中期望的字节数。其中每个字段的说明如下名称长度描述Length3 字节表示帧负载的长度(取值范围为 2^142^24-1 字节)。 请注意,214 字节是默认的最大帧大小,如果需要更大的帧,必须在 SETTINGS 帧中设置Type1 字节当前帧类型Flags1 字节具体帧类型的标识R1 位保留位,不要设置,否则可能带来严重后果Stream Identifier31 位每个流的唯一 IDFrame Payload长度可变真实的帧内容,长度是在 Length 字段中设置的相比依靠分隔符的 h1,h2 还有另一大优势:如果使用 h1 的话,你需要发送完上一个请求或者响应,才能发送下一个;由于 h2 是分帧的,请求和响应可以交错甚至多路复用。多路复用有助于解决类似队头阻塞的问题。h2 有十种不同的帧类型:名称ID (Type)描述DATA0x0数据帧,传输流的核心内容HEADERS0x1报头帧,包含 HTTP 首部,和可选的优先级参数PRIORITY0x2优先级帧,指示或者更改流的优先级和依赖RST_STREAM0x3流终止帧,允许一端停止流(通常由于错误导致的)SETTINGS0x4设置帧,协商连接级参数PUSH_PROMISE0x5推送帧,提示客户端,服务器要推送些东西PING0x6PING 帧,测试连接可用性和往返时延(RTT)GOAWAY0x7GOAWAY 帧,告诉另一端,当前端已结束WINDOW_UPDATE0x8窗口更新帧,协商一端将要接收多少字节(用于流量控制)CONTINUATION0x9延续帧,用以扩展 HEADER 数据块扩展帧 :HTTP/2 内置了名为扩展帧的处理新的帧类型的能力。依靠这种机制,客户端和服务器的实现者可以实验新的帧类型,而无需制定新协议。按照规范,任何客户端不能识别的帧都会被丢弃,所以网络上新出现的帧就不会影响核心协议。当然,如果你的应用程序依赖于新的帧,而中间代理会丢弃它,那么可能会出现问题。5.4 流HTTP/2 规范中的流(stream):HTTP/2 连接上独立的、双向的帧序列交换。你可以将流看作在连接上的一系列帧,用来传输一对请求/响应消息。如果客户端想要发出请求,它会开启一个新的流,服务器也在这个流上回复。这与 h1 的请求、响应流程类似,区别在于,因为有分帧,所以多个请求和响应可以交错,而不会互相阻塞。流 ID(帧首部的第 6~9 字节)用来标识帧所属的流。客户端到服务器的 h2 连接建立之后,通过发送 HEADERS 帧来启动新的流,如果首部需要跨多个帧,可能还发会送 CONTINUATION 帧。5.4.1 消息HTTP 消息泛指 HTTP 请求或响应。一个消息至少由 HEADERS 帧(用来初始化流)组成,并且可以另外包含 CONTINUATION 和 DATA 帧,以及其他的 HEADERS 帧。 下图是普通 GET 请求的示例流程POST 和 GET 的主要差别之一就是 POST 请求通常包含客户端发出的大量数据。下图是 POST 消息对应的各帧可能的样子h1 的请求和响应都分成消息首部和消息体两部分;与之类似,h2 的请求和响应分成 HEADERS 帧和 DATA 帧。HTTP/1 和 HTTP/2 消息的下列差别是需要注意一切都是header :h1 把消息分成请求/状态行、首部两部分。h2 取消了这种区分,并把这些行变成了魔法伪首部没有分块编码(chunked encoding) :只有在无法预先知道数据长度的情况下向对方发送数据时,才会用到分块。在使用帧作为核心协议的 h2 里,不再需要分块不再有101的响应 :Switching Protocol 响应是 h1 的边缘应用。它如今最常见的应用可能就是用以升级到 WebSocket 连接。ALPN 提供了更明确的协议协商路径,往返的开销也更小5.4.2 流量控制h2 的新特性之一是基于流的流量控制。h1 中只要客户端可以处理,服务端就会尽可能快地发送数据,h2 提供了客户端调整传输速度的能力,服务端也同样可以调整传输的速度。WINDOW_UPDATE 帧用于执行流量控制功能,可以作用在单独某个流上(指定具体 Stream Identifier)也可以作用整个连接 (Stream Identifier 为 0x0),只有 DATA 帧受流量控制影响。初始化流量窗口后,发送多少负载,流量窗口就减少多少,如果流量窗口不足就无法发送,WINDOW_UPDATE 帧可以增加流量窗口大小。流建立的时候,窗口大小默认 65535(2^16-1)字节。5.4.3 优先级流的最后一个重要特性是依赖关系。现代浏览器会尽量以最优的顺序获取资源,由此来优化页面性能。在没有多路复用的时候,在它可以发出对新对象的请求之前,需要等待前一个响应完成。有了 h2 多路复用的能力,客户端就可以一次发出所有资源的请求,服务端也可以立即着手处理这些请求。由此带来的问题是,浏览器失去了在 h1 时代默认的资源请求优先级策略。假设服务器同时接收到了 100 个请求,也没有标识哪个更重要,那么它将几乎同时发送每个资源,次要元素就会影响到关键元素的传输。h2 通过流的依赖关系来解决上面这个问题。通过 HEADERS 帧和 PRIORITY 帧,客户端可以明确地和服务端沟通它需要什么,以及它需要这些资源的顺序。这是通过声明依赖关系树和树里的相对权重实现的。依赖关系 为客户端提供了一种能力,通过指明某些对象对另一些对象有依赖,告知服务器这些对象应该优先传输权重 让客户端告诉服务器如何确定具有共同依赖关系的对象的优先级流可以被标记为依赖其他流,所依赖的流完成后再处理当前流。每个依赖 (dependency) 后都跟着一个权重 (weight),这一数字是用来确定依赖于相同的流的可分配可用资源的相对比例。其他时候也可以通过 PRIORITY 帧调整流优先级。设置优先级的目的是为了让端点表达它所期望对端在并发的多个流之间如何分配资源的行为。更重要的是,当发送容量有限时,可以使用优先级来选择用于发送帧的流。5.5 服务端推送升单个对象性能的最佳方式,就是在它被用到之前就放到浏览器的缓存里面。这正是 h2 服务端推送的目的。5.5.1 推送对象如果服务器决定要推送一个对象(RFC 中称为『推送响应』),会构造一个 PUSH_PROMISE 帧。这个帧有很多重要属性:PUSH_PROMISE 帧首部中的流 ID (Promised Stream ID)用来响应相关联的请求。推送的响应一定会对应到客户端已发送的某个请求。如果浏览器请求一个主体 HTML 页面,如果要推送此页面使用的某个 JavaScript 对象,服务器将使用请求对应的流 ID 构造 PUSH_PROMISE 帧。PUSH_PROMISE 帧的首部块与客户端请求推送对象时发送的首部块是相似的。所以客户端有办法放心检查将要发送的请求。被发送的对象必须确保是可缓存的。:method 首部的值必须确保安全。安全的方法就是幂等的那些方法,这是一种不改变任何状态的好办法。例如,GET 请求被认为是幂等的,因为它通常只是获取对象,而 POST 请求被认为是非幂等的,因为它可能会改变服务器端的状态。理想情况下,PUSH_PROMISE 帧应该更早发送,应当早于客户端接收到可能承载着推送对象的 DATA 帧。假设服务器要在发送 PUSH_PROMISE 之前发送完整的 HTML,那客户端可能在接收到 PUSH_PROMISE 之前已经发出了对这个资源的请求。h2 足够健壮,可以优雅地解决这类问题,但还是会有些浪费。PUSH_PROMISE 帧会指示将要发送的响应所使用的流 ID如果客户端对 PUSH_PROMISE 的任何元素不满意,就可以按照拒收原因选择重置这个流(使用 RST_STREAM),或者发送 PROTOCOL_ERROR (在 GOAWAY 帧中)。常见的情况是缓存中已经有了这个对象。假设客户端不拒收推送,服务端会继续进行推送流程,用 PUSH_PROMISE 中指明 ID 对应的流来发送对象 5.5.2 选择要推送的资源如果服务器接收到一个页面的请求,它需要决定是推送页面上的资源还是等客户端来请求。决策的过程需要考虑到如下方面资源已经在浏览器缓存中的概率从客户端看来,这些资源的优先级 (参见 5.4.3 节)可用的带宽,以及其他类似的会影响客户端接收推送的资源如果服务器选择正确,那就真的有助于提升页面的整体性能,反之则会损耗页面性能。5.6 首部压缩现代网页平均有很多请求,这些请求之间几乎没有新的的内容,这是极大的浪费。首部列表 (Header List) 是零个或多个首部字段 (Header Field) 的集合。当通过连接传送时,首部列表通过压缩算法(即下文 HPACK) 序列化成首部块 (Header Block),不用 GZIP 是因为它有泄漏加密信息的风险。HPACK 是种表查找压缩方案,它利用霍夫曼编码获得接近 GZIP 的压缩率。然后,序列化的首部块又被划分成一个或多个叫做首部块片段 (Header Block Fragment) 的字节序列,并通过 HEADERS、PUSH_PROMISE,或者 CONTINUATION 帧进行有效负载传送。假设客户端按顺序发送如下请求首部:Header1: fooHeader2: barHeader3: bat当客户端发送请求时,可以在首部数据块中指示特定首部及其应该被索引的值。它会创建一张表:索引首部名称值62Header1foo63Header2bar64Header3bat如果服务端读到了这些请求首部,它会照样创建一张表。客户端发送下一个请求的时候, 如果首部相同,它可以直接发送:62 63 64 ,服务器会查找先前的表格,并把这些数字还原成索引对应的完整首部。首部压缩机制中每个连接都维护了自己的状态。HPACK 的实现比这个要复杂得多,比如:请求端和响应端各维护了两张表格。其中之一是动态表,创建方法和上面差不 多。另一张是静态表,它由 61 个最常见的首部的键值组合而成。例如 :method: GET 在静态表中索引为 2。按规定,静态表包含 61 个条目,所以上例索引编号从 62 开始。关于字段如何索引,有很多控制规则:发送索引编号和文本值仅发送文本值,不对它们进行索引(对于一次性或敏感首部)发送索引的首部名,值用文本表示,但不进行索引处理(如:path: /foo.html,其值每次都不同)发送索引过的首部名和值(如上例中的第二个请求)使用打包方案的整数压缩,以实现极高的空间效率利用霍夫曼编码表进一步压缩字符串5.7 线上传输线上传输的 h2 信息是经过压缩的二进制数据。一个简单的GET请求下面是一个简单的 h2 的 get 请求:authority: www.akamai.com:method: GET:path: /:scheme: httpsaccept: text/html,application/xhtml+xml,…accept-language: en-US,en;q=0.8cookie: sidebar_collapsed=0; _mkto_trk=…upgrade-insecure-requests: 1user-agent: Mozilla/5.0 (Macintosh;…下面是 h2 的一个响应:status: 200cache-control: max-age=600content-encoding: gzipcontent-type: text/html;charset=UTF-8date: Tue, 31 May 2016 23:38:47 GMTetag: “08c024491eb772547850bf157abb6c430-gzip"expires: Tue, 31 May 2016 23:48:47 GMTlink: <https://c.go-mpulse.net>;rel=preconnectset-cookie: ak_bmsc=8DEA673F92AC…vary: Accept-Encoding, User-Agentx-akamai-transformed: 9c 237807 0 pmb=mRUM,1x-frame-options: SAMEORIGIN在这个响应中,服务器表示请求已成功受理(状态码 200),设置了 cookie(cookie 首部),表示返回的内容使用 gzip 压缩(content-encoding 首部)6. HTTP/2性能HTTP/2 大部分情况下传输 web 页面比 HTTP/1.1 快。对于包含很多小型资源的页面,h2 加载页面的时间比 h1 更短。这是因为 h1 下(有 6 个 TCP 连接)服务器只能并行发送 6 个资源(由于队头阻塞),而 h2 下多个流(stream)可以共用连接。进一步说,随着网络条件变差,h1 和 h2 下页面加载时间(PLT)都会增加;但是 h2 由于单连接,如果唯一的连接发生了丢包,所有工作都会受影响。对于包含少量大型资源的页面,在所有网络条件下,h1 性能上都比 h2 表现要好。这个多少令人感到意外的结果是初始拥塞窗口导致的。如果开启 6 个连接,h1 的初始拥塞窗口大小实际上是 h2 的 6 倍。因此在会话开始阶段,h2 的连接窗口尚未增长到最佳值,但 h1 早就能更快地传输更多数据了。这个问题目前仍在解决,因为它导致初始拥塞窗口对 h2 而言太小,然而对 h1 而言又太大。 此外,h2 比 h1 更容易受丢包的影响。对于包含一些极大型资源的 Web 页面,两者没有任何差异。h2 的初始拥塞窗口劣势被整体下载时长掩盖了,多路复用此时也不再具有优势。6.1 客户端实现网络条件相同,使用不同浏览器客户端,同样的网站页面加载性能可能差别很大。协议的具体实现很重要并非所有请求在任何情况下都会从 HTTP/2 受益,即便如此,URL 使用 h2 后性能提升的比例也依旧高于下降的比例6.2 延迟延迟是指数据包从一个端点到另一个端点所花的时间。有时,它也表示数据包到达接收方然后返回发送方所需的时间,又称为往返时延(RTT),长度一般以毫秒计。 影响延迟的因素众多,但有两个是最重要的:端点间的距离,以及所用传输介质两点之间的网线不会是笔直的,另外各种网关、路由器、交换机以及移动基站等(也包括服务器应用本身)都会增加延迟6.3 丢包如果网络中传输的数据包没有成功到达目的地,就会发生丢包,这通常是由网络拥堵造成的。频繁丢包会影响 h2 的页面传输,主要是因为 h2 开启单一 TCP 连接,每次有丢包/拥堵时,TCP 协议就会缩减 TCP 窗口。如果 TCP 流上丢了一个数据包,那么整个 h2 连接都会停顿下来,直到该数据包重发并被接收到。6.4 服务端推送服务端推送让服务器具备了在客户端请求之前就推送资源的能力。 测试表明,如果合理使用推送,页面渲染时间可以减少 20%50%。 然而,推送也会浪费带宽,这是因为服务端可能试图推送那些在客户端已经缓存的资源,导致客户端收到并不需要的数据。客户端确实可以发送 RST_STREAM 帧来拒绝服务器的 PUSH_PROMISE 帧,但是 RST_STREAM 并不会即刻到达,所以服务器还是会发送一些多余的信息。如果用户第一次访问页面时,就能向客户端推送页面渲染所需的关键 CSS 和 JS 资源,那么服务端推送的真正价值就实现了。不过,这要求服务器端实现足够智能,以避免『推送承诺』(push promise)与主体 HTML 页面传输竞争带宽。理想情况下,服务端正在处理 HTML 页面主体请求时才会发起推送。有时候,服务端需要做一些后台工作来生成 HTML 页面。这时候服务端在忙,客户端却在等待,这正是开始向客户端推送所需资源的绝佳时机。6.5 首字节时间首字节时间(TTFB)用于测量服务器的响应能力。是从客户端发起 HTTP 请求到客户端浏览器收到资源的第一个字节所经历的时间。由 socket 连接时间、发送 HTTP 请求所需时间、收到页面第一个字节所需时间组成。h1 中,客户端针对单个域名在每个连接上依次请求资源,而且服务器会按序发送这些资源。客户端只有接收了之前请求的资源,才会再请求剩下的资源,服务器接着继续响应新的资源请求。这个过程会一直重复,直到客户端接收完渲染页面所需的全部资源。与 h1 不同,通过 h2 的多路复用,客户端一旦加载了 HTML,就会向服务器并行发送大量请求。相比 h1,这些请求获得响应的时间之和一般会更短;但是因为是请求是同时发出的,而单个请求的计时起点更早,所以 h2 统计到的 TTFB 值会更高。HTTP/2 比 h1 确实做了更多的工作,其目的就是为了从总体上提升性能。下面是一些 h1 没有,但 h2 实现了的窗口大小调节依赖树构建维持首部信息的静态 / 动态表压缩 / 解压缩首部优先级调整(h2 允许客户端多次调整单一请求的优先级)预先推送客户端尚未请求的数据流下图是使用 h1 和 h2 加载同一个页面的加载时序对比,总体来说 h2 体验更好6.6 第三方资源许多网站会使用各种统计、跟踪、社交以及广告平台,就会引入各种第三方的资源。第三方请求往往通过不同域名发送;由于浏览器需要解析 DNS、建立 TCP 连接、协商 TLS,这将严重影响性能;因为第三方资源在不同域名下,所以请求不能从服务端推送、资源依赖、请求优先级等 h2 特性中受益。这些特性仅是为请求相同域名下的资源设计的;你无法控制第三方资源的性能,也无法决定它们是否会通过 h2 传输;6.7 HTTP/2反模式h1 下的一些性能调优办法在 h2 下会起到反作用。下面列出了一些用于优化 h1 请求的常用技巧,并标注了 h2 方面的考虑。名称描述备注资源合并把多个文件(JavaScript、CSS) 合成一个文件,以减少 HTTP 请求在 HTTP/2 下这并非必要,因为请求的传输字节数和时间成本更低,虽然这种成本仍然存在极简化去除 HTML、JavaScript、CSS 这类文件中无用的代码很棒的做法,在 HTTP/2 下也要保留域名拆分把资源分布到不同的域名上面去,让浏览器利用更多的 socket 连接HTTP/2 的设计意图是充分利用单个 socket 连接,而拆分域名会违背这种意图。建议取消域名拆分,但请注意本表格之后的附注框会介绍这个问题相关的各种复杂情况禁用 cookie 的域名为图片之类的资源建立单独的域名,这些域名不用 cookie,以尽可能减少请求尺寸应该避免为这些资源单独设立域名(参见域名拆分),但更重要的是,由于 HTTP/2 提供了首部压缩,cookie 的开销会显著降低生成精灵图把多张图片拼合为一个文件,使用 CSS 控制在 Web 页面上展示的部分与极简化类似,只不过用 CSS 实现这种效果的代价高昂;不推荐在 HTTP/2 中使用6.7.1 生成精灵图和资源合并/内联精灵图(spriting)是指把很多小图片拼合成一张大图,这样只需发起一个请求就可以覆盖多个图片元素。在 HTTP/2 中,针对特定资源的请求不再是阻塞式的,很多请求可以并行处理;就性能而言,生成精灵图已失去意义,因为多路复用和首部压缩去掉了大量的请求开销。与之类似,小的文本资源,例如 JS 和 CSS,会依照惯例合并成一份更大的资源,或者直接内嵌在主体 HTML 中,这也是为了减少客户端-服务器连接数。这种做法有个问题是,那些小的 CSS 或 JS 自身也许可缓存,但如果它们内嵌在不可缓存的 HTML 中的话,当然也就不可缓存了。把很多小的 JS 脚本合并成一个大文件可能仍旧对 h2 有意义,因为这样可以更好地压缩处理并节省 CPU。6.7.2 域名拆分域名拆分(sharding)是为了利用浏览器针对每个域名开启多个连接的能力来并行下载资源。对于包含大量小型资源的网站,普遍的做法是拆分域名,以利用现代浏览器针能对每个域名开启 6 个连接的特性,充分利用可用带宽。因为 HTTP/2 采取多路复用,所以域名拆分就不是必要的了,并且反而会让协议力图实现的目标落空。比较好的办法就是继续保持当前的域名拆分,但是确保这些域名共享同一张证书 [ 通配符 / 存储区域网络(SAN)],并保持服务器 IP 地址和端口相同,以便从浏览器网络归并(network coalescence)中收益,这样可以节省为单个域名连接建立的时间。6.7.3 禁用cookie的域名在 HTTP/1 下,请求和响应首部从不会被压缩。随着时间推移,首部大小已经增长了,超过单个 TCP 数据包的 cookie 可以说司空见惯。因此,在内容源和客户端之间来回传输首部信息的开销可能造成明显的延迟。 因此,对图片之类不依赖于 cookie 的资源,设置禁用 cookie 的域名是个合理的建议。 但是 HTTP/2 中,首部是被压缩的,并且客户端和服务器都会保留『首部历史』,避免重复传输已知信息。所以,如果你要重构站点,大可不必考虑禁用 cookie 的域名,这样能减少很多包袱。 静态资源也应该从同一域名提供;使用与主页面 HTTP 相同的域名,消除了额外的 DNS 查询以及(潜在的)socket 连接,它们都会减慢静态资源的获取。把阻塞渲染的资源放在同样的域名下,也可以提升性能。6.7.4 资源预取资源预取也是一项 Web 性能优化手段,它提示浏览器只要有可能就继续下载可缓存资源,并把这些资源缓存起来。尽管如此,如果浏览器很忙,或者资源下载花的时间太 长,预取请求将会被忽略。资源预取可以在 HTML 中插入 link 标签实现:<link rel=“prefetch” href="/important.css”>也可以使用 HTTP 响应中的 Link 首部: Link: </important.css>; rel=prefetch 资源预取与 h2 引入的服务端推送并没多少关联。服务端推送用于让资源更快到达浏览器, 而资源预取相比推送的优点之一是,如果资源已经在缓存里,浏览器就不会浪费时间和带宽重复请求它。所以,可以把它看作 h2 推送的补充工具,而不是将被替代的特性。6.8 现实情况中的性能网络丢包是 h2 的命门,一次丢包机会就会让它的所有优化泡汤。7. HTTP/2 实现7.1 桌面Web浏览器所有浏览器在进行 HTTP/2 传输时都需要使用 TLS(HTTPS),即使事实上HTTP/2 规范本身并没有强制要求 TLS。这个原因是:从之前对 WebSocket 和 SPDY 的实验看来,使用 Upgrade 首部,通过 80 端口(明文的 HTTP 端口)通信时,通信链路上代理服务器的中断等因素会导致非常高的错误率。如果基于 443 端口(HTTPS 端口)上的 TLS 发起请求,错误率会显著降低,并且协议通信也更简洁。人们越来越相信,考虑到安全和隐私,一切都应该被加密。HTTP/2 被视为一次推动全网加密通信发展的机会。7.1.2 禁用HTTP/2HTTP/2 毕竟是新鲜事物,现在很多浏览器都支持启用或禁用 h2。7.1.3 支持 HTTP/2 服务端推送服务端推送是 h2 中最令人兴奋也最难正确使用的特性之一,现在所有的主流浏览器都已经支持了此特性。7.1.4 连接归并如果需要建立一个新连接,而浏览器支持连接归并,那么通过复用之前已经存在的连接,就能够提升请求性能。这意味着可以跳过 TCP 和 TLS 的握手过程,改善首次请求新域名的性能。如果浏览器支持连接归并,它会在开启新连接之前先检查是否已经建立了到相同目的地的连接。相同目的地具体指的是:已经存在连接,其证书对新域名有效,此域名可以被解析成那个连接对应的 IP 地址。如果上述条件都满足,那么浏览器会在已建立的连接上向该域名发起 HTTP/2 请求。7.2 服务器、代理以及缓存如果要通过 h2 传输内容,我们有几个选择。支持 HTTP/2 的网络设施大致有以下两类。 Web服务器 :通常所说的提供静态和动态内容服务的程序。 代理/缓存 :一般处在服务器和最终用户之间,可以提供缓存以减轻服务器负载,或进行额外加工。许多代理也能扮演 Web 服务器的角色。 在选择 HTTP/2 服务器时,我们需要检查、评估一些关键点。除了基本的通用性能、操作系统支持、学习曲线、可扩展性以及稳定性,还应当关注 Web 请求的依赖项和优先级,以及对服务端推送的支持。7.3 内容分发网络 CDN内容分发网络(CDN)是反向代理服务器的全球性分布式网络,它部署在多个数据中心。CDN 的目标是通过缩短与最终用户的距离来减少请求往返次数,以此为最终用户提供高可用、高性能的内容服务。大多数主流 CDN 是支持 HTTP/2 的,选择 CDN 时主要考虑的两点是:对服务端推送的支持,以及它们处理优先级的方式。这两点对现实世界中的 Web 性能影响重大。8. HTTP/2调试8.1 chrome devtools 可视化Chrome 开发者工具中的 Network 栏,有助于简单直观地跟踪客户端和服务端的通讯,它 按下面表格的形式展示了若干信息:资源名资源大小状态码优先级总加载时间使用时间线方式分解加载时间打开 devtools 的 Network 栏,鼠标放在瀑布流 Waterfall 的资源上,就会看到资源加载过程中各个阶段的详细时间Connection Setup (连接设置)Queueing :请求被渲染引擎或者网络层延迟的时间,浏览器在以下情况下对请求排队存在更高优先级的请求。此源已打开六个 TCP 连接,达到限值。 仅适用于 HTTP/1.0 和 HTTP/1.1浏览器正在短暂分配磁盘缓存中的空间Connection Start (开始连接阶段)Stalled :请求可能会因 Queueing 中描述的任何原因而停止Proxy negotiation :浏览器与代理服务器协商请求花费的时间DNS Lookup :浏览器解析请求的 IP 地址花费的时间Request/Response (请求 / 响应)Request Sent :发送请求包含的数据花费的时间Waiting (TTFB) :等待初始响应花费的时间,也就是所说的首字节时间;这个数字包括等待服务器传输响应的时间,以及往返服务器的延迟Content Download :接收响应的数据所花费的时间Explanation (总时间)其他ServiceWorker Preparation :浏览器正在启动 Service WorkerRequest to ServiceWorker :正在将请求发送到 Service WorkerReceiving Push :浏览器正在通过 HTTP/2 服务器推送接收此响应的数据Reading Push :浏览器正在读取之前收到的本地数据9. 展望未来HTTP/2 的弱点之一就是依赖主流 TCP 实现。在 3.1.3 节中已经讨论过,TCP 连接受制于 TCP 慢启动、拥塞规避,以及不合理的丢包处理机制。用单个链接承载页面涉及的所有资源请求,就能享受多路复用带来的好处;然而面对 TCP 层级的队首阻塞时,我们还是束手无策。所以 Google 开发的 QUIC 采纳了 HTTP/2 的优点,并且避免了这些缺点。推介阅读:HTTP2 详解 | Wangriyu’s BlogPS:欢迎大家关注我的公众号【前端下午茶】,一起加油吧 ...
前言其实18年之前写过一篇关于chrome插件的文章,里面安利了4个chrome插件。鉴于这已经是9102年了,之前觉得好用的chrome插件跟新了解到的比起来,还是差了那么点味道。所以决定再更新一波,顺便补上之前漏掉的几个插件。运用有效的有用的插件,会让我们的工作在一个相对舒适的状态,工作起来更加的流畅。Toby-不直观算我输大家可以将Toby当成加强版的书签栏,大家可以分类把常去的网站收藏起来,还可以正对每个分类打上对应的tag,在右上角进行筛选。当你打开了很多要用的网站,但是不得不关闭电脑时,toby可以一次性将目前打开的所有的网页全部存下来,下次再打开浏览器时,直接恢复就可以了。官网地址:–>戳这<–安装地址:–>戳这<–todoist-忘了要做什么是不可能的如果你每天都太多的事情需要做,但是记性不太好,有些时候会把大部分需要做的事情忘记,那么你可能需要todoist。这是一个插件式的to-do-list。在App Store里面还有桌面版的todoist,登陆同样的账号就可以实现数据联动。官网地址:–>戳这<–安装地址:–>戳这<–recover close tab-再也不翻历史记录经常手残把需要用到的网页给关掉了?关掉了还不不知道网址?不知道网址还要去历史记录中找一遍?recover-close-tab就是为你准备的。它可以直接打开你最后一个关闭的网页。特别适合手残党。安装地址:–>戳这<–saladict-妈妈再也不用担心我看不懂英文文档这是一款翻译用的插件。对一个需要翻译的句子或者单词,给出多种翻译工具的参考。只需要简单的双击单词,再hover旁边的logo,就可以了。有的时候这个翻译工具的翻译结果和另外一个工具的翻译结果是有一定的差别的,所以综合多个结果参考,才能更好的理解原文的意思。官网地址:–>戳这<–安装地址:–>戳这<–crx-mouse-这辈子不可能再点回退键自从用了这个插件之后,我觉得再点击左上角的退回键是有多么的难受。利用这个鼠标手势插件,按住右键轻轻往左一滑,就可以回退到上个访问的网页。就像我之前说的,很流畅。还有很多其他的手势可以用,例如移动到左边的tab、移动到右边的tab、new一个tab、关闭当前tab、前进、后退、快速向下滑一页、快速向上滑一页等等,十分有用。官网地址:–>戳这<–安装地址:–>戳这<–octotree-终于可以安静的看源码了在github上看一些项目的源码的时候,回到上一级的体验很不好,有的时候要等很久,有的时候找文件也要很久。而这个插件可以像你的ide一样将整个项目的树形结构展示出来。安装地址:–>戳这<–写在后面欢迎各位大佬安利你们觉得好用的插件,大家可以一起分享交流,相互提升自己的"用户体验"。
比如下图这种,记性不好,忘记了密码该怎么办?很简单,Chrome浏览器里按F12打开开发者工具,切换到Elements面板,点击下图Step1的图标,然后单击密码显示框,保持这个字段处于选中状态,然后切换到Console标签页,输入$0.value, 回车就能看到密码了。要获取更多Jerry的原创文章,请关注公众号"汪子熙":
上次研究了如何在java内置javascript解释器nashorn中加载npm模块,这两天手头又有一个需求,要在Chrome浏览器的开发者控制台中加载同样的npm模块,以便在控制台中验证一些想法。因为对前端开发不算熟悉,不知道有没有其它的好方案,这个是我自己摸索的,但确实可用。流程npm下载browserifynpm i browserify -g写一个简单js文件,使用require加载npm模块,并注入到window对象window.acorn = require("./acorn")使用browserify打包这个js,该工具会自动把所有依赖的npm模块和简单js打包成单个js文件browserify entry.js > require_acorn.js把require_acorn.js文件的内容粘贴到开发者控制台并执行,然后就可以使用了代码压缩到这里其实桌面Chrome浏览器就没有问题了,我又用inspector连上安卓版Chrome试了一下,结果控制台崩了……估计是acorn太大了,毕竟有5000多行,于是又试了试压缩:npm下载uglify.jsnpm i uglify-js -g压缩前面生成的单个js包uglifyjs require_acorn.js > require_acorn.min.js这次果然没有问题了,在控制台执行typeof acorn可看到模块已经正确加载。
查看网络面板响应比较慢可以从两个层次去考虑连接初始化阶段耗时请求和响应耗时查看关键指标:排队达到浏览器最大并发数量限制有更高优先级的请求插队,低优先级的任务被延后系统内存空间不足,浏览器使用磁盘空间拥堵 原因和排队中类似DNS查询 花在DNS查询上的时间Proxy negotiation. 代理协商Request sent. 请求被发送Request to ServiceWorker. 请求被发送到ServiceWorkWaiting (TTFB). 等待收到响应的第一个字节Content Download. 内容下载Receiving Push. 浏览器通过HTTP/2 Server Push接受数据Reading Push. 浏览器读取之前收到的数据常见问题现象及解决方法出现长时间的排队或者拥堵原因 浏览器对同一域名最大的TCP链接数有限制,超过限制的请求会被排队。参见浏览器同域名请求的最大并发数限制。为什么会达到最大并发数?一次性获取的资源数量太多资源体积太大,很多都在下载中有些请求响应太慢或者无响应。例如1分钟之内,每隔10秒钟发送一个无响应的请求,随着可用的请求慢慢被占满,正常的请求排队数量会越来越多。解决方法问题1和问题2比较容易发现,也比较好处理。主要从两个角度解决减少请求数量。可以移除不必要的请求,或者将多个请求合并成1个。例如雪碧图使用域名分片。例如使用不同的域名指向相同的资源,从而突破单域名的限制。例如img1.tt.cc/1.jpg和img2.tt.cc/1.jpg前端给每个ajax请求设置超时 防止过多的无响应请求占据着连接资源,可以在超时之后释放连接。有些ajax库,例如jQuery的ajax, 默认是没有设置超时时长的,当你在使用这些库时,最好明确的设置后端设置请求处理超时 后端接口应当设置最大超时时长长时间的TTFB出现这种问题要从两个方面排查客户端到服务端之间的网络通信比较慢服务端的响应比较慢,可能是服务端压力太大,到达带宽上线,内存溢出,高CPU, IOwait高, Recv-Q高,或者sql查询慢等各种原因。注意:对于同一个源的请求,如果有些请求很快,有些请求很慢。那么问题一般是服务端的问题。因为如果是出现网络通信比较慢,那么则所有的请求都会变慢。解决方案知道问题的真实原因,其实问题也就解决了一半了。参考network-performancenetwork issue guide浏览器同域名请求的最大并发数限制
对某网站加密混淆后的javascript代码也算分析了一段时间了,虽然还没搞出来,但多少有些心得,这里记录一下。工具和资料前一篇文章 - 记录了之前尝试的一些方案awesome-java-crawler - 我收集的爬虫相关工具和资料java-curl - 本人写的java HTTP库,可用来替换chrome网络后端,更方便控制底层行为,如缓存、代理、监控、修改请求和应答等cdp4j - java版的Chrome Devtools Protocol实现,用于控制Chrome浏览器。最大的特点就是没有“特点”,你懂的……beautifier.io - js代码在线格式化estree - ECMAScript抽象语法树(AST)业界标准ECMAScript262语言规范 - 帮助理解estreeacornjs - ECMAScript编译器前端,将js源码解析成estree格式的ASTastring - ECMAScript代码生成器,将AST重新还原成js源码nashorn - java8以上自带的javascript解释器,性能接近原生nodejava中调用npm模块 - 我一直用的java和kotlin,为了调用js原生库,需用这个方案类似网站的破解 - 神箭手云的大佬写的很早的一篇分析文 - 看特征是这种加密的早期版本分析过程获取javascript代码加密的核心代码只有一小部分是直接写在网页的<script>里面的,有些代码是eval出来的可以用cdp4j监听Debugger.ScriptParsed事件,并在监听器中调用Debugger.getScriptSource来获取js代码文本这样是可以获取到所有前端javascript源码的,即使源码在网络应答中是加密的,但用eval执行前也必须还原为合法的js源码为了方便分析,可将代码保存为文件。该网站js会用定时器不断重复eval一段代码,因此可以用ScriptParsed.hash作为文件名,避免重复保存文件获取常量映射拿到js之后,格式化一下,发现还是一团乱麻,所有的变量,函数都是"$xx",可读性约等于0在Chrome控制台里试了一下,发现全局变量和函数都保存在window中了一部分无参调用的函数,其实返回的就是常量字符串还有一些$xx.call的,看了一下,其实就是系统方法,比如String.fromCharChode等因此可以编写一段代码,遍历window对象中所有形似_$xx的成员,这样既可将常量字符串映射、系统方法映射等搞出来可读性还原拿到映射关系之后是不是简单用正则表达式替换回去就万事大吉了呢?哪有那么简单!函数的局部变量、局部函数有很大可能性和全局变量重名,如果用正则无脑替换回去绝对会被坑死!!要是代码少倒也罢了,这里可有5000行代码,差之毫厘谬以千里!另外,不同函数的局部变量也存在大量重名,静态分析时干扰严重,因此,应该将局部变量也替换成唯一且更有意义的名字,比如<函数名><变量索引>因此,正确的方法是基于编译原理进行语法级别的替换,看到这里是不是要弃疗了?老子爬点数据还要写编译器?!还好,js上已经有很成熟的业界标准和若干老练的第三方库了,至少不用从龙书搞起……我这里选择了acornjs和astring,前者用于将js源码解析成抽象语法树AST,后者将AST还原成js源码。当然,在AST上我们是可以上下其手的……为了在java代码中运行acornjs和astring,请参见参考中《java中调用npm模块》一文。注意astring还依赖endswith和repeat两个polyfill,均可以npm下载到用acorn.parse()搞到AST之后,用递归的方式扫描每个节点进入每个FunctionDeclaration/FunctionExpression节点前,创建一个新的作用域对象放到栈顶,里面放该域内所有局部变量(含函数的参数)和新名称的映射表;退出时将栈顶弹出遇到Identifier节点,首先在作用域栈中自顶向下依次寻找当前变量名,找到了,则是本方法局部变量或闭包外局部变量,用新名字替换之;否则,则是全局变量,去映射表中查找注意,遇到CallExpression则要特殊处理,前面的AST变换只涉及修改标识符名,而为了将$xx()变换为"xxx",则涉及到结构变换,要把CallExpression节点修改为Literal节点并添加value属性全部处理完成后,就可以用astring.generate()产生还原后的代码了代码分析上面步骤完成后,这代码至少勉强能看了,别放松,后面还有无数的坑……还原前的代码只能是让人一脸懵逼,还原后的代码则足以让人咬牙切齿啊,多大仇啊,满满登登5000行全是正面硬钢的……我现在也就还卡在这一步,这里先记录一部分已经发现的反破解手法吧。不断主动中断干扰调试,并检测是否有动态分析行为var eI_v1 = window[“eval”]("(function() {var a = new Date(); debugger; return new Date() - a > 100;}())");_$n1 = _$n1 || eI_v1;//这个在上篇文章分析了,在这找到调用来源了。注意,在可读性还原之前这货长这样:var _$pW = $u9_$mz();$n1 = _$n1 || $pW;js代码动态混淆上一篇文章已经说过了,每次刷新js代码都会完全变化,包括全局/局部变量名、函数排列顺序等设断点会被干扰,而且代码无法重复执行对于调试意味着什么?检查关键函数是否被注入替换function __RW_checkNative(rh_p0, rh_p1) { // 函数名我手动改的 try { var rh_v2 = Function[“prototype”][“toString”]“apply”; var rh_v3 = new RegExp("{\s*\[native code\]\s*}"); if (typeof rh_p0 !== “function” || !rh_v3“test” || rh_p1 != undefined && rh_p0 !== rh_p1) _GL_undefined$sy = true; } catch ($r0) {}}会用这个函数检测eval, Function, setTimeout, setInterval几个系统函数是不是被注入了搞明白了,就可以用一些手段骗过去,不明白的话……检测当前窗口是否隐藏状态 document[“addEventListener”](“visibilitychange”, _$r0);会监控当前窗口是否在最上方,要是用cdp4j多开浏览器同时爬取……检测Selenium, WebDriver, PhantomJS var rm_v5 = “_Selenium_IDE_Recorder,selenium,callSelenium” , rm_v6 = “__driver_evaluate,__webdriver_evaluate,__selenium_evaluate,__fxdriver_evaluate,__driver_unwrapped,__webdriver_unwrapped,__selenium_unwrapped,__fxdriver_unwrapped,__webdriver_script_func,__webdriver_script_fn” , rm_v7 = [“selenium”, “webdriver”, “driver”]; if ($un(window, “callPhantom,_phantom”)) { … }看到这里想必就知道会发生些什么了……Hook住AJAX var ec_v4 = window[“XMLHttpRequest”]; if (ec_v4) {var ec_v5 = ec_v4[“prototype”];if (ec_v5) { __GL_f_open = ec_v5[“open”]; __GL_f_send = ec_v5[“send”]; ec_v5[“open”] = function () { _$t5(); arguments[1] = $pK(arguments[1]); return __GL_f_open[“apply”](this, arguments); };} else { … } }检查navigator是否是伪造的 var hi_v14 = window[“navigator”]; for (hi_v11 in hi_v14) {try { hi_v13 = hi_v14“hasOwnProperty”;} catch ($r0) { hi_v13 = false;} }如果你的navigator对象是自己注入的水货版本,那就露馅了……检查浏览器特征这块代码很复杂,还没分析完,现在能看出来的包括:navigator.languages - 据说在headless chrome中是没有这个字段的navigator.plugins - 据说无头和有头的chrome返回的插件列表不一样WebGL能力检查有一大段代码是在canvas上用webgl绘图,没搞过webgl,现在还不明白,但肯定也是检查浏览器特征手段之一破解思路其实并没有,还在抓瞎中……一些很不成熟的点子:可以拦截window对象的写入,把全局函数通过toString()搞到源码,再通过acorn/astring大法搞事情之后替换成eval版本但是函数名、变量名全部都混淆了,因此需要用一些特征来检测每个函数,目前想到的点子是将AST上所有节点分类计数连接成字符串,这样绝大多数方法的特征都是唯一的需要将浏览器特征检测代码在桌面浏览器、手机浏览器、桌面无头浏览器分别运行,看看到底有啥区别,然后注入代码修改特征进行欺骗 ...
这篇文章将介绍下实际使用performance对页面进行优化的过程。总的来说,chrome performance工具让我们更方便的发现在代码运行过程中的问题在哪里,便于对一些可能注意不到的问题进行定位、分析和优化。原文首发于个人博客渲染优化首先,我们对进入整个详情页进行分析,整个页面的结构大致如下,主要包含三个部分:基本信息,可视化图和时间轴。时间轴内部包含时间轴的详情信息,包括表格和几种类型的可视化图等,内部比较重。如图所示:我们点击录制,查看进入页面的性能图:优化点1可以看到,渲染当前页面的耗时将近1.2s,我们看看到底是哪里出现了问题。对渲染部分进行放大,我们发现在渲染时间轴的过程中,大部分时间耗费在了每个时间节点详情内容的展示上面,但是默认状态下,他们是进行隐藏的。也就是说,我们进行了N个节点的不必要的渲染,只需把他们干掉就好了。查看代码,发现是以下位置导致的,我们进行了一个展开盒子的封装,类似这样:<Box data={data} border collapse loading={loading}> <AlertDetail /></Box>在非展开状态下,我们依然对内容进行了渲染,只是使用样式进行了隐藏,但是这样React仍然会进行虚拟dom的渲染和计算,在这里我们对其进行改造,让其只在展开时进行渲染,并尽量缓存渲染的结果。修改完成后,我们会发现,Scripting由之前的880+ms变成了670ms减少了200ms左右:优化点2我们继续看,会发现页面初始化时,详情部分会有大量的Evaluate Script耗时,主要是耗费在告警详情右侧的分类及各分类下的可视图及详情引起的。我们可能会想,这里在初始化时并没有进行渲染,为什么仍然会耗费时长进行计算呢?继续追查我发现原因在这里:在整个详情组件中,我们对包括http,tcp等所有类型的组件进行了引用,然后根据其类型进行组件的匹配,在各个组件中可能包含了每个类型对应的定义、分类和计算等等等等,不仅增加了加载时间,更延长了初始化时间,显然我们这么做是不对的。在此,我们可以使用懒加载方式对其进行优化,仅展示其对应类型的图,避免了不必要的资源浪费和计算时间。修改之后,我们再次进行性能测试,发现在进入页面时,详情组件的耗费时长由260ms变为不到2ms:而Scripting由之前的670+ms变成了415ms减少了250ms左右:至此,进入页面的耗时已由最开始的1.2s左右变成了现在的0.7s左右。更新优化我们在点击时间轴查看详情时,会进行几个操作。关闭其他已开启的详情内容,展开当前详情内容,根据当前的类型进行对应类型的详情内容展示,上边已经提到过。这个过程会涉及到React的update操作,我们来对这个过程进行一下优化。优化点1首先我们点击录制按钮,然后点击展开,并对其进行录制。我们会发现以下的结果:点击展开按钮,Timeline组件进行了多次重复渲染,显然这是不应该的,我们来看下是哪里导致的。我们看到整个时间轴组件中,有这样一段代码,在时间轴组件中使用connect连接了timeline和alertList两个数据,其中,alertList数据是详情内种中对应的告警列表。两组数据对应的更改都会映射到组件的更新,比如时间轴的展开收起,以及alertList请求,请求成功及失败等。按理来说,alertList对应的请求,仅对应到当前展开内容的更新即可。因此,我们对此有几种修改方案:时间轴组件中弃用对alertList的引用,以保证alertList不会牵连到时间轴组件整体更新将时间轴的渲染和详情渲染进行分离,向其传递各自对应的数据,通过PureComponent来控制更新使用shouldComponentUpdate进行优化在此我们采用第一种,避免alertList对整个组件的影响。我们会发现,点开详情后,整个timeLine只进行了一次大更新,详情的更新只在展开的组件中进行。优化点2我们会发现,在对某一个时间点进行展开时,整个list列表的节点都进行了更新,如下图所示。显然这样的性能耗费是及其大且不重要的。假如列表的内容很多,那极有可能造成大量的更新导致页面卡死。因为其他的list列表节点都引用了alertList这个prop, 当其进行改变时,所有的节点都会进行更新,所以我们需要使用shouldComponentUpdate手动对其进行优化:// 当开关状态变化时,才从新渲染,否则不需要shouldComponentUpdate (nextProps, nextState) { const opened = .get(this.props, ‘open’) const willOpen = .get(nextProps, ‘open’) if (opened === willOpen && !willOpen) { return false } else { // 类似pureComponent进行浅比较 return !.isEqual(this.props, nextProps) || !.isEqual(this.state, nextState) }}再次进行录制,我们发现只要当前Item进行了更新,整个Timeline更新时间缩短到不到40ms,极大的增加了体验。其他优化过程与上面类似,不再赘述。总结通过配合performance工具进行一步步优化,整个页面的渲染和更新性能有了极大的提升,我们也借此知道了在平时书写代码过程中需要注意的问题。我们简单的做一下总结:避免非展示状态的不必要的渲染必要时,手动进行懒加载以避免大型模块对页面进行营销,避免加载不必要的模块保证展示组件props的纯净性,避免因其他props的更改导致组件进行更新必要时,可使用shouldComponent进行手动优化平时可通过PureComponent来避免不必要的组件更新