两年前刚接触移动端开发,刚开始比较疑惑,每次遇到问题都是到社区里提问或者吸收前辈的经验分享,感谢热衷于分享的开发者为前端社区带来欣欣向上的生命力。本文结合先前写的文章和开发经验分享给你们,但愿也能帮助刚步入移动端开发的新人解惑。如下会以其中一个以公积金页面开发项目做为例子,介绍移动端的一些常见问题和使用Vuejs做为lib进行多页开发的经验。javascript
在项目中移动端最经常使用的自适应布局方案就是flexbox结合rem。规范的分栏式使用flexbox,其余大部分不规则视图使用rem,对于rem最经常使用的方案就是淘宝开源的可伸缩布局方案。css
根据设备设备像素比设置scale的值(scale = 1 / deviceRatio),这样能够保持视口device-width始终等于设备物理像素,接着根据屏幕大小动态计算根字体大小,具体是将屏幕划分为100等分,每份为a,1rem就等于10a。html
一般咱们会拿到750宽的设计稿,这是基于iPhone6的物理分辨率。有的设计师也许会偷懒,设计图上面没有任何的标注,若是咱们边开发边量尺寸,无疑效率是比较低的。要么让设计师标注上,要么自食其力。前端
若是设计师实在没有时间,推荐使用markman进行标注,免费版阉割了一些功能(好比没法保存本地)不过基本知足了咱们的需求了。vue
后来我发现比markman更好的标注工具PxCook,该工具能够显示PSD设计图中的图层的样式代码,对于前端来讲简直方便极了。java
标注完成后开始写咱们的样式,使用了淘宝的lib-flexible库以后,咱们的根字体基准值就为750/100*10 = 75px。此时咱们从图中若某个标注为100px,那么css中就应该设置为100/75 = 1.333333rem。因此为了提升开发效率,可使用px转化为rem的插件。下面是sublimeText和Vscode的转换插件:node
左图的表单高度单位因为下边空距较大,使用px在不一样屏幕显示更加;而右边的活动注册页因为不能出现滚动条,全部的众向高度、margin、padding都应该使用rem。nginx
以前发布的文章中,有个SF的前端小伙伴提出的问题: 文中做者有重点强调布局所有铺满,和下方与不少空隙的处理方案是不一样的,在工做中我遇到这种状况,设计师的设计稿宽度为750×1334,但实际的展现高度并无那么多,由于上方有导航栏还包括手机本身的状态栏展现,因此总体高度就达不到750,可是设计师设计稿是严格按照750进行设计的,这种状况下使用rem,严格按照设计师尺寸进行还原就会出现屏幕出现滚动条状况,请问针对这种状况您是怎么处理的?是从设计稿上规范,仍是从开发上有相应的措施
依旧以个人分享界面为例: 展现高度不一样一般发生在微信及浏览器端,由于前者没有地址栏和工具栏,这样显示高度一般会和设计师设计的视图吻合。那若是按照纯padding,margin即便所有使用rem,在浏览器端依旧会超出一屏高度,对于分享页面这种不是咱们想要看到的。这时候就要作出取舍,我对主体区域采用绝对定位,这样上面间隙虽然小,不过仍能保持在一个屏幕高度显示。若采用margin padding在设置,必然已出现滚动条。固然这样的前提是依赖设计图的,一般设计师会为了空间感有保留必定的间隙,也不会将主要对象高度设的太高,不然太撑满也很差看,开发上若是设计图宽高没有在必定界限以内,超出也没法避免,不过咱们这种分享界面一般是经过微信分享好友,经过浏览器打开的视图效果出现滚动条其实也不怎么影响不是么? 下面附上微信端和浏览器端的效果图:
微信端:
浏览器端:
通常移动端使用vue是为了数据交互频繁并且快速开发的页面,为何不使用单页SPA开发模式,缘由大概几点。
抛开使用单页的架构,开发多页应用时,一个页面交互逻辑与一个Vue实例对应。
"基于接口返回数据的属性注入"是我的建立的话术,抛开此概念,先说一下表单数据的绑定方式。
一个重要的点是有几份表单就分开几个表单对象进行数据绑定。
以上图公积金查询为例,因为不一样城市会有不一样的查询要素,可能登录方式只有一种,也可能有几种。好比上图有三种登录方式,在使用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本地服务器预览,因此请求接口会遇到跨域的问题,若是使用Gulp进行打包,可使用插件http-proxy-middleware,或者使用nginx。
在项目构建的时候一般咱们源代码会放在src文件夹下,而后使用gulp进行代码的压缩、合并、图片的优化(根据须要)等等,咱们会使用gulp。
解决跨域的问题能够用gulp-connect结合http-proxy-middleware,此时咱们在gulp-connect中的本地服务器进行预览调试。
gulpfile.js以下: 开发过程使用gulp server:dev
命令,监听文件改动并使用livereload刷新,而且代理src目录;使用gulp
命令进行打包;使用gulp server:dist
代理dist生产目录。
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');
// 开发跨域代理 将localhost:8088/api 映射到 https://api.xxxxx.com/
gulp.task('server:dev', ['listen'], function() {
var middleware = proxyMiddleware(['/api'], {
target: 'https://api.xxxxx.com/',
changeOrigin: true,
pathRewrite: {
'^/api': '/'
}
});
connect.server({
root: env == 'dev' ? './src' : './dist',
port: 8088,
livereload: true,
middleware: function(connect, opt) {
return [middleware]
}
});
});
// 打包后跨域代理
gulp.task('server:dist', ['listen'], function() {
var middleware = proxyMiddleware(['/api'], {
target: 'https://api.xxxxx.com/',
changeOrigin: true,
pathRewrite: {
'^/api': '/'
}
});
connect.server({
root: './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']);
复制代码
在nginx配置使用proxy_pass,须要注意一点: 若是在proxy_pass后面的url加/,表示绝对根路径;若是没有/,表示相对路径,把匹配的路径部分也给代理走。
server {
listen 80;
server_name localhost;
root [Your project root];
index index.html index.htm default.html default.htm;
location ^~/api {
proxy_pass https://api.xxxxx.com/;
}
}
复制代码
若是你开发的H5基于微信jsSDK,你必定接触过微信受权域名,微信会将受权数据传给一个回调页面,而回调页面必须在你配置的域名下(含子域名)。好比咱们获取用户的openid操做。而微信配置域名回去该域名根目录下检测一个xxx_verify_xxx.txt
文件,确保该域名是属于你的。
因此要想在微信开发调试工具中获取openid,咱们须要使用一种叫作内网穿透的工具。下面是本身比较经常使用的两个工具:
ngrok执行命令
ngrok -config ngrok.cfg start web
复制代码
在ngrok.exe目录须要一个配置文件ngrok.cfg
如下是配置示例:
server_addr: "tunnel.cn:4443"
trust_host_root_certs: false
tunnels:
web:
subdomain: "xxx"
proto:
http: 8086
https: 8086
复制代码
启动后xxx.tunnel.cn:4443会指向你本地的8086端口,将xxx_verify_xxx.txt
文件放到8086端口根目录便可配置受权域名成功。
花生壳免费版对于我的开通仅需6元,而后每个月会提供给你1G的流量,免费版不支持80端口,最多支持两个域名,须要下载桌面客户端。
添加域名映射很简单,免费版没法配置自定义域名,由花生壳自动生成。
PS:更多前端资讯、技术干货,请关注公众号「前端新视界」