石家庄网站定制模板建站,微商代理平台,手把手教你做网站视频,做一个企业网站多少钱对于依赖接口渲染的页面#xff0c;在拿到数据之前页面往往是空白的#xff0c;为了提示用户当前正在加载中#xff0c;往往会使用进度条、loading图标或骨架屏的方式。对于前两种方案而言#xff0c;实现比较简单#xff1b;本文主要研究骨架屏的应用及实现#xff0c;并… 对于依赖接口渲染的页面在拿到数据之前页面往往是空白的为了提示用户当前正在加载中往往会使用进度条、loading图标或骨架屏的方式。对于前两种方案而言实现比较简单本文主要研究骨架屏的应用及实现并给出一种使用Chrome扩展工具快速生成骨架屏的方案。首先看看效果 先放一个动图展示掘金首页百度首页知乎首页安装插件后访问任意网页基本上均可以一键生成骨架屏(由于时间关系部分节点并没有完全实现不过目前展示Demo应该是够了本文所有代码均放在https://github.com/tangxiangmin/web-skeleton-extension上面了时间关系代码写的比较潦草~(写完准备提交Chrome应用商店的时候才发现居然有一个类似的应用skeleton extention~囧就当瞎折腾一番了骨架屏方案目前有几种比较常见的骨架屏方案使用图片、SVG实现骨架屏效果可以让设计在提供页面设计稿的时候同步一份当前页面的骨架屏图片资源这种方案开发成本较低缺点是不太灵活如果页面迭代比较频繁会导致设计同事的工作量增大手写HTMLCSS实现骨架屏较第一种图片方案而言可以更灵活地定制骨架屏UI和动画效果缺点也很明显开发和维护成本都较高目前可以使用诸如react-content-loader等现有的骨架屏库通过配置快速生成对应的骨架屏但对于某些高度定制的页面也不能很好地满足业务需求饿了么前端团队提供的一套方案page-skeleton-webpack-plugin其大致原理是使用puppeteer向指定页面注入代码遍历页面节点根据节点的类型渲染对应的骨架屏样式然后结合webpack将返回的HTML自动注入到项目模板文件中传送门:一种自动化生成骨架屏的方案https://github.com/Jocs/jocs.github.io/issues/22在最开始调研骨架屏方案的时候也注意到了page-skeleton-webpack-plugin体验了一下发现并不能完全满足我的需求对于骨架屏方案我的期望是使用方便能够根据给定页面自动生成对应的骨架屏不需要安装额外的工具(不用安装puppeteer)能够灵活地处理骨架屏生成的HTML文件支持首屏或者部分区域使用骨架屏(不需要webapck自动注入模板文件反之需要实现为项目单个或多个页面配置对应骨架屏)能够自定义骨架屏各个区块的样式能够指定包含或者忽略某些节点生成的代码体积要足够小(骨架屏并不需要完全还原原本页面)恰好调研骨架屏方案的那段时间正在处理Chrome扩展程序的工单发现page-skeleton-webpack-plugin借助puppeteer执行页面脚本的方案完全可以通过扩展程序的content.js实现这样不需要借助本地开发环境也可以渲染骨架屏了骨架屏实现原理何为骨架首先需要保留节点的布局信息这样就可以复用节点原有的样式不会破坏整体布局最后生成的骨架屏样式就可以与原本的页面保持一致。然后为了保证生成样式的统一需要覆盖节点原本的UI信息包括背景色、图片、边框等。由于骨架屏仅仅作为数据返回之前的占位并不需要完全复原原本的页面因此为了使整个骨架屏看起来比较简洁可以忽略不必要的节点。那么要处理哪些节点、忽略哪些节点呢因此针对不同节点我们需要进行判断并分别处理。区分不同节点这部分大量参考了一种自动化生成骨架屏的方案https://github.com/Jocs/jocs.github.io/issues/22 这篇文章的思路不妨移步阅读下面简单整理一下各种不同节点的处理方案并增加了一些额外的节点处理策略。由于涉及大量的节点操作因此使用了jQuery。文字把文字占据的空间看做上行高、内容、下行高行高部分用透明色内容部分用灰色这样就可以展示原本的文字区域了对于多行文字而言显示的就是斑马状条纹形式的骨架屏结构.line { background-image: linear-gradient(red 25%,blue 25%, blue 75%, red 75%); /*background-image: linear-gradient(red 25%,blue 0, blue 75%, red 0); // 与上面等价*/}复制代码这里需要计算元素的行高、字体大小等信息renderText($dom) { let fontSize parseFloat($dom.css(font-size)); let lineHeight $dom.css(line-height); // todo 处理浏览器默认行高、包含继承、自定义等属性 if (lineHeight normal) { lineHeight fontSize * 1.4; } else { lineHeight parseFloat(lineHeight); } const textHeightRatio fontSize / lineHeight; const firstColorPoint (((1 - textHeightRatio) / 2) * 100).toFixed(2); const secondColorPoint (((1 - textHeightRatio) / 2 textHeightRatio) * 100).toFixed(2); const style --fp:${firstColorPoint}%;--sp:${secondColorPoint}%;--lh:${lineHeight}px;; $dom.addClass(sk-text); $dom.attr(style, style);}复制代码因为每个文本节点的字体大小和行高可能不一样如果全在style标签上添加样式则生成的HTML文件可能比较大因此这里使用了CSS Var然后统一在sk-text的样式类中处理背景色.sk-text { --c: #eee; --fp: 0%; --sp: 0%; --lh: 0; display: inline-block; background-origin: content-box !important; background-clip: content-box !important; background-color: transparent !important; background-repeat: repeat-y !important; background-image: linear-gradient(transparent var(--fp), var(--c) 0, var(--c) var(--sp), transparent 0); background-size: 100% var(--lh); color: transparent !important;}复制代码图片获取原始图片节点的宽高然后使用1像素的base64灰色图片替换原始节点的src属性renderImg($img) { let width $img.width() let height $img.height() // 一像素灰色图片 let emptyImage data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7 $img.attr(src, emptyImage); $img.css({ background: #eee, width: width px, height: height px })}复制代码对于包含背景图片的区块而言只需要将其背景覆盖成灰色即可。按钮、input使用灰色背景的块占据边框替换对应边框的颜色为骨架屏灰色列表列表元素是骨架屏中一个比较常见的区块列表元素可以由上面这些区块组成但是为了保证生成规整的骨架屏采取的策略是使用移除多余的元素使用第一个元素克隆占位function renderList($dom) { $dom.addClass(sk-list) let $children $dom.children() let $child $children.first() let len $children.length // 列表元素子节点统一保证页面骨架整齐 for (let i 1; i $children.eq(i).remove() } for (let i 1; i let tmp $child.clone(true) $dom.append(tmp) }}复制代码遍历DOM树当确定了不同类型节点的处理策略之后就可以从入口节点遍历整个DOM树执行处理方法了function preorder($dom) { // ...获取节点类型执行相关策略放啊 // 遍历子节点 $dom.children().each(function () { const $this $(this) preorder($this) });}复制代码在遍历期间还需要处理一些特殊情况不可见的元素及其子节点应该停止遍历对于某些节点而言可能存在一些定制操作由用户指定节点类型而不是默认根据节点nodeType推断忽略某些节点的处理如一个ul标签我们可能并不希望将其转换成列表直接隐藏节点如某些小图标等为了保证骨架屏简洁可能需要直接隐藏节点基于这些场景引入了skeleton-type的概念对应上面章节提到的节点类型在遍历时会优先读取节点上的该属性值只有当属性值不存在时才会根据nodeType自行推断相关的类型对于需要隐藏的节点直接指定skeleton-type为ignore对于忽略节点类型而言则使用skeleton-exclude-type let type $dom.attr(KEY) || getNodeSkeletonType($dom) // 自动检测节点类型并附上typelet excludeType $dom.attr(KEY_EXCLUDE)if (!excludeType || type ! excludeType) { let handlers { [TEXT]: renderText, [IMAGE]: renderImg, [BLOCK]: renderBlock, [BORDER]: renderBorder, [BUTTON]: renderButton, [LIST]: renderList, [BACKGROUND_IMAGE]: renderBackgroundImage, [INPUT]: renderInput, [IGNORE]: renderIgnore } let handler handlers[type] handler handler($dom)}复制代码这样相当于暴露了skeleton-type与skeleton-exclude-type两个HTML属性在开发页面的时候就可以直接指定骨架屏区块类型方便定制业务需要的骨架屏。当然逐个去配置skeleton-type也会显得比较繁琐在getNodeSkeletonType中会尽可能地推断并生成比较符合要求的骨架屏效果此外基于Chrome扩展程序的工具还暴露了一个配置参数 renderSkeleton(body, { ignore: , selector: { [key]: {include: , exclude: } },})复制代码用掘金首页试一下原本的效果如果不过滤的话因为顶部导航栏使用的是ul会默认识别成list导致展示出现问题自定义配置后的效果{ ignore: [.banner .label].join(,), // 隐藏右侧广告栏的提示按钮 selector: { block: { // 将表单转换成一个BLOCK include: [.add-group .more, .search-form].join(,) }, list: { // 排除导航栏的ul标签 exclude: [.nav-list].join(,) }, },}复制代码此外我们也可以只传入某个节点而非body的选择器这样就只会生成对应节点的HTML将骨架屏嵌入应用骨架屏有两种比较常见的应用场景用于首屏或某个页面打开时等待数据返回时的占位用于滚动加载等列表页面占位接下来研究在这两种场景下如何嵌入骨架屏获取骨架屏代码在前面遍历入口节点DOM树渲染骨架屏之后我们只需要获取对应节点的HTML代码即可。由于没有修改原本布局信息因此这段HTML代码再同一个页面是完全可以展示的。我们要做的就是拿到对应的骨架屏HTML代码。由于整个操作都是在当前页面上执行的拿到当前页面上某个节点的的html内容就变得十分简单了通过控制台找到节点然后访问innerHTML通过调试工具Elements面板然后Copy HTML上面的方式需要手动去操作我们可以使用扩展程序在骨架屏渲染结束之后自动保存对应的HTML代码// chromeMsg是自己封装的一个chrome.runtime.onMessage工具chromeMsg.on(createSkeleton, (params) { const {config} params // 默认页面根节点可以导出某个dom容器的骨架屏结构 let content renderSkeleton(.page, config) // 处理对应的骨架屏HTML代码这里只是简单打印也可以直接保存文件到本地或者通过HTTP接口调用某些钩子服务完成自动注入等功能 console.log(content)})复制代码整页应用对于整页骨架屏我们需要先使用测试数据生成对应的骨架屏然后将得到的HTML放入页面中即可。以Vue等单页项目而言在整个应用初始化之前页面上只存在根节点用户看见的是一片空白对于这种首屏应用我们可以将得到的骨架屏代码直接嵌入到根节点中这样当页面加载至应用初始化之前用户都能看见完整的骨架屏效果。对于其他单页应用其他页面而言也可以采用类似的原理等待接口返回前展示骨架屏SkeletonFrame :framehtml :loadingisLoading RealPageRealPageSkeletonFrame复制代码比如我们可以封装一个SkeletonFrame组件其接口包括frame和loading两个props表示骨架屏HTML和是否正在加载当loading为true时展示骨架屏default slot需要真实渲染的页面组件当loading为false时展示真正页面组件局部骨架屏对于一些需要持续加载的数据如滚动加载等可以先获取对应区块的骨架屏HTML代码(不需要整个页面的骨架屏代码)然后将其封装成待展示组件与上面SkeletonFrame逻辑比较类似小结本文整理了骨架屏的实现原理然后提供了一种通过Chrome扩展程序生成任意网页的骨架屏方案主要包括将页面按节点类型拆分成不同区块支持自定义节点类型、忽略或隐藏节点导出并使用骨架屏HTML代码当然上面只是介绍了大概的实现思路并给出了一个简单的Demo距离实际应用还有一些距离等后面有时间再进一步完善吧。本文所有代码均放在github上面了欢迎提PR和issue。近期程序员都喜欢用的架构图工具vuets打造一个酷炫的星空聊天室若此文有用何不素质三连❤️