小程序跳转H5实现长截图功能;vue-cli3通过rem适配

20 篇文章 1 订阅
10 篇文章 0 订阅

一、需求:
  微信小程序部分页面需要一键长图的功能。
  通过html2canvas插件可以实现,具体可参考微信小程序实现一键长图并保存图片到相册。由于该插件只能在H5项目中使用,则需要截图的小程序页面点击后跳转到H5页面,把所需的token及接口参数带过去,在H5页面进行长截图并保存或转发,点击左上角返回即回到小程序。
在这里插入图片描述

二、思路:
  首先,考虑需要长截图的页面根据需求可能会迭代增加,新建了一个vue项目,基于vue-cli脚手架创建了一个项目;
  其次,H5页面最重要的就是适配,决定使用rem,根据相关资料发现使用postcss-pxtorem和lib-flexible可以实现vue项目自动将px转成rem ,进行相关下载,配置,在写具体页面时可直接使用px。
  最后,打包部署到服务上,微信小程序通过web-view加载该H5页面,在H5页面中引入html2canvas插件实现页面长截图。

 1. 搭建项目,通过rem适配机型
 2. 页面请求、路由跳转、插件引入、rem配置、跨域问题
 3. 截图样式,滚动、提示
 4. 小程序跳转H5传参,H5接收参数
 5. 动态表头匹配问题、截图时新结构的添加
 6. 取消截图,通过点击触发实现
 7. 项目打包部署,替换小程序中跳转的服务地址
 8. git创建仓库,上传代码

三、代码:
H5项目
首先安装这两个包
npm install amfe-flexible --save
npm install postcss-pxtorem --save-dev
在这里插入图片描述
app.vue

<template>
  <div id="app">
    <router-view></router-view>    
  </div>
</template>

<script>
export default {
  name: "app",
  data () {
    return {
      // token: ''
    }
  },
  created() {
    let params = window.location.href.split("?")[1]
    let token  = params.substring(params.lastIndexOf("=")+1)
    sessionStorage.setItem('token', token)
  }
};
</script>

<style>
*{
  margin: 0;
  padding: 0;
}
html,body{
  width: 100%;
  height: 100%;
}
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  width: 100%;
  height: 100%;
  overflow-x:hidden;
}
</style>

main,js

import Vue from "vue";
import App from "./App.vue";
import {
  Toast,
} from "vant";
import "amfe-flexible/index.js";
import router from "./router";
import axios from 'axios'
import html2canvas from 'html2canvas'
Vue.prototype.$html2canvas = html2canvas

Vue.prototype.$http = axios
// import Echarts from 'echarts'
// Vue.prototype.echarts = Echarts
// Vue.use(Echarts)

Vue.use(Toast);

Vue.config.productionTip = false;

new Vue({
  router,
  render: h => h(App)
}).$mount("#app");

untils/request.js

import axios from 'axios'

const service = axios.create({
  // 请求基础路径
  // baseURL: process.env.VUE_APP_BASE_API,
  baseURL: '/jupiter/i',
  timeout: 5000 // 请求超时时间5秒
})

// 请求拦截器 给请求头设置token
service.interceptors.request.use(
  request => {
    // 携带token
    let token = sessionStorage.getItem('token')
    if (token) {
      request.headers['authorization'] = token
    }
    if (request.method === 'post') {
      request.params = {}
    }
    // eslint-disable-next-line no-unused-expressions
    error => {
      return Promise.reject(error)
    }
    return request
  }
)

// response interceptor
service.interceptors.response.use(
  response => {
    const res = response.data
    console.log('axios的res', res)
    const {status} = res
    switch (status) {
      // token失效!
      case 710:
        console.log('token失效,请返回小程序')
        break
      case 0:
        console.log('请返回小程序')
        break
      // 统一处理200和错误message
      case 200:
        return res.data
      default:
        console.log
    }
  },
  // 作用 catch的处理
  error => {
    const { response } = error
    if (response && response.status === 404) {
      return Promise.reject(error)
    }
    if (response && response.status === 500) {
      return Promise.reject(error)
    }
    if (response && response.status === 405) {
      return Promise.reject(error)
    }
    return Promise.reject(error)
  }
)

