第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单位注意以下几点:

  1. 在所有的单位中,font-size推荐使用px,然后结合媒体查询进行重要节点的控制,这样可以满足突出或者弱化某些字体的需求,而非整体调整。
  2. 纵向的单位可以全部使用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 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命令进行打包。

 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']);

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏腾讯NEXT学位

前端、设计师福利再升级:用FSP彻底拥抱中文WebFont时代

1932
来自专栏腾讯开源的专栏

【开源公告】高性能的图片框架 LKImageKit 正式开源

LKImageKit LKImageKit 是一个高性能的图片框架,包括了图片控件,图片下载、内存缓存、磁盘缓存、图片解码、图片处理等一系列能力。合理的架构和...

3804
来自专栏数据小魔方

左手用R右手Python系列——任务进度管理

一直觉得运行代码的时候,如果有一个提示任务运行进度的进度条提示就好,很多时候我们的程序运行时间普遍较长,如果程序运行没有任何提示,那简直是一场噩梦,根本不知道到...

3895
来自专栏龙首琴剑庐

原 linux中goldendict发声词

2564
来自专栏程序你好

使用Firefox开发工具做性能审计

在本文中,我们将介绍Firefox Web开发工具(“DevTools”)——一组工具,旨在帮助开发人员检查、调试、配置和优化网站和Web应用程序的性能。我们将...

1664
来自专栏程序员的碎碎念

JavaGUI编程之·引用类库Beautyeye改变外观

上一期讲到JavaGUI默认界面在win7上显示得比较粗糙,小编在原来的基础上对各种组件的属性进行大量修改才得以让原界面扁平化。上期推文发出后,就有大神提供建议...

1.1K9
来自专栏微信小程序开发

微信小程序开发常见问题(七)

知晓程序员,专注微信小程序开发的程序员! 一、判断小程序版本号 小程序的API是不断更新的,你可能使用某个API时,文档里会说明,此API在1.x.x版本开始...

5715
来自专栏程序员同行者

html基础+常用标签

1501
来自专栏武军超python专栏

2018年8月7号升级飞机大战的总结

今天学到的新单词: indentation  n缩进 transform  v改变,变换 collide v碰撞 recursion  n递归

1042
来自专栏编程微刊

手把手教你用vue-cli构建一个简单的路由应用

上一章说道:十分钟上手-搭建vue开发环境(新手教程)https://www.jianshu.com/p/0c6678671635

1091

扫码关注云+社区

领取腾讯云代金券