从零开始 搭建本身的vue 移动项目

由于公司项目须要,须要用vue作一个移动端版本;如今从0开始搭建,顺便记下搭建过程,方便往后回顾;欢迎 你们指出不足。
先看设计稿:

clipboard.png

布局页面

把头部抽离出来,做为全局组件;

目录位置

clipboard.png

定义为全局组件

在main.js 中写下css

//全局组件
import topBar from '@/components/mobileTop.vue'
Vue.component('topBar', topBar);

布局 topBar 组件

1,去定义阿里图标,并引入html

clipboard.png

clipboard.png

2,设置 icon 组件,并写html前端

clipboard.png

3,由于vue 模板没有引入 scss ,因此要本身安装sass-loader,node-loadervue

cnpm i node-sass sass-loader -D

4,写less,此布局icon 使用 position 定位布局node

clipboard.png

因為後面加上 flexible-js 自適應 ,因此我們用 rem 佈局,參看下面的代碼,我們直接用實際尺寸除以100就能够;ios

.content{
    position: relative;
    padding:0 1.2rem 0 1.2rem;
  }

头部 ,main,bottom 三个部分应该怎么布局?

top,bottom main 都用absolute

这个布局在调试的时候彻底没有问题;可是在真机调试的时候回存在问题,中间的 main 若是设置了移动端滚动 -webkit-overflow-scrolling:touch;会致使 滑动不了;可是若是不设置,就只能用插件去模拟滚动,很是很差;
而且 若是采用此方式,到了移动端会不时的拉起底部的bottom,让人感受就是一个网页css3

top,bottom 用 absolute,main两头padding:

.box{
    position: relative;
    height: 100%;
  }
  .top{
    height: 1rem;
    background: red;
    position: absolute;
    top:0;
    left: 0;
    right: 0;
    z-index: 2;
  }
  .bot{
    height: 1rem;
    background: red;
    position: absolute;
    bottom:0;
    left: 0;
    right: 0;
    z-index: 2;
  }
  .mid{
    height: 100%;
    padding:1rem 0;
    width: 100%;
    overflow-y: auto;
    overflow-x: hidden;
    background: blue;
    box-sizing: border-box;
  }

这个结构能够知足几乎全部要求,可是仍是会有拉起底部的问题;web

top,bottom 用fixed,main 用padding:1.2rem 0;

采用此方式可能会有一些忽隐忽现的问题,可是最大的好处就是 页面看起来彻底像移动页面,底部也很差拉起来;滚动条至关于原生的滚动条,因此基本没有bug;有个存在的问题在这个文章中提到 https://www.cnblogs.com/xiahj...
目前我遇到的bug 在文章最后排除了。ajax

这个算是我比较看中的方式,由于效果基本跟原生同样;npm

我试过用 better-scroll 处理滚动问题,可是由于微信端会有卡顿感,因此最后仍是放弃了;有需求的朋友,能够根据本身状况加入better-scroll方案;

移动端自适应的基础设置

在 app.vue 中 引入 flexible-js 代码

<script>
  document.head.appendChild(meta);
  (function(doc, win){
    var docE1 = doc.documentElement,
      resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
      recalc = function(){
        var clientWidth = docE1.clientWidth;
        if(!clientWidth) return;
        //docE1.style.fontSize = clientWidth / 375  + 'px'; 这里但愿设置 1rem = 1px,实验证实,这样作 会致使 html 的 fontsize小于 12px
        docE1.style.fontSize = (clientWidth / 750)*100  + 'px'; //乘以100的意义是,1为了避免受fontsize小于12的影响,2为了计算方便;
      };
    if (!doc.addEventListener) return;
    win.addEventListener(resizeEvt,recalc,false);
    doc.addEventListener('DOMContentLoaded',recalc,false);
  })(document,window);
</script>

在main.js 引入全局公共样式 和 一些 模块

//引入公共样式
import '@/common/reset.css'
import '@/common/common.css'

封装 axios 生产出 http.js

/*
 * @Author lizhenhua
 * @version 2018/5/17
 * @description
 */