export default service

api/test.js

import service from '../untils/request.js';

// 列表
export function boxDataList (data) {
    return service({
        url: '/box/day/boxData/list',
        method: 'get',
        params: data
    })
}
// 大盘数据
export function boxDataMax (data) {
    return service({
        url: '/box/day/boxData/max',
        method: 'get',
        params: data
    })
}

vue.config.js

module.exports = {
  publicPath: '/',
  css: {
    loaderOptions: {
      postcss: {
        plugins: [
          require("postcss-pxtorem")({
            // 把px单位换算成rem单位
            rootValue: 75, // vant官方使用的是75
            selectorBlackList: ["vant", "mu"], // 忽略转换正则匹配项
            propList: ["*"]
          })
        ]
      }
    }
  },
  devServer: {
    open: true, //是否自动弹出浏览器页面
    host: "0.0.0.0",
    port: "8081",
    https: false,
    hotOnly: false,
    proxy: {
      "/jupiter/i": {
        target: "https://testdata.aaa.com",
        ws: true, //代理websockets
        changeOrigin: true, // 虚拟的站点需要更管origin
        pathRewrite: {
          //重写路径 比如'/api/aaa/ccc'重写为'/aaa/ccc'
          "^/": ""
        }
      }
    }
  }
};

router.index.js

import Vue from "vue";
import VueRouter from "vue-router";

Vue.use(VueRouter);

const router = new VueRouter({
  mode: 'history',
  base: '/',
  routes: [
    {
      path: '/dayList',
      name: '日榜',
      component: () => import('./../components/dayList.vue')
    },
    // {
    //   path: "/home",
    //   name: "年榜",
    //   component: () => import("./../components/Home.vue")
    // },
  ]
});

export default router;

components/dayList.vue

<template>
  <div class="dayListBox" @click="cancel">
    <div ref="area">
      <div class="logoBox" style="display: none">
        <img src="../assets/1.png" alt="">
      </div>
      <div class="headBox">
        <div class="headTitle">日票房榜<span>{{dateTime}}·{{platformIdName}}</span></div>
        <div class="longPic" @click="generateImage" style="display: block">
          <img src="../assets/long.png" alt="">
          生成长图
        </div>
        <div class="datetime">
          <div class="today">
            <div class="today-num">
              {{boxMaxData.sum}}<span class="wan"></span>
            </div>
            <div class="explain">
              次日更新,数据来自爱奇艺、优酷、腾讯
            </div>
            <div class="dayTop" v-if="platformIdIndex > 0"><span>日最高票房:{{boxMaxData.dayMax ? boxMaxData.dayMax : '--'}}万元</span><span class="platProp">平台占比:{{boxMaxData.pre}}%</span></div>
          </div>
          <div class="daypalt" v-if="platformIdIndex == 0"><span>爱奇艺:{{boxMaxData.aqy ? boxMaxData.aqy : '--'}}万元</span><span>优酷:{{boxMaxData.yk ? boxMaxData.yk : '--'}}万元</span><span>腾讯视频:{{boxMaxData.tx ? boxMaxData.tx : '--'}}万元</span></div>
        </div>
      </div>
      <div class="interval"></div>
      <div class="listBox">
        <div class="boxOfficeList">
          <div class="dailyList">票房排名</div>
        </div>
        <div class="tableList">
          <div class="listHead">
            <div>排名</div>
            <div>影片名</div>
            <div class="dayBoxOff">当日票房()</div>
            <div v-for="(item, index) in selectListed" :key="index">{{item.nameC}}</div>
          </div>
          <div class="li" v-for="(item, index) in list" :key="index">
            <div>
              <div>{{ item.top }}</div>
            </div>
            <div>
              <div class="name">
                <span class="newTip" v-if="item.issueDay<=3">new </span>{{ item.name }}
              </div>
              <div class="file">上映{{item.issueDay ? item.issueDay : '--'}}<img v-if="item.platformId1 == '1'" class="imgStyle1 aqy" src="../assets/aqy.png">
                <img v-if="item.platformId2 == '2'" class="imgStyle1 yk" src="../assets/yk.png">
                <img v-if="item.platformId3 == '3'" class="imgStyle1 tx" src="../assets/tx.png">
              </div>
            </div>
            <div>
              <div class="dayBoxOff">{{ item.income }}</div>
            </div>
            <div>
              {{ showList[index]['async' + 0] ? showList[index]['async' + 0] : '--' }}
            </div>
            <div>
              {{ showList[index]['async' + 1] ? showList[index]['async' + 1] : '--' }}
            </div>
          </div>
        </div>
      </div>

      <div class="codeBox" style="display: none">
        <div class="codeBoxMain">
          <div class="codeBoxLeft">
            <img src="../assets/2.png" alt="">
          </div>
          <div class="codeBoxRight">
            <p>AAAA服务</p>
            <p>互联网视频数据查询平台</p>
          </div>
        </div>
      </div>
    </div>
    <div class="imgBox" style="display: none">
      <div class="imgMain">
        <p style="font-size: 14px; font-weight: bold; color: rgb(88, 88, 88);">长按下图保存或分享</p>
        <div class="canvasImg">
          <img src="" alt="" id="canvasPic">
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { boxDataList, boxDataMax } from '../api/test.js'

