第130天:移动端-rem布局
一、關于布局方案
當拿到設計師給的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
使用rem單位注意以下幾點:
在所有的單位中,font-size推薦使用px,然后結合媒體查詢進行重要節點的控制,這樣可以滿足突出或者弱化某些字體的需求,而非整體調整。
縱向的單位可以全部使用px,橫向的使用rem,因為移動設備寬度有限,而高度可以無限向下滑動。但這也有特例,比如對于一些活動注冊頁面,需要在一屏幕內完全顯示,沒有下拉,這時候所有縱向或者橫向都應該使用rem作為單位。如圖:
左圖的表單高度單位由于下邊空距較大,使用px在不同屏幕顯示更加;而右邊的活動注冊頁由于不能出現滾動條,所有的眾向高度、margin、padding都應該使用rem。
?3. border、box-shadow、border-radius等一些效果應該使用px作為單位。
二、基于接口返回數據的屬性注入
可能大家不明白什么叫"基于接口返回數據的屬性注入",在此之前,先說一下表單數據的綁定方式,一個重要的點是有幾份表單就分開幾個表單對象進行數據綁定。
已上圖公積金查詢為例,由于不同城市會有不同的查詢要素,可能登陸方式只有一種,也可能有幾種。比如上圖有三種登陸方式,在使用vue布局時,有兩種方案。一是只建立一個表單用于數據綁定,點擊按鈕觸發判斷;而是有幾種登陸方式建立幾個表單,用一個字段標識當前顯示的表單。由于使用第三方的接口,一開始也沒有先進行接口返回數據結構的查看,采用了第一種錯誤的方式,錯誤一是每種登陸方式下面的登陸要素的數量也不同,錯誤二是數據綁定在同一個表單data下,當用戶在用戶名登陸方式輸入用戶名密碼后,切換到客戶號登陸方式,就會出現數據錯亂的情況。
解決完布局問題后,我們需要根據設計圖定義一些狀態,比如當前登陸方式的切換、同意授權狀態的切換、按鈕是否可以點擊的狀態、是否處于請求中的狀態。當然還有一些app穿過來的數據,這里就忽略了。
1 data: { 2 tags: { 3 arr: [''], 4 activeIndex: 0 5 }, 6 isAgreeProxy: true, 7 isLoading: false 8 }接著審查一下接口返回的數據,推薦使用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字段用于綁定,這就是所謂的基于接口返回數據的屬性注入。
1 methods: { 2 queryloginWays: function(channel_type, channel_code) { 3 var params = new URLSearchParams(); 4 params.append('channel_type', channel_type); 5 params.append('channel_code', channel_code); 6 axios.post(this.loginParamsProxy, params) 7 .then(function(res) { 8 console.log(res); 9 var code = res.code || res.data.code; 10 var msg = res.message || res.data.message; 11 var loginWays = res.data.data ? res.data.data : res.data; 12 // 查詢失敗 13 if (code != 2005) { 14 alert(msg); 15 return; 16 } 17 // 添加input字段用于v-model綁定 18 loginWays.forEach(function(loginWay) { 19 loginWay.fields.forEach(function(field) { 20 field.input = ''; 21 }) 22 }) 23 this.loginWays = loginWays; 24 this.tags.arr = loginWays.map(function(loginWay) { 25 return loginWay.label; 26 }) 27 }.bind(this)) 28 } 29 }即使返回的數據有我們不需要的數據也沒有關系,這樣保證我們不會遺失進行下一步登陸所需要的數據。
這樣多個表單綁定數據問題解決了,那么怎么進行頁面間數據傳遞?如果是app傳過來,那么通常使用URL拼接的方式,使用window.location.search獲得queryString后再進行截取;如果通過頁面套入javaWeb中,那么直接使用"${字段名}"就能獲取,注意要js中獲取java字段需要加雙引號。
computed: {// 真實姓名realName: function() {return this.getQueryVariable('name') || ''},// 身份證identity: function() {return parseInt(this.getQueryVariable('identity')) || ''},/*If javaWebrealName: 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命令進行打包。
1 var gulp = require('gulp'); 2 var concat = require('gulp-concat'); 3 var uglify = require('gulp-uglify'); 4 var autoprefixer = require('gulp-autoprefixer'); 5 var useref = require('gulp-useref'); 6 var connect = require('gulp-connect'); 7 var proxyMiddleware = require('http-proxy-middleware'); 8 // 定義環境變量,若為 dev,則代理src目錄; 若為prod,則代理dist目錄 9 var env = 'prod' 10 // 跨域代理 將localhost:8088/api 映射到 https://api.shujumohe.com/ 11 gulp.task('server', ['listen'], function() { 12 var middleware = proxyMiddleware(['/api'], { 13 target: 'https://api.shujumohe.com/', 14 changeOrigin: true, 15 pathRewrite: { 16 '^/api': '/' 17 } 18 }); 19 connect.server({ 20 root: env == 'dev' ? './src' : './dist', 21 port: 8088, 22 livereload: true, 23 middleware: function(connect, opt) { 24 return [middleware] 25 } 26 }); 27 }); 28 gulp.task('html', function() { 29 gulp.src('src/*.html') 30 .pipe(useref()) 31 .pipe(gulp.dest('dist')); 32 }); 33 gulp.task('css', function() { 34 gulp.src('src/css/main.css') 35 .pipe(concat('main.css')) 36 .pipe(autoprefixer({ 37 browsers: ['last 2 versions'], 38 cascade: false 39 })) 40 .pipe(gulp.dest('dist/css/')); 41 gulp.src('src/css/share.css') 42 .pipe(concat('share.css')) 43 .pipe(autoprefixer({ 44 browsers: ['last 2 versions'], 45 cascade: false 46 })) 47 .pipe(gulp.dest('dist/css/')); 48 gulp.src('src/vendors/css/*.css') 49 .pipe(concat('vendors.min.css')) 50 .pipe(autoprefixer({ 51 browsers: ['last 2 versions'], 52 cascade: false 53 })) 54 .pipe(gulp.dest('dist/vendors/css')); 55 return gulp 56 }); 57 gulp.task('js', function() { 58 return gulp.src('src/vendors/js/*.js') 59 .pipe(concat('vendors.min.js')) 60 .pipe(uglify()) 61 .pipe(gulp.dest('dist/vendors/js')); 62 }); 63 gulp.task('img', function() { 64 gulp.src('src/imgs/*') 65 .pipe(gulp.dest('dist/imgs')); 66 }); 67 gulp.task('listen', function() { 68 gulp.watch('./src/css/*.css', function() { 69 gulp.src(['./src/css/*.css']) 70 .pipe(connect.reload()); 71 }); 72 gulp.watch('./src/js/*.js', function() { 73 gulp.src(['./src/js/*.js']) 74 .pipe(connect.reload()); 75 }); 76 gulp.watch('./src/*.html', function() { 77 gulp.src(['./src/*.html']) 78 .pipe(connect.reload()); 79 }); 80 }); 81 gulp.task('default', ['html', 'css', 'js', 'img']);?
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的第130天:移动端-rem布局的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: maven jdk 版本配置
- 下一篇: “六神”——技术提高开发效率的一个方案