基于web项目加入独立h5应用配置与搭建

1,491 阅读5分钟

前言

最近项目中遇到需要在原来已有的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
      }
    }
}