Git Product home page Git Product logo

blog's People

Contributors

punkmoon avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

blog's Issues

高性能Javascript读书笔记

加载和执行

  • 脚本的位置放到body标签的底部,避免脚本阻塞
  • 通过合并脚本文件,减少脚本文件的请求
  • 脚本延迟执行。defer需要等待页面完成后执行,async是加载完后自动执行。使用defer时,需要确保不会修改DOM

数据存取

作用域链

解释

当函数被创建的时候,他的作用域链中就被插入了一个对象变量,这个全局对象代表着在全局范围内定义的变量。例如window/document/navigater等

执行这个函数时会创建一个称为执行环境 的内部对象。每个执行环境都是独一无二的,每执行一次就会创建一次执行环境。每个执行环境都有自己的作用域链。

当执行环境被创建的时候,他的作用域链就初始化为当前运行函数[[Scope]]属性的对象,也就是那个全局对象

这个初始化的过程一旦完成,一个称为活动对象的新对象就为执行环境创建好了。活动对象同时被推入作用域链的最前端。

在函数执行过程中,每遇到一个变量就会搜索执行环境中的作用域链,查找同名的标识符。搜索过程中作用域链头部开始,也就是当前运行函数的活动对象。如果找到,就是用这个标识符对应的变量,反之继续搜索作用域链中的下个对象。

应用

  • 尽可能是使用局部变量,如果多次引用全局变量,可以储存到局部变量里。

  • with语句 try-catch语句 会动态改变作用域所以尽量避免使用。

  • 当闭包被创建的时候,它的[[Scope]]被初始化成和当前函数一样的对象,由于闭包的[[Scope]]属性包含了和执行环境作用域链相同的对象的引用,因此会产生副作用。

    引入闭包时,由于引用仍然存在于闭包的[[Scope]]对象中,因此激活对象无法被销毁。这意味着闭包函数需要更多的内存开销。

    闭包代码执行时会创建一个自身创建的活动对象。会涉及到频繁的跨作用域访问标识符。

    减轻闭包对执行速度的影响:将常用的跨作用域变量存储在局部变量中,然后直接访问局部变量。

  • 嵌套对象成员会影响性能

  • 属性或方法在原型链中的位置月神,访问它的速度越慢

DOM

重绘与重排

  • DOM树表示页面结构,渲染树表示DOM节点如何显示
  • DOM的变化影响了元素的几何属性,浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树,这个过程叫做重排
  • 完成重排后,浏览器重新绘制受影响的部分到屏幕中,该过程成为重绘
  • 不改变几何属性的DOM变化,例如背景颜色改变只会发生重绘,不会重排
  • 当需要对DOM元素进行一系列操作的时候,可以通过以下步骤来减少重绘和和重排
    • 使元素脱离文档流
      • 隐藏元素,应用修改,重新显示
      • 使用document fragment创建一个子树 完成后替换原始元素document.createDocumentFragment();
      • 原始元素拷贝到一个脱离文档的节点中,修改副本,完成后替换原始元素
    • 使其应用多重改变
    • 将元素带回文档中

事件委托

每个事件都会经历三个阶段:

  • 捕获
  • 到达目标
  • 冒泡

算法和流程控制

  • for-in循环明显要慢,因为每次循环都要同事搜索实例和原型属性
  • 优化if-else
    • 确保最可能出现的条件放在首位
    • 将条件组织成一系列的嵌套

字符串和正则表达式

  1. 正则表达处理步骤:
    • 编译
    • 设置起始位置:lastIndex属性
    • 匹配每个正则表达式字元
    • 匹配成功或者失败

快速响应的用户界面

  1. setTimeout中的第二个参数表示的是任务何时被添加到到UI队列,而不是一定会在这段时间后执行
  2. 无论发生何种情况,创建一个定时器会造成ui线程暂停。
  3. setInterval()和setTimeout()最主要的区别是如果UI队列中已经存在同一个setInterval()创建的任务,那么后续的任务不会被添加到UI队列中