export default {
  name:"home",
  data() {
    return {
      params: {},
      href: '',
      isShow: true,
      queryParam: {
        dateStart: '2021-07-27',
        dateStop: '',
        platformId: 0
      },
      platformIdIndex: 0,
      platformIdName: '全网',
      dateTime: '2021-07-27',
      list: [],
      selectList: [],
      selectListed: [],
      showList: [],
      items1: [{
        name: '排名比',
        value: '01',
        checked: false,
        nameC: '排名比',
        backendName: 'topR'
      },
      {
        name: '日环比',
        value: '04',
        checked: true,
        nameC: '日环比',
        backendName: 'incomeR'
      },
      {
        name: '累计票房',
        value: '05',
        checked: true,
        nameC: '累计票房(万)',
        backendName: 'tIncome'
      },
      {
        name: '当日观影人次',
        value: '07',
        checked: false,
        nameC: '当日观影人次(万)',
        backendName: 'ev'
      },
      {
        name: '累计观影人次',
        value: '09',
        checked: false,
        nameC: '累计观影人次(万)',
        backendName: 'tEv'
      },
      {
        name: '制作成本',
        value: '12',
        checked: false,
        nameC: '制作成本(万)',
        backendName: 'cost'
      }
      ],

      maxParam: {
        dateStart: '2021-07-27',
        platformId: 0
      },
      boxMaxData: {
        sum: '',
      },
    }
  },
  created() {
    // dateStart=2021-08-01&platformId=0&selectList=incomeR,tIncome
    this.href = window.location.href.split("?")[1]
    this.parseParams()
    this.getBoxDataList(this.queryParam)
    this.getBoxDataMax(this.maxParam)
  },
  mounted() {
    
  },
  methods: {
    // 小程序中的表头是动态的,将表头对应的name带过来,先将其字符串分割成数组,selectList则是后两列表头
    parseParams () {
      let urls = this.href
      let theRequest = new Object()
      if (urls.indexOf("?") == -1) {
        let strs = urls.split("&") // ['dateStart=2021-08-01', 'platformId=0', 'selectList=incomeR,tIncome']
        for(var i = 0; i < strs.length; i ++) {
          theRequest[strs[i].split("=")[0]]= strs[i].split("=")[1]
        }
        this.params = theRequest
        this.dateTime = this.params.dateStart
        this.platformIdIndex = this.params.platformId
        if (this.platformIdIndex == 0) {
          this.platformIdName = '全网'
        } else if (this.platformIdIndex == 1) {
          this.platformIdName = '爱奇艺'
        } else if (this.platformIdIndex == 2) {
          this.platformIdName = '优酷'
        } else if (this.platformIdIndex == 3) {
          this.platformIdName = '腾讯视频'
        }
        this.queryParam.dateStart = this.params.dateStart
        this.queryParam.platformId = this.params.platformId
        this.maxParam.dateStart = this.params.dateStart
        this.maxParam.platformId = this.params.platformId
        // 将字符串转成数组,通过数组中的每一项和后两列所有可能是表头的数组items1对比,如果相等,则将该项push到新的数组,以便于html结构中进行循环渲染其中文name
        this.selectList = this.params.selectList.split(',') // ["incomeR","tIncome"]
        this.selectList.forEach((selectListItem) => {
            let obj = {}
            this.items1.forEach(listItem => {
              if (listItem.backendName == selectListItem) {
                obj = listItem
              }
            })
          this.selectListed.push(obj) 
        })
        // console.log(theRequest) // {dateStart: "2021-08-01", platformId: "0", selectList: "incomeR,tIncome"}
      }
    },
    generateImage () {
      // 点击截图时,页面要头部添加logo,底部添加二维码及名称
      //  通过在截图前将该结构进行显示,以达到截图效果
      document.getElementsByClassName('logoBox')[0].style.display = "block"
      document.getElementsByClassName('longPic')[0].style.display = "none"
      document.getElementsByClassName('codeBox')[0].style.display = "block"
      const rect = this.$refs.area.getBoundingClientRect() // 关键代码
      // console.log(rect)
      document.body.scrollTop = 0
      document.documentElement.scrollTop = 0
      this.$html2canvas(this.$refs.area, {
        scrollY: rect.top, // 关键代码
        height: rect.height + 50 // 加高度,避免截取不全
      }).then(canvas => {
        canvas.toBlob(() => {
          var imgBoxEle = document.getElementsByClassName('imgBox')[0]
          imgBoxEle.style.display = "block"
          // console.log('blob', blob)
          this.imgUrl = canvas.toDataURL('image/jpeg')
          const aImg = document.getElementById('canvasPic')
          aImg.style = 'width: 100%;height: auto;-webkit-touch-callout: none;margin-left: 20'
          aImg.src = this.imgUrl
        }, 'image/png')
      })
    },
    ellipsis (val) {
      if (val.length > 8) {
        return val.substr(0, 7) + '...'
      }
      return val
    },
    getBoxDataList (queryParam) {
      boxDataList(queryParam).then(data => {
        data.list.forEach(item => {
          if (item.platformId) {
            if (item.platformId.indexOf('1') !== -1) {
              item.platformId1 = '1'
            }
            if (item.platformId.indexOf('2') !== -1) {
              item.platformId2 = '2'
            }
            if (item.platformId.indexOf('3') !== -1) {
              item.platformId3 = '3'
            }
          }
          if (item.incomeR) {
            item.incomeR = item.incomeR + '%'
          }
          item.name = this.ellipsis(item.name)
        })
        this.list = data.list
        this.list.forEach(listItem => {
          const obj = {}
          this.selectList.forEach((selectListItem, i) => {
            obj['async' + i] = listItem[selectListItem]
          })
          this.showList.push(obj)
        })
      })
    },
    getBoxDataMax (maxParam) {
      boxDataMax(maxParam).then(data => {
        this.boxMaxData = data
      })
    },
    cancel () {
      document.getElementsByClassName('logoBox')[0].style.display = "none"
      document.getElementsByClassName('longPic')[0].style.display = "block"
      document.getElementsByClassName('codeBox')[0].style.display = "none"
      document.getElementsByClassName('imgBox')[0].style.display = "none"
    }
  },
}; 
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
.imgBox {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: #eee;
  color: #607d8b;
  text-align: center;
  z-index: 10000;
  .imgMain {
    position: relative;
    margin: 0;
    padding: 0 0 15px 0;
    width: 100%;
    max-width: 750px;
    min-width: 300px;
    height: auto;
    top: 0;
    left: 50%;
    transform: translate(-50%, 0);
    p {
      padding: 10px 0px;
    }
    .canvasImg {
      max-height: 1300px;
      position: relative;
      margin-left: 30px;
      margin-right: 30px;
      border-radius: 10px;
      overflow-x: hidden;
      overflow-y: auto;
      img {
        width: 100%;
        height: auto;
        background: #525252;
      }
    }
  }
}
.interval {
  height: 21px;
  background-color: #F7F7F7;
}

