最近參與了APP內(nèi)嵌H5頁面的開發(fā),這次使用vuejs替代了jQuery古胆,僅僅把vuejs當(dāng)做一個庫來使用,效率提高之外代碼可讀性更強(qiáng)筛璧,在此分享一下自己的一些開發(fā)中總結(jié)的經(jīng)驗(yàn)逸绎。
關(guān)于布局方案
當(dāng)拿到設(shè)計(jì)師給的UI設(shè)計(jì)圖,前端的首要任務(wù)就是布局和樣式夭谤,相信這對于大部分前端工程師來說已經(jīng)不是什么難題了棺牧。移動端的布局相對PC較為簡單,關(guān)鍵在于對不同設(shè)備的適配朗儒。之前介紹了一篇關(guān)于移動端rem布局方案颊乘,這大致是網(wǎng)易H5的適配方案。不過實(shí)踐中發(fā)現(xiàn)淘寶開源的可伸縮布局方案效果更好且更容易使用醉锄。
網(wǎng)易云的方案總結(jié)為:根據(jù)屏幕大小 / 750 = 所求字體 / 基準(zhǔn)字體大小比值相等乏悄,動態(tài)調(diào)節(jié)html的font-size大小。
淘寶的方案總結(jié)為:根據(jù)設(shè)備設(shè)備像素比設(shè)置scale的值恳不,保持視口device-width始終等于設(shè)備物理像素檩小,接著根據(jù)屏幕大小動態(tài)計(jì)算根字體大小,具體是將屏幕劃分為10等分妆够,每份為a识啦,1rem就等于10a。
通常我們會拿到750寬的設(shè)計(jì)稿神妹,這是基于iPhone6的物理分辨率颓哮。有的設(shè)計(jì)師也許會偷懶,設(shè)計(jì)圖上面沒有任何的標(biāo)注鸵荠,如果我們邊開發(fā)邊量尺寸冕茅,無疑效率是比較低的。要么讓設(shè)計(jì)師標(biāo)注上蛹找,要么自食其力姨伤。如果設(shè)計(jì)師實(shí)在沒有時間,推薦使用markman進(jìn)行標(biāo)注庸疾,免費(fèi)版閹割了一些功能(比如無法保存本地)不過基本滿足了我們的需求了乍楚。
標(biāo)注完成后開始寫我們的樣式,使用了淘寶的lib-flexible庫之后届慈,我們的根字體基準(zhǔn)值就為750/100*10 = 75px徒溪。此時我們從圖中若某個標(biāo)注為100px,那么css中就應(yīng)該設(shè)置為100/75 = 1.333333rem金顿。所以為了提高開發(fā)效率臊泌,可以使用px轉(zhuǎn)化為rem的插件。如果你使用sublimeText揍拆,可以用 rem-unit
如果你用vscode編輯器渠概,推薦 cssrem
使用rem單位注意以下幾點(diǎn):
- 在所有的單位中,font-size推薦使用px嫂拴,然后結(jié)合媒體查詢進(jìn)行重要節(jié)點(diǎn)的控制播揪,這樣可以滿足突出或者弱化某些字體的需求,而非整體調(diào)整筒狠。
-
眾向的單位可以全部使用px剪芍,橫向的使用rem,因?yàn)橐苿釉O(shè)備寬度有限窟蓝,而高度可以無限向下滑動罪裹。但這也有特例,比如對于一些活動注冊頁面运挫,需要在一屏幕內(nèi)完全顯示状共,沒有下拉,這時候所有眾向或者橫向都應(yīng)該使用rem作為單位谁帕。如圖:
左圖的表單高度單位由于下邊空距較大峡继,使用px在不同屏幕顯示更加;而右邊的活動注冊頁由于不能出現(xiàn)滾動條匈挖,所有的眾向高度碾牌、margin康愤、padding都應(yīng)該使用rem峻仇。
- border业汰、box-shadow、border-radius等一些效果應(yīng)該使用px作為單位坯屿。
基于接口返回?cái)?shù)據(jù)的屬性注入
可能大家不明白什么叫"基于接口返回?cái)?shù)據(jù)的屬性注入"誓琼,在此之前检激,先說一下表單數(shù)據(jù)的綁定方式,一個重要的點(diǎn)是有幾份表單就分開幾個表單對象進(jìn)行數(shù)據(jù)綁定腹侣。
已上圖公積金查詢?yōu)槔迨眨捎诓煌鞘袝胁煌牟樵円兀赡艿顷懛绞街挥幸环N傲隶,也可能有幾種饺律。比如上圖有三種登陸方式,在使用vue布局時跺株,有兩種方案蓝晒。一是只建立一個表單用于數(shù)據(jù)綁定,點(diǎn)擊按鈕觸發(fā)判斷帖鸦;而是有幾種登陸方式建立幾個表單芝薇,用一個字段標(biāo)識當(dāng)前顯示的表單。由于使用第三方的接口作儿,一開始也沒有先進(jìn)行接口返回?cái)?shù)據(jù)結(jié)構(gòu)的查看洛二,采用了第一種錯誤的方式,錯誤一是每種登陸方式下面的登陸要素的數(shù)量也不同攻锰,錯誤二是數(shù)據(jù)綁定在同一個表單data下晾嘶,當(dāng)用戶在用戶名登陸方式輸入用戶名密碼后,切換到客戶號登陸方式娶吞,就會出現(xiàn)數(shù)據(jù)錯亂的情況垒迂。
解決完布局問題后,我們需要根據(jù)設(shè)計(jì)圖定義一些狀態(tài)妒蛇,比如當(dāng)前登陸方式的切換机断、同意授權(quán)狀態(tài)的切換、按鈕是否可以點(diǎn)擊的狀態(tài)绣夺、是否處于請求中的狀態(tài)吏奸。當(dāng)然還有一些app穿過來的數(shù)據(jù),這里就忽略了陶耍。
data: {
tags: {
arr: [''],
activeIndex: 0
},
isAgreeProxy: true,
isLoading: false
}
接著審查一下接口返回的數(shù)據(jù)奋蔚,推薦使用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": "登錄要素請求成功"
}
可以看到呼和浩特有兩種授權(quán)登陸方式,我們在data中定義了一個loginWays泊碑,初始為空數(shù)組坤按,接著methods中定義一個請求接口的函數(shù),里面就是基于返回?cái)?shù)據(jù)的基礎(chǔ)上為上面fields對象注入一個input字段用于綁定馒过,這就是所謂的基于接口返回?cái)?shù)據(jù)的屬性注入臭脓。
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))
}
}
即使返回的數(shù)據(jù)有我們不需要的數(shù)據(jù)也沒有關(guān)系,這樣保證我們不會遺失進(jìn)行下一步登陸所需要的數(shù)據(jù)沉桌。
這樣多個表單綁定數(shù)據(jù)問題解決了,那么怎么進(jìn)行頁面間數(shù)據(jù)傳遞算吩?如果是app傳過來留凭,那么通常使用URL拼接的方式,使用window.location.search獲得queryString后再進(jìn)行截荣顺病蔼夜;如果通過頁面套入javaWeb中,那么直接使用"${字段名}"就能獲取压昼,注意要js中獲取java字段需要加雙引號求冷。
computed: {
// 真實(shí)姓名
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);
}
}
關(guān)于前端跨域調(diào)試
在進(jìn)行接口請求時,我們的頁面通常是在sublime的本地服務(wù)器或者vscode本地服務(wù)器預(yù)覽窍霞,所以請求接口會遇到跨域的問題匠题。
在項(xiàng)目構(gòu)建的時候通常我們源代碼會放在src文件夾下,然后使用gulp進(jìn)行代碼的壓縮但金、合并韭山、圖片的優(yōu)化(根據(jù)需要)等等,我們會使用gulp冷溃。這里解決跨域的問題可以用gulp-connect結(jié)合http-proxy-middleware钱磅,此時我們在gulp-connect中的本地服務(wù)器進(jìn)行預(yù)覽調(diào)試。
gulpfile.js如下: 開發(fā)過程使用gulp server命令似枕,監(jiān)聽文件改動并使用livereload刷新盖淡;使用gulp命令進(jìn)行打包。
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');
// 定義環(huán)境變量凿歼,若為 dev褪迟,則代理src目錄; 若為prod答憔,則代理dist目錄
var env = 'prod'
// 跨域代理 將localhost:8088/api 映射到 https://api.shujumohe.com/
gulp.task('server', ['listen'], function() {
var middleware = proxyMiddleware(['/api'], {
target: 'https://api.shujumohe.com/',
changeOrigin: true,
pathRewrite: {
'^/api': '/'
}
});
connect.server({
root: env == 'dev' ? './src' : './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']);
原文鏈接: http://www.huzerui.com/blog/2017/07/03/vuejs-develop-h5-experience/
歡迎關(guān)注公眾號前端新視界牵咙,把握技術(shù)前沿資訊。