数据请求

  1. 对于那些不会改变服务器状态,只会获取数据的请求,应该使用GET。经get请求的数据会被缓存起来。
  2. 对于少量数据而言,一个GET请求往服务器只发送一个数据包。而一个POST请求,至少要发送两个数据包,一个装载头信息,另一个装载POST正文。
  3. 优化AJAX性能:
    • 通过设置HTTP头信息里的缓存时间缓存数据
    • 减少请求数
    • 缩短页面的加载时间

[华硕AC68U+梅林固件]折腾记

上手体验

  • 用之前的渣路由器访问NAS的文件速度惨不忍睹,连上这款路由器的5G信号,传输速度峰值目测能到80+ MB/s,电脑上看NAS上的电影再也没了卡顿的感觉
  • 连接外网的下载速度没有多大提升,50M带宽PT满速下载可以到7MB/s

刷梅林固件

紧急救援模式

可能由于新买的路由器固件版本太高,第一次没有刷成功。后来自己尝试了好久紧急救援模式都没成功,最后拿出说明书一看,原来是按错了RESET按钮...

具体操作步骤以下的清单都有说明。梅林固件最新版本是7.8,需要注意的是只有7.x以上的固件才可以在软件中心离线安装。

参考清单

感谢开发者和出教程的大佬!尤其看BIG东东的视频教程可以少走很多弯路,我折腾了好了才想起来看(⊙﹏⊙)b

最期待的两个功能

科学上网

因为NAS上需要使用Dropbox和GoogleDrive的同步功能,苦于一直没有好的解决方案,路由器这个层面实现了科学上网后连接NAS,这个痛点就轻而易举的解决了。

电脑和手机因为有ShadowSocks,所以路由器设置了这两个设备不走ss。

去广告

视频APP去广告需要在路由里添加HTTPS规则,然后安装证书。目前没有发现什么问题。

参考清单

iPhone X适配总结

屏幕尺寸

垂直方向上,iPhone X的显示宽度与iPhone 6,iPhone 7 和 iPhone 8 的 4.7 英寸一样,但是比4.7英寸的显示屏高145pt。

安全区域

安全区域指的是一个可视窗口范围,处于安全区域的内容不受圆角(corners)、齐刘海(sensor housing)、小黑条(Home Indicator)影响

viewport-fit

通过对meta标签viewport的扩展,可以调整页面的展现区域。viewport-fit有三个可选值:

  • contain: 使页面展示在安全区域内。
  • cover: 使页面占满屏幕。
  • auto: 和 contain 选项表现一样

env() 和 constant()

iOS11 新增特性,Webkit 的一个 CSS 函数,用于设定安全区域与边界的距离,有四个预定义的变量:

  • safe-area-inset-left:安全区域距离左边边界距离
  • safe-area-inset-right:安全区域距离右边边界距离
  • safe-area-inset-top:安全区域距离顶部边界距离
  • safe-area-inset-bottom:安全区域距离底部边界距离

因为之前使用的constant已经被弃用,所以需要我们向后兼容:

padding-bottom: constant(safe-area-inset-bottom); /* 兼容 iOS < 11.2 */
padding-bottom: env(safe-area-inset-bottom); /* 兼容 iOS >= 11.2 */

适配

  1. 设置网页在可视窗口的布局方式

    使页面完全覆盖整个窗口

    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"/>

    只有设置了 viewport-fit=cover,才能使用 env()。

  2. 页面主体内容限定在安全区域内

    body {
      padding-bottom: constant(safe-area-inset-bottom);
      padding-bottom: env(safe-area-inset-bottom);
    }
    
  3. fixed 元素的适配

    如果元素是fixed定位且bottom=0,那么除了设置body的padding-bottom还不够,还需要给它自身添加padding,因为它是相对于屏幕最底部定位的。

    {
      padding-bottom: constant(safe-area-inset-bottom);
      padding-bottom: env(safe-area-inset-bottom);
    }
    

    或者通过计算函数 calc 覆盖原来高度:

    {
         height: calc(60px(假设值) + constant(safe-area-inset-bottom));
         height: calc(60px(假设值) + env(safe-area-inset-bottom));
    }
    

    注意,这个方案需要吸底条必须是有背景色的,因为扩展的部分背景是跟随外容器的,否则出现镂空情况。

    如果元素是fixed定位且bottom不等于0,则只调整位置就可以了,增加margin-bottom或者改变bottom。

