由于公司项目须要,须要用vue作一个移动端版本;如今从0开始搭建,顺便记下搭建过程,方便往后回顾;欢迎 你们指出不足。
先看设计稿:
在main.js 中写下css
//全局组件 import topBar from '@/components/mobileTop.vue' Vue.component('topBar', topBar);
1,去定义阿里图标,并引入html
2,设置 icon 组件,并写html前端
3,由于vue 模板没有引入 scss ,因此要本身安装sass-loader,node-loadervue
cnpm i node-sass sass-loader -D
4,写less,此布局icon 使用 position 定位布局node
因為後面加上 flexible-js 自適應 ,因此我們用 rem 佈局,參看下面的代碼,我們直接用實際尺寸除以100就能够;ios
.content{ position: relative; padding:0 1.2rem 0 1.2rem; }
这个布局在调试的时候彻底没有问题;可是在真机调试的时候回存在问题,中间的 main 若是设置了移动端滚动 -webkit-overflow-scrolling:touch;会致使 滑动不了;可是若是不设置,就只能用插件去模拟滚动,很是很差;
而且 若是采用此方式,到了移动端会不时的拉起底部的bottom,让人感受就是一个网页css3
.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
采用此方式可能会有一些忽隐忽现的问题,可是最大的好处就是 页面看起来彻底像移动页面,底部也很差拉起来;滚动条至关于原生的滚动条,因此基本没有bug;有个存在的问题在这个文章中提到 https://www.cnblogs.com/xiahj...;
目前我遇到的bug 在文章最后排除了。ajax
这个算是我比较看中的方式,由于效果基本跟原生同样;npm
我试过用 better-scroll 处理滚动问题,可是由于微信端会有卡顿感,因此最后仍是放弃了;有需求的朋友,能够根据本身状况加入better-scroll方案;
<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>
//引入公共样式 import '@/common/reset.css' import '@/common/common.css'
/* * @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;
<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>
// api 表 export default { test: { list:"/api/data/document" } } //引入 api 表 import ajaxApi from "@/util/ajaxApi" Vue.prototype.ajaxApi = ajaxApi
// 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;
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 是不具备 局域网 访问性的,因此咱们要改一下 项目的配置
在 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/#/ 就能够访问到你的页面了
刚刚开始 开发的时候,开发出来的页面 在webview上 滑动老是卡卡的感受,人家的页面是丝质顺滑,跟原生的同样;后来想到了 用一个插件
模拟这种效果,iscroll.js;
其实 只须要一句css 就能解决这个缓动的效果
在 app.vue 下写
html, body { -webkit-overflow-scrolling: touch; }
MDN上是这样定义的:
-webkit-overflow-scrolling 属性控制元素在移动设备上是否使用滚动回弹效果. auto: 使用普通滚动, 当手指从触摸屏上移开,滚动会当即中止。 touch: 使用具备回弹效果的滚动,
当手指从触摸屏上移开,内容会继续保持一段时间的滚动效果。继续滚动的速度和持续的时间和滚动手势的强烈程度成正比。同时也会建立一个新的堆栈上下文。
咱们一般会在移动端布局中接触到 流程进度列表 结构的设计图;例如淘宝的:物流进度图;此次拿到的设计稿是这样:
这两个结构能够看作同样的;总体分为左(标签)中(长线和点)右(内容)三部分,其中,最左边考虑到它位置直接贴在边上,一定是用绝对定位实现;最右边是普通的布局,难点在于怎么处理中间这根贯穿整个列表的线,和上面的圆点。
目前我知道的有两种方法:
一个是每一个li 中用div画出本身的线和点,而后每一个线拼接起来组成长线,这个方法的缺点是比较难定线的长度,想用height:100%,可是不生效,极可能要用到js;
第二种是本次我采用的方法,用li的伪元素before画线,after 画圆点,经过z-index设置覆盖层级;这样的好处是,线的高度能够用height:100%;难点是圆点在不一样分辨率下,可能会出现偏移的状况(线没有穿过圆心);这里直接给圆点作了css3的居中定位,并用magin-right的负值微调了一下,初步测试在不一样分辨率下表现都比较好;在这里给出实现代码:
<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>
/*流程表*/ .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; } }
需求常见: 点击某个input,划开一个新页面,里面能够填300字意见;我watch show/hide 变量,若是显示状态的话,就让子组件的textarea focus;这个时候,这个时候就出现了光标在中间的状况;
解决方案是设置100 毫秒的延迟
watch:{ control(val){ if(val){ /*延迟100毫秒,fix 光标定位在中间的bug*/ setTimeout(()=>{ this.$refs.textBox.focus(); },100) } } }
解决方案是当弹窗的时候,给发生滚动的盒子 加上 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{ position: relative; height: 100%; -webkit-overflow-scrolling: touch; //多是跟这个属性冲突了 /*overflow-y: auto;*/ /*overflow-x: hidden;*/ /*这里不能加overflow全部属性,在苹果下会有上下拉盖住顶部底部的bug */ }
传统的作法是用 百分比定高,可是要一层层设置 父元素的高度。否则百分比获取不到值。因此这里建议用 vh 代替 %
最后的解决方案是经过定位 把输入部分上提
详细的讨论在下面作了笔记
https://segmentfault.com/n/13...