在我们平时使用vue开发手机端时,我们需要三大部分作为前端需要考虑的问题,我们第一部分要考虑移动端布局适配以及字体大小适配之前总结过一次移动端适配问题,第二部分咋样写法上优化自己的组件,指令,混入,尽量使用vue提供的特性去封装自己业务逻辑vue优雅使用技巧(一),第三部分,手机端交互行为体验,是让用户有直观感受,接下来主要总结第三部分
移动端体验比pc端更主要
1. 添加页面切换时,有一个加载进度条,通过vue-router 导航守卫,来控制跳转前和跳转后加载进度条的样式,代码如下
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
NProgress.configure({ showSpinner: false });
router.beforeEach((to, from, next) => {
NProgress.start()
next()
})
router.afterEach(() => {
NProgress.done()
})
注意
-
导航守卫可以处理 页面的权限问题(判断是否登录过),一般针对后台管理系统。
import router from './router' import store from './store' import { Message } from 'element-ui' import NProgress from 'nprogress' // progress bar import 'nprogress/nprogress.css' // progress bar style import { getToken } from '@/utils/auth' // get token from cookie import getPageTitle from '@/utils/get-page-title' NProgress.configure({ showSpinner: false }) // NProgress Configuration const whiteList = ['/login', "/regist", "/forget"] // no redirect whitelist router.beforeEach(async(to, from, next) => { // start progress bar NProgress.start() // set page title document.title = getPageTitle(to.meta.title) // determine whether the user has logged in const hasName = store.getters.name; if (hasName) { next() NProgress.done() } else { if (whiteList.indexOf(to.path) !== -1) { next() } else { Message.error("由于您没有登录,请到登录页登录") setTimeout(() => { next(`/login?redirect=${to.path}`) NProgress.done() }, 1000) } } }) router.afterEach(() => { // finish progress bar NProgress.done() })
-
手机端一把判断(是否登陆过),通过指令的封装,大部分按钮触发的。
敬请期待 -
还有是直接在 请求接口中拦截(axios),适用 手机端项目和PC端后台系统。
2. 骨架屏(可以自己写/也可以用UI组件库)
什么是骨架屏
什么是骨架屏呢?骨架屏(Skeleton Screen)是指在页面数据加载完成前,先给用户展示出页面的大致结构(灰色占位图),在拿到接口数据后渲染出实际页面内容然后替换掉。Skeleton Screen 是近两年开始流行的加载控件,本质上是界面加载过程中的过渡效果。
假如能在加载前把网页的大概轮廓预先显示,接着再逐渐加载真正内容,这样既降低了用户的焦灼情绪,又能使界面加载过程变得自然通畅,不会造成网页长时间白屏或者闪烁。这就是 Skeleton Screen !
Skeleton Screen 能给人一种页面内容“已经渲染出一部分”的感觉,相较于传统的 loading 效果,在一定程度上可提升用户体验。
我们通过vant 自带骨架屏的 组件举例。
- 引入第三方的组件,尽量按需导入,需要什么引入什么组件。这样优化代码的打包后的体积
import Vue from "vue";
import "vant/lib/index.css"
import { Tabbar, TabbarItem,Search,Tab, Tabs,Image as VanImage,Tag,Col, Row,List,Cell,PullRefresh,Skeleton,NavBar,Popup,Button } from "vant"
let components = [
Tabbar,TabbarItem,Search,Tab, Tabs,VanImage,Tag,Col, Row,List,Cell,PullRefresh,Skeleton,NavBar,Popup,Button
]
components.map(component => {
component.install = Vue => {
Vue.component(component.name, component);
}
})
components.map(component => {
Vue.use(component);
})
- 组件使用
<van-skeleton title :row="4" :loading="loading">
<div>实际内容</div>
<p>
发射点发射点
法撒旦发射点发生
发射点发射点发射点发射点
啊发射点发射点发撒打发微软微软为
</p>
</van-skeleton>
具体组件的参数到官网查看 vant骨架屏
- 效果
注意:
- 虽然解决用用户进入当前页面的等待时间的体验感,但是改骨架屏的样式和真实的dom结构没有关联,其实还是不够人性化体验。
- 骨架屏不是什么场景都要使用,我们不能滥用骨架屏,大部分使用场景,首页模块展示,详情模块展示,因为列表模块展示,因为可以通过展位图(懒加载图片解决)
我们可以自己写骨架屏样式
优势: 因为当前的页面的dom结构也是我们写的。
html
<!-- 真实的渲染结果 -->
<div class="card">
<div class="image">
<img src="https://assets.codepen.io/2002878/wake-up-and-code.jpg" alt="">
</div>
<div class="content">
<h4>CodingStartup</h4>
<div class="description">
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Ex quasi enim facere commodi omnis...
</div>
</div>
</div>
<!-- 渲染之前的dom -->
<div class="card loading">
<div class="image">
</div>
<div class="content">
<h4></h4>
<div class="description">
</div>
</div>
</div>
css
.card {
width: 320px;
background-color: #fff;
border-radius: 6px;
overflow: hidden;
box-shadow: 0px 4px 6px rgba(0, 0, 0, .12);
}
.image {
height: 200px;
}
.image img {
display: block;
width: 100%;
height: inherit;
object-fit: cover;
}
.content {
padding: 2rem 1.8rem;
}
h4 {
margin: 0 0 1rem;
font-size: 1.5rem;
line-height: 1.5rem;
}
.description {
font-size: 1rem;
line-height: 1.4rem;
}
/* 定义骨架屏的样式 */
.loading .image,
.loading h4,
.loading .description {
background: linear-gradient(100deg,
rgba(255,255,255, 0) 40%,
rgba(255, 255, 255, 0.5) 50%,
rgba(255,255,255, 0) 60%
) #ededed;
background-size: 200% 200%;
background-position-x: 180%;
animation:loading 1s ease-in-out infinite;
}
/* 添加动画 */
@keyframes loading {
0% {
background-position-x: 180%;
}
100% {
background-position-x: -20%;
}
}
/* 文本信息和图片动画时间延迟 */
.loading h4 {
min-height: 1.6rem;
border-radius: 4px;
animation-delay: .1s;
}
.loading .description {
min-height: 4rem;
border-radius: 4px;
animation-delay: .1s;
}
效果:
g2.png
g1.jpg
生成骨架屏方案
- vue-skeleton-webpack-plugin插件 https://blog.csdn.net/qq_41725450/article/details/104925718
- 编写骨架屏的组件
在这个页面里面我们可以根据需要来编写代码,最好使用样式或者base64的图片,以减少初始的请求。
<!-- detailSkeleton.vue -->
<template>
<div class="container">
详情骨架屏
<img
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTA4MCAyNjEiPjxkZWZzPjxwYXRoIGlkPSJiIiBkPSJNMCAwaDEwODB2MjYwSDB6Ii8+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjIwMCUiIGhlaWdodD0iMjAwJSIgeD0iLTUwJSIgeT0iLTUwJSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48ZmVPZmZzZXQgZHk9Ii0xIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIi8+PGZlQ29sb3JNYXRyaXggaW49InNoYWRvd09mZnNldE91dGVyMSIgdmFsdWVzPSIwIDAgMCAwIDAuOTMzMzMzMzMzIDAgMCAwIDAgMC45MzMzMzMzMzMgMCAwIDAgMCAwLjkzMzMzMzMzMyAwIDAgMCAxIDAiLz48L2ZpbHRlcj48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDEpIj48dXNlIGZpbGw9IiMwMDAiIGZpbHRlcj0idXJsKCNhKSIgeGxpbms6aHJlZj0iI2IiLz48dXNlIGZpbGw9IiNGRkYiIHhsaW5rOmhyZWY9IiNiIi8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCA0NGg1MzN2NDZIMjMweiIvPjxyZWN0IHdpZHRoPSIxNzIiIGhlaWdodD0iMTcyIiB4PSIzMCIgeT0iNDQiIGZpbGw9IiNGNkY2RjYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCAxMThoMzY5djMwSDIzMHpNMjMwIDE4MmgzMjN2MzBIMjMwek04MTIgMTE1aDIzOHYzOUg4MTJ6TTgwOCAxODRoMjQydjMwSDgwOHpNOTE3IDQ4aDEzM3YzN0g5MTd6Ii8+PC9nPjwvc3ZnPg=="
/>
</div>
</template>
<script>
export default {
components: {},
data () {
return {}
},
mounted () {},
methods: {}
}
</script>
<style scoped lang="scss">
</style>
- 编写Skeleton.js文件,与main.js同一级
import Vue from "vue";
import listSkeleton from "./skeleton/listSkeleton.vue";
import detailSkeleton from "./skeleton/detailSkeleton.vue";
export default new Vue({
components: {
listSkeleton,
detailSkeleton
},
template:`
<div>
<listSkeleton id="listSkeleton" style="display:none;" />
<detailSkeleton id="detailSkeleton" style="display:none;" />
</div>
`
})
- 基于vue-cli@3.0配置文件
const PrerenderSPAPlugin = require('prerender-spa-plugin');
const SkeletonWebpackPlugin = require('vue-skeleton-webpack-plugin')
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer;
const path = require('path');
// vue.config.js
module.exports = {
configureWebpack: config => {
// if (process.env.NODE_ENV !== 'production') return;
return {
plugins: [
// new PrerenderSPAPlugin({
// staticDir: path.join(__dirname, 'dist'),
// routes: ['/', '/Second', '/Detail'],
// renderer: new Renderer({
// inject: {
// foo: "bar"
// },
// headless: false,
// renderAfterDocumentEvent: 'render-event'
// })
// }),
// 骨架屏
new SkeletonWebpackPlugin({
webpackConfig: {
entry: {
app: path.join(__dirname, './src/Skeleton.js')
}
},
minimize: true,
quiet: true,
router: {
mode: 'hash',
routes: [
{ path: /^\/home/, skeletonId: 'listSkeleton' },
{ path: /^\/Detail/, skeletonId: 'detailSkeleton' }
]
}
})
]
}
},
css: {
// 是否使用css分离插件 ExtractTextPlugin
extract: true,
// 开启 CSS source maps?
sourceMap: false,
// 启用 CSS modules for all css / pre-processor files.
modules: false
}
}
- 自动生成指定页面的骨架屏 饿了么团队设计的page-skeleton-webpack-plugin
该方案通过一个 webpack 插件 page-skeleton-webpack-plugin 的方式与项目开发无缝集成,属于在自动生成骨架屏方面做的非常强大的了,并且可以启动 UI 界面专门调整骨架屏,但是在面对复杂的页面也会有不尽如人意的地方,而且生成的骨架屏节点是基于页面本身的结构和 CSS,存在嵌套比较深的情况,体积不会太小,并且只支持 history 模式。
// webpack.conf.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { SkeletonPlugin } = require('page-skeleton-webpack-plugin')
const path = require('path')
plugins: [
//...
new HtmlWebpackPlugin({
// Your HtmlWebpackPlugin config
}),
new SkeletonPlugin({
pathname: path.resolve(__dirname, `${customPath}`), // 用来存储 shell 文件的地址
staticDir: path.resolve(__dirname, './dist'), // 最好和 `output.path` 相同
routes: ['/', '/search'], // 将需要生成骨架屏的路由添加到数组中
})
]
- 自动生成骨架屏 draw-page-structure 强烈推荐使用插件
-
全局安装,npm i draw-page-structure – g
-
dps init 生成配置文件 dps.config.js
-
修改 dps.config.js 进行相关配置
-
dps start 开始生成骨架屏
生成dps.config.js
const dpsConfig = {
url: 'http://192.168.1.96:8080/#/home', // 待生成骨架屏页面的地址,用百度(https://baidu.com)试试也可以
output: {
filepath: '', // 生成骨架屏的存放页面,一般为项目的入口页面
injectSelector: '#app' // 生成的骨架屏插入页面的节点
},
// header: {
// height: 40,
// background: '#1b9af4'
// },
// background: '#eee',
// animation: 'opacity 1s linear infinite;',
// includeElement: function(node, draw) {
// 定制某个节点画出来的样子,带上return false
// if(node.id == 'ui-alert') {
// 跳过该节点及其子节点
// return false;
// }
// if(node.tagName.toLowerCase() === 'img') {
// 对该图片生成宽100%,高8%,颜色为红色的色块
// draw({
// width: 100,
// height: 8,
// left: 0,
// top: 0,
// zIndex: 99999999,
// background: 'red'
// });
// return false;
// }
// },
// writePageStructure: function(html) {
// 自己处理生成的骨架屏
// fs.writeFileSync(filepath, html);
// console.log(html)
// },
init: function() {
// 生成骨架屏之前的操作,比如删除干扰节点
}
}
module.exports = dpsConfig;
生成骨架屏的效果
draw.png
3. 过渡动画
尽情期待
4. 懒加载/防抖/节流 常用的实现
尽情期待
网友评论