参考

webpack配合gulp构建多页面项目笔记

之前在网上找前端模块化以及自动化构建的解决方案,大多主要是针对单页面的,而且介绍的也很浅显,后来找到这篇博客gulp + webpack 构建多页面前端项目,然后在他的基础上针对自己项目做了修改和优化,并加上适当的注释,方便理解,在此对原作者表示感谢。

项目结构

入口文件放在根目录src

├─app
│  │  index.html
│  │
│  └─user
│          index.html
│          login.html
│          register.html
│
├─css
│      reset.css
│      sprite.css
│      style.scss
│
├─images
│      logo.png
│      user_bg.jpg
│
└─js
    │  index.js
    │  login.js
    │  reg.js
    │
    └─lib
            touch.js
            utils.js

出口文件在根目录dist下.

页面配置

  • css合并为一个文件,在head内通过link标签引入

webpack配置

  • 在body底部引入公共js文件以及页面对应的出口文件
var path=require('path');
var webpack=require('webpack');
var fs=require('fs');
var uglifyJsPlugin = webpack.optimize.UglifyJsPlugin;
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
var srcDir = path.resolve(process.cwd(), 'src');
//获取入口文件
function getEntry() {
    var jsPath = path.resolve(srcDir, 'js');
    var dirs = fs.readdirSync(jsPath);
    var matchs = [], files = {};
    var chunks=[];
    dirs.forEach(function (item) {
        matchs = item.match(/(.+)\.js$/);
        if (matchs) {
          chunks.push(matchs[1]);
            files[matchs[1]] = path.resolve(srcDir, 'js', item);
        }
    });
    return {
      files:files,
      chunks:chunks
    };
}
module.exports={
  devtool: 'eval-source-map',//配置生成Source Maps,方便出错时调试
  entry:getEntry().files,
  output:{
    path:path.join(__dirname,'dist/js/'),
    filename:'[name].js',
    chunkFilename: "[chunkhash].js",
    publicPath:'/js/'//设置devServer时需要
  },
  //生成静态服务器
  devServer: {
    contentBase: __dirname+'/dist',//本地服务器所加载的页面所在的目录
    colors: true,//终端中输出结果为彩色
    historyApiFallback: true,//不跳转
    inline: true,//实时刷新
    port:3000
  },
  module:{
    loaders:[
      {
        test:/\.scss$/,
        loader:'css!sass'
      },
      {
        test:/\.css$/,
       loader: 'css'
      },
      {
        test: /\.(png|jpg|gif)$/,
        loader: 'url',
        query:{
          limit:8192,//小于80k的图片自动转成base64
          name:'../images/'+'[name].[ext]'
        }
       }
    ]
  },
  resolve:{
    alias:{
      //定义别名,方便引用
      zepto:__dirname+'/node_modules/webpack-zepto/index.js',//不使用webpack-zepto会报错
      touch:srcDir+'/js/lib/touch.js',
      utils:srcDir+'/js/lib/utils.js',
      scss:srcDir+'/css/style.scss',
      reset:srcDir+'/css/reset.css'
    }
  },
  plugins:[
    //公用模块,自动加载为全局变量
    new webpack.ProvidePlugin({
      $:'zepto',
      Zepto:'zepto'
    }),
    //将入口文件里的公用js提取出来并合并成common.js
    new CommonsChunkPlugin('common.js',getEntry().chunks)
     new uglifyJsPlugin({
       compress:{
          warnings:false
       }
     })
  ]
};

gulp配置

gulp这一部分主要任务是实现css压缩合并、修改文件后自动刷新、自动生成精灵图等功能。

var gulp = require('gulp'),
    sass=require('gulp-sass'),
    gutil = require('gulp-util'),
    fileinclude = require('gulp-file-include'),
    webpack = require('webpack'),
    webpackConfig = require('./webpack.config.js'),
    spritesmith = require('gulp.spritesmith'),
    clean=require('gulp-clean'),
    browserSync = require('browser-sync').create(),
    reload = browserSync.reload,
    minifyCss = require('gulp-minify-css'),
    buffer = require('vinyl-buffer'),
    merge = require('merge2'),
    concat = require('gulp-concat');