.logoBox {
  img {
    width: 100%;
    height: 300px;
  }
}
/* 头部 */
.headBox {
  width: 100%;
  position: relative;
}
.headTitle {
  color: #000;
  font-size: 36px;
  display: inline-block;
  padding: 10px 0 0 20px;
}
.headTitle span {
  color: #888;
  font-size: 22px;
}
.longPic img {
  display: block;
  padding-left: 40px;
  width: 40px;
  height: 40px;
}
.longPic {
  display: inline-block;
  font-size: 26px;
  color: #E8380D;
  position: absolute;
  right: 20px;
  top: 10px;
}

.wan {
  font-size: 20px;
  color: #E8380D;
}
/* 日期 */
.datetime {
  display: flex;
  flex-direction: row;
  height: 240px;
  position: relative;
}

.today {
  flex: 1.25;
  height: 54px;
  background-color: #fff;
  font-size: 26px;
  border-radius: 8px;
  margin-top: 42px;
  color: #000;
  display: flex;
  align-items: center;
  justify-content: center;
}
.today-num {
  color: #E8380D;
  font-size: 60px;
  font-weight: 600;
  margin-top: 6px;
}
.explain {
  font-size: 22px;
  color: #888888;
  position: absolute;
  top: 120px;
}
.dayTop {
  position: absolute;
  top: 180px;
  font-size: 22px;
  color: #888888;
}
.dayTop span {
  flex: 1;
  text-align: center;
}
.platProp {
  padding-left: 50px;
}
.daypalt {
  position: absolute;
  top: 180px;
  font-size: 22px;
  color: #888888;
  width: 100%;
  display: flex;
  justify-content: center;
}
.daypalt span {
  flex: 1;
  text-align: center;
}
/* 列表头部 */
.boxOfficeList {
  height: 76px;
  line-height: 76px;
  overflow: hidden;
}
.dailyList {
  display: inline-block;
  font-size: 26px;
  color: #000000;
  padding-left: 35px;
}
/* 列表样式 */
.tableList {
  font-size: 21px;
}
.listHead {
  display: flex;
  text-align: center;
  align-items: center;
  height: 70px;
  background-color: #F7F7F7;
  color: #333;
  font-size: 24px;
}
.listHead div:nth-child(1){
  flex: 1;
}
.listHead div:nth-child(2){
  text-align: left;
  flex: 3;
}
.listHead div:nth-child(3){
  flex: 1.5;
}
.listHead div:nth-child(4){
  flex: 1.5;
}
.listHead div:nth-child(5){
  flex: 1.5;
  position: relative;
}
/* 表体样式 */
.dayBoxOff {
  color: #E8380D;
}
.newTip {
  color: #E1BC74;
  font-size: 24px;
}
.li {
  display: flex;
  flex-direction: row;
  text-align: center;
  background-color: #fff;
  color: #333;
  justify-content: center;
  height: 100px;
  line-height: 100px;
}
.li:nth-child(2n-1) {
  background-color: #F7F7F7;
}
.li div:nth-child(1) {
  flex: 1;
  font-size: 26px;
}
.imgStyle {
  width: 39px;
  height: 39px;
  padding-top: 21px;
}
.imgStyle1 {
  padding-left: 10px;
}
.li div:nth-child(2) {
  position: relative;
  flex: 3;
  text-align: left;
  line-height: 70px;
  font-size: 23px;
}
.name {
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
  font-size: 24px;
  color: #000000;
  line-height: 62px;
  height: 58px;
}
.fileNum {
  padding-left: 4px;
}
/* 上映样式 */
.li div:nth-child(2) .file {
  font-size: 20px;
  color: #999;
  line-height: 8px;
}
.li div:nth-child(2) .file  div {
  display: inline-block;
}
.li div:nth-child(3) {
  flex: 1.5;
  font-size: 24px;
  color: #333;
}
.mg {
  width: 24px;
  height: 26px;
}
.yk {
  width: 25px;
  height: 20px;
}
.aqy {
  width: 24px;
  height: 19px;
}
.tx {
  width: 20px;
  height: 20px;
}
.li div:nth-child(4) {
  flex: 1.5;
  font-size: 24px;
  color: #333;
}
.li div:nth-child(5) {
  flex: 1.5;
  font-size: 24px;
  color: #333;
}
.codeBox {
  .codeBoxMain {
    padding: 40px 0 50px 0;
    display: flex;
    align-items: center;
    .codeBoxLeft {
      flex: 1;
      text-align: center;
      img {
        width: 200px;
        height: 200px;
      }
    }
    .codeBoxRight {
      flex: 1;
      font-size: 30px;
      p:last-child {
        font-size: 26px;
        padding-top: 8px;
      }
    }
  }
}
</style>

