作为一名有操守的程序员,鄙人,只发干货!
此篇文章作为 h5 前端路上的功能点、工具总结;如有更好的方法,请在评论区留下你的身影
连做三个 h5 项目,或多或少积累一点开发的经验,此篇文章一是记录,二为分享。
开整!
基础环境
三次项目皆使用 vue 框架开发,使用 vue-cli3.x 生成项目主体已是相当熟稔,vue-cli 传送门。
使用 axios 请求库,也可自行选择顺手的使用。
微信 sdk 的再次封装
微信官方提供一个 jsbridge 脚本,能够很方便的使用设备的一些功能及微信本身提供的功能。
我们需要做的是在项目中更好的调用它。
在
public/index.html
模版 html 里的 head 中加入 wxsdk(传送门),或直接复制此条http://res.wx.qq.com/open/js/jweixin-1.4.0.js
(微信目前最新版本 js)在
src
目录下,我们创建一个utils
文件夹内创建wxsdk.js
,在其中加上
import axios from "axios";
const wxsdk = {
init(url) {
// 顾名思义,用于初始化wxconfig的方法
return new Promise(resolve => {
axios
.get("请求微信config的后端接口", { params: { url } })
.then(({ appId, timestamp, nonceStr, signature }) => {
wx.config({
debug: false, // 后面可以用于调试config是否配置成功
appId,
timestamp,
nonceStr,
signature,
jsApiList: [
"chooseImage",
"uploadImage",
"getLocalImgData",
"updateAppMessageShareData",
"updateTimelineShareData"
] // 在此注册你需要使用的api
});
resolve(); // 配置完成的promise回调
});
});
}
};
export default wxsdk;
- 下面把初始化方法放入全局的路由守卫中,使用户在每个页面打开时都注册一次。
// router.js中
import Vue from 'vue'
import Router from 'vue-router'
import wxsdk from '../utils/wxsdk'
Vue.use(Router)
const router = new Router({
// 没看错,使用的是hash,
// 使用hash是因为方便后端直接使用链接进行其他的带参操作
// 同时,也避免了在nginx中部署时对前端路由的操控
mode: 'hash',
routes: [...]
})
router.afterEach((to, from, next) => {
// 此处较为关键,由于我们使用的是单页应用,内部路由皆为虚拟路由,
// 并且是hash模式,需要获取 # 前的路由作为请求路径
wxsdk.init(location.href.split('#')[0])
})
export default router
- 加入一个封装上传本地图片方法作为例子
chooseImage (count) {
return new Promise(resolve => {
wx.ready(() => {
wx.chooseImage({
count, // 用于控制上传图片的数量
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: ({ localIds }) => {
resolve(localIds)
}
})
})
})
},
- 若项目中有需要判断微信的 js 是否加载完毕,可以使用下面方法
// wxsdk.js中
const wxsdk = {
...,
check (cb) {
if (typeof WeixinJSBridge === 'object' && typeof WeixinJSBridge.invoke === 'function') {
cb()
} else {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', cb, false)
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', cb)
document.attachEvent('onWeixinJSBridgeReady', cb)
}
}
},
}
- 可以在页面的
mounted
周期中引入
<script>
export default {
mounted() {
this.$wxsdk.check(() => {
console.log("微信js加载完毕,进入页面");
});
}
};
</script>
- 为了方便我们对 wxsdk 的使用,可以在
main.js
中将此对象挂载到 vue.prototype 上
import wxsdk from "./utils/wxsdk";
Vue.prototype.$wxsdk = wxsdk;
微信 jssdk 的封装使用基本完成。
页面适配
我使用的是 rem 单位布局,根据设配的屏幕宽度与设计稿的宽度比计算出根元素的像素大小,从而使一张设计图适配不同的手机屏幕
// utils/util.js中
const deviceRem = size => {
const docEl = document.documentElement;
const clientWidth = docEl.clientWidth;
const reSize = () =>
(docEl.style.fontSize =
clientWidth >= size ? "100px" : 100 * (clientWidth / size) + "px");
document.addEventListener("DOMContentLoaded", reSize, false);
};
deviceRem(1125);
第三方字体包
根据 h5 的不同业务特点,可能需要引入各种个样的字体包,比如我的第一个 h5 就使用了站酷快乐体xxx
的字体包
@font-face {
font-family: "happyFonts";
src: url("../fonts/站酷快乐体2016修订版.ttf");
font-weight: normal;
font-style: normal;
}
body {
font-family: "happyFonts";
}
引入后,打包到线上环境测试发现,即使字体包用font-spider
筛选一遍,可真机上打开或多或少会出现字体加载过慢,导致文字区域空白一段时间再出现,或者先出现系统默认字体,之后再突然出现字体包字体。
这种体验…很难受啊!
所以我们急需一个能够监听第三方字体包是否加载完成的方法。
抓耳挠腮,捶胸顿足之下,终于找到一个神器 Font Face Observer!!!
如同描述的一般,它提供两个回调函数,字体加载成功或失败,页面可以在其回调内决定是否出现。
import Font from "fontfaceobserver";
export default {
mounted() {
const font = new Font("happyFonts");
font.load().then(
() => {
console.log("字体加载完成");
this.$router.replace({ name: "start" });
},
() => {
console.log("字体加载失败");
this.$router.replace({ name: "start" });
}
);
}
};
BGM!
大部分活动 h5 少不了会用上 bgm,建议 bgm 使用某一段或几段可以循环播放的音频,越短越好(占用加载时间和服务器资源)。
“音频文件太长了,我只需要其中一小段怎么办”,我给出的方法是:找一个会剪音乐的漂亮妹子,嘿嘿嘿(咽口水)
。
或者点击此处
好了,由于 h5 中的 bgm 占用了和字体包差不多的大小,我们也需要一个能够判断音频文件加载完成的事件。
下面是 audio 相关的事件
<body>
<audio id="music" src="" loop="loop"></audio>
<script>
/**
* @func play 播放 可判断音频是否正在播放
* @func pause 暂停 可判断音频是否暂停
* @funcs 一下事件是音频在加载过程中依次触发的
* loadstart => durationchange => loadeddata => progress => canplay => canplaythrough
*/
const audio = document.getElementById("music");
audio.oncanplay = () => {
console.log("音乐可以播放,做你想做的吧");
};
</script>
</body>
正在我志得意满打开测试链接时,发现无论如何点击播放都没有音乐声音!😱
着急忙慌打开微信开发者工具调试后发现,我的音乐地址找不到资源,这是为何?分明就放在src/assets/music
中啊,找不到资源…难不成!
心头一点灵光乍现,当即打开vue-cli,急急望去
{
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: ['xlink:href', 'href'],
use: ['xlink:href', 'href']
}
果然不出所料,vue-cli 的 loader 配置默认没有 audio 的配置!至此,情绪再次恢复平静,打开vue.config.js
,缓缓书写几行代码
module.exports = {
assetsDir: "static",
chainWebpack: config => {
config.module
.rule("vue")
.use("vue-loader")
.loader("vue-loader")
.tap(options => {
options.transformAssetUrls = {
audio: "src"
};
return options;
});
}
};
重新打包,点开dist
文件夹,static/media/xxx.mp3
已安稳的进入其中,吾心甚慰。
html2canvas
h5 的最后推广,同样少不了的一个功能就是,生成图片。
对于生成图片,这里有两个模块可供使用html2canvas
和dom-to-image
,两个模块的使用方法都很简单,这里使用的是 html2canvas.
随之而来的就是老生常谈的两个问题,一是图片跨域问题,二是生成图片清晰度优化。
图片跨域
启用 html2canvas 中的
{ useCORS: true }
配置,即可渲染 cdn 的图片和本地图片。若有其他来源图片(比如微信用户头像),要么走接口转化下图片链接,要么将图片转成 base64 格式再行渲染即可。
清晰度优化
- 将选区 dom 的布局单位改为 px 像素
- 使用
{ scale: 2 }
将 canvas 的宽高扩大为原本 dom 的两倍,或设置为 devicePixelRatio 倍,最后将生成图片的大小设置为原先 dom 大小 - 可以使用 css 绘制的样式,尽量不要使用图片代替
<script> import html2canvas from "html2canvas"; export default { // 获取可视区域的宽高 getClientSize() { const app = this.$refs.app; return [app.clientWidth, app.clientHeight]; }, // 生成图片 createPhoto() { const content = document.getElementById("container"); const canvas = document.createElement("canvas"); const [width, height] = this.getClientSize(); const scale = 2; canvas.width = width; canvas.height = height; canvas.getContext("2d").scale(scale, scale); const opts = { logging: true, // 打印生成图片的日志 canvas: canvas, // 导入生成的canvas width: width, height: height, scale: scale, useCORS: true, // 允许图片跨域 timeout: 1000 // 设置图片超时时间 }; html2canvas(content, opts).then(c => { this.photo = c.toDataURL("image/png", 1); }); } }; </script>
完美~
图片压缩
图片压缩有我们熟知的熊猫压缩,这个在线压缩网站可以极大的减少图片的大小同时不会出现失真,但对于工程师来说,拖入本地图片压缩的做法未免略显蠢笨。
于是,有了这么一个图片压缩的工具imageMin
,及其 webpack 的扩展imagemin-webpack-plugin。
const ImageminPlugin = require('imagemin-webpack-plugin').default
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV) // 判断当前环境
module.exports = {
configureWebpack: {
plugins: {
new ImageminPlugin({
disable: !IS_PROD,
pngquant: {
quality: '80-100', // 控制图片质量区间
progressive: true // 图片加载时的显示方式是否是渐变出现
}
})
}
}
}
亲测实用,image
文件夹原本 5mb(已被 tinypng 压缩),打包后只有 3.5mb。