//实现自动刷新
gulp.task('browser-sync',function() {
    browserSync.init({
      //这里自动生成http://loaclhost:3000的静态服务器
        server: {
            baseDir: "./dist"
        }
    });
    gulp.watch('src/css/*',['sass']);//监听css改动,无需刷新就可以看到改动效果
    gulp.watch('src/app/**/*.html',['fileinclude'])
    .on('change', reload);//监听html文件改动,自动刷新
});
//拷贝图片
gulp.task('copy:images', function (done) {
    gulp.src(['src/images/**/*']).pipe(gulp.dest('dist/images')).on('end', done);
});
//用于在html文件中直接include文件
gulp.task('fileinclude', function (done) {
    gulp.src(['src/app/**/*.html'])
        .pipe(fileinclude({
          prefix: '@@',
          basepath: '@file'
        }))
        .pipe(gulp.dest('./dist'))
        .on('end', done);
});
gulp.task('sass', ['sprite'],function() {
   return  gulp.src(['src/css/*.scss', 'src/css/*.css'])
    .pipe(sass())
    .on('error', sass.logError)
    .pipe(concat('style.min.css'))
    .pipe(minifyCss({
      keepSpecialComments: 0
    }))
    .pipe(gulp.dest('./dist/css/'))
    .pipe(browserSync.stream());//此处重要
});
//自动生成精灵图并生成公共样式
gulp.task('sprite', function () {
  var spriteData = gulp.src('src/images/icons/*.png').pipe(spritesmith({
    imgName: 'sprite.png',
    cssName: 'sprite.css',
    cssFormat: 'css'
  }));
  //生成的精灵图及样式分别存放在不同的目录下
  var imgStream=spriteData.img
  .pipe(buffer())
  .pipe(gulp.dest('./dist/images/'));
  var cssStream=spriteData.css
  .pipe(gulp.dest('./src/css'));
  return merge(imgStream, cssStream);
});
gulp.task('watch', function (done) {
    gulp.watch('src/js/**/*.js',['build-js'])
        .on("change", reload)
        .on('end', done);
    gulp.watch('src/images/**/*',['copy:images'])
        .on("change", reload)
        .on('end', done);
    gulp.watch('src/images/icons/*',['sass'])
        .on("change", reload)
        .on('end', done);
});
var myDevConfig = Object.create(webpackConfig);
var devCompiler = webpack(myDevConfig);
//引用webpack对js进行操作
gulp.task("build-js", ['fileinclude'], function(callback) {
    devCompiler.run(function(err, stats) {
        if(err) throw new gutil.PluginError("webpack:build-js", err);
        gutil.log("[webpack:build-js]", stats.toString({
            colors: true,
            progress:true
        }));
        callback();
    });
});
//清除目录
gulp.task('clean',function(done){
  gulp.src('dist')
  .pipe(clean())
  .on('end',done);
});
//发布
gulp.task('dev', [ 'build-js','browser-sync','copy:images','sass','watch']);

package.json文件:

  "name": "nsyc",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "webpack --progress --colors",
    "build": "NODE_ENV=production webpack --config ./webpack.production.config.js --progress"
  },
  "keywords": [],
  "author": "vanboo",
  "license": "ISC",
  "devDependencies": {
    "browser-sync": "^2.17.6",
    "css-loader": "^0.25.0",
    "file-loader": "^0.9.0",
    "gulp": "^3.8.10",
    "gulp-clean": "^0.3.2",
    "gulp-concat": "^2.6.0",
    "gulp-file-include": "0.13.7",
    "gulp-sass": "latest",
    "gulp-util": "~2.2.9",
    "gulp-watch": "4.1.0",
    "gulp.spritesmith": "^6.2.1",
    "merge2": "^1.0.2",
    "sass-loader": "^4.0.2",
    "style-loader": "^0.13.1",
    "url-loader": "^0.5.7",
    "vinyl-buffer": "^1.0.0",
    "webpack": "^1.13.3",
    "webpack-dev-server": "^1.16.2",
    "webpack-zepto": "0.0.1"
  }
}

