從零開始 搭建本身的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...

相關文章
相關標籤/搜索