likes
comments
collection
share

骨架屏的几种方案

作者站长头像
站长
· 阅读数 55

背景:

解决首页首屏加载慢或加载时出现白屏问题,以前的方案首页首屏可视区域显示的加载,要么懒加载,要么loading一个gif的小图,要么预加载部分内容等。

何为骨架屏

简单来说,骨架屏就是在页面内容未加载完成的时候,先使用一些图形进行占位,待内容加载完成之后再把它替换掉。相对传统加载方案,skelteton方案 用户体验感增强。

骨架屏的几种方案

方案一、taro-plugin-skeleton

配合index.config使用 单独写骨架屏页面,与原有业务耦合度较低。 当页面渲染固定元素,骨架屏会被遮挡 原理:拦截原有小程序底层页面,使用骨架屏页面替换。 当小程序View 没有渲染的时候展示骨架屏,小程序View展示会遮挡住骨架屏页面。

缺点: 上拉下拉会露出骨架屏页面。 骨架屏页面需要单独写样式。 此方案无项目实践,感兴趣的朋友可自己玩玩

方案二、taro-skeleton

骨架屏组件,封装了基础样式 可以根据业务定制骨架屏。也可以仅针对首页设置,下面案例二有针对taro 小程序的骨架屏配置及效果

缺点: 与业务耦合度较高,需要单独写骨架屏逻辑。

方案三、page-skeleton-webpack-plugin

自动生成骨架屏,H5项目建议使用此方案 原理: 依赖 html-webpack-plugin 对html进行计算,替换dom。

案例一

skeleton 技术主要使用在以内容为主的APP和网页应用较多,接下来以VUE工程为例,在基于Vue的SPA项目中实现骨架屏。

分析Vue页面的内容加载过程

初始化一个vue-cli项目

npm install --global vue-cli   vue-cli@3.0
vue create my-project
cd my-project
npm install
npm run serve 或 yarn run serve

执行npm run seve 命令后,先看下index.html 写的具体内容

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <!-- <link rel="icon" href="<%= BASE_URL %>favicon.ico"> -->
    <title>jingbo</title>
</head>
  <body>
    <div id="app">
    <!-- built files will be auto injected -->
    <script src="iconfont.js" type="text/javascript"></script>
  </body>
</html>

可以看到,DOM里面有且仅有一个div#app,当js被执行完成之后,此div#app会被整个替换掉,因此,我们可以来做一下实验,在此div里面添加一些内容:

<div id="app">
  <p>Hello skeleton</p>
  <p>Hello skeleton</p>
  <p>Hello skeleton</p>
</div>

打开chrome的开发者工具,在Network里面找到Online功能,调节网速为“Slow 3G”,刷新页面,就能看到页面先是展示了三句“Hello skeleton”,待js加载完了才会替换为原本要展示的内容

骨架屏的几种方案

也就是说,我们只要在inde.htm文件中 div#app内直接插入骨架替换即可。

这里补充一个小插曲: 因使用的element-ui,需要安装Node-sass,国内镜像的问题node-sass就像是狗皮膏药,挺烦的,这里提供一种离线命令安装方式 在packsge.json,script中添加preinstall指令;配置如下

 "scripts": {
    "serve": "vue-cli-service serve --mode dev",
    "serve:test": "vue-cli-service serve --mode test",
    "serve:qa": "vue-cli-service serve --mode qa",
    "serve:stage": "vue-cli-service serve --mode stage",
    "serve:prod": "vue-cli-service serve --mode prod",
    "build": "vue-cli-service build --mode dev",
    "build:test": "vue-cli-service build --mode test",
    "build:qa": "vue-cli-service build --mode qa",
    "build:stage": "vue-cli-service build --mode stage",
    "build:prod": "vue-cli-service build --mode prod",
    "lint": "vue-cli-service lint",
    "skeleton": "webpack --config ./skeleton/webpack.skeleton.conf.js && node ./skeleton/skeleton.js",
    "analyz": "build for production and view the bundle analyzer report",
    "mock": "json-server --watch mock/db.json",
    "preinstall": "node -p \"[process.platform, process.arch, process.versions.modules].join('-')\" "
  },

在下载之前一定要弄清楚你的服务器环境,要和你下载的离线包一致。npm i 时可以看到 看到了你的配置,我的是win32-x64-93

骨架屏的几种方案 包安装后在node_modules下找到node-sass看有没有对应的vendor文件夹

实现

显然,如果手动在工程中inde.html文件中 div#app里面写入骨架屏内容是不科学的,我们需要一个扩展性强且自动化的易维护方案。既然是在Vue项目里,我们当然希望所谓的骨架屏也是一个.vue文件,它能够在构建时由工具自动注入到div#app里面。

