兩年前剛接觸移動端開發,剛開始比較疑惑,每次遇到問題都是到社區裏提問或者吸收前輩的經驗分享,感謝熱衷於分享的開發者爲前端社區帶來欣欣向上的生命力。本文結合先前寫的文章和開發經驗分享給你們,但願也能幫助剛步入移動端開發的新人解惑。如下會以其中一個以公積金頁面開發項目做爲例子,介紹移動端的一些常見問題和使用Vuejs做爲lib進行多頁開發的經驗。javascript
在項目中移動端最經常使用的自適應佈局方案就是flexbox結合rem。規範的分欄式使用flexbox,其餘大部分不規則視圖使用rem,對於rem最經常使用的方案就是淘寶開源的可伸縮佈局方案。css
根據設備設備像素比設置scale的值(scale = 1 / deviceRatio),這樣能夠保持視口device-width始終等於設備物理像素,接着根據屏幕大小動態計算根字體大小,具體是將屏幕劃分爲100等分,每份爲a,1rem就等於10a。html
一般咱們會拿到750寬的設計稿,這是基於iPhone6的物理分辨率。有的設計師也許會偷懶,設計圖上面沒有任何的標註,若是咱們邊開發邊量尺寸,無疑效率是比較低的。要麼讓設計師標註上,要麼自食其力。前端
若是設計師實在沒有時間,推薦使用markman進行標註,免費版閹割了一些功能(好比沒法保存本地)不過基本知足了咱們的需求了。vue
後來我發現比markman更好的標註工具PxCook,該工具能夠顯示PSD設計圖中的圖層的樣式代碼,對於前端來講簡直方便極了。java
標註完成後開始寫咱們的樣式,使用了淘寶的lib-flexible庫以後,咱們的根字體基準值就爲750/100*10 = 75px。此時咱們從圖中若某個標註爲100px,那麼css中就應該設置爲100/75 = 1.333333rem。因此爲了提升開發效率,可使用px轉化爲rem的插件。下面是sublimeText和Vscode的轉換插件:node
左圖的表單高度單位因爲下邊空距較大,使用px在不一樣屏幕顯示更加;而右邊的活動註冊頁因爲不能出現滾動條,全部的衆向高度、margin、padding都應該使用rem。nginx
以前發佈的文章中,有個SF的前端小夥伴提出的問題: 文中做者有重點強調佈局所有鋪滿,和下方與不少空隙的處理方案是不一樣的,在工做中我遇到這種狀況,設計師的設計稿寬度爲750×1334,但實際的展現高度並無那麼多,由於上方有導航欄還包括手機本身的狀態欄展現,因此總體高度就達不到750,可是設計師設計稿是嚴格按照750進行設計的,這種狀況下使用rem,嚴格按照設計師尺寸進行還原就會出現屏幕出現滾動條狀況,請問針對這種狀況您是怎麼處理的?是從設計稿上規範,仍是從開發上有相應的措施
依舊以個人分享界面爲例: 展現高度不一樣一般發生在微信及瀏覽器端,由於前者沒有地址欄和工具欄,這樣顯示高度一般會和設計師設計的視圖吻合。那若是按照純padding,margin即便所有使用rem,在瀏覽器端依舊會超出一屏高度,對於分享頁面這種不是咱們想要看到的。這時候就要作出取捨,我對主體區域採用絕對定位,這樣上面間隙雖然小,不過仍能保持在一個屏幕高度顯示。若採用margin padding在設置,必然已出現滾動條。固然這樣的前提是依賴設計圖的,一般設計師會爲了空間感有保留必定的間隙,也不會將主要對象高度設的太高,不然太撐滿也很差看,開發上若是設計圖寬高沒有在必定界限以內,超出也沒法避免,不過咱們這種分享界面一般是經過微信分享好友,經過瀏覽器打開的視圖效果出現滾動條其實也不怎麼影響不是麼? 下面附上微信端和瀏覽器端的效果圖:
微信端:
瀏覽器端:
通常移動端使用vue是爲了數據交互頻繁並且快速開發的頁面,爲何不使用單頁SPA開發模式,緣由大概幾點。
拋開使用單頁的架構,開發多頁應用時,一個頁面交互邏輯與一個Vue實例對應。
"基於接口返回數據的屬性注入"是我的建立的話術,拋開此概念,先說一下表單數據的綁定方式。
一個重要的點是有幾份表單就分開幾個表單對象進行數據綁定。
以上圖公積金查詢爲例,因爲不一樣城市會有不一樣的查詢要素,可能登錄方式只有一種,也可能有幾種。好比上圖有三種登錄方式,在使用vue佈局時,有兩種方案。
因爲使用第三方的接口,一開始也沒有先進行接口返回數據結構的查看,採用了第一種錯誤的方式,錯誤一是每種登錄方式下面的登錄要素的數量也不一樣,錯誤二是數據綁定在同一個表單data下,當用戶在用戶名登錄方式輸入用戶名密碼後,切換到客戶號登錄方式,就會出現數據錯亂的狀況。
解決完佈局問題後,咱們須要根據設計圖定義一些狀態,好比當前登錄方式的切換、贊成受權狀態的切換、按鈕是否能夠點擊的狀態、是否處於請求中的狀態。固然還有一些app穿過來的數據,這裏就忽略了。
data: {
tags: {
arr: [''],
activeIndex: 0
},
isAgreeProxy: true,
isLoading: false
}
複製代碼
接着審查一下接口返回的數據,推薦使用chrome插件postman,好比呼和浩特的登錄要素以下:
{
"code": 2005,
"data": [
{
"name": "login_type",
"label": "身份證號",
"fields": [
{
"name": "user_name",
"label": "身份證號",
"type": "text"
},
{
"name": "user_pass",
"label": "密碼",
"type": "password"
}
],
"value": "1"
},
{
"name": " login_type",
"label": "公積金帳號",
"fields": [
{
"name": "user_name",
"label": "公積金帳號",
"type": "text"
},
{
"name": "user_pass",
"label": "密碼",
"type": "password"
}
],
"value": "0"
}
],
"message": "登陸要素請求成功"
}
複製代碼
能夠看到呼和浩特有兩種受權登錄方式,咱們在data中定義了一個loginWays,初始爲空數組,接着methods中定義一個請求接口的函數,裏面就是基於返回數據的基礎上爲上面fields對象注入一個input字段用於綁定,這就是所謂的基於接口返回數據的屬性注入。
methods: {
queryloginWays: function(channel_type, channel_code) {
var params = new URLSearchParams();
params.append('channel_type', channel_type);
params.append('channel_code', channel_code);
axios.post(this.loginParamsProxy, params)
.then(function(res) {
console.log(res);
var code = res.code || res.data.code;
var msg = res.message || res.data.message;
var loginWays = res.data.data ? res.data.data : res.data;
// 查詢失敗
if (code != 2005) {
alert(msg);
return;
}
// 添加input字段用於v-model綁定
loginWays.forEach(function(loginWay) {
loginWay.fields.forEach(function(field) {
field.input = '';
})
})
this.loginWays = loginWays;
this.tags.arr = loginWays.map(function(loginWay) {
return loginWay.label;
})
}.bind(this))
}
}
複製代碼
即便返回的數據有咱們不須要的數據也沒有關係,這樣保證咱們不會遺失進行下一步登錄所須要的數據。
這樣多個表單綁定數據問題解決了,那麼怎麼進行頁面間數據傳遞?若是是app傳過來,那麼一般使用URL拼接的方式,使用window.location.search得到queryString後再進行截取;若是經過頁面套入javaWeb中,那麼直接使用"${字段名}"就能獲取,注意要js中獲取java字段須要加雙引號。
computed: {
// 真實姓名
realName: function() {
return this.getQueryVariable('name') || ''
},
// 身份證
identity: function() {
return parseInt(this.getQueryVariable('identity')) || ''
},
/*If javaWeb realName: function() { return this.getQueryVariable('name') || '' }, identity: function() { return parseInt(this.getQueryVariable('identity')) || '' }*/
},
methods: {
getQueryVariable: function(variable) {
var query = window.location.search.substring(1);
var vars = query.split('&');
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split('=');
if (decodeURIComponent(pair[0]) == variable) {
return decodeURIComponent(pair[1]);
}
}
console.log('Query variable %s not found', variable);
}
}
複製代碼
在進行接口請求時,咱們的頁面一般是在sublime的本地服務器或者vscode本地服務器預覽,因此請求接口會遇到跨域的問題,若是使用Gulp進行打包,可使用插件http-proxy-middleware,或者使用nginx。
在項目構建的時候一般咱們源代碼會放在src文件夾下,而後使用gulp進行代碼的壓縮、合併、圖片的優化(根據須要)等等,咱們會使用gulp。
解決跨域的問題能夠用gulp-connect結合http-proxy-middleware,此時咱們在gulp-connect中的本地服務器進行預覽調試。
gulpfile.js以下: 開發過程使用gulp server:dev
命令,監聽文件改動並使用livereload刷新,而且代理src目錄;使用gulp
命令進行打包;使用gulp server:dist
代理dist生產目錄。
var gulp = require('gulp');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var autoprefixer = require('gulp-autoprefixer');
var useref = require('gulp-useref');
var connect = require('gulp-connect');
var proxyMiddleware = require('http-proxy-middleware');
// 開發跨域代理 將localhost:8088/api 映射到 https://api.xxxxx.com/
gulp.task('server:dev', ['listen'], function() {
var middleware = proxyMiddleware(['/api'], {
target: 'https://api.xxxxx.com/',
changeOrigin: true,
pathRewrite: {
'^/api': '/'
}
});
connect.server({
root: env == 'dev' ? './src' : './dist',
port: 8088,
livereload: true,
middleware: function(connect, opt) {
return [middleware]
}
});
});
// 打包後跨域代理
gulp.task('server:dist', ['listen'], function() {
var middleware = proxyMiddleware(['/api'], {
target: 'https://api.xxxxx.com/',
changeOrigin: true,
pathRewrite: {
'^/api': '/'
}
});
connect.server({
root: './dist',
port: 8088,
livereload: true,
middleware: function(connect, opt) {
return [middleware]
}
});
});
gulp.task('html', function() {
gulp.src('src/*.html')
.pipe(useref())
.pipe(gulp.dest('dist'));
});
gulp.task('css', function() {
gulp.src('src/css/main.css')
.pipe(concat('main.css'))
.pipe(autoprefixer({
browsers: ['last 2 versions'],
cascade: false
}))
.pipe(gulp.dest('dist/css/'));
gulp.src('src/css/share.css')
.pipe(concat('share.css'))
.pipe(autoprefixer({
browsers: ['last 2 versions'],
cascade: false
}))
.pipe(gulp.dest('dist/css/'));
gulp.src('src/vendors/css/*.css')
.pipe(concat('vendors.min.css'))
.pipe(autoprefixer({
browsers: ['last 2 versions'],
cascade: false
}))
.pipe(gulp.dest('dist/vendors/css'));
return gulp
});
gulp.task('js', function() {
return gulp.src('src/vendors/js/*.js')
.pipe(concat('vendors.min.js'))
.pipe(uglify())
.pipe(gulp.dest('dist/vendors/js'));
});
gulp.task('img', function() {
gulp.src('src/imgs/*')
.pipe(gulp.dest('dist/imgs'));
});
gulp.task('listen', function() {
gulp.watch('./src/css/*.css', function() {
gulp.src(['./src/css/*.css'])
.pipe(connect.reload());
});
gulp.watch('./src/js/*.js', function() {
gulp.src(['./src/js/*.js'])
.pipe(connect.reload());
});
gulp.watch('./src/*.html', function() {
gulp.src(['./src/*.html'])
.pipe(connect.reload());
});
});
gulp.task('default', ['html', 'css', 'js', 'img']);
複製代碼
在nginx配置使用proxy_pass,須要注意一點: 若是在proxy_pass後面的url加/,表示絕對根路徑;若是沒有/,表示相對路徑,把匹配的路徑部分也給代理走。
server {
listen 80;
server_name localhost;
root [Your project root];
index index.html index.htm default.html default.htm;
location ^~/api {
proxy_pass https://api.xxxxx.com/;
}
}
複製代碼
若是你開發的H5基於微信jsSDK,你必定接觸過微信受權域名,微信會將受權數據傳給一個回調頁面,而回調頁面必須在你配置的域名下(含子域名)。好比咱們獲取用戶的openid操做。而微信配置域名回去該域名根目錄下檢測一個xxx_verify_xxx.txt
文件,確保該域名是屬於你的。
因此要想在微信開發調試工具中獲取openid,咱們須要使用一種叫作內網穿透的工具。下面是本身比較經常使用的兩個工具:
ngrok執行命令
ngrok -config ngrok.cfg start web
複製代碼
在ngrok.exe目錄須要一個配置文件ngrok.cfg
如下是配置示例:
server_addr: "tunnel.cn:4443"
trust_host_root_certs: false
tunnels:
web:
subdomain: "xxx"
proto:
http: 8086
https: 8086
複製代碼
啓動後xxx.tunnel.cn:4443會指向你本地的8086端口,將xxx_verify_xxx.txt
文件放到8086端口根目錄便可配置受權域名成功。
花生殼免費版對於我的開通僅需6元,而後每個月會提供給你1G的流量,免費版不支持80端口,最多支持兩個域名,須要下載桌面客戶端。
添加域名映射很簡單,免費版沒法配置自定義域名,由花生殼自動生成。
PS:更多前端資訊、技術乾貨,請關注公衆號「前端新視界」