前言
最近项目中遇到需要在原来已有的web项目的基础上,引入h5应用,都属于一个项目组件,但是h5和web端相互不能影响。下面对引入h5应用的相关配置和搭建做了一下整理。
整体结构
我们将web端的项目整理放到platform,包括api,store,router等,然后新建cemsMobilePerson文件,里面也是一个完整的项目结构,包括api,router,store等。
webpack配置
这里主要列出主要配置
1、配置多页面显示
const pages = {
index: {
entry: 'src/platform/main.js',
template: 'src/platform/index.html',
filename: 'index.html',
chunks: ['chunk-vendors', 'chunk-common', 'index']
},
cemsMobilePerson: {
entry: 'src/cemsMobilePerson/main.js',
template: 'src/cemsMobilePerson/cemsMobilePerson.html',
filename: 'cemsMobilePerson.html',
chunks: ['chunk-vendors', 'chunk-common', 'cemsMobilePerson']
}
}
我们将web端和h5配置为多页面显示,项目启动,默认显示web端界面,添加对应路由地址后,就可访问h5界面。
2、配置决定路径
configureWebpack: {
resolve: {
// 使用path将相对路径转化为绝对路径
alias: {
'@': path.resolve('./', 'src/platform'),
'@cemsMobilePerson': path.resolve('./', 'src/cemsMobilePerson')
}
}
}
这里将web端和h5分别配置多路径,方便项目引用
3、配置h5默认首页
在devServer里面有个historyApiFallback的属性,是用于如果找不到界面就返回默认首页,我们这里配置h5的默认首页。
devServer: {
historyApiFallback: {
rewrites: [
{ from: /^\/cems\/personMenu/, to: '/cemsMobilePerson.html' }]
}
},
4、vue-cli的浏览器兼容babel的配置
有时我们引用组件会用到es6语法,需要配置一下babel,将ES6转ES5,这里增加一个babel.config.js
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}
下面附上我项目中vue.config.js的完整配置
const path = require("path");
const pages = {
index: {
entry: 'src/platform/main.js',
template: 'src/platform/index.html',
filename: 'index.html',
chunks: ['chunk-vendors', 'chunk-common', 'index']
},
cemsMobilePerson: {
entry: 'src/cemsMobilePerson/main.js',
template: 'src/cemsMobilePerson/cemsMobilePerson.html',
filename: 'cemsMobilePerson.html',
chunks: ['chunk-vendors', 'chunk-common', 'cemsMobilePerson']
}
}
module.exports = {
pages,
chainWebpack: (webpackConfig) => {
// vuepress演示文档引入src的alias
webpackConfig.resolve.alias.set(
"vessel",
path.resolve(__dirname, "./src/.vessel")
);
},
devServer: {
proxy: {
'/cems/ui/': {
target: 'http://emptycup.hikvision.com.cn:61744', // 测试环境
changeOrigin: true
}
},
historyApiFallback: {
rewrites: [
{ from: /^\/cems\/personMenu/, to: '/cemsMobilePerson.html' }]
}
},
configureWebpack: {
resolve: {
// 使用path将相对路径转化为绝对路径
alias: {
'@': path.resolve('./', 'src/platform'),
'@cemsMobilePerson': path.resolve('./', 'src/cemsMobilePerson')
}
}
}
};
h5项目搭建
引入h5需要的依赖
这里我们主要是基于vant进行h5界面开发.
# Vue 2 项目,安装 Vant 2:
npm i vant -S
# Vue 3 项目,安装 Vant 3:
npm i vant@next -S
vue-router,vuex,axios我们在web应用中已经下载过,就不需要重新引入了。
创建h5基本项目结构
这里大部分和web端使用一样,不多做说明
在router下的index.js下配置我们的h5路由,这里的路由配置需要和webpack配置中的保持一致
import Vue from 'vue'
import Router from 'vue-router'
// basePath 可自定义
const basePath = '/cems/personMenu'
const routerConfig = [{
path: basePath,
component: () => import('@cemsMobilePerson/App.vue'),
children: [{
path: 'cemsMenu',
component: () => import('@cemsMobilePerson/pages/cems/cemsMenu.vue')
},
{
path: 'config',
component: () => import('@cemsMobilePerson/components/Config.vue')
}]
}]
const routes = createRouter(routerConfig)
const router = new Router({
mode: 'history',
routes
})
Vue.use(Router)
function createRouter (obj) {
return obj.map(({ path, redirect, name, component, children }) => ({
path,
redirect,
name,
component,
children: children ? createRouter(children) : null
}))
}
export default router
在main.js中引入我们需要的插件和样式
import Vue from 'vue'
import router from './router'
import store from './store'
import Vant from 'vant'
import 'vant/lib/icon/local.css'
import '@cemsMobilePerson/assets/css/vant.css'
// reset样式
import '@cemsMobilePerson/assets/css/reset.css'
// 符合视觉标准,覆盖vant的默认样式
import '@cemsMobilePerson/assets/css/main.less'
// viewport-units的buggyfill
import viewportUnitsBuggyfill from 'viewport-units-buggyfill'
import viewportUnitsBuggyfillHacks from 'viewport-units-buggyfill/viewport-units-buggyfill.hacks'
import { ContactCard } from 'vant';
Vue.use(ContactCard);
Vue.config.productionTip = false
Vue.use(Vant)
main()
async function main () {
await preload()
new Vue({
router,
store,
render () {
return <router-view />
}
}).$mount('#app')
}
async function preload () {
// 初始化viewportUnitsBuggyfill
viewportUnitsBuggyfill.init({
hacks: viewportUnitsBuggyfillHacks
})
}
这里我们引入viewport-units-buggyfill 进行vw的兼容处理
npm install viewport-units-buggyfill
// viewport-units的buggyfill
import viewportUnitsBuggyfill from 'viewport-units-buggyfill'
import viewportUnitsBuggyfillHacks from 'viewport-units-buggyfill/viewport-units-buggyfill.hacks'
async function preload () {
// 初始化viewportUnitsBuggyfill
viewportUnitsBuggyfill.init({
hacks: viewportUnitsBuggyfillHacks
})
}
移动端postcss适配问题、根目录创建.postcssrc.js 的配置文件,配置如下
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
plugins: {
'postcss-write-svg':{
utf8: false
},
'postcss-px-to-viewport':{
viewportWidth: 375, // (Number) The width of the viewport.
viewportHeight: 1334, // (Number) The height of the viewport.
unitPrecision: 3, // (Number) The decimal numbers to allow the REM units to grow to.
viewportUnit: 'vw', // (String) Expected units.
selectorBlackList: ['.ignore', '.hairlines','.van-icon'], // (Array) The selectors to ignore and leave as px.
minPixelValue: 1, // (Number) Set the minimum pixel value to replace.
mediaQuery: false, // (Boolean) Allow px to be converted in media queries.
exclude: []
}
}
}
接下来,可以开始我们的h5应用开发了。
项目开发中遇到的坑
1、TypeError: this.getOptions is not a function
原因: less-loader安装的版本过高 解决方案:
1.npm uninstall less-loader
2.npm install less-loader@5.0.0
在上传人脸时,IOS13.4以上,14.1以下某些版本对照片进行了自行回正处理,无需代码中回正的问题
在拍摄的照片,会附加上 EXIF 信息,这样会导致图片在 canvas 渲染或者图片上传后会根据相机拍摄角度产生旋转,不会回正。这时需要我们进项回证处理,IOS13.4以上,14.1以下某些版本对照片进行了自行回正处理,就导致有些版本的手机拍摄的照片出现了方向不对的问题,这里引入判断,安卓直接旋转,ios判断系统有没有自己做处理再旋转。函数如下:
function detectImageAutomaticRotation() {
// IOS13.4以上,14.1以下某些版本对照片进行了自行回正处理,需要判断下是否再旋转
return new Promise((resolve) => {
if (isImageAutomaticRotation === undefined) {
const img = new Image();
img.onload = () => {
// 如果图片变成 1x2,说明浏览器对图片进行了回正
isImageAutomaticRotation = img.width === 1 && img.height === 2;
resolve(isImageAutomaticRotation);
};
img.src = testAutoOrientationImageURL;
} else {
resolve(isImageAutomaticRotation);
}
});
}
async function transformCoordinate (canvas, ctx, width, height, orientation) {
canvas.width = width
canvas.height = height
let u = navigator.userAgent
let isRotateByOperateSystem = false
let isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
if(isIOS) {
isRotateByOperateSystem = await detectImageAutomaticRotation()
}
// 安卓直接旋转,ios判断系统有没有自己做处理再旋转
if (!isIOS || !isRotateByOperateSystem) {
switch (orientation) {
case 2:
// horizontal flip
ctx.translate(width, 0)
ctx.scale(-1, 1)
break
case 3:
// 180 rotate left
ctx.translate(width, height)
ctx.rotate(Math.PI)
break
case 4:
// vertical flip
ctx.translate(0, height)
ctx.scale(1, -1)
break
case 5:
canvas.width = height
canvas.height = width
// vertical flip + 90 rotate right
ctx.rotate(0.5 * Math.PI)
ctx.scale(1, -1)
break
case 6:
canvas.width = height
canvas.height = width
// 90 rotate right
ctx.rotate(0.5 * Math.PI)
ctx.translate(0, -height)
break
case 7:
canvas.width = height
canvas.height = width
// horizontal flip + 90 rotate right
ctx.rotate(0.5 * Math.PI)
ctx.translate(width, -height)
ctx.scale(-1, 1)
break
case 8:
canvas.width = height
canvas.height = width
// 90 rotate left
ctx.rotate(-0.5 * Math.PI)
ctx.translate(-width, 0)
break
default:
break
}
}
}