fredshare / blog Goto Github PK
View Code? Open in Web Editor NEW护卫银河
Home Page: https://fredshare.github.com/blog/
护卫银河
Home Page: https://fredshare.github.com/blog/
//热力图上报
(function(){
//确定url_id
var hash = location.hash,urlId = 10001;
function initHash(){
var hash = location.hash;
if(hash == "#"){
urlId = 10001;
}else if(hash == "#hash1"){
urlId = 10002;
}else {
urlId = 10001;
}
}
initHash();
window.addEventListener("hashchange",function(){
initHash();
loadHeatmapJsBack();
});
//动态载入热力图js
var s = document.createElement("script");
s.src = "http://res.wx.qq.com/mmbizwap/en_US/htmledition/js/report/heatmapReport.js";//热力图js的cdn路径;
s.onload =loadHeatmapJsBack;
document.getElementsByTagName("head")[0].appendChild(s);
//载入热力图js后回调
function loadHeatmapJsBack(){
if(window.HeatmapReport){
HeatmapReport.init({
url_id:urlId,
radius:4//收归半径4*4
});
}
}
})();
XHProf是facebook开源出来的一个php轻量级的性能分析工具,跟Xdebug类似,但性能开销更低,还可以用在生产环境中,也可以由程序开关来控制是否进行profile。
以下是我在虚拟机上安装的过程记录:
在虚拟机上安装lampp,必须是develop版本的。否则编译是通不过的。我就是因为编译时遇到 include php.h 时就失败了。所以需要先确保下载并安装了 xampp 的 devel packages。下载地址请点击,然后解压到安装的目录tar -xvzf file -C /opt。下载 xampp 的 develop包的时候,注意要选择和自己当前的版本一致的 devel 包,因为 php 的扩展编译的时候,会附加版本信息,启动时进行检查,如果不一致,即便能够编译成功,也是不能够使用的。
wget http://pecl.php.net/get/xhprof-0.9.2.tgz
tar zxvf xhprof-0.9.2.tgz
cd xhprof-0.9.2
cp -r xhprof_htmlxhprof_lib /opt/lamp/htdocs #应用程序所在目录,其中xhprof_lib是生成统计数据用到的类库。xhprof_html是查看统计数据的时候,用到的类库。
cd extension
/opt/lampp/bin/phpize
./configure
make
make install
编译xhprof的时候出了很多问题,比如:
这个问题是需要安装 此插件。可查看解决。
[xhprof]
extension=xhprof.so
xhprof.output_dir=/home/sharexie/xhprof //如果不加存放目录的话,默认是放在/tmp下面
重启lampp:
/opt/lamp/lamp restart
cd /usr/src
wget http://www.graphviz.org/pub/graphviz/stable/SOURCES/graphviz-2.24.0.tar.gz
tarzxf graphviz-2.24.0.tar.gz
cd graphviz-2.24.0
./configure&&make && make install
上面的那个工具依赖libpng。到libpng官网down分源码,再次编译一下。
SF.NET上的地址,我下的是这个地址
测试代码:
<?php
index();
function index(){
phpinfo();
b();
}
function b(){
echo "i love you";
}
//启动xhprof
xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);
//停止xhprof
$xhprof_data = xhprof_disable();
//取得统计数据
print_r($xhprof_data);
$XHPROF_ROOT = realpath(dirname(__FILE__) . '/');
echo $XHPROF_ROOT;
include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_lib.php";
include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_runs.php";
include_once $XHPROF_ROOT . "/xhprof_lib/utils/callgraph_utils.php";
//保存统计数据,生成统计ID和source名称
$xhprof_runs = new XHProfRuns_Default();
print_r($xhprof_runs);
$run_id = $xhprof_runs->save_run($xhprof_data, "test"); //source名称是xhprof_foo
//弹出一个统计窗口,查看统计信息
echo "<script language='javascript'>window.open('../xhprof_html/index.php?run=" . $run_id . "&source=test');</script>";
?>
此类适用于目标对象的图片蒙板的透明度编辑,分离出蒙板的透明管道,复制颜色管道。比如,这能用在更改一个目标的透明蒙板,也能够通过一个压缩过的rgb图片和一个png32的透明蒙板合成一个jpg图片,相比一个单纯的包含argb值的png32格式图片,这能明显的减少图片大小。
重要提示:这个过滤器类暂时还不能正确的支持targetCtx,targetX/Y 参数。
这个例子创建了一个渐变的容器,然后将它用cacheCanvas缓存透明蒙板为一个100*100的图片。
function init() {
window.img = new Image();
img.src = "http://imgtest-lx.meilishuo.net/pic/r/72/31/ee3a366cf629d2ee840831352a29_600_600.jpeg";
img.onload = function(){
drawBitmap();
}
}
function drawBitmap(){
var stage = new createjs.Stage("demoCanvas");
var box = new createjs.Shape();
box.graphics.beginLinearGradientFill(["#000000", "rgba(0, 0, 0, 0)"], [0, 1], 0, 0, 200, 200);
box.graphics.drawRect(0, 0, 200, 200);
box.cache(0, 0, 200, 200);
var bitmap = new createjs.Bitmap(img);
bitmap.filters = [
new createjs.AlphaMaskFilter(box.cacheCanvas)
];
bitmap.cache(0, 0, 200, 200);
stage.addChild(bitmap);
bitmap.x = 0;
bitmap.y = 0;
stage.update();
bitmap.onClick = function(event) {
bitmap.rotation = 35;
bitmap.alpha = 0.5;
stage.update();
}
}
原始模式主要是两种,对象字面量和Object构造。如下
var a = {
name : 'share',
age : 19,
method : function(){console.log(this.name)}
}
var a = new Object();
a.name = 'share';
a.age = 19;
a.method = function(){console.log(this.name)}
原始模式有一个问题,如果要批量生成多个a1、a2,就得不断重复copy代码了,所以有了下面的方法,工厂模式、构造器模式、圣杯模式等。
function parent(name){
var child = {};
child.name = name;
child.method = function(){
console.log(this.name);
};
return child;
}
var childA = parent('lily');
var childB = parent('lucy');
childA.method(); //lily
childB.method(); //lucy
console.log(childA.method === childB.method) //false
工厂模式就是批量化生产,简单调用就可以进入造人模式。
由于是工厂暗箱操作的,所以你不能识别这个对象到底是什么类型。
经典工厂模式是直接执行parent()方法,而不是通过new的方式。混合工厂模式尽量不用。
工厂模式中每生成一个子类,就会重复生成相应的方法,代码臃肿。
function Parent(name){
this.name = name;
this.method = function(){
console.log(this.name);
}
}
var childA = new Parent('lily');
var childB = new Parent('lucy');
childA.method(); //lily
childB.method(); //lucy
console.log(childA.method === childB.method) //false
构造器模式,用new的方式构造子类。同时构造器函数首字母大写。
如果直接执行Parent(),那么属性方法都赋值给window对象了。
function Parent(){}
Parent.prototype.name = 'share';
Parent.prototype.method = function(){console.log(this.name)};
var childA = new Parent();
var childB = new Parent();
console.log(childA.name === childB.name); //true
console.log(childA.method === childB.method); //true
或者
function Parent(){}
Parent.prototype = {
name : 'share',
method : function(){console.log(this.name)}
};
var childA = new Parent();
var childB = new Parent();
console.log(childA.name === childB.name); //true
console.log(childA.method === childB.method); //true
原型属性和方法的共享,即所有实例中都只是引用原型中的属性方法,任何一个地方产生的改动会引起其他实例的变化。
function Parent(name){
this.name = name;
}
Parent.prototype = {
method : function(){
console.log(this.name);
}
}
var childA = new Parent('lily');
var childB = new Parent('lucy');
console.log(childA.name === childB.name); //false
console.log(childA.method === childB.method); //true
将构造器和原型模式的优点结合起来了。
需要独立的属性方法放入构造函数中,而可以共享的部分则放入原型中,这样做可以最大限度节省内存而又保留对象实例的独立性。
Object.create()可以接收两个参数:提供原型的对象,可选属性对象(这个对象包含对新创建对象的配置)。
var Car = {
drive: function (miles) {
return this.odometer += miles;
}
};
var tesla = Object.create(Car, {
'odometer': {
value: 0,
enumerable: true
}
));
console.log(tesla.drive(10));//10
今天一朋友对我说得很好,为什么回不去成都了?
在一线城市就现在的我来说,就缺钱,但是钱可以慢慢赚,但是在二线城市可能现阶段能有点钱,但是其他什么都没有,生活环境,便利性,机会,成就感,自由与相对的公平,二三线城市很多时候是靠关系的。
前言
之前在大公司里有现成文件发布系统。今年在创业公司,很多工具都得自己从头搭建,比如日志、itil监控、用户信息监控。这次是源码发布系统。在最开始我们直接使用ftp上传到idc机器,因为idc机器只有一台,用ftp很简单;随着业务不断扩大,idc机器越来越多,ftp发布就变得很麻烦了,你得一个文件一个文件上传不说,还得切换机器上传。我们研究了常见的文件同步软件后,采用了rsync软件同步。 rsync很方便的就是可以一次同步到多个机器,可以备份,方便回退;唯一的缺点就是命令行操作,不能查看以往的发布记录。所以当团队越来越大的时候很有必要基于sync做一个可视化的发布系统。
需要解决的问题点
一个一个解决
随便百度了下,发现有很多这样的开源项目,采用了jstree项目,这个项目对服务器上的路径的目录文件能很好的解析,同时配备js代码绘制树形结构,很方便就画好文档树了。如下图
* 任务发布
当点击文件的时候,将文件展示出来,并添加查看源码和文件对比的功能。
$('#tree')
.jstree({
'core' : {
'data' : {
'url' : '/release/getNodeInfo?path='+path+'&operation=get_node',
'data' : function (node) {
return { 'id' : node.id };
}
},
'check_callback' : function(o, n, p, i, m) {
if(m && m.dnd && m.pos !== 'i') { return false; }
if(o === "move_node" || o === "copy_node") {
if(this.get_node(n).parent === this.get_node(p).id) { return false; }
}
return true;
},
'themes' : {
'responsive' : false,
'variant' : 'small',
'stripes' : true
}
},
'sort' : function(a, b) {
return this.get_type(a) === this.get_type(b) ? (this.get_text(a) > this.get_text(b) ? 1 : -1) : (this.get_type(a) >= this.get_type(b) ? 1 : -1);
},
'contextmenu' : {
'items' : function(node) {
var tmp = $.jstree.defaults.contextmenu.items();
delete tmp.create.action;
tmp.create.label = "New";
tmp.create.submenu = {
"create_folder" : {
"separator_after" : true,
"label" : "Folder",
"action" : function (data) {
var inst = $.jstree.reference(data.reference),
obj = inst.get_node(data.reference);
inst.create_node(obj, { type : "default" }, "last", function (new_node) {
setTimeout(function () { inst.edit(new_node); },0);
});
}
},
"create_file" : {
"label" : "File",
"action" : function (data) {
var inst = $.jstree.reference(data.reference),
obj = inst.get_node(data.reference);
inst.create_node(obj, { type : "file" }, "last", function (new_node) {
setTimeout(function () { inst.edit(new_node); },0);
});
}
}
};
if(this.get_type(node) === "file") {
delete tmp.create;
}
return tmp;
}
},
'types' : {
'default' : { 'icon' : 'folder' },
'file' : { 'valid_children' : [], 'icon' : 'file' }
},
'unique' : {
'duplicate' : function (name, counter) {
return name + ' ' + counter;
}
},
'plugins' : ['state','dnd','sort','types','contextmenu','unique']
})
.on('delete_node.jstree', function (e, data) {
$.get('/release/getNodeInfo?path='+path+'&operation=delete_node', { 'id' : data.node.id })
.fail(function () {
data.instance.refresh();
});
})
.on('create_node.jstree', function (e, data) {
$.get('/release/getNodeInfo?path='+path+'&operation=create_node', { 'type' : data.node.type, 'id' : data.node.parent, 'text' : data.node.text })
.done(function (d) {
data.instance.set_id(data.node, d.id);
})
.fail(function () {
data.instance.refresh();
});
})
.on('rename_node.jstree', function (e, data) {
$.get('/release/getNodeInfo?path='+path+'&operation=rename_node', { 'id' : data.node.id, 'text' : data.text })
.done(function (d) {
data.instance.set_id(data.node, d.id);
})
.fail(function () {
data.instance.refresh();
});
})
.on('move_node.jstree', function (e, data) {
$.get('/release/getNodeInfo?path='+path+'&operation=move_node', { 'id' : data.node.id, 'parent' : data.parent })
.done(function (d) {
//data.instance.load_node(data.parent);
data.instance.refresh();
})
.fail(function () {
data.instance.refresh();
});
})
.on('copy_node.jstree', function (e, data) {
$.get('/release/getNodeInfo?path='+path+'&operation=copy_node', { 'id' : data.original.id, 'parent' : data.parent })
.done(function (d) {
//data.instance.load_node(data.parent);
data.instance.refresh();
})
.fail(function () {
data.instance.refresh();
});
})
.on('changed.jstree', function (e, data) {
if(data && data.selected && data.selected.length) {
var id = data.selected.join(':');
if(id.split('.').length>1){
//保存id信息
if(!isIdExist(id)){
saveId(id);
$('#js_file_list').append('<li class="jstree-node jstree-no"><span class="js_path">'+ id +'</span> <a href="#del" class="js_del">删除</a> <a href="#query" class="js_query">查看文件</a> <a href="#query" class="js_diff">对比文件</a></li>');
}
}
}
else {
//$('#data .content').hide();
$('#data .default').html('Select a file from the tree.').show();
}
});
function isIdExist(id){
for (var i = 0; i < fileList.length; i++) {
if(fileList[i] == id){
return true;
}
};
return false;
}
function saveId(id){
fileList.push(id);
}
function delId(id){
for (var i = 0; i < fileList.length; i++) {
if(fileList[i] == id){
fileList.splice(i,1)
}
};
}
$('body').delegate('.js_del','click',function(e){
var target = $(e.target).closest('li'),
id = target.find('.js_path').text();
console.log(target);
target.remove();
delId(id);
})
$('body').delegate('.js_query','click',function(e){
var target = $(e.target).closest('li'),
id = target.find('.js_path').text();
var loading_id = layer.load(2);
$.get('/release/getNodeInfo?path='+path+'&operation=get_content&id=' + id, function (d) {
if(d && typeof d.type !== 'undefined') {
switch(d.type) {
case 'text':
case 'txt':
case 'md':
case 'htaccess':
case 'log':
case 'sql':
case 'php':
case 'js':
case 'json':
case 'css':
case 'html':
//$('#data .code').show();
//$('#code').val(d.content);
//break;
case 'png':
case 'jpg':
case 'jpeg':
case 'bmp':
case 'gif':
/*$('#data .image img').one('load', function () { $(this).css({'marginTop':'-' + $(this).height()/2 + 'px','marginLeft':'-' + $(this).width()/2 + 'px'}); }).attr('src',d.content);
$('#data .image').show();*/
layer.close(loading_id);
layer.open({
type: 2,
title: false,
closeBtn: 0, //不显示关闭按钮
shade: [0],
area: ['340px', '215px'],
offset: 'rb', //右下角弹出
time: 1000, //1秒后自动关闭
shift: 2,
content: ['/release/fileInfo?path='+path+'&operation=get_content&id='+id, 'no'], //iframe的url,no代表不显示滚动条
end: function(){ //此处用于演示
layer.open({
type: 2,
title: '源码查看-'+id,
shadeClose: true,
shade: false,
maxmin: true, //开启最大化最小化按钮
area: ['893px', '600px'],
content: '/release/fileInfo?path='+path+'&operation=get_content&id='+id
});
}
});
break;
default:
//$('#data .default').html(d.content).show();
//$('#js_file_list').append('<li class="jstree-node jstree-no">'+data.selected.join(':')+' <a href="#del">删除</a> <a href="#query">查看文件</a></li>');
break;
}
}
});
})
$('body').delegate('#js_release','click',function(e){
console.log(fileList);
})
$('body').delegate('.js_diff','click',function(e){
var target = $(e.target).closest('li'),
id = target.find('.js_path').text();
var loading_id = layer.load(2);
layer.close(loading_id);
layer.open({
type: 2,
title: false,
closeBtn: 0, //不显示关闭按钮
shade: [0],
area: ['340px', '215px'],
offset: 'rb', //右下角弹出
time: 1000, //1秒后自动关闭
shift: 2,
content: ['/release/diffFile?path='+path+'&id='+id, 'no'], //iframe的url,no代表不显示滚动条
end: function(){ //此处用于演示
layer.open({
type: 2,
title: '源码对比-'+id,
shadeClose: true,
shade: false,
maxmin: true, //开启最大化最小化按钮
area: ['893px', '600px'],
content: '/release/diffFile?path='+path+'&id='+id
});
}
});
})
});
效果如下图:
文件源码查看和问价对比
文件源码查看比较简单,路径发到后台,后台读出文档内容返回给前端,前端用codemirror渲染下就行了,如下:
文件对比就比较麻烦了,你得分别读开发环境下的文件的线上的文件,再一行一行的对比,还得渲染出来,不过网上还蛮多这样的开源项目,拿来主义,哈哈。我采用了[php-diff](https://github.com/chrisboulton/php-diff)项目。效果如下:
审视产品、团队、市场
创业过程中遇到的坑
关于带团队的理解
寻找早中期公司机会时,一般是从产品、团队、市场三个方面来考察这个公司、项目是否可靠可行。三个方面的情况都需要自己亲自勘察。
比如初创团队和创始人。除了单独和他面谈,通过他对这个市场的分析,对产品的规划等了解1、公司业务的情况;2、了解这个人对整个行业的理解;3、分析人物性格,做事风格。同时也需要多从行业人士或者其他人的角度来了解这些初创团队,特别是创始人。因为创始人在初创公司太重要,如果多人做决定,出现妥协的情况就会容易导致没有人来负责;所以一般是必须有一个一锤定音的人,一般就是创始人。如果创始人(或者ceo)对行业了解不深(很多互联网从业者有很好的背景,打着互联网+的名号杀入其他行业),请做好碰多次壁的准备。同时从面谈来分析一个人毕竟有限且容易被误导,从事偏运营的人说话易满,很容易被鼓舞;从事偏技术的人说话很实在,很多人有货说不出来。所以听运营讲话要听一半信一半,听技术出身的人讲可以适当放大一下。
不太清楚其他公司的情况,反正我们当时公司的情况到中后期,产品负责人调去负责供应链,然后又调去负责各地城市站, 运营。技术负责人负责产品线。下面的人的工作职责也变化很快。当然创业公司业务线不断调整变化我没什么意见,但是人员职责变动太快导致了一个问题:员工没有负责任的心态,同时成就感不强,渐渐的就会失去工作积极性。
从2015年3月成立时候15个人左右,到A轮9月份融完资80多人,到2016年1月最高140多人。团队扩张速度不可谓不快,但是相应的业务其实没有跟上,我理解天使轮应该是提出方向,组建团队,开始业务模式的时期;A轮应该是模式验证时期;B轮是模式验证清晰,开始业务复制时期;C、D轮开始业务扩展盈利时期。但是我们在A轮的时候就把自己定位成B轮之后了,业务模式还处在验证时期,最多的时候开了北上广深、武汉、西安、成都、重庆八个城市站。每个城市的具体情况不同,自身产品流程有待验证,各城市沟通麻烦。最终的结果就是产品方向还在不断的调整,各城市也跟不上,一个一个裁掉城市站,浪费了大量的人力物力时间成本。
核心团队人员不可谓不豪华,核心团队都是BAT等各大互联网巨头背景出身的人,大数据,亿万流量,行业分析都是得心应手。但是落到具体执行上面,经常开会结论没有具体方案,结论是大家好好干,技术做好技术,运营用心服务用户。其实创业公司很多底层员工经验不够丰富,这样的结论让大家很懵逼。作为老大,leader不仅需要定好方向,同时需要定好执行的步骤,要下面的员工按照分解的工作,定好deadline一步一步的做。
那我们公司之前的例子:
市场总监招过4个,两个贪污,一个无所事事,一个异地办公,沟通麻烦。
hr总监之前和公司闹掰了,赔了不少钱。
天使轮500万,A轮3000万,一年半全部用完,主要花在了市场推广方面。一次市场补贴几百万,朋友圈广告100万,mp广告100万,各种地推,各种营销补贴。
这个还是我上面说的,应该是在B轮后,产品模式验证清晰了之后才应该有的动作,没验证清晰之前盲目扩张,补贴的时候看着数据噌噌噌的往上涨,很开心呀, 结果一没补贴,数据全掉了。因为没有达到用户痛点,用户是来领补贴的,没有补贴当然就流失了。
其实创业过程中,现金流真的很重要,有钱可以让你撑到最后,即使到那个时候还没有盈利,谁撑到了最后谁就是赢家。例子太多了,我们楼上有一家公司,A轮的钱到账了一分钱没用存到银行,我们天使轮、A轮的前都快花完的时候,楼上融了B轮,A轮的钱还没动。至少说明了,真正找到用户的痛点得产品是不需要花那么多冤枉钱的。通过广告的方式烧钱只能得到一时的繁荣,后面都是在裸泳。
我理解的带团队,一是把自己高效的工作方式流程化、标准化,让团队的工作效率都要得到提成,让团队的战斗力大大增强;二是解决团队中每个人员的难处,包括工作难题、生活问题、待遇,只有你真正关心大家,大家才会死心塌地的干活,团队才有凝聚力;三事信息的传递上,作为一个中间干部,需要对上下级做合适的信息传递。上面传达的压力需要分解成有计划有步骤的工作安排,下面的功劳需要向上面及时反应,让团队都知道;有一点非常重要,如果团队中有人出了问题,leader一定不要让他站出来,二是首先自己扛下来。
npm install -g gulp
npm install --save-dev gulp
gulp的任务都是以插件的形式存在,以安装gulp-jshint为例
npm install gulp-jshint --save-dev
var gulp = require('gulp');
var jshint = require('gulp-jshint');
var paths = {
scripts: 'js/**/*.js',
};
gulp.task('lint', function() {
return gulp.src(paths.scripts)
.pipe(jshint())
.pipe(jshint.reporter('default'));
});
gulp lint
其实安装gulp和gulp的插件都是使用node在本地进行安装的,会自动生成相应的目录。最主要的还是这个gulpfile.js。就相当于main函数,当执行gulp的时候,就会主动地去gulpfile.js中去执行相应的任务。
什么是src和pipe?
先看一个unix命令:cat gulpfile.js | wc -l
这是两个独立的命令,cat guplfile.js 表示获取gulpfile.js的内容,wc -l表示统计文件中的行数,中间用|分隔,表示前面命令的输出作为后面命令的输入。src就是获取源文件,pipe就是管道操作。
5个最常见的命令:
gulp.task(name, fn)定义一个任务
gulp.run(tasks...)尽可能多的并行运行多个task
gulp.watch(glob, fn)当glob内容发生改变时,执行fn
gulp.src(glob)返回一个可读的stream
gulp.dest(glob)返回一个可写的stream
查看各个gulp插件的官网:https://www.npmjs.org/
好了,介绍完了,gulp,我们来说说集成开发环境是什么?
我个人是在看了张云龙的博客之后才对前端工程、前端集成开发环境有了一定的了解的。那么我所理解的前端集成开发环境是指 一套解决js自身三大缺陷的通用解决方案。前端语言有哪三大缺陷:
资源定位:获取任何开发中所使用资源的线上路径;
内容嵌入:把一个文件的内容(文本)或者base64编码(图片)嵌入到另一个文件中;
依赖声明:在一个文本文件内标记对其他资源的依赖关系;
前端集成开发环境的目标是:
代码部署
文件压缩
文件合并
文件加MD5戳
前端模块化开发
组合插件
css sprite
less开发
内容嵌入
支持less编译css
支持react
var gulp = require('gulp'),
uglify = require('gulp-uglify'),
md5 = require('gulp-md5-plus'),
seajs = require('gulp-seajs'),
replace = require('gulp-replace'),
minifyCss = require('gulp-minify-css'),
less = require('gulp-less'),
cli = require('./cli.js'),
htmlmin = require('gulp-htmlmin'),
jshint = require('gulp-jshint'),
imagemin = require('gulp-imagemin'),
pngquant = require('imagemin-pngquant'),
cache = require('gulp-cache'),
react = require('gulp-react'),
newDep = require('gulp-dep');
//变量设置
var jsPath = {
src : '../static/src/scripts',
release : '../static/release/scripts'
},cssPath = {
src : '../static/src/styles',
release : '../static/release/styles'
},htmlPath = {
src : '../application/views',
release : '../application/views/'
},imgPath = {
src : '../static/src/images',
release : '../static/release/images/'
};
//默认执行方法
gulp.task('default',["main"],function(){
});
process.on('uncaughtException', function(err) {
console.log(err);
})
//html处理,处理<!--script-->等标签
gulp.task('htmldep',function(){
gulp.src(htmlPath.src + '/**/*.php')
.pipe(newDep())
.pipe(gulp.dest(htmlPath.release));
})
//html压缩
gulp.task('htmlmin',function(){
gulp.src([htmlPath.src + '/m3/channel/index.php',"!" + htmlPath.src + 'apiview.php'])
.pipe(htmlmin({collapseWhitespace: true}))
.pipe(gulp.dest(htmlPath.release + '/m3/channel/'));
})
//移动lib任务
gulp.task('libmove',function(){
//移动lib第三方库 不压缩
gulp.src(jsPath.src + '/library/**/*.js')
.pipe(gulp.dest(jsPath.release + '/library/'));
})
//移动common、mobile、www业务库,seajs处理、压缩
gulp.task('jsmin',function(){
gulp.src([jsPath.src + '/**/*.js', "!" + jsPath.src + "/library/**/*.js"])
.pipe(seajs())
.pipe(uglify({
mangle:true
}))
.pipe(gulp.dest(jsPath.release));
})
//css压缩、移动
gulp.task('cssmin',function(){
gulp.src(cssPath.src + '/**/*.css')
//.pipe(less()) //less编译
.pipe(minifyCss({compatibility: 'ie8'}))
.pipe(gulp.dest(cssPath.release));
})
//css的less编译
gulp.task('less',function(){
gulp.src(cssPath.src + '/**/*.less')
.pipe(less()) //less编译
.pipe(minifyCss({compatibility: 'ie8'}))
.pipe(gulp.dest(cssPath.release));
})
//图片压缩
gulp.task('imgmin',function(){
gulp.src(imgPath.src + '/**/*.{png,jpg,gif,ico}')
.pipe(cache(imagemin({
optimizationLevel: 5,
progressive: true, //类型:Boolean 默认:false 无损压缩jpg图片
interlaced: true, //类型:Boolean 默认:false 隔行扫描gif进行渲染
multipass: true //类型:Boolean 默认:false 多次优化svg直到完全优化
})))
.pipe(gulp.dest(imgPath.release));
});
//react
gulp.task('react-dev',['htmldep'],function(){
gulp.src(jsPath.src + '/**/react-test.js')
.pipe(react())
.pipe(gulp.dest(jsPath.release))
})
//react
gulp.task('react-idc',['htmldep'],function(){
gulp.src(jsPath.src + '/**/react-test.js')
.pipe(react())
.pipe(uglify({
mangle:true
}))
.pipe(gulp.dest(jsPath.release))
})
//react
gulp.task('main',['htmldep','libmove','jsmin','cssmin','less'],function() {
});
//监听任务
gulp.task('watch', function() {
gulp.watch([jsPath.src + '/**/*.js', "!" + jsPath.src + "/library/**/*.js"]).on('change', function(event) {
/*var dir = event.path.match(/\/src\/(styles|scripts)(.*)\/.*\.(css|js)$/)[2];
console.log(event.path);
console.log(dir);
gulp.src(event.path)
.pipe(seajs())
.pipe(gulp.dest(jsPath.release + dir));*/
console.log('files changed');
gulp.src([jsPath.src + '/**/*.js', "!" + jsPath.src + "/library/**/*.js"])
.pipe(seajs())
.pipe(gulp.dest(jsPath.release));
});
});
Issues
11
GitLab,是一个利用 Ruby on Rails 开发的开源应用程序,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目。
它拥有与Github类似的功能,能够浏览源代码,管理缺陷和注释。可以管理团队对仓库的访问,它非常易于浏览提交过的版本并提供一个文件历史库。团队成员可以利用内置的简单聊天程序(Wall)进行交流。它还提供一个代码片段收集功能可以轻松实现代码复用,便于日后有需要的时候进行查找。
开源项目地址:https://github.com/gitlabhq/gitlabhq
一,安装源和依赖包
cd /usr/local/src
#增epel源,如果你是i686系统,请把x86_64修改下。
wget -O /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6 https://www.fedoraproject.org/static/0608B895.txt
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
#确认是否安装成功
rpm -qa gpg*
gpg-pubkey-0608b895-4bd22942
#增puias源
wget -O /etc/yum.repos.d/PUIAS_6_computational.repo https://gitlab.com/gitlab-org/gitlab-recipes/raw/master/install/centos/PUIAS_6_computational.repo
wget -O /etc/pki/rpm-gpg/RPM-GPG-KEY-puias http://springdale.math.ias.edu/data/puias/6/x86_64/os/RPM-GPG-KEY-puias
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-puias
#验证是否成功
rpm -qa gpg*
gpg-pubkey-41a40948-4ce19266
查看仓库源
yum repolist
已加载插件:fastestmirror
Loading mirror speeds from cached hostfile
如果此列表中没有epel和PUIAS_6_computational,请执行以下命令
yum-config-manager --enable epel --enable PUIAS_6_computational
二,安装依赖包
yum -y update
yum -y groupinstall 'Development Tools'
yum -y install readline readline-devel ncurses-devel gdbm-devel glibc-devel tcl-devel openssl-devel curl-devel expat-devel db4-devel byacc sqlite-devel libyaml libyaml-devel libffi libffi-devel libxml2 libxml2-devel libxslt libxslt-devel libicu libicu-devel system-config-firewall-tui redis sudo wget crontabs logwatch logrotate perl-Time-HiRes git cmake libcom_err-devel.i686 libcom_err-devel.x86_64
安装邮件服务
postfix或者sendmail,官网上安装是postfix,本人默认就装来sendmial,所以此步骤省略
三,安装git
默认centos的git版本是1.7.10,所以要先删除,然后再下载源码安装
yum remove git -y
yum install zlib-devel perl-CPAN gettext curl-devel expat-devel gettext-devel openssl-devel
mkdir /tmp/git && cd /tmp/git
curl --progress https://www.kernel.org/pub/software/scm/git/git-2.1.3.tar.gz | tar xz
cd git-2.1.3/ && ./configure && make && make prefix=/usr/local install
完成后验证
which git
/usr/local/bin/git
git --version
git version 2.1.3
四,安装ruby
ruby版本需要2.0+,所以先卸载系统已存在的
yum remove ruby
#如果是源码安装的
cd (your-ruby-source-path) && make uninstall
mkdir /tmp/ruby && cd /tmp/ruby
curl --progress ang.org/pub/ruby/2.1/ruby-2.1.2.tar.gz | tar xz
cd ruby-2.1.2 && ./configure --disable-install-rdoc && make && make prefix=/usr/local install
安装bundler
gem install bundler --no-doc
完成后验证
which ruby
/usr/local/bin/ruby
ruby -v
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-linux]
五,创建系统用户
adduser --system --shell /bin/bash --comment 'GitLab' --create-home --home-dir /home/git/ git
增加/usr/local/bin
visudo
#修改以下内容
Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
六,安装mysql
官网给出两个选择,mysql、postgreSQL,我使用的mysql
centos默认会安装5.1版本的mysql,所以要源码安装,此步骤掠过。。。
#mysql的版本至少5.5.14或更新
mysql --version
mysql Ver 14.14 Distrib 5.5.40, for Linux (x86_64) using readline 5.1
创建数据库用户并授权
mysql -u root -p
mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY 'gitpwd';
mysql> show variables like "%engine";
+------------------------+--------+
| Variable_name | Value |
+------------------------+--------+
| default_storage_engine | InnoDB |
| storage_engine | InnoDB |
+------------------------+--------+
2 rows in set (0.01 sec)
#如果不是InnoDB引擎,需执行下面命令
mysql> SET storage_engine=INNODB;
创建数据库
mysql> CREATE DATABASE IF NOT EXISTS gitlabhq_production
DEFAULT CHARACTER SET utf8
COLLATE utf8_unicode_ci
;
给用户授权
mysql> GRANT SELECT, LOCK TABLES, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON gitlabhq_production
.* TO 'git'@'localhost';
测试是否能够成功登录
mysql -ugit -pgitpwd -D gitlabhq_production
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 30
Server version: 5.5.40-log Source distribution
Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> \q
七,配置redis
chkconfig redis on
cp /etc/redis.conf /etc/redis.conf.orig
#修改监听端口
sed 's/^port .*/port 0/' /etc/redis.conf.orig |tee /etc/redis.conf
#增加内容
echo 'unixsocket /var/run/redis/redis.sock' |tee -a /etc/redis.conf
echo -e 'unixsocketperm 0770' |tee -a /etc/redis.conf
创建目录改权限
mkdir /var/run/redis
chown redis:redis /var/run/redis
chmod 755 /var/run/redis
启动服务
service redis restart
附加git到redis组
usermod -aG redis git
八,安装gitlab
cd /home/git
#下载源码
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 7-4-stable gitlab
修改配置
cd gitLab/
sudo -u git -H cp config/gitlab.yml.example config/gitlab.yml
sudo -u git -H vim config/gitlab.yml
gitlab:
## Web server settings (note: host is the FQDN, do not include http://)
host: localhost
port: 80
https: false
修改目录权限
chown -R git log/
chown -R git tmp/
chmod -R u+rwX log/
chmod -R u+rwX tmp/
chmod -R u+rwX tmp/pids/
chmod -R u+rwX tmp/sockets/
chmod -R u+rwX public/uploads
创建目录
sudo -u git -H mkdir /home/git/gitlab-satellites
chmod u+rwx,g=rx,o-rwx /home/git/gitlab-satellites
编辑配置文件unicorn.rb
sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb
#查看系统核心数
nproc
4
#编辑配置
sudo -u git -H vim config/unicorn.rb
worker_processes 4
拷贝配置文件rack_attack.rb
sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb
定义全局的用户和邮箱
sudo -u git -H git config --global user.name "GitLab"
sudo -u git -H git config --global user.email "[email protected]"
sudo -u git -H git config --global core.autocrlf input
编辑连接redis配置
#拷贝配置
sudo -u git -H cp config/resque.yml.example config/resque.yml
#连接redis配置,默认配置,未修改
sudo -u git -H vim config/resque.yml
九,配置gitlab数据库文件
sudo -u git cp config/database.yml.mysql config/database.yml
#编辑配置文件
sudo -u git -H vim config/database.yml
production:
adapter: mysql2
encoding: utf8
collation: utf8_general_ci
reconnect: false
database: gitlabhq_production
pool: 10
username: git
password: "gitpwd"
host: localhost
socket: /var/lib/mysql/mysql.sock
#修改文件权限,只有git用户可读
sudo -u git -H chmod o-rwx config/database.yml
十,安装gem
cd /home/git/gitLab
sudo -u git -H bundle install --deployment --without development test postgres aws
十一,安装gitlab-shell
官网上给的gitlab-shell版本是2.1.0,后面会有问题(本地到远程不能连接),网上查找问题是版本问题,需要gitlab-shell的版本是2.0.1
sudo -u git -H bundle exec rake gitlab:shell:install[v2.0.1] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production
user: git
gitlab_url: https://localhost/
http_settings:
self_signed_cert: true
repos_path: "/home/git/repositories/"
auth_file: "/home/git/.ssh/authorized_keys"
redis:
bin: "/usr/bin/redis-cli"
namespace: resque:gitlab
socket: "/var/run/redis/redis.sock"
log_level: INFO
audit_usernames: false
十二,初始化数据库
sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production
#可以设置管理员密码(此步骤可省略。。。)
sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production GITLAB_ROOT_PASSWORD=newpassword
十三,下载服务脚本
wget -O /etc/init.d/gitlab https://gitlab.com/gitlab-org/gitlab-recipes/raw/master/init/sysvinit/centos/gitlab-unicorn
chmod +x /etc/init.d/gitlab
chkconfig --add gitlab
chkconfig gitlab on
#设置logrotate
cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
#检测应用状态
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
#启动服务
service gitlab start
十四,配置web服务
#本人使用的nginx
yum -y install nginx
chkconfig nginx on
wget -O /etc/nginx/conf.d/gitlab.conf https://gitlab.com/gitlab-org/gitlab-ce/raw/master/lib/support/nginx/gitlab-ssl
添加nginx用户到git组
usermod -a -G git nginx
chmod g+rx /home/git/
修改配置,修改server_name
vim /etc/nginx/conf.d/gitlab.conf
启动服务
service nginx start
#访问登录
#用户名:root
#密码:5iveL!fe
这样写 是比较方便,
没感觉界面很不友好么.
还有分类和tag根本没有额
最近需要对网站的nginx进行配置,学习了下相关的知识。
需要解决的问题:
没有www的域名test.com指向到www.test.com
移动端自动指向到m.test.com上,pc上访问自动指向到www.test.com
对于第一个问题:
域名提供商管理页面进行相关配置,新增一个域名解析记录,记录类型为A记录,意思是说当用户输入test,com时候将转到ip为xx.xx.xx.xx的机器
设置相关机器的nginx配置,新增一条server记录
一般nginx的配置都在/usr/local/nginx/conf/
server {
server_name test.com;
rewrite ^/(.*)$ http://www.test.com/home_page permanent;
root html;
index index.html index.htm;
}
对于第二个问题:
server {
server_name test.com;
if ( $http_user_agent ~* "(Android|webOS|iPhone|iPod|BlackBerry)" ) {
rewrite ^/(.*)$ http://mobile.test.com/home_page permanent;
}
rewrite ^/(.*)$ http://www.test.com/home_page permanent;
root html;
index index.html index.htm;
}
Linux中session失效的原因之一是session所在的目录没有权限的问题,开始的时候认为是程序和数据的问题,死活登陆不进去,网上搜遍了找不到问题所在,最后通过调试城市发现时session引起的登陆信息报错,最终想到是session目录的权限问题,加上相应的权限就可以了,还是有点小棘手的,window下面就不存在这种问题,chmod 777 session所在的路径名称
我们首先把html文档弄好,然后在里面来体验EaselJS。 一下所有实例,楼主都亲测过。有问题可交流。
<head>
<script src="http://code.createjs.com/easeljs-0.7.0.min.js"></script>
<script>
unction init() {
// code here.
}
</script>
</head>
<body onLoad="init();">
<canvas id="demoCanvas" width="500" height="300">
alternate content
</canvas>
</body>
函数调用情况:
参数含义
Inclusive Time 包括子函数所有执行时间。
Exclusive Time/Self Time函数执行本身花费的时间,不包括子树执行时间。
Wall时间 花去了的时间或挂钟时间。
CPU时间 用户耗的时间+内核耗的时间
Function Name 函数名
Calls 调用次数
Calls% 调用百分比
Incl. Wall Time 调用的包括子函数所有花费时间,以微秒算(一百万分之一秒)
IWall% 调用的包括子函数所有花费时间的百分比
Excl. Wall Time 函数执行本身花费的时间,不包括子树执行时间,以微秒算
EWall% 函数执行本身花费的时间的百分比不包括子树执行时间
Incl. CPU 调用的包括子函数所有花费的cpu时间。减Incl. Wall Time即为等待cpu的时间减Excl. Wall Time即为等待cpu的时间ICpu% Incl. CPU(microsecs)的百分比
Excl. CPU 函数执行本身花费的cpu时间,不包括子树执行时间,以微秒算。
ECPU% Excl. CPU的百分比
Incl.MemUse 包括子函数执行使用的内存
IMemUse% Incl.MemUse的百分比
Excl.MemUse 函数执行本身内存,以字节算
EMemUse% Excl.MemUse的百分比
Incl.PeakMemUse Incl.MemUse的峰值
IPeakMemUse% Incl.PeakMemUse的峰值百分比
Excl.PeakMemUse Excl.MemUse的峰值
EPeakMemUse% EMemUse%峰值百分比
数据主要查看的参数是:wall time和cpu time
图片查看:性能有问题的最严重的模块是红色,其次是黄色(可查看附件)
按照显示的属性绘制一幅图,一个视频或者画到画布上,构造函数可以传url或者图片对象。
function drawBitmap() {
var stage = new createjs.Stage("demoCanvas");
var bitmap = new createjs.Bitmap(img);
bitmap.cache(0, 0, 500, 500);
stage.addChild(bitmap);
bitmap.x = 0;
bitmap.y = 0;
stage.update();
}
function init() {
window.img = new Image();
img.src = "./logo.jpg";
img.onload = function(){
drawBitmap();
}
}
img.crossOrigin="Anonymous"
RBAC是基于角色的权限管理系统。
AUTH是基于节点的权限管理系统。
对于这两种权限管理的认识来自两个不同的系统,PHPCMS V9 与OneThink。
RBAC 可以将不同的用户分配给不同的用户组,即角色,每个角色享有不同的系统管理权限,但是如果角色之间的权限有交差时,可能会出现分工不明的情况,比如,有编 辑与管理员两个组,编辑要求对文章列表有添加与修改的权限,管理员对文章列表有添加与修改及删除操作,还可以进行文章分类的管理,但是如果发现在编辑里有 个主编,我想让他也可以删除文章,而又不想给他文章分类管理的权限,这时候用RBAC系统除了再建立一个用户组以外,没有其它方式,至少我没想到。
而 基于节点的权限管理AUTH就不会有这个问题,因为相对于所有用户来说,每个操作都是一个节点,我可以自由的分配每一个节点给每一个用户,但同时,也会出 现用户组不明确的问题,就是说,没有用户组,只有用户,当然这是原本的问题,其实AUTH也是可以先建立用户组,给用户组分配权限而后再将用户分配到相应 的组里,AUTH里还有个用户组明细表,就是用来对应用户跟组的关系,这样就解决了上边所说的问题,用户没有所在组。用户又可以同时属于不同的组,进而拥 有两个组组合的权限。
其实两种方式最根本的区别在于,RBAC中的用户只能属于一个组,AUTH中的用户可以属于不同的组,到于THINKPHP官网上这篇文章(http://www.thinkphp.cn/topic/4029.html)中说的condition的问题,在V9中也有data附件属性可以操作使用。
RBAC中数据库的基本设计如下:
1、用户角色表role
roleid tinyint(3) unsigned 主 INDEX
rolename varchar(50)
disabled tinyint(1) unsigned 1
2、管理员表admin
adminid mediumint(6) unsigned 主 INDEX
roleid tinyint(3)
adminname varchar(20) INDEX
password varchar(32)
lastloginip varchar(15)
lastlogintime int(10) unsigned
realname varchar(50)
email varchar(50)
tel varchar(20)
3、管理员权限表admin_priv
roleid tinyint(3) unsigned INDEX
m char(20) INDEX
c char(20) INDEX
a char(20) INDEX
data char(30) index
AUTH中数据库的基本设计如下:
1、用户角色表role
roleid tinyint(3) unsigned 主 INDEX
rolename varchar(50)
disabled tinyint(1) unsigned 1
rules varchar(500)
2、管理员表admin
adminid mediumint(6) unsigned 主 INDEX
adminname varchar(20) INDEX
password varchar(32)
lastloginip varchar(15)
lastlogintime int(10) unsigned
realname varchar(50)
email varchar(50)
tel varchar(20)
3、管理员权限表admin_priv
roleid tinyint(3) unsigned INDEX
m char(20) INDEX
c char(20) INDEX
a char(20) INDEX
condition char(100) NOT NULL
4、用户组明细表group_access
adminid mediumint(6) unsigned
roleid tinyint(3) unsigned
在使用ES6之前,我们使用ES5规范的javascript,那使用过程中有哪些地方是我们觉得比较麻烦的地方,基本上就是ES6改进的点,包括:js继承、数据模板、js模块化、js回调、js异步和一些基本方法不完善等。
ES5通过构造函数,定义并生成新对象。下面是一个例子。
function Point(x,y){
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '('+this.x+', '+this.y+')';
}
ES6引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。上面的代码用“类”改写,就是下面这样。
//定义类
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '('+this.x+', '+this.y+')';
}
}
var point = new Point(2,3);
point.toString() // (2, 3)
上面代码定义了一个“类”,可以看到里面有一个constructor函数,这就是构造函数,而this关键字则代表实例对象。这个类除了构造方法,还定义了一个toString方法。注意,定义方法的时候,前面不需要加上function这个保留字,直接把函数定义放进去了就可以了。
Class之间可以通过extends关键字,实现继承。
class ColorPoint extends Point {}
上面代码定义了一个ColorPoint类,该类通过extends关键字,继承了Point类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个Point类。下面,我们在ColorPoint内部加上代码。
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 等同于super.constructor(x, y)
this.color = color;
}
toString() {
return this.color+' '+super();
}
}
上面代码中,constructor方法和toString方法之中,都出现了super关键字,它指代父类的同名方法。在constructor方法内,super指代父类的constructor方法;在toString方法内,super指代父类的toString方法。
我们一般如何进行模板替换
<script type="text/html" id="tpl_coupon_show">
<div class="card_ticket_wrp">
<div class="card_ticket">
{data.brand_name}
</div>
</div>
</script>
在ES6之前,js没有模块化的概念,想要使用模块化开发,就需要依赖外部库比如requireJs、seaJs、labJs或者按照AMD或者CMD规范定义自己写类似的定义函数。那ES6是怎么做的了?
ES6实现了模块功能,试图解决JavaScript代码的依赖和部署上的问题,取代现有的CommonJS和AMD规范,成为浏览器和服务器通用的模块解决方案。
模块功能有两个关键字:export和import。export用于用户自定义模块,规定对外接口;import用于输入其他模块提供的功能,同时创造命名空间(namespace),防止函数名冲突。
ES6允许将独立的JS文件作为模块,也就是说,允许一个JavaScript脚本文件调用另一个脚本文件。该文件内部的所有变量,外部无法获取,必须使用export关键字输出变量。下面是一个JS文件,里面使用export关键字输出变量。
// profile.js
export var firstName = 'David';
export var lastName = 'Belle';
export var year = 1973;
上面是profile.js文件,ES6将其视为一个模块,里面用export关键字输出了三个变量。export的写法,除了像上面这样,还有另外一种。
// profile.js
var firstName = 'David';
var lastName = 'Belle';
var year = 1973;
export {firstName, lastName, year};
上面代码在export关键字后,使用大括号输出一组变量,它与前一种写法是等价的。
使用export定义模块以后,其他JS文件就可以通过import关键字加载这个模块(文件)。
import {firstName, lastName, year} from './profile';
function setHeader(element) {
element.textContent = firstName + ' ' + lastName;
}
文件需要从原路径到发布路径
资源定位:获取任何开发中所使用资源的线上路径;
内容嵌入:把一个文件的内容(文本)或者base64编码(图片)嵌入到另一个文件中;
依赖声明:在一个文本文件内标记对其他资源的依赖关系;以上内容学习至 fis介绍
文件移动功能就是解决资源定位的问题,我们的开发路径和发布路径是有必要不一样的,那么我们开发完成之后需要有一套构建工具自动帮我们完成这项功能。
//变量设置
var jsPath = {
src : '../static/src/scripts',
release : '../static/release/scripts'
},cssPath = {
src : '../static/src/styles',
release : '../static/release/styles'
},htmlPath = {
src : '../application/views/m3',
release : '../application/views/m3/'
},imgPath = {
src : '../static/src/images',
release : '../static/release/images/'
};
//移动lib任务
gulp.task('libmove',function(){
//移动lib第三方库 不压缩
gulp.src(jsPath.src + '/library/**/*.js')
.pipe(gulp.dest(jsPath.release + '/library/'));
})
整个static目录分为src和release目录,src放得是源码文件,是不会发布到外网的,release放得是预处理后的文件,包括压缩、混淆、用md5加后缀、less编译、react预处理等操作,是直接发布到外网的。
最后执行 gulp move,那么src下的library文件都移动到了release目录下了。
若干文件自定义合并
npm install --save-dev gulp-concat
[{
"src": [
"/lib/jquery.js",
"/lib/template.js",
"/lib/json2.js",
"/lib/prototype.js"
],
"dest": "/lib/lib_combo.js"
}]
var gulp = require('gulp');
var uglify = require('gulp-uglify');
var concat = require('gulp-concat');
var paths = {
scripts: {
source : '../src/js/**/*.js',
release: '../release/js'
},
css : {
source : '../src/css/**/*.css',
release : '../release/css'
},
html : {
source : '../src/html/**/*.html',
release : '../release/html'
}
};
process.on('uncaughtException', function(err) {
console.log(err);
})
/*
* 文件压缩
*/
gulp.task('move', function() {
gulp.src(paths.scripts.source,"!" + paths.scripts.source + "/lib/*.js")
.pipe(uglify())
.pipe(gulp.dest(paths.scripts.release));
});
//js合并任务(读取合并配置)
gulp.task('merge', function() {
var data = require('./merge.json');
data.forEach(function(o) {
var arr = [];
o.src.forEach(function(s) {
arr.push(paths.scripts.source + "/" + s);
});
o.src = arr;
gulp.src(o.src).pipe(concat(o.dest)).pipe(gulp.dest(paths.scripts.release));
});
});
gulp.task('default', ["merge","move"], function() {
});
同时看到我们把move任务中取出了common/lib文件夹的处理。
JS单线程原因:如果多线程,那么删除或者创建dom元素,都需要在线程之间通信。因为浏览器不确定dom状态。所以,js设计之初就是单线程。
好处:简单,没有线程切换维护开销,省内存。
浏览器一般至少实现三个常驻线程:JS引擎线程,GUI渲染线程,浏览器事件触发线程。
JS引擎的工作机制是:当线程中没有执行任何同步代码的前提下才会执行异步代码,setTimeout是异步代码,所以setTimeout只能等JS引擎空闲才会执行,但死循环是永远不会空闲的,所以setTimeout也永远不会执行。
一道面试题:js中在一个时间节点执行
var startTime = +(new Date()),endTime;
setTimeout(function(){
endTime = +(new Date());
console.log(1);
console.log(endTime - startTime);
},3000);
setTimeout(function(){
endTime = +(new Date());
console.log(2);
console.log(endTime - startTime);
},2000);
while(+(new Date()) - startTime < 5000){}
console.log(+(new Date()) - startTime);
请问alert(1)和alert(2)的先后顺序和时间间隔。
5s钟的同步执行完之后,开始执行setTimeout异步代码,因为第二个插入的时间间隔早,先执行。
因为5s时间超过了3s,所以两个函数几乎同时执行。如果同步时间是1s,那就是2s之后执行第二个函数,3s后执行第一个函数。
setTimeout(..) 并没有把你的回调函数挂在事件循环队列中。它所做的是设 定一个定时器。
当定时器到时后,环境会把你的回调函数放在事件队列中,如果这时候事件循环中已经有 20 个项目了会怎样呢?你的回调就会等待,定时器只能确保你的回调函数不会在指定的 时间间隔之前运行,但可能会在那个时刻运行,也可能在那之后运行,要根据事件队列的 状态而定(PS: 这就是造成定时器不准确的缘由)。
setTimeout(..0)(hack)进行异步调度,基本上它的意思就是把这个函数插入到当前事件循环队列的结尾处。
首先为了搭建一个稳定的lamp的练习环境,确保你的虚拟机可以连网,这里我们使用的yum安装,它可以帮助我们解决软件自己的依赖关系.我还在后面加了postgresql数据库如果不需要的话可以去掉和postgresql的参数.命令如下yum -y install httpd mysql mysql-server php php-mysql postgresql postgresql-server php-postgresql php-pgsql php-devel,
1、重复字符串的创建
// 创建32个0的商品ID
new Array(33).join('0');
// 即,创建N个重复字符串
new Array(N + 1).join(repeater);
2、判断是否闰年
var year = 2008;
new Date( +new Date( year, 2, 1 ) - 1 ).getDate() === 29
3、使用a标签内置的机制来解析url地址
这是一个使用a标签内置的机制来解析url地址的方法,大家或许平时都在用string和正则的方法来处理url解析,其实浏览器本身就具有解析url的能力,快来看看吧
var parser = document.createElement('a');
parser.href = "http://example.com:3000/pathname/?search=test#hash";
console.log(parser.protocol); // => "http:"
console.log(parser.hostname); // => "example.com"
console.log(parser.port); // => "3000"
console.log(parser.pathname); // => "/pathname/"
console.log(parser.search); // => "?search=test"
console.log(parser.hash); // => "#hash"
console.log(parser.host); // => "example.com:3000"
4、获取服务器端的时间
巧妙利用http请求的head方法来让服务器端返回时间,不用额外的去开发后端接口。 不过这种做法有一点风险: 1、服务器的时间要有同步,不然可能在路由到不同服务器的时候出现不同的时间结果 2、确认服务器的gzip配置是否正常,否则会导致下一个异步请求出错。
function $getServerTime(url) {
var xhr = $xhrMaker(), url = url || "http://" + window.location.hostname+"/favicon.ico";
try{
xhr.open("HEAD", url, false);
xhr.send();
}catch(e){
return new Date();
}
return new Date(xhr.getResponseHeader("Date"));
}
5、获取对象类型
function getType(obj) {
return obj === null ? 'null' : (obj === undefined ? 'undefined' : Object.prototype.toString.call(obj).slice(8, -1).toLowerCase());
}
6、短小精悍的微型模板引擎
一个简单的模板引擎。它设计精巧,支持普通变量填充与循环,代码量很小,同时支持传入模板字符串及从页面的隐藏textarea取模板内容。
function $SimpleTemplate() {
return {
/**
*使用JSON数据填充模板
*@param {object} json json对象
*@param {string} tpl 模板字符串
*@example
*@return 替换结果字符串
*/
parse : function(json,tpl,splitStr)
{
var blocks=[];
for(var i=0,len=json.length; i<len; i++){="" blocks.push(tpl.replace(="" {(\w+)}="" g,function(a,="" b){="" return="" json[i][b]="" !="=" undefined="" ?="" :="" a;="" }));="" }="" *$$.each(json,function(i,="" el){="" blocks.push($strtrim(tpl).replace(="" el[b]="" })*="" blocks.join(splitstr="" ||="" '');="" },="" **="" *="" *@param="" {object}="" json="" json对象="" {string}="" target="" 目标填充元素id="" tpl="" 模板textarea的id,也可以直接传入模板内容="" stuff="" function(json,target,tpl,splitstr)="" {="" '#'+target+'_tpl').val();="" var="" *传入的模板内容*="" $id(target+'_tpl').value="" *从隐藏的textarea取*="" ;="" m="tpl.match(/<#([\s\S]+)#">/);
var allTpl = m ? m[0] : tpl;
var blockTpl= m ? m[1] : tpl;
var html = tpl.replace(allTpl, this.parse(json, blockTpl, splitStr));
var dom = document.getElementById(target);
if (dom) dom.innerHTML = html;
}
};
}
function $id(id){
return typeof(id)=="string"?document.getElementById(id):id;
}
</len;>
前言
先在这里感谢phalapi框架创始人@dogstar,为我们提供了这样一个优秀的开源框架.
离上一次更新过去了快两周,在其中编写了一个关于DB分表分库解决大数据量的拓展,有兴趣的童鞋可以了解了解.废话不多说,本小节在于解释一下在PhalApi框架中两个比较好的**,单例模式和依赖注入.
单例模式对于长期进行过面向对象编程的童鞋应该不算陌生,在学习php的童鞋也应该有听过,这里简单的聊一聊单例模式到底是一个怎么样东西,解决了什么问题,并且在PhalApi中是如何实现的.
单例单例,所谓单例也就是有且只有一个存在,这就是单例,不难看出他的好处资源利用少因为只有一个,大家都是知道要使用一个类必须要实例他也就是new在每次new一个对象的时候都会在内存中生成一块区域来存放这个实例,如果在程序一次运行中使用了很多的new实例化了同一个对象,那就比较消耗资源了,但是如果是通用一个使用全局变量global程序又会显得不优雅而且会很乱,在这种情况下单例模式就产生了.
单例模式就是一个两全其美的一个方法既可以全局通用,二不用担心占用过多的资源,三又非常优雅,我们来一起看看在PhalApi中是如何实现单例模式的:
//大家看到我们常用的DI方法内部实现的是PhalApi_DI中的静态方法one方法
function DI() {
return PhalApi_DI::one();
}
然后我们看向one方法内部
每当我们请求过来的时候先验证静态变量instance是不是没有被初始化,如果是第一次调用他会在内部去实例化PhalApi_DI类然后负值给$instance然后返回实例好的这个静态变量,当我们下次在请求过来的时候这个静态变量已经被实例化好了就会直接跳过实例的过程直接返回这个对象,也就是我们在PhalApi框架所有的地方使用的DI方法其实都是一个对象,在内存中只存在一块区域,代码如下:
public static function one(){
if(self::$instance == NULL){
self::$instance = new PhalApi_DI();
self::$instance->onConstruct();
}
return self::$instance;
}
其实不难我们只用吧new一个类的操作封装到我们需要new的类的静态方法在向上面一样的去判断,就可以很简单的去实现单例模式.
2. 依赖注入
依赖注入又称之为"控制反转",如果是熟悉javaweb开发的spring框架应该有比较深的感触,在这里也不往深的讲,就简单讲解一下PhalApi中DI依赖注入的实现让大家了解这种设计模式如何实现以及自此基础上实现的惰性加载机制.
2.1 DI依赖注入实现
大家在PhalApi中常用的DI()方法,也就是采用我们上面所谓的单例模式不用多说了,也就是我们每次使用DI()其实就是在使用PhalApi_DI类,那么我们依赖注入的关键也就是在PhalApi_DI之中
先来讲一下他的一个实现方式在来讲具体实现,这里举个例子:
//配置
DI()->config = new PhalApi_Config_File(API_ROOT . '/Config');
其实在内部有一个数组,它吧config作为了key,吧new PhalApi_Config_File(API_ROOT . '/Config')作为了value然后保存了起来当我们下一次使用DI->config->get();的时候它就会根据key值config拿出开始new好的类,所以可以说config操作是依赖于DI(),而且在使用DI()->config的时候永远都是在使用的一个实例,也能减少资源的消耗.
有的童鞋就好奇了为什么DI()->config会存到数组需要的时候会拿出来呢,感兴趣的童鞋可以百度一下魔法方法ser和get
/**大家可以看到这是PhalApi_DI中的魔法方法__set
public function __get($name){
return $this->get($name, NULL);
}
看完之后大家是不是觉得很简单啊,大家以后也可以在自己设计类的时候采用这种灵活的魔法方法实现.
2.2 惰性加载
在PhalApi中的DI()方法也提供惰性加载,惰性加载如字意也就是当类没有被使用到的时候不会加载,这样的操作也是为了避免浪费不必要的资源,当我们不是用的时候永远不会去实例化只有当你使用到的时候才会去new这个类然后实例化之后使用,我们来看看是如何实现的.
//当我们执行以下语句的时候,在依赖注入的时候存的是value值是字符串的test
DI()->test = 'test';
//使用DI()->test->test();的时候会使用到PhalApi中的get方法,在get方法中有一段代码
$this->data[$key] = $this->initService($this->data[$key]);
//在initService方法内部验证了value是字符串就实例化了再返回
if($config instanceOf Closure){
$rs = $config();
}elseif(is_string($config) && class_exists($config)){
$rs = new $config();
if(is_callable(array($rs, 'onInitialize'))){
call_user_func(array($rs, 'onInitialize'));
}
}else{
$rs = $config;
}
作者: PhalApi
链接:http://www.imooc.com/article/6113
来源:慕课网
浏览器暴露给js的一个接口,可以通过这个接口查看用户访问网站的连接建立时间、dns时间等信息。使用该api时需要在页面完全加载完成之后才能使用,最简单的办法是在window.onload事件中读取各种数据,因为很多值必须在页面完全加载之后才能得出。
IE9和chrome6以上的版本都支持:
navigationStart:准备加载新页面的起始时间
redirectStart:如果发生了HTTP重定向,并且从导航开始,中间的每次重定向,都和当前文档同域的话,就返回开始重定向的timing.fetchStart的值。其他情况,则返回0
redirectEnd:如果发生了HTTP重定向,并且从导航开始,中间的每次重定向,都和当前文档同域的话,就返回最后一次重定向,接收到最后一个字节数据后的那个时间.其他情况则返回0
fetchStart:如果一个新的资源获取被发起,则 fetchStart必须返回用户代理开始检查其相关缓存的那个时间,其他情况则返回开始获取该资源的时间
domainLookupStart:返回用户代理对当前文档所属域进行DNS查询开始的时间。如果此请求没有DNS查询过程,如长连接,资源cache,甚至是本地资源等。 那么就返回 fetchStart的值
domainLookupEnd:返回用户代理对结束对当前文档所属域进行DNS查询的时间。如果此请求没有DNS查询过程,如长连接,资源cache,甚至是本地资源等。那么就返回 fetchStart的值
connectStart:返回用户代理向服务器服务器请求文档,开始建立连接的那个时间,如果此连接是一个长连接,又或者直接从缓存中获取资源(即没有与服务器建立连接)。则返回domainLookupEnd的值
(secureConnectionStart):可选特性。用户代理如果没有对应的东东,就要把这个设置为undefined。如果有这个东东,并且是HTTPS协议,那么就要返回开始SSL握手的那个时间。 如果不是HTTPS, 那么就返回0
connectEnd:返回用户代理向服务器服务器请求文档,建立连接成功后的那个时间,如果此连接是一个长连接,又或者直接从缓存中获取资源(即没有与服务器建立连接)。则返回domainLookupEnd的值
requestStart:返回从服务器、缓存、本地资源等,开始请求文档的时间
responseStart:返回用户代理从服务器、缓存、本地资源中,接收到第一个字节数据的时间
responseEnd:返回用户代理接收到最后一个字符的时间,和当前连接被关闭的时间中,更早的那个。同样,文档可能来自服务器、缓存、或本地资源
domLoading:返回用户代理把其文档的 "current document readiness" 设置为 "loading"的时候
domInteractive:返回用户代理把其文档的 "current document readiness" 设置为 "interactive"的时候.
domContentLoadedEventStart:返回文档发生 DOMContentLoaded事件的时间
domContentLoadedEventEnd:文档的DOMContentLoaded 事件的结束时间
domComplete:返回用户代理把其文档的 "current document readiness" 设置为 "complete"的时候
loadEventStart:文档触发load事件的时间。如果load事件没有触发,那么该接口就返回0
loadEventEnd:文档触发load事件结束后的时间。如果load事件没有触发,那么该接口就返回0
DNS查询耗时 :domainLookupEnd - domainLookupStart
TCP链接耗时 :connectEnd - connectStart
request请求耗时 :responseEnd - responseStart
解析dom树耗时 : domComplete - domInteractive
白屏时间 :responseStart - navigationStart
domready时间 :domContentLoadedEventEnd - navigationStart
onload时间 :loadEventEnd - navigationStart
比如图中type为img的图片整个加载时间为duration ms。
可以通过这个接口统计整个页面有多少img、css、js以及对应的下载时间等信息。
/**
* 上报Performance timing数据;
* 如果某个时间点花费时间为0,则此时间点数据不上报。
* @param {String}
* f1 flag1简写,测速系统中的业务ID,譬如校友业务为164
* @param {String}
* f2 flag2简写,测速的站点ID
* @param {String}
* f3_ie flag3简写,测速的页面ID
*(因为使用过程中我们发现IE9的某些数据存在异常,
* 如果IE9和chrome合并统计,会影响分析结果,所以这里建议分开统计)
* @param {String}
* f3_c flag3简写,测速的页面ID
* (如果为空,则IE9和chrome合并统计)
*/
function setTimingRpt(f1, f2, f3_ie, f3_c){
var _t, _p = (window.webkitPerformance ? window.webkitPerformance : window.msPerformance), _ta = ["navigationStart","unloadEventStart","unloadEventEnd","redirectStart","redirectEnd","fetchStart","domainLookupStart","domainLookupEnd","connectStart","connectEnd","requestStart",/*10*/"responseStart","responseEnd","domLoading","domInteractive","domContentLoadedEventStart","domContentLoadedEventEnd","domComplete","loadEventStart","loadEventEnd"], _da = [], _t0, _tmp, f3 = f3_ie;
_p = (_p ? _p : window.performance);
if (_p && (_t = _p.timing)) {
if (!_t.domContentLoadedEventStart) {
_ta.splice(15, 2, 'domContentLoaded', 'domContentLoaded');
} else {
if (f3_c) {
f3 = f3_c;
}
}
_t0 = _t[_ta[0]];
for (var i = 1, l = _ta.length; i < l; i++) {
_tmp = _t[_ta[i]];
_tmp = (_tmp ? (_tmp - _t0) : 0);
if (_tmp > 0) {
_da.push( i + '=' + _tmp);
}
}
if (window.d0) {//统计页面初始化时的d0时间
_da.push('30=' + (window.d0 - _t0));
}
var url = 'http://www.report.com?flag1=' + f1 + '&flag2=' + f2 + '&flag3=' + f3 + '&' + _da.join('&');
var _img = new Image();
setTimeout(function(){
_img.src = url;
}, 10);
}
};
//注意!需要在onload事件后进行计算上报。
window.onload = function(){
setTimingRpt(7839, 7, 3);//活动页面
};
未完待续!!
参考:
body { -webkit-text-size-adjust : none; -ms-text-size-adjust : none; }
a { background-color : transparent; }
WP手机:<meta name="msapplication-tap-highlight" content=“no”/>
Webkit:a { -webkit-tap-highlight-color : transparent; }
img { -webkit-touch-callout : none; }
input { -webkit-appearance : none; }
内容垂直居中一般用于弹出层,如果尺寸固定的话,用CSS绝对定位就可以搞定,但是如果尺寸是不固定的,就需要使用js动态定位。
其实在移动端还可以使用display:box;就可以轻松的让内容垂直居中,如下:
<div class="box">
<div class="content">
<p>我是内容-1</p>
<p>我是内容-2</p>
</div>
</body>
<style>
.box {
width:800px;height:600px;background:red;
display : -webkit-box;
-webkit-box-orient : horizontal;
-webkit-box-pack : center;
-webkit-box-align : center;
display : -moz-box;
-moz-box-orient : horizontal;
-moz-box-pack : center;
-moz-box-align : center;
}
</style>
在IOS系统中,当我们点击输入框拉起虚拟键盘的时候,默认情况下对于英文输入法键盘是开启了首字母大写功能的。但是在我们项目里面为了更好的体验需要默认将该特性去掉。此时,可以使用移动webkit浏览器支持的特性将其去除:在input元素上添加autocapitalize属性。通过使用autocapitalize="off"来关闭默认虚拟键盘首字母大写。如果要开启,可以使用 autocapitalize="on"。
<div class="ui-searchbar-input"><input value="" type="text" placeholder="请输入游戏名称" autocapitalize="off"></div>
IOS下,输入英文情况下,回退清除字符不会触发keypress事件,android下可以。此外,IOS下切换为中文输入法时候,输入过程中输入框对产生的字母会响应keypress,但是对于单击选择中文到输入框后后不会触发keypress事件。以此类推,剩下的keyDown、keyUp事件都差不多。
总结:keyPress之类的键盘事件监听不适合移动端的虚拟键盘监听,总是会引发这种或者那种问题。
在手机上实时捕获用户输入搜索框的内容明显跟PC有区别的,最大的不同就是PC有实体键盘,而移动web捕获的是虚拟键盘。既然键盘事件不行,那么我们就从Input元素输入框入手,看看有什么事件可以利用。
onpropertychange事件:该事件是IE浏览器特供,只要当前对象属性发生改变(例如输入框文案),都会触发事件,webkit就忽略吧。
onchange事件: webkit可以用,但是只有当当前元素失去了焦点后,才会去校验属性是否有更改,才会触发这个事件(也就是说一直输入内容是不会触发该事件的,也许select元素标签适合这个特性),明显不符合用户输入体验。
oninput事件: chrome webkit也可以使用,IE9+也可使用。也是表示输入时候内容一旦有变更就实时触发,而且不会受限于必须失去焦点才会触发的制约。
oninput事件bugfix总结:
1、在IOS下,输入中文点击选择后会连续触发两次oninput事件,android下我使用的机型未出现该问题
2、在IOS下,中文输入法输入英文时候,字母之间会出现一个类似于空格的东东,研究了一下这种空格可以称之为“六分之一空格”
/**
* 对于用户在Input框里面的输入进行过滤,例如 1、对于IOS六分之一空格情况 2、对于IOS输入中文时候会连续两次触发onInput情况
*
* @param str {String} 从input框读取的值
* @returns {String} 返回过滤后的值
*
*/
function filterString(str) {
if (!isAndroid) {
// IOS下需要做的处理
var cache = arguments.callee;
var curTime = new Date().getTime() / 1000;
// 当前搜索的字符串在这个delta
// ms时间区间里面如果跟最近的一次搜索字符串相同,则不对该结果进行响应,解决情况2问题
var delta_time = 400;
// 保存当前的字符串信息
var keyObj = {
str : str,
time : curTime
};
if (cache.keyObj && cache.keyObj.str == str) {
// 读取最近一次的字符串信息
var lastTime = cache.keyObj.time;
if (curTime - lastTime < delta_time) {
// 此时可认为丢弃此次相同关键字查找
return "";
}
// 重新更新cache
cache.keyObj = keyObj;
} else {
// 这是新开页面的第一次成功检索或者和最近一次检索的字符串是不一样的关键字
// 更新cache
cache.keyObj = keyObj;
}
// 接着进行六分之一空格的处理
str = str.replace(/\u2006/g, "");
}
return str;
}
vm和vh:这两种单位是以视窗(viewpoint)的宽度和高度为基准,1vw = 1/100 视窗宽度,1vh = 1/100 视窗高度,举个栗子:如果浏览器的视窗宽高分别为1000px和900px,那么1vw = 1000/100 = 10px、1vh = 900/100 = 9px。如果想让一个元素占据全屏,你可以这样:
<div class="content">
<h2>标题文字</h2>
<p>我是内容-1</p>
<p>我是内容-2</p>
</body>
<style>
*{margin:0;padding:0;}
.box {
position:absolute;top:0;left:0;background:red;
width: 100vw;
height: 100vh;
}
</style>
vmin:vmin的值是当前vw和vh中较小的值。
vmax:vmax的值是当前vw和vh中较大的值。
要想进一步查看出现这个的原因应该要开启debug模式,可以在入口文件中的index.php中开启,网页上面会有相应的调试信息,按照那上面的提示一步步的,一般都可以解决我当时是因为本地的redis没有启动,所以手动的去启动一下,OK。
在对文件内容进行压缩
js采用uglify混淆压缩
npm install gulp-uglify --save-dev
var gulp = require('gulp');
var uglify = require('gulp-uglify');
var paths = {
scripts: {
source : '../src/js/**/*.js',
release: '../release/js'
},
css : {
source : '../src/css/**/*.css',
release : '../release/css'
},
html : {
source : '../src/html/**/*.html',
release : '../release/html'
}
};
process.on('uncaughtException', function(err) {
console.log(err);
})
/*
* 文件压缩
*/
gulp.task('move', function() {
gulp.src(paths.scripts.source)
.pipe(uglify())
.pipe(gulp.dest(paths.scripts.release));
});
gulp move
这样就完成了在文件移动过程中的压缩操作了。查看被压缩的情况:
define("home/index",["home/b","home/c"],function(e){alert(1);var c=e("home/b"),a=e("home/c");a.callback(),c.callback()});
常见的前端框架分析
框架需要解决哪些问题?
设计框架从哪些方面入手?
到现在为止,前端框架有以下五类:
prototype 类
这类框架
jQuery 类
以dom操作为中心,jQuery就是一个选择器,通过批量选择HTMLELEMENTS对象进行操作,可以很方便的对页面进行管理。
seajs 类
前端模块框架的代表
backbone 类
agular 类
已数据为中心
react 类
我的mac环境的charles版本是3.9.1。希望配置移动端代理https抓包。
分享mac下charles破解安装包。
http抓包配置:
1、打开charles,proxy->proxy settings。端口配置8888。
2、查看当前电脑的ip,在终端输入ifconfig
当前ip为192.168.31.125。打开手机wifi设置,设置da代理:192.168.31.125,端口8888。
这时候在手机上打开需要抓包的网页,charles会弹出一个是否允许的框,点击允许即可抓包。
https抓包配置:
1、下载charles证书,见微云链接,或者去官网下载,导入手机,不能用微信导入手机,因为微信会阻断文件执行,有邮件的方式发送手机,打开即可安装。
2、charles->tools->proxy setting。勾选enable ssl proxy。点击add,添加域名和端口(443)。
3、在手机上配置代理。成功。
Graphics类作用于创建并绘制到网页具体位置的矢量图形。比如你可以通过使用Graphics类中的draw方法绘制图形,然后使用Shap对象轻松地将矢量图形放置到指定的canvas之中。
var g = new createjs.Graphics(); //实例化一个Graphics类
g.setStrokeStyle(1); //1像素的画笔
g.beginStroke(createjs.Graphics.getRGB(0,0,0));
//等同于g.beginStroke('#f00');画边
g.beginFill(createjs.Graphics.getRGB(255,0,0)); //用红色填充图形
g.drawCircle(0,0,50); //画一个半径是50的圆
var s = new createjs.Shape(g); //将圆实例成一个shape
s.x = 100; //横坐标为100
s.y = 100; //纵坐标为100
stage.addChild(s);
stage.update(); //update把stage的改动更新到canvas里
Graphics类里的所有绘图方法都会返回一个graphics实例,因此可以使用链式操作。下面这段代码能够绘制出红边蓝色填充的矩形,并放置到指定的context2D中。
var canvas = document.getElementById("demoCanvas");
var cxt = canvas.getContext("2d");
var myGraphics = new createjs.Graphics();
myGraphics.beginStroke("#F00").beginFill("#00F").drawRect(20, 20, 100, 50).draw(cxt);
Graphics类支持简写方法,官方称为tiny api:
Tiny | Method | Tiny | Method |
mt | moveTo | lt | lineTo |
a/at | arc / arcTo | bt | bezierCurveTo |
qt | quadraticCurveTo (also curveTo) | r | rect |
cp | closePath | c | clear |
f | beginFill | lf | beginLinearGradientFill |
rf | beginRadialGradientFill | bf | beginBitmapFill |
ef | endFill | ss | setStrokeStyle |
s | beginStroke | ls | beginLinearGradientStroke |
rs | beginRadialGradientStroke | bs | beginBitmapStroke |
es | endStroke | dr | drawRect |
rr | drawRoundRect | rc | drawRoundRectComplex |
dc | drawCircle | de | drawEllipse |
dp | drawPolyStar | p | decodePath |
var canvas = document.getElementById("demoCanvas");
var cxt = canvas.getContext("2d");
var myGraphics = new createjs.Graphics();
myGraphics.s("#F00").f("#00F").r(20, 20, 100, 50).draw(myContext2D);
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.