运行npm run build 将dist文件夹发送给后端部署即可

微信小程序:
微信小程序通过按钮点击跳转新的页面,新页面则只有web-view标签
boxList.wxml

<text class="longGraph" bindtap="longGraphClick">一键长图</text>

boxList.js

longGraphClick: function (e) {
    let dateStart = this.data.queryParam.dateStart
    let platformId = this.data.queryParam.platformId
    let selectList = [this.data.selectList[0].backendName, this.data.selectList[1].backendName]
    // hash模式
    // let url = 'https://testdata.aaa.com/#/dayList'
    // history模式
    let url = 'https://testdata.aaa.com/yearList'
    wx.navigateTo({
      url: '/pages/boxOfficeModule/boxOfficePic/boxOfficePic?url=' + url + '&dateStart=' + dateStart + '&platformId=' + platformId + '&selectList=' + selectList
    })
  },

boxOfficePic.wxml

<web-view src="{{url}}"></web-view>

boxOfficePic.js

// pages/boxOfficeModule/boxOfficePic/boxOfficePic.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    url: '',
    dateStart: ''
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    let k = wx.getStorageSync('token')
    this.setData({
      "url": options.url + '?' + 'dateStart=' + options.dateStart + '&platformId=' + options.platformId + '&selectList=' + options.selectList + '&k=' + k, 
      "dateStart": options.dateStart
    })
    console.log('跳转H5的url', this.data.url)
  },
})