不足

原本想webpack任务和gulp任务单独运行的,webpack监听js,gulp监听HTML和css文件,但是devServer与gulp的browser-sync在引用js时有冲突,研究了一段时间也没找到好的解决方案,所以每次js变化都会重新打包一次。希望后面能继续优化吧。

解决IOS11光标错位的问题

问题

在ios11系统中模态框如果使用fixed定位,input在输入的时候会出现光标错位的问题。

之所以会有这个 bug 是因为,当输入框获取焦点弹出输入法时,浏览器向下移动页面,导致光标不跟随焦点。

有人已经给苹果提了提了bug ,不过截止到11.2.1中仍然没有解决。

DEMO

html

<section class="modal-layer">
        <div class="address">
          ...
        </div>
</section>

css

.modal-layer{
    display: none;
    position:fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    background: rgba(0, 0, 0, 0.77);
    z-index: 99;
  }
  .address{
      	position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
  }

页面

解决方案

如果将fixed定位改成relative定位是可行的,但是需要考虑这一层蒙版的对于body的定位,在触发focus事件时,记录scrollTop值,同时给蒙版.modal-layer加上一个类:

.ios-bugfix{
        position: absolute;
        height: 100vh;
    }

同时监听touchmove事件,禁止默认事件,禁止body滚动。

失去焦点时,去掉这个类即可,body滚动到之前记录的位置。

gulp监听文件中被@import引入的文件

gulp监听文件中被@import引入的sass文件

背景

由于公司静态文件目录的css文件众多,没办法监听所有的.scss文件然后编译成一个css文件,所以需要监听具体哪个.scss文件变化然后编译为对应css文件。随之而来带来的问题是,如果a.scss中通过@import引入了一个b.scss文件,如果b.scss文件变化,也需要编译一下a.scss。实际上这个时候a.scss是监听不到变化的,所以这个a.scss也不会去编译。

寻找解决方案

网上一通搜索,最后通过google watch scss import file 这些关键字找到了 Watch for changes in imported files as well,然后按图索骥找到了 Use sass-graph for accurate sass watching 。最后就是用sass-graph 这个插件获取到目录内文件的依赖关系。如下:

{ index: {,
    '/path/to/test/fixtures/a.scss': {
        imports: ['b.scss'],
        importedBy: [],
    },
    '/path/to/test/fixtures/b.scss': {
        imports: ['_c.scss'],
        importedBy: ['a.scss'],
    },
    '/path/to/test/fixtures/_c.scss': {
        imports: [],
        importedBy: ['b/scss'],
    },
}}

这时候监听所有被引入的文件变化,然后拿到被引入的文件list就迎刃而解了。

项目代码实现

  • config.json

    在这个文件里配置需要监听的被引入的文件目录

     "importPaths":["sass/m5/common/**.scss"]
    
  • Gulpfile.js

     var grapher = require('sass-graph');
     var importPaths=require('./config.json').importPaths;
     var importFolders=grapher.parseDir('./sass',['scss']);
     gulp.task('importWatch',function(){
        gulp.watch(importPaths,function (e) {
            var importedFiles=importFolders.index[e.path].importedBy;
            importedFiles.forEach(function (el) {
                var relativePath=getPath(el);//获取相对目录
                sassTask(relativePath);//执行sass任务编译为css
            });
        });
    });
    

Angular2学习笔记之HTTP

Angular2学习笔记之HTTP

service

一般HTTP请求在service中完成,因此需要在service中封装一个公共方法用来获取数据。

service.ts

import {Injectable} from '@angular/core';
import {Http,Response} from '@angular/http';
import 'rxjs/Rx';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';

@Injectable()
export class dataService{
	data:Object[];//声明变量类型
	constructor(private http: Http) {}
	getData(sendData:Object){
		if (this.data) {
		   return Promise.resolve(this.data);
		 }
		return new Promise(resolve=>{
			this.http
			.post('requestUrl',sendData)
			.map((r:Response)=>r.json()) 
			.subscribe(data => {
		        this.data = JSON.parse(data.result);//此处对获取到的数据类型的处理特别重要,必须为Object
		        resolve(this.data);
	      });
		})
	}
}

