punkmoon / blog Goto Github PK
View Code? Open in Web Editor NEW笔记&日常记录
笔记&日常记录
如果事先没有安装homebrew,执行命令
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew install bash-completion
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即可。
一般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);
});
})
}
}
在组件中引入对应的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;
})
}
}
这两种都是对异步数据流的处理方法。
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相当于angular1中的filter,用法也和filter差不多,要注意的是自定义的pipe必须注入到AppModule的declarations中,angular默认提供的pipe不需要这一步。
当函数被创建的时候,他的作用域链中就被插入了一个对象变量,这个全局对象代表着在全局范围内定义的变量。例如window/document/navigater等
执行这个函数时会创建一个称为执行环境 的内部对象。每个执行环境都是独一无二的,每执行一次就会创建一次执行环境。每个执行环境都有自己的作用域链。
当执行环境被创建的时候,他的作用域链就初始化为当前运行函数[[Scope]]属性的对象,也就是那个全局对象
这个初始化的过程一旦完成,一个称为活动对象的新对象就为执行环境创建好了。活动对象同时被推入作用域链的最前端。
在函数执行过程中,每遇到一个变量就会搜索执行环境中的作用域链,查找同名的标识符。搜索过程中作用域链头部开始,也就是当前运行函数的活动对象。如果找到,就是用这个标识符对应的变量,反之继续搜索作用域链中的下个对象。
尽可能是使用局部变量,如果多次引用全局变量,可以储存到局部变量里。
with语句 try-catch语句 会动态改变作用域所以尽量避免使用。
当闭包被创建的时候,它的[[Scope]]被初始化成和当前函数一样的对象,由于闭包的[[Scope]]属性包含了和执行环境作用域链相同的对象的引用,因此会产生副作用。
引入闭包时,由于引用仍然存在于闭包的[[Scope]]对象中,因此激活对象无法被销毁。这意味着闭包函数需要更多的内存开销。
闭包代码执行时会创建一个自身创建的活动对象。会涉及到频繁的跨作用域访问标识符。
减轻闭包对执行速度的影响:将常用的跨作用域变量存储在局部变量中,然后直接访问局部变量。
嵌套对象成员会影响性能
属性或方法在原型链中的位置月神,访问它的速度越慢
每个事件都会经历三个阶段:
最近在用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);
前两种方法都失败了,只能寄希望于这个办法可以成功,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
可能由于新买的路由器固件版本太高,第一次没有刷成功。后来自己尝试了好久紧急救援模式都没成功,最后拿出说明书一看,原来是按错了RESET按钮...
具体操作步骤以下的清单都有说明。梅林固件最新版本是7.8,需要注意的是只有7.x以上的固件才可以在软件中心离线安装。
感谢开发者和出教程的大佬!尤其看BIG东东的视频教程可以少走很多弯路,我折腾了好了才想起来看(⊙﹏⊙)b
因为NAS上需要使用Dropbox和GoogleDrive的同步功能,苦于一直没有好的解决方案,路由器这个层面实现了科学上网后连接NAS,这个痛点就轻而易举的解决了。
电脑和手机因为有ShadowSocks,所以路由器设置了这两个设备不走ss。
视频APP去广告需要在路由里添加HTTPS规则,然后安装证书。目前没有发现什么问题。
在ios11系统中模态框如果使用fixed定位,input在输入的时候会出现光标错位的问题。
之所以会有这个 bug 是因为,当输入框获取焦点弹出输入法时,浏览器向下移动页面,导致光标不跟随焦点。
有人已经给苹果提了提了bug ,不过截止到11.2.1中仍然没有解决。
<section class="modal-layer">
<div class="address">
...
</div>
</section>
.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滚动到之前记录的位置。
下载微博官方APP,个人主页->右上角设置->账号与安全->微博安全中心->其他账号类问题->如何注销微博,然后就看到了如下注册须知:(入口藏的真特么隐蔽)
PC登陆网页版微博,点右上角昵称进入个人主页,点击第三个Tab管理中心,左边栏有个我的应用,点击进入可以看到很多绑定的第三方应用,挨个解除绑定。
对于大多数人来说做完这一步理论上可以注销了,如果依然通不过,可以对照注销须知check一下有哪一步没有完成。
如果确认都解除完了,但还是出现这个提示,等几分钟再试就好。
上述步骤通过之后,对于大多数普通未认证的用户来说,把下面几条信息发送给微博客服的邮箱就可以了,然后等通知吧。
注册时间如果忘了的话可以抓包解决,
接口名称:https://api.weibo.cn/2/profile
字段:created_at。
至此,可以跟恶心的渣浪说拜拜了。
垂直方向上,iPhone X的显示宽度与iPhone 6,iPhone 7 和 iPhone 8 的 4.7 英寸一样,但是比4.7英寸的显示屏高145pt。
安全区域指的是一个可视窗口范围,处于安全区域的内容不受圆角(corners)、齐刘海(sensor housing)、小黑条(Home Indicator)影响
通过对meta标签viewport的扩展,可以调整页面的展现区域。viewport-fit有三个可选值:
iOS11 新增特性,Webkit 的一个 CSS 函数,用于设定安全区域与边界的距离,有四个预定义的变量:
因为之前使用的constant已经被弃用,所以需要我们向后兼容:
padding-bottom: constant(safe-area-inset-bottom); /* 兼容 iOS < 11.2 */
padding-bottom: env(safe-area-inset-bottom); /* 兼容 iOS >= 11.2 */
使页面完全覆盖整个窗口
<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()。
body {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
如果元素是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。
之前在网上找前端模块化以及自动化构建的解决方案,大多主要是针对单页面的,而且介绍的也很浅显,后来找到这篇博客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
下.
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这一部分主要任务是实现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变化都会重新打包一次。希望后面能继续优化吧。
由于公司静态文件目录的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
});
});
});
官方推荐的命名方式感觉有点繁琐,我比较习惯于在app目录下新建一个providers的文件夹存放可复用的service组件
在export我们的service之前,需要引入Injectable,并增加@Injectable装饰器。
import { Injectable } from '@angular/core';
@Injectable()
export class myService {
}
constructor(private myservice: myService) { }
providers: [myService]
每个组件都有自己从创建到销毁的生命周期,angular提供了能够捕获到这个生命周重要节点(key life moments)的钩子。ngOnInit是其中的一个。
在调用service中的方法时,使用ngOninit
ngOnInit(): void {
this.method();
}
在调取后台数据时,使用promise异步调取数据,等获取到数据时,通过回调函数返回到组件中.
getHeroes(): Promise<Hero[]> {
return Promise.resolve(HEROES);
}
调用service方法时:
getHeroes(): void {
this.heroService.getHeroes().then(heroes => this.heroes = heroes);
}
1111
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.