在这里插入图片描述

三、问题:

1. 在H5项目中引入插件后,浏览器上并没有将px转为rem

vue-cli2的项目运行后
最初基于vue2.0及vue-cli2脚手架,通过命令vue init webpack my-proj创建了一个项目配置文件如下,运行之后,选中页面元素后发现px并没有转成rem如上图。
vue-cli2创建的项目
解决:
  发现通过vue-cli3创建的项目进行上面的lib-flexible和postcss-pxtorem相关配置成功实现了px转rem。
  而针对vue-cli2则需要通过lib-flexiblepx2rem-loader实现rem的响应。
参考:
  使用postcss-pxtorem和lib-flexible来实现vue项目自动将px转成rem
  vue cli3中使用postcss-pxtorem进行适配(亲测有效!)
  通过插件postcss-pxtorem轻松实现px到rem转换,完成移动端适配
  vue-cli3.0使用amfe-flexible + postcss-pxtorem,适配移动端并px自动转rem
  vue开发之vue-cli2与vue-cli3的对比
  vue-cli2用px2rem实现响应式布局

2. 运行成功的H5项目,通过手机无法打开页面

  本地运行vue项目npm run serve,在浏览器打开后,将该地址复制发送到手机上,通过手机打开,页面打不开报错。

解决:
  需要手机和电脑在同一网络下,将dev里的host: "localhost"改成host: “0.0.0.0” 在这里插入图片描述