component

在组件中引入对应的service,执行service中的相关函数,将获取到的数据渲染到组件中。

import { Component,OnInit  } from '@angular/core';
import {dataService} from '../../service/service';
@Component({
  selector: 'page-home',
  providers:[dataService],//在providers添加引入的service
  templateUrl: 'home.html'
})
export class HomePage implements OnInit {
	data:Object[];
  constructor(
  public navCtrl: NavController,
  private dataService:dataService
  ){
  }
  ngOnInit():void{
  	var sendData={
  		
  	};
  	this.dataService.getData(sendData)
    .then(data=>{
      this.data=data;
    })
  	}
}

Promise与Observable

这两种都是对异步数据流的处理方法。

angular的http.get返回的是一个 RxJS `Observable ,需要使用一个toPromise方法转成一个Promise。

如果直接使用Observable的话就免去了这个转换的过程,按照我对文档的理解,如果数据不是一次请求结束后就万事大吉了,比如我们可能发起一个请求,中途可能要结束这次请求,在第一次请求结束前又要发起另一个请求。如果要实现这个需求,Promise会比较困难。

因此,Observable算是更高级一点的Promise,所以一般情况下还是用Observable比较好一点。

用法:(可以与上面的Promise进行对照)

service.ts

getData(sendData:Object,method:string):Observable<any>{
		return this.http
		.post('requestUrl',sendData)
		.map((r:Response)=>r.json().result);
	});
	}

component

this.dataService.getData(sendData)
  	.subscribe(
  		homeData=>this.data=Json.parse(homeData),
  		err=>{
  			console.log(err);
  		});
  	}

pipe

pipe相当于angular1中的filter,用法也和filter差不多,要注意的是自定义的pipe必须注入到AppModule的declarations中,angular默认提供的pipe不需要这一步。

Angular2 service学习笔记

Angular2 service组件学习笔记

命名方式

官方推荐的命名方式感觉有点繁琐,我比较习惯于在app目录下新建一个providers的文件夹存放可复用的service组件

Injectable

在export我们的service之前,需要引入Injectable,并增加@Injectable装饰器。

import { Injectable } from '@angular/core';

@Injectable()
export class myService {
}

调用service

  • 定义一个私有变量constructor(private myservice: myService) { }
  • 在调用service的@component添加providers: [myService]

 ngOnInit 生命周期钩子

每个组件都有自己从创建到销毁的生命周期,angular提供了能够捕获到这个生命周重要节点(key life moments)的钩子。ngOnInit是其中的一个。

在调用service中的方法时,使用ngOninit

 ngOnInit(): void {
    this.method();
  }

promise

在调取后台数据时,使用promise异步调取数据,等获取到数据时,通过回调函数返回到组件中.

getHeroes(): Promise<Hero[]> {
  return Promise.resolve(HEROES);
}

调用service方法时:

getHeroes(): void {
  this.heroService.getHeroes().then(heroes => this.heroes = heroes);
}

注销新浪微博简易指南

注销新浪微博简易指南

入口

下载微博官方APP,个人主页->右上角设置->账号与安全->微博安全中心->其他账号类问题->如何注销微博,然后就看到了如下注册须知:(入口藏的真特么隐蔽)

解绑授权

PC登陆网页版微博,点右上角昵称进入个人主页,点击第三个Tab管理中心,左边栏有个我的应用,点击进入可以看到很多绑定的第三方应用,挨个解除绑定。

对于大多数人来说做完这一步理论上可以注销了,如果依然通不过,可以对照注销须知check一下有哪一步没有完成。

如果确认都解除完了,但还是出现这个提示,等几分钟再试就好。

邮件申请

上述步骤通过之后,对于大多数普通未认证的用户来说,把下面几条信息发送给微博客服的邮箱就可以了,然后等通知吧。

注册时间如果忘了的话可以抓包解决,

接口名称:https://api.weibo.cn/2/profile

字段:created_at。

拜拜

至此,可以跟恶心的渣浪说拜拜了。

Angular2中将cordova获取到的照片上传

最近在用Angular2+ionic2写一个饭否的第三方客户端,需要将拍照或者手机相册里得到的照片,通过饭否的Api上传,折腾了一个星期后,终于找到了解决方案。

需要传何种格式的照片数据

最常见的做法是通过前台一个向后台传一个formData,但是cordova调取手机相册或者相机得到的数据是base64或者一个文件路径,饭否的Api是没办法接收base64格式的photo数据的,另外如果照片数据过大,显然也不适合使用base64。虽然我也可以通过添加一个file类型的input去获取照片,但是这样的话,手机上点击后出现的选择菜单我没办法控制。所以,如何把一个图片路径或者base64转成formData才是关键。

寻找解决方案

首先我尝试了能否把图片路径进行转化,我找到了stackoverflow上一年前的一个问题:Append image file to form data ,里面提到用 cordova-plugin-file-transfer这个插件。

var fd = new FormData();
     window.resolveLocalFileSystemURL(attachment.img, function(fileEntry) {
         fileEntry.file(function(file) {
             var reader = new FileReader();
                 reader.onloadend = function(e) {
                      var imgBlob = new Blob([ this.result ], { type: "image/jpeg" } );
                      fd.append('attachment', imgBlob);
                      fd.append('uuid', attachment.uuid);
                      fd.append('userRoleId', 12345);
                      console.log(fd);
                      //post form call here
                 };
                 reader.readAsArrayBuffer(file);

         }, function(e){$scope.errorHandler(e)});
    }, function(e){$scope.errorHandler(e)});

我做了一下尝试,但是不知道是什么原因始终触发不了reader.onloadend这个函数,于是我又在cordova-plugin-file-transfer找到了另一个办法,但这个方法传到后台的数据,后台解析不到formData,所以这个办法也宣告失败。

var win = function (r) {
    console.log("Code = " + r.responseCode);
    console.log("Response = " + r.response);
    console.log("Sent = " + r.bytesSent);
}

var fail = function (error) {
    alert("An error has occurred: Code = " + error.code);
    console.log("upload error source " + error.source);
    console.log("upload error target " + error.target);
}

var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
options.mimeType = "text/plain";

var params = {};
params.value1 = "test";
params.value2 = "param";

options.params = params;

var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);

base64转formData

前两种方法都失败了,只能寄希望于这个办法可以成功,Google了一下,果然有解决方案:Convert Data URI to File then append to FormData ,而且已经是5年前的答案了,惭愧。这个方法通过将base64转成Blob,然后formData将转换过来的Blob append进去,这个Blob就是饭否api所需要接收的photo参数。不过这个方法依然不支持图片路径转换。

function dataURItoBlob(dataURI) {
    // convert base64/URLEncoded data component to raw binary data held in a string
    var byteString;
    if (dataURI.split(',')[0].indexOf('base64') >= 0)
        byteString = atob(dataURI.split(',')[1]);
    else
        byteString = unescape(dataURI.split(',')[1]);

    // separate out the mime component
    var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

    // write the bytes of the string to a typed array
    var ia = new Uint8Array(byteString.length);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }

    return new Blob([ia], {type:mimeString});
}

改好之后重新打包测试,上传照片成功!

由于饭否对上传图片的大小有限制,所以控制在2M左右的大小比较合适。通过测试,在进行Camera插件相关选项设置时,quality:80并且destinationType:'jpeg',比较合适。如果要生成png图片的话虽然照片质量要好一点,但是照片大小很容易超出限制。

ps,如果想选择相册照片进行上传时,无需安装Image Picker这个插件,只需要在Camera的选项配置中修改sourceType:PHOTOLIBRARY就可以了。

pps:后台接收formData的方法可参考这里:Posting images to twitter in Node.js using Oauth

mac配置git自动补全

mac配置git自动补全

安装homebrew

如果事先没有安装homebrew,执行命令

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

安装 bash-completion

brew install bash-completion

修改 ~/.bash_profile

cd ~
nano .bash_profile

将下面的语句添加到.bash_profile

source ~/.git-completion.bash
if [ -f $(brew --prefix)/etc/bash_completion ]; then
  . $(brew --prefix)/etc/bash_completion
fi

保存后重启terminal即可。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.