import axios from 'axios'
import store from '../store'
import {Message} from 'element-ui'
import {getToken,removeToken} from '@/util/cookie'
import tools from '@/util/tools'
import qs from 'qs'
// 建立axios实例
const service = axios.create({
  baseURL: process.env.BASE_API, // api的base_url
  timeout: 1000 // 请求超时时间
})

// request拦截器
service.interceptors.request.use(config => {

  //若是是开发环境,为非模拟接口加上“跨域”前缀 'ln'
  if(process.env.NODE_ENV=='development'&& config.url.indexOf('api')==-1){
    config.url = 'ln/'+ config.url;
  }

  let token = getToken();

  //若是data参数为 对象或者数组,就把参数 封装到key为data 属性中;
  if(typeof config.data == "object" ||typeof config.data == "Object" ||typeof config.data == "OBJECT"){
    let data = tools.cloneObj(config.data);
    config.data = {};
    config.data['data'] = JSON.stringify(data);
  }else {
    config.data = {};
  }

  //统一为全部请求加上 这两个参数
  if (token) {
    config.data['LE_AUTH_TOKEN'] = token
    config.data['token'] = token
  }

  //设置头部token
  if (store.getters.token) {
    config.headers['X-Token'] = token // 让每一个请求携带自定义token 请根据实际状况自行修改
  }

  //不设置 这样,后台拿不到数据
  config.headers['Content-Type'] = 'application/x-www-form-urlencoded'

  //get请求,只能放在 params 中,转为url传参的方式
  //因此统一使用post请求,只有post存在 paramBody,咱们能够吧参数放在 data 中
  config.method = "POST"

  //把全部参数处理为 form 表单提交的方式,而且转义,若是不这样,后端(会直接获得字符串,不是正常对象)解析不出来;
  //前端发送:data=%7B%22loginName%22%3A%22lzh%22%2C%22loginPassword%22%3A%22123456%22%2C%22appId%22%3A%22lext79987422-5180-40%22%2C%22platType%22%3A1%7D
  //后端收到:{data={"loginName":"lzh","loginPassword":"123456","appId":"lext79987422-5180-40","platType":1}}
  config.data = qs.stringify(config.data)

  return config
}, error => {
  // Do something with request error
  console.log(error) // for debug
  Promise.reject(error)
})

