项目作者: CJY0208

项目描述 :
基于 jsdom 的、自动生成骨架的 Webpack 插件,工作方式参考 Prerender-SPA-Plugin
高级语言: JavaScript
项目地址: git://github.com/CJY0208/auto-skeleton-plugin.git
创建时间: 2020-07-30T12:51:42Z
项目社区:https://github.com/CJY0208/auto-skeleton-plugin

开源协议:

下载


AutoSkeletonPlugin

基于 jsdom 的、自动生成骨架的 Webpack 插件,工作方式参考 prerender-spa-plugin

工作方式

  1. 等待 webpack 完成打包

  2. 启动一个本地静态服务器,按配置的 routes,用 jsdom 启动无头浏览器访问打包结果

  3. jsdom 中页面完成渲染后,执行 generateSkeleton 骨架生成方法,将内容转换为骨架

  4. 截取当前页面的 innerHTML 作为骨架预渲染结果,输出成 html 文件

  5. 根据配置的 staticDir 将 html 文件存进去,over


基本使用方式

  1. const AutoSkeletonPlugin = require('auto-skeleton-plugin')
  2. module.exports = {
  3. plugins: [
  4. ...
  5. new AutoSkeletonPlugin({
  6. // Required - The path to the webpack-outputted app to prerender.
  7. staticDir: paths.appBuild,
  8. // Required - Routes to render.
  9. routes: ['/'],
  10. })
  11. ]
  12. }

自定义骨架生成过程

目前的生成骨架生成规则比较简陋,需要调整生成过程以适应不同项目

  1. const AutoSkeletonPlugin = require('auto-skeleton-plugin')
  2. module.exports = {
  3. plugins: [
  4. ...
  5. new AutoSkeletonPlugin({
  6. // Required - The path to the webpack-outputted app to prerender.
  7. staticDir: paths.appBuild,
  8. // Required - Routes to render.
  9. routes: ['/'],
  10. generateSkeleton(window, route) {
  11. const { document } = window
  12. const spinner = document.getElementById('spinner')
  13. spinner.parentNode.removeChild(spinner)
  14. return AutoSkeletonPlugin.generateSkeleton(window, route)
  15. },
  16. })
  17. ]
  18. }

默认的生成过程

  1. function generateSkeleton(window, route) {
  2. const { document, Text } = window
  3. const style = document.createElement('style')
  4. style.innerHTML = `
  5. ._ske {
  6. background: #f2f2f2;
  7. color: transparent;
  8. text-decoration: none;
  9. user-select: none;
  10. pointer-events: none;
  11. }
  12. ._ske_img {
  13. opacity: 0;
  14. display: inline-block;
  15. }
  16. `
  17. document.head.appendChild(style)
  18. // 文字节点
  19. ;[...document.querySelectorAll('*')]
  20. .filter(
  21. (node) =>
  22. !['script', 'style', 'html', 'body', 'head', 'title'].includes(
  23. node.tagName.toLowerCase()
  24. )
  25. )
  26. .map((node) => [...node.childNodes].filter((node) => node instanceof Text))
  27. .flat(Infinity)
  28. .forEach((node) => {
  29. let span = document.createElement('span')
  30. node.parentNode.insertBefore(span, node)
  31. span.appendChild(node)
  32. span.classList.add('_ske')
  33. })
  34. // 图标节点
  35. ;[...document.querySelectorAll('*')]
  36. .filter((node) => ['i'].includes(node.tagName.toLowerCase()))
  37. .forEach((node) => {
  38. node.classList.add('_ske')
  39. })
  40. // 图片节点
  41. ;[...document.querySelectorAll('*')]
  42. .filter((node) => ['img'].includes(node.tagName.toLowerCase()))
  43. .forEach((node) => {
  44. let span = document.createElement('span')
  45. node.parentNode.insertBefore(span, node)
  46. span.appendChild(node)
  47. span.classList.add('_ske')
  48. node.classList.add('_ske_img')
  49. })
  50. }