在这里插入图片描述

3. npm run serve运行项目时报错Error:Loading PostCSS Plugin failed: Cannot find module ‘postcss-pxtorem‘

在这里插入图片描述
解决:
  把文件名改成了postcss.config.js
在这里插入图片描述
参考:
  Error:Loading PostCSS Plugin failed: Cannot find module ‘postcss-pxtorem‘报错解决

4. vue项目中的token保存

  由于不涉及其他数据存储,没有使用vuex,通过sessionStorage实现。
  保存token的方法
   sessionStorage.setItem(‘token’,response.data.token)
  获取token的方法
   sessionStorage.getItem(“token”)
参考:
  vue项目中token的保存和使用

5. 微信小程序通过web-view向H5页面传值

微信小程序通过 <web-view src="{{url}}"></web-view>加载H5页面,在url后面拼接上所需参数即可

 this.setData({
	"url": options.url + '?' + 'dateStart=' + options.dateStart + '&platformId=' + options.platformId + '&selectList=' + options.selectList, 
 })

在H5页面通过window.location.href.split("?")[1]获取url后面拼接的所有参数

参考:
  【微信小程序】webview向h5页面传值


6. H5项目打包发布后,无法打开页面,报错

  npm run build打包发给后端,部署服务后,无法打开页面,放到浏览器运行报错Uncaught SyntaxError: Unexpected token ‘<‘
在这里插入图片描述
在这里插入图片描述
可以看到,当出错的时候会强制跳转到 index.html 页面,而js文件是不会识别html,因此报Uncaught SyntaxError: Unexpected token < 错误

可将vue路由模式改为 history模式,并且打包路径使用绝对路径(module.exports={publicPath:"/"};)

解决:
  找到vue.config.js文件,添加publicPath: '/',
  找到router/index.js文件,添加new VueRouter({ mode: 'history', base: '/',})
在这里插入图片描述
在这里插入图片描述
参考:
  【Vue中的坑】Vue打包上传线上报Uncaught SyntaxError: Unexpected token <
  vue js报Uncaught SyntaxError: Unexpected token < 错误
  vue项目部署后Uncaught SyntaxError: Unexpected token <
  vue打包后页面空白,提示Uncaught SyntaxError:Unexpected token

7. H5页面截图后新增头部及底部结构

  最初考虑,在截图的时候如果给图片画进去新的内容,后面发现有点困难。
  采取提前将截图写好,给其设置style="display: none"样式,当触发截图事件时,在截图之前将其样式设置为display: block。

8. H5页面截图后新增头部及底部结构

  最初考虑,在截图的时候如果给图片画进去新的内容,后面发现有点困难。
  采取提前将截图写好,给其设置style="display: none"样式,当触发截图事件时,在截图之前将其样式设置为display: block。

9. 当数据列表多的时候,在浏览器截图不全、真机图片不显示

  当列表数据很多的时候,在浏览器上截图时,会比较慢卡顿并截取不全底部的二维码。在小程序中打开时页面空白,没有图片。
  考虑高度设置、元素显示隐藏、设置定时器发现都无法解决。
  猜测可能是图片太长,生成的base64的url图片地址不对,具体还不确定,随后将页面数据都设定为最多显示50条可以正常使用截图功能。
  通过引入vConsole,在真机上可以看到打印内容,发现数据过多时出现了报错。
  vConsole使用
