vuejs開發H5頁面總結 關於佈局方案 基於接口返回數據的屬性注入 關於前端跨域調試

最近參與了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。

  3. 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/

歡迎關注公衆號前端新視界,把握技術前沿資訊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章