// respone拦截器
service.interceptors.response.use(
  response => {
    let res = response.data;
    if (res.status == 1) {
      return res.data
    }if(res.status ==-1208){
      Message({
        message: res.errorMsg,
        type: 'error',
        duration: 5 * 1000
      })
      removeToken();
    } else {  //这里处理 全部数据错误
      Message({
        message: res.errorMsg,
        type: 'error',
        duration: 5 * 1000
      })
      return Promise.reject(res.errorMsg)
    }
  },
  error => {  //这里处理的是 全部网络请求错误
    console.log('err' + error)// for debug
    let err = error + '', info = '';
    if (err.indexOf('timeout') != -1) {
      info = "请求超时";
    } else {
      info = err
    }
    Message({
      message: info,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)


export default service

能够根据本身状况,拦截 ajax 后做统一处理;
把http.js 在main.js 中 引入

//引入 axios 实例
import $http from '@/util/$http' 
Vue.prototype.$http = $http;

在vue 中直接使用 $http 模块

<script>
  export default {
    data: function () {
      return {
        document: {}
      }
    },
    created() {
      this.$http({
        url: this.ajaxApi.test.list,
        data: {
          id: "docid:6CFE06297BBA4E1FBAA00BDE2809198F"
        },
      }).then(res => {
        if(res){
          this.document = this.tools.cloneObj(res.document)
        }
      })
    }
  }
</script>

配置 ajaxApi.js 为全局,统一管理接口

// api 表
export default {
  test: {
    list:"/api/data/document"
  }
}

//引入 api 表
import ajaxApi from "@/util/ajaxApi"
Vue.prototype.ajaxApi  = ajaxApi

配置 tools.js 为全局,提供经常使用工具函数

// tools.js
export default {
  cloneObj (obj){
    var str, newobj = obj.constructor === Array ? [] : {};
    if (typeof obj !== 'object') {
      return;
    } else if (window.JSON) {
      str = JSON.stringify(obj), //序列化对象
        newobj = JSON.parse(str); //还原
    } else {
      for (var i in obj) {
        newobj[i] = typeof obj[i] === 'object' ? cloneObj(obj[i]) : obj[i];
      }
    }
    return newobj;
  }
}

//引入工具库
import tools from "@/util/tools"
Vue.prototype.tools = tools;

再配置一个 cookie 方法文件,方便操做cookie

import Cookies from 'js-cookie'

const TokenKey = 'LtpaToken2'

export function getToken() {
  return Cookies.get(TokenKey)
}

export function setToken(token,time) {
  return Cookies.set(TokenKey, token,{expiry:time})
}

export function removeToken() {
  return Cookies.remove(TokenKey)
}

由于是移动端开发,因此你须要真机调试

由于localhost 是不具备 局域网 访问性的,因此咱们要改一下 项目的配置
clipboard.png

在 config/index.js 找到以下代码:

// host: 'localhost', // can be overwritten by process.env.HOST
    host: '10.20.139.118', // can be overwritten by process.env.HOST
    port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined

改成你的ip 之后,重启项目

在手机浏览器输入 http://10.20.139.118:8080/#/ 就能够访问到你的页面了

一个很是重要的 css 属性

刚刚开始 开发的时候,开发出来的页面 在webview上 滑动老是卡卡的感受,人家的页面是丝质顺滑,跟原生的同样;后来想到了 用一个插件
模拟这种效果,iscroll.js;

其实 只须要一句css 就能解决这个缓动的效果
在 app.vue 下写

html, body {
  -webkit-overflow-scrolling: touch;
}

-webkit-overflow-scrolling:touch是什么?

MDN上是这样定义的:

-webkit-overflow-scrolling 属性控制元素在移动设备上是否使用滚动回弹效果. auto: 使用普通滚动, 当手指从触摸屏上移开,滚动会当即中止。 touch: 使用具备回弹效果的滚动,
当手指从触摸屏上移开,内容会继续保持一段时间的滚动效果。继续滚动的速度和持续的时间和滚动手势的强烈程度成正比。同时也会建立一个新的堆栈上下文。

常见流程列表布局与样式

咱们一般会在移动端布局中接触到 流程进度列表 结构的设计图;例如淘宝的:物流进度图;此次拿到的设计稿是这样:

clipboard.png

clipboard.png

这两个结构能够看作同样的;总体分为左(标签)中(长线和点)右(内容)三部分,其中,最左边考虑到它位置直接贴在边上,一定是用绝对定位实现;最右边是普通的布局,难点在于怎么处理中间这根贯穿整个列表的线,和上面的圆点。
目前我知道的有两种方法:
一个是每一个li 中用div画出本身的线和点,而后每一个线拼接起来组成长线,这个方法的缺点是比较难定线的长度,想用height:100%,可是不生效,极可能要用到js;
第二种是本次我采用的方法,用li的伪元素before画线,after 画圆点,经过z-index设置覆盖层级;这样的好处是,线的高度能够用height:100%;难点是圆点在不一样分辨率下,可能会出现偏移的状况(线没有穿过圆心);这里直接给圆点作了css3的居中定位,并用magin-right的负值微调了一下,初步测试在不一样分辨率下表现都比较好;在这里给出实现代码:

html

<ul class="item-ul">
  <li class="flex-bet">
    <div class="list-left">拟稿意见</div>
    <div class="list-right">
      <div class="top flex-bet">
        <div class="top-left"><span class="yl">公伟杰</span>信息技术部</div>
        <div class="date">2018-07-12</div>
      </div>
      <p>这个提议不错,试试看</p>
    </div>
  </li>
    //如下重复这个li
</ul>

scss

/*流程表*/
    .flex-bet{
      display: flex;
      justify-content: space-between;
      align-items: center;
    }

  .item-ul {
    padding: 0.2rem 0;
    li {
      position: relative;
      padding-left: 1.2rem;
      padding-bottom: 0.2rem;
      &:before {
        content: "";
        position: absolute;
        left: 1rem;
        top: 8px;
        width: 1px;
        background: #58a8ff;
        height: 100%;
      }
      &:after {
        content: "";
        position: absolute;
        left: 0.945rem;
        top: 8px;
        width: 0.1rem;
        height: 0.1rem;
        border: 1px solid #57a9ff;
        border-radius: 50%;
        background: #fff;
        margin-right: -2px;
      }
      .list-left {
        position: absolute;
        width: 1.4rem;
        height: 0.57rem;
        line-height: 0.57rem;
        font-size: 12px;
        text-align: center;
        color: #fff;
        background: #58a8ff;
        border-radius: 0 0.7rem 0.7rem 0;
        left: 0;
        top: 0;
        z-index: 5;
      }
      .list-mid {
        align-self: start;
        z-index: 4;
        height: 100%;
        text-align: center;
        width: 20px;
        margin-top: -4px;
        .icon {
          display: inline-block;
          width: 0.1rem;
          height: 0.1rem;
          border: 1px solid #57a9ff;
          border-radius: 50%;
          background: #fff;
          margin-right: -2px;
        }
      }
      .list-right {
        width: 6.1rem;
        padding: 0.05rem 0.3rem 0 0.5rem;
        font-size: 12px;
        .top-left {
          color: #8c8c8c;
          span {
            display: inline-block;
            padding: 0.05rem 0.2rem;
            color: #484848;
            border-radius: 15px;
            margin-right: 0.2rem;
            background: transparent;
            font-size: 15px;
          }
          .yl {
            background: #ffedd9;
          }
          .bl {
            background: #daecff;
          }
          .pin {
            background: #ffe0eb;
          }
          .zl {
            background: #e3e0ff;
          }
        }
        .date {
          color: #8c8c8c;
        }
        p {
          text-align: left;
          font-size: 12px;
          color: #484848;
          line-height: 0.57rem;
          padding-left: 4px;
        }
        .keyword {
          text-align: left;
          margin-top: 0.1rem;
          span {
            display: inline-block;
            width: 1rem;
            height: 0.35rem;
            line-height: 0.35rem;
            text-align: center;
            font-size: 12px;
            border: 1px solid #b3b3b3;
            margin-right: 0.15rem;
            &:first-child {
              border: 1px solid #57a9fb;
              color: #57a9fb;
            }
          }
        }
      }
    }
    li:last-child:before {
      height: 0.1rem;
    }
  }

一些容易遇到的坑

js focus textarea 光标定位在中间

需求常见: 点击某个input,划开一个新页面,里面能够填300字意见;我watch show/hide 变量,若是显示状态的话,就让子组件的textarea focus;这个时候,这个时候就出现了光标在中间的状况;

clipboard.png

解决方案是设置100 毫秒的延迟

watch:{
      control(val){
        if(val){
          /*延迟100毫秒,fix 光标定位在中间的bug*/
          setTimeout(()=>{
            this.$refs.textBox.focus();
          },100)
        }
      }
    }

弹窗后,页面依然能够滚动页面的bug,滚动穿透

解决方案是当弹窗的时候,给发生滚动的盒子 加上 overflow:hide;关闭弹窗的时候移除;
你还能够open的时候记住滚动条的位置,close的时候复原;
这里最大的坑多是,要找清楚真实发生了滚动的盒子;极可能不是body;

.oh{
    overflow:hidden !important;
}

 openPop(value){
        this[value] = true;
        document.getElementById('app').className ='oh'
      },
      closePop(value){
        this[value] = false;
        document.getElementById('app').className=' ';
      }
  • html,body标签非弹窗千万不能有overflow设置,会有苹果设备 fixed 布局下滚动条不出来,同时还会出现盖住 top,bottom的状况
html, body{
  position: relative;
  height: 100%;
  -webkit-overflow-scrolling: touch;    //多是跟这个属性冲突了
  /*overflow-y: auto;*/
  /*overflow-x: hidden;*/ /*这里不能加overflow全部属性,在苹果下会有上下拉盖住顶部底部的bug */
}

scrllow 组件要求父元素定高,可是父元素又不能肯定高度。

传统的作法是用 百分比定高,可是要一层层设置 父元素的高度。否则百分比获取不到值。因此这里建议用 vh 代替 %

输入板遮挡textarea 或者input

最后的解决方案是经过定位 把输入部分上提

clipboard.png

详细的讨论在下面作了笔记
https://segmentfault.com/n/13...

移动端,安卓键盘弹起,顶起底部的bug

移步 https://segmentfault.com/n/13...

相关文章
相关标签/搜索