最近參與了APP內嵌H5頁面的開發,這次使用vuejs替代了jQuery,僅僅把vuejs當做一個庫來使用,效率提高之外代碼可讀性更強,在此分享一下自己的一些開發中總結的經驗。
關於布局方案
當拿到設計師給的UI設計圖,前端的首要任務就是布局和樣式,相信這對於大部分前端工程師來說已經不是什么難題了。移動端的布局相對PC較為簡單,關鍵在於對不同設備的適配。之前介紹了一篇關於移動端rem布局方案,這大致是網易H5的適配方案。不過實踐中發現淘寶開源的可伸縮布局方案效果更好且更容易使用。
網易雲的方案總結為:根據屏幕大小 / 750 = 所求字體 / 基准字體大小比值相等,動態調節html的font-size大小。
淘寶的方案總結為:根據設備設備像素比設置scale的值,保持視口device-width始終等於設備物理像素,接着根據屏幕大小動態計算根字體大小,具體是將屏幕划分為10等分,每份為a,1rem就等於10a。
通常我們會拿到750寬的設計稿,這是基於iPhone6的物理分辨率。有的設計師也許會偷懶,設計圖上面沒有任何的標注,如果我們邊開發邊量尺寸,無疑效率是比較低的。要么讓設計師標注上,要么自食其力。如果設計師實在沒有時間,推薦使用markman進行標注,免費版閹割了一些功能(比如無法保存本地)不過基本滿足了我們的需求了。
標注完成后開始寫我們的樣式,使用了淘寶的lib-flexible庫之后,我們的根字體基准值就為750/100*10 = 75px。此時我們從圖中若某個標注為100px,那么css中就應該設置為100/75 = 1.333333rem。所以為了提高開發效率,可以使用px轉化為rem的插件。如果你使用sublimeText,可以用 rem-unit
如果你用vscode編輯器,推薦 cssrem
使用rem單位注意以下幾點:
1.在所有的單位中,font-size推薦使用px,然后結合媒體查詢進行重要節點的控制,這樣可以滿足突出或者弱化某些字體的需求,而非整體調整。
2.眾向的單位可以全部使用px,橫向的使用rem,因為移動設備寬度有限,而高度可以無限向下滑動。但這也有特例,比如對於一些活動注冊頁面,需要在一屏幕內完全顯示,沒有下拉,這時候所有眾向或者橫向都應該使用rem作為單位。如圖:
左圖的表單高度單位由於下邊空距較大,使用px在不同屏幕顯示更加;而右邊的活動注冊頁由於不能出現滾動條,所有的眾向高度、margin、padding都應該使用rem。
- border、box-shadow、border-radius等一些效果應該使用px作為單位。
基於接口返回數據的屬性注入
可能大家不明白什么叫"基於接口返回數據的屬性注入",在此之前,先說一下表單數據的綁定方式,一個重要的點是有幾份表單就分開幾個表單對象進行數據綁定。
已上圖公積金查詢為例,由於不同城市會有不同的查詢要素,可能登陸方式只有一種,也可能有幾種。比如上圖有三種登陸方式,在使用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本地服務器預覽,所以請求接口會遇到跨域的問題。 在項目構建的時候通常我們源代碼會放在src文件夾下,然后使用gulp進行代碼的壓縮、合並、圖片的優化(根據需要)等等,我們會使用gulp。這里解決跨域的問題可以用gulp-connect結合http-proxy-middleware,此時我們在gulp-connect中的本地服務器進行預覽調試。 gulpfile.js如下: 開發過程使用gulp server命令,監聽文件改動並使用livereload刷新;使用gulp命令進行打包。
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');
// 定義環境變量,若為 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/