接下来,我们在项目中创建skeleton文件夹,分别添加

Skeleton.vue文件

skeleton.entry.js入口文件

完成上述文件创建后,需要安装个关键插件vue-server-renderer。该插件本用于服务端渲染,但是项目中主要利用它能够把.vue文件处理成htmlcss字符串的功能,来完成骨架屏的注入,流程如下:

骨架屏的几种方案

根据流程图,我们还需要在skeleton文件夹新建一个webpack.skeleton.conf.js文件,以专门用来进行骨架屏的构建。

详见工程文件配置

module.exprot = {
target: 'node',
externals: nodeExternals({
    whitelist: /.css$/
  }),
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    },
    extensions: ['*', '.js', '.vue', '.json']
  },
  plugins: [
    new VueSSRServerPlugin({
      filename: 'skeleton.json'
    })
  ]
 }

该配置文件和普通的配置文件基本完全一致,主要的区别在于其target: 'node',配置了externals,以及在plugins里面加入了VueSSRServerPlugin。在VueSSRServerPlugin中,指定了其输出的json文件名。我们可以通过运行下列指令,在/dist目录下生成一个skeleton.json文件:


webpack --config ./webpack.skeleton.conf.js

骨架屏的几种方案 这个文件在记载了骨架屏的内容和样式,会提供给vue-server-renderer使用。

接下来,在skeleton文件夹下新建一个skeleton.js,该文件即将被用于往index.html内插入骨架屏。

/*
 * @Descripttion:
 * @Author: lzx
 * @Date: 2019-04-11 11:01:03
 * @LastEditors  : lzx
 * @LastEditTime : 2019-12-27 12:58:55
 */
const fs = require('fs')
const htmlMinifier = require('html-minifier')
const {
  resolve
} = require('path')

const createBundleRenderer = require('vue-server-renderer').createBundleRenderer

// 读取`skeleton.json`,以`index.template.html`为模板写入内容
const renderer = createBundleRenderer(resolve(__dirname, './dist/skeleton.json'), {
  template: fs.readFileSync(resolve(__dirname, '../public/index.template.html'), 'utf-8')
})
// 把上一步模板完成的内容写入(替换)`index.html`
renderer.renderToString({}, (err, html) => {
  console.log(err)
  html = html.replace(/&%##/g, '<%= BASE_URL %>')
  html = htmlMinifier.minify(html, { // 将html内容压缩
    collapseWhitespace: true,
    minifyCSS: true
  })
  fs.writeFileSync('./public/index.html', html, 'utf-8')
})
注:为了区分 工程文件的index.html文件和骨架屏index.html文件(index.template.html)
作为骨架屏模板的index.template.html文件,需要在被写入内容的位置添加<!--vue-ssr-outlet-->占位符,
<div id="app">
	<!-- 作为模板的html文件,需要在被写入内容的位置添加<vue-ssr-outlet>占位符, -->
	<!--vue-ssr-outlet-->
</div>

最后执行

执行命令:node skeleton.js  就可完成骨架屏的注入。

`或在package.json文件中添加

"script": {

"skeleton": "webpack --config ./skeleton/webpack.skeleton.conf.js && node ./skeleton/skeleton.js",

}

执行命令 npm run skeleton 即可

最终效果预览

骨架屏的几种方案

注意事项

skeleton.vue骨架文件根据不同的项目要求,可自定义展示dom结构及布局

案例二

基于taro 小程序的骨架方案

app.js入口文件引入css

import 'taro-skeleton/dist/index.css'; // 引入骨架屏组件样式

首页面引入

import Skeleton from 'taro-skeleton';

使用

  const [isLoading3, setIsLoading3] = useState(false)  // 是否显示 taro-skeleton
  
    const getHotGoods = () => {
    const param = {
      url: `v1/ext/item/getPopularItem`,
      method: 'get',
      data: Taro.getStorageSync('userInfo').salesmanTob === true ? {
        userId: Taro.getStorageSync('customerInfo').userId,
        isGroupBuy: Taro.getStorageSync('isGroupBuy') || false,
      } : {}
    }
    Request(param).then(res => {
      if (res.resultCode === '0') {
        if (res.data && res.data.length > 0) {
          setIsLoading3(false)
        }
      }
    })
  }
  
     <View className='classGoods'>
          <GoodsTop topParam={{ title: '爆款推荐' }} getMore={() => getGoodsMore('stat')} />
          <Skeleton loading={isLoading3} avatar title row={3} avatarShape='square' avatarSize='96px' action >
            <Goods data={hotGoodsList} ></Goods>
          </Skeleton>
        </View>

npm: www.npmjs.com/package/tar…

git: github.com/lentoo/taro…

转载自:https://juejin.cn/post/7248509579125424189
评论
请登录