在这里插入图片描述

四、相关文章:
手机端页面自适应解决方案—rem布局
移动端(手机端)页面自适应解决方案—rem布局篇
vue开发H5步骤详解(环境搭建+rem布局+域名配置+请求)
vue.cli项目封装全局axios,封装请求,封装公共的api和调用请求的全过程
关于vue-cli3中配置请求跨域的问题
vue-cli3 配置请求代理,去除开发环境api跨域问题
在vue项目中如何关闭Eslint校验
解决html2canvas截图不全问题
Vue-router 中hash模式和history模式的区别
vue路由的两种模式,hash与history的区别

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: vue-cli2和vue-cli3有一些重要的区别: 1.配置文件: vue-cli2使用的是`config/index.js`文件来配置项目,而vue-cli3使用的是`.env`和`.env.xxx`文件来配置项目. 2.构建工具: vue-cli2使用的是webpack1,而vue-cli3使用的是webpack4. 3.项目结构: vue-cli2项目结构比较简单,而vue-cli3提供了更加灵活的项目结构. 4.插件系统: vue-cli2使用的是全局插件,而vue-cli3使用的是局部插件. 5.使用方式: vue-cli2是全局安装,而vue-cli3是局部安装. ### 回答2: Vue-cli是一个vue.js的官方脚手架工具,它可以帮助我们更快地搭建一个基于Vue的项目。Vue-cli2和Vue-cli3是Vue-cli的不同版本,在一些方面存在一些显著的差异。 1. 项目结构: Vue-cli2生成的项目结构中,代码和配置信息都放在同一个文件夹中,每一个功能模块都需要手动创建;而Vue-cli3采用了新的配置方案,将项目的配置信息单独抽离出来,并且在工具创建项目时自动生成了更完整的项目结构,使项目结构更加清晰和易于管理。 2. 配置方式: Vue-cli2是通过修改webpack.config.js文件来进行项目配置的,而Vue-cli3则是通过创建vue.config.js文件来进行项目配置的。在Vue-cli3中,我们可以直接在vue.config.js中添加一些特定的配置,而无需修改webpack.config.js文件。这样做更加方便,也更加容易管理项目的开发和部署。 3. 优化: 在Vue-cli3中,作者对项目进行了一些自动优化的处理,例如:自动抽取第三方库等,以优化打包和运行速度。而Vue-cli2则需要手动配置优化选项。 总之,Vue-cli3相比Vue-cli2在工程化方面有了更多的改进和优化,我们将会更加方便、快捷地构建一个基于Vue的项目。 ### 回答3: Vue是一个流行的JavaScript框架,它允许开发人员构建动态Web应用程序。而Vue CLI是Vue的脚手架工具,用于快速构建Vue项目。Vue CLI 2和Vue CLI 3是Vue CLI的不同版本,下面将介绍它们之间的不同: 1.项目结构:Vue CLI 2生成的项目结构是采用传统的单一的Webpack配置文件,而Vue CLI 3则是基于插件的灵活的配置,将Webpack配置拆分为多个小的配置文件。 2.依赖:Vue CLI 2使用的是Vue Router 2,而Vue CLI 3升级到了Vue Router 3,同时也使用了全新的Vuex。 3.插件化: Vue CLI 3采用了插件化的概念,每个插件都可以为开发者提供定制的Webpack配置和功能增强。 4.内置功能:Vue CLI 3集成了一些内置功能如:PWA支持、自动生成样式和文档等。 5.提高性能:Vue CLI 3对webpack的配置进行了深度优化,提供了更优秀的性能表现,同时还加入了预编译,tree shaking 外置化了 `webpack`,对项目编译速度支持了更大的提升。 总的来说,Vue CLI 3采用了更先进的技术、更简单的配置结构、更多的内置功能和更好的性能。如果你计划开始一个新的Vue项目,强烈建议使用Vue CLI 3,以提高代码的开发效率和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值