前端开发工程师 @ buxuku
- 🔭 目前在成都工作
- 🌱 目前在学习算法与设计模式
- ✍️ 我喜欢在我的博客上面写一些文章 林晓东的个人博客
- 🖥 我曾是一名草根站长,因此涉足过前端,后端,数据库,服务器,推广,SEO等等
- 😄 我喜欢阅读与写作
📝 最近博客更新:
在javascript中的函数参数传值中,对于基本数字类型,我们都能够很好理解,它是按值传递的.比如下面这样:
var num = 10;
function add(num){
return num+1;
}
console.log(add(num));//11
console.log(num);//10
而对于对象的传递,我们可能就不太好理解了,因为如果是同基本类型一样,按值传递,但其实际表现又像引用类型一样.比如下面这样
情形一:
var a=[],
b={},
c={};
function change(a,b,c){
a.push(1);
b.name = "my name";
c.age = 23;
}
change(a,b,c);
console.log(a);//[1]
console.log(b);// Object { name="my name"}
console.log(c.age);//23
如果是按值传递,为什么函数把外面的值给改变了呢?我们又继续看下面这个例子:
情形二:
var a=[],
b={},
c={};
function change(a,b,c){
a = [1];
b = [2];
c = {age:3}
}
change(a,b,c);
console.log(a);//[]
console.log(b);//object
console.log(c.age);//undefined
同样是进行操作,为什么这次又 没有任何变化呢?
其实深入理解,我们必须相信的是,在javascript中,函数的参数确实是按值传递的.对于以上现象,我们可以这样理解:
变量a保存的是一个对象,而我们又知道,对象类型的变化保存的其实是对象引用的一个地址,当把变化a传递的函数的参数a的时候,函数参数a复制了变量a,实际上也就是复制了变量a引用对象的地址,因为是同一个地址,所以指向的是同一个变量.所以对函数中变量a进行的操作,都会反映到这个地址引用的对象上面.这就是情形1的执行结果.
而在函数中,当我们把变量a赋值给另外一个对象后,它保存的就是另外一个对象的引用地址,所以对它进行任何操作,都不会反映到最开始变量a的对象上面.
通过以上两种情形,我们就能够很好地理解下面这种情况了:
var person ={
name : "my name"
}
function change(person){
person.name = "your name";
person = {
name : "her name"
}
}
change(person);
console.log(person.name);//your name
所以,当有一天,我们能够改变var person
里面的引用地址的时候,我们才敢说是按照引用传值的.
在javascript中的一个重大的难点就是对this的理解,因为javascript虽然是一种词法作用域的语言,但里面的this却似乎总是在变化中的,我们很难有一个既定的规则来描述在表示的是什么。同时它也未非像它的字面意思那样,表示指向当前函数。
比如
var a = 1;
function test(){
var a = 2;
console.log(this.a);
}
test();//1
从字面上来理解,也许this.a表示test中的a更容易让我们授受,反而结果却并不是这样的。这也就是为什么在ES6中会出现=>
符号的原因了。
借力,也许是最轻松的办法,在这里,也许我们通过借力js中的call方法能够让我们更轻松地弄清楚js中的this在各种情况之下的指向。
一.call的本来面目
我们知道,js中的call就是改变一个对象的引用,它可以修改函数中的this引用。这点就可以让我们很轻松地明白了,你call了什么,this就是代表的什么了。
function show(b){
console.log("a:"+this.a+" b:"+b);
}
var a = 1;
var obj1 = {
a:2
};
var obj2 = {
a:3
};
show.call(obj1,4);//a:2 b:4
show.call(obj2,4);//a:3 b:4
二.没有call,call依然存在
function show(b){
console.log("a:"+this.a+" b:"+b);
}
var a = 1;
var obj1 = {
a:2
};
var obj2 = {
a:3
};
show(4);//a:1 b:4
show.call(window,4);//a:1 b:4
换一个思路来理解这段代码,其实show(4)
也就是相当于show.call(window,4)
;
也就是说,直接执行函数的时候,其实就是call了一个window对象进去。只是我们把它给简写了而已。
三.隐式调用
function show(b){
console.log("a:"+this.a+" b:"+b);
}
var a = 1;
var obj = {
a:2,
show:show
}
obj.show(3);//a:2 b:3
show.call(obj,3);//a:2 b:3
看得出,在对象的执行上下方下中的代码,其obj.show(3)
也就相当于show.call(obj.3)
四.当有了new
var a = 1;
function show(a){
this.a = a
}
var b = new show(2);
console.log(b.a);//2
我们知道,new操作符是新生成一个实例对象出来,这里的操作其实也就是有点类似于var b = {}
,当new了之后,其执行上下文也就是在b中了,所以它的指向也就是b了,和上面第三点的隐似调用非常类似。
微信的流行,让我们经常会在微信中打开一些网页,但因为微信的webview是内置的浏览器,没有给我们直接进行浏览器设置的地方,比如清理缓存之类的。
经常在做微信网页开发的时候,在调试阶段,这种缓存是非常令人头痛的。
还有就是浏览过一些网页之后,希望能够清理掉cookie,比如登录信息之类的。
这些尝试过在应用设置里面清理微信的缓存,用安全工具清理掉缓存,都办法清理掉。
一个解决办法就是我们自己做开发的,可以给静态资源添加一个时间戳,可以解决一部分问题。
一个就是因为andorid版本的微信是内置的QQ浏览器x5的内核,我们可以通过在微信中打开QQ浏览器的调试页面:http://debugx5.qq.com
,在里面找到清理的选项,如下图,然后勾选我们要清理的内容,点清理就可以了。
有一个例子可以很好地展示javascript中的语法作用域的特性:
function foo(){
console.log(a);
}
function bar(){
var a = 3;
foo();
}
var a = 2;
bar();//2
同大多数语言一样,javascript也是采用的词法作用域.
作用域分为两种,一种就是词法作用域(Lexical Scope),一种是动态作用域(Dynamic Scope).
词法作用域是属静态作用域模型,也就是变量,函数的声明,引用都是在编译阶段就完成了,它们不会受到执行上下文,调用方式的影响.比如上面定义的函数foo,它里面对a的RHS查询,是基于它本身内部变量定义,函数参数以及全局变量中进行查询的,所以就算在bar中进行执行,它的静态模型也是不会被改变的.因此是正常地输出2.
相反的,如果是动态作用域,则RHS查询是在当前的执行环境中进行查询,所以它是动态改变的,它会受到执行上下文,调用方式的改变,如上面这个例子,如果采用动态作用域的方式来执行的话,在bar内部执行foo,foo对a进行RHS查询时,自己内部作用域中进行查询,没有查询到,继续向当前执行环境中进行查询,于是查询到了bar中定义的a,所以最终输出了结果3.
总结下来就是:
{% cq %} 遇到既不是形参也不是函数内部定义的局部变量的变量时,词法作用域的函数去函数定义时的环境中查询,动态域的函数到函数调用时的环境中查询。 {% endcq %}
function foo(str){
eval(str);
console.log(a);
}
function bar(){
foo("var a = 3");
}
var a = 2;
bar();//3
我们知道,javascript中分词阶段,只会对变量进行声明.在对foo进行分词时,首先会忽略eval内部的执行代码,这个时候对a的静态引用还是基于函数内部定义,参数和全局变量的,但这个时候没有进行"a = 2"这个赋值操作,因为它应该是在执行环节进行的.
接下来在执行环节就会出现问题了,因为eval是立即执行内部代码的,我们在"a = 2"赋值操作之前,执行了"var a = 3"这句操作,它覆盖了原来的"var a = 2"操作,所以完成了一次词法作用域的欺骗,最终输出结果3.
var a = 10;
function go(obj) {
with(obj) {
a = 2;
}
}
var foo = {
a: 1
},
bar = {
b: 1
};
go(foo);
console.log(foo.a); // 2
console.log(a); //10
go(bar);
console.log(bar.a); // undefined
console.log(a); // 2(对头,a变成了一枚金闪闪的全局变量)
我们常常不喜欢with的原因就是这样的:当bar对象中没有a这个属性的时候,with不是给它新增一个a的属性,而是在全局变量中新增了一个属性,也正因为这样的特性,导致它改变了原来定义的"var a = 10"这个值.
和之前的一篇文章《不使用var就不是声明变量》一样,这里不是声明一个变量,而是给window新增加了一个属性.通过代码可以测试出.
function go(obj) {
with(obj) {
a = 2;
}
}
var bar = {
b: 1
};
go(bar);
console.log(bar.a); // undefined
console.log(a); //2
console.log(delete a); //true
最开始发现宽带问题是用手机下载APP的时候,经常会先下载一个360手机助手,第二次下载的时候才会下载到正常的APP,后来发现我在使用京东,淘宝,亚马逊,包括百度的时候,都会自动变成联盟网站的链接,而且平时看电视基本上不卡的,而且是基本上都是卡的,最开始也不解,以为是应用商店的问题,因为在应用商店里面也有人评价说下载成了其它的APP,后来在电脑上下载APP的时候,在APP的官网上下载也会出现这样的问题,这下肯定不是应用商店的问题,估计应该是DNS挟持的问题吧,更改DNS为谷歌的,阿里的,问题依然如故。后来发现这下肯定是被运营商给TCP挟持了,这种情况下再怎么改DNS也没有办法的。在网上找到解决办法就是安装一个smart header的插件,用这个插件把连接请求中的user-agent删除掉就可以了,网上的说法是运营商的TCP挟持没有了user-agent就没办法起作用了,安装了这个插件之后,确实好像没有被挟持了,但在浏览过程中会出现问题,比如京东,淘宝没办法下单等,而且也没办法解决手机下载APP的问题。
于是决定还是最终极的办法,就是投诉,先打电话给长宽的客户,一直问我是不是要弹出长宽的广告页面,我一直解释说不是弹出页面,是TCP挟持,会变成联盟链接,下载APP的问题等,后来客服如网上的说话类似,说以前确实没有接到类似的投诉,帮我记录一下。
看来客服不好沟通,打开长宽的网页,在意见反馈里面说明了我的情况,要求在48小时内给我解决结果。
然后又打开他们的在线客户,说明情况,说让技术联系我,
最开始技术说不会做这些处理,我也懒得给他们解释,再给他们一个台阶下,让他们检查一下,否则就投诉到工信部,相信他们会心知肚明的,他们马上说稍等一下,结果一个晚上就再也没有回复了。
第二天,我继续联系客服,
看到没,直接给我说没办法处理,一下子就生气了,那我就直接说投诉到工信部了,看来他们还是怕工信部的。
后来技术给我打电话,我说了我的问题,他还在狡辩说什么京东这些网站都是走的他们的资源,什么是联盟链接说不知道,后来我说让你去找我们的主管或者懂这方面的技术的。后来他就说昨天晚上已经把我的商品更新了一下,让我再看一下。
晚上回家,测试,已经没有进行TCP挟持了,同时我还发现我的IP段前三位是固定的了,不管怎么断线重连,只有最后一位会变化。同时看电视也还没有出现卡的情况了。
我们知道,在js中采用var
就可以声明一个变量了,同时我们也可能会很自豪地告诉自己,如果不使用var
就会在全局变量中声明一个全局变量.
可是问题真的是如此吗?正如本文的标题一样,我们来一次大胆的尝试:没有使用var
就不是声明变量!!!
关于不使用var
的方式实现的效果相信都知道了,这里就不演示了,我们主要来说明他不是在声明一个变量.
我们知道,js引擎会对声明进行提升,如果不使用var
的话,我们可以当作是在全局执行环境中进行的变量声明,那它也应该进行变量声明提升了.我们来测试:
console.log(a);//undefined
console.log(b);//ReferenceError
b = 10;
var a = 10;
我们发现,打印a是undefined
,说明变量a是正常提升了的,但b呢,是ReferenceError
,引用错误,说明b的声明并没有提升.
我们先假设一下,b在这里并不是在进行变量声明.
delete
特性我们知道,变量是不是被删除了,而对象的属性是可以被删除的.我们再来测试:
b = 10;
console.log(delete b);//true
console.log(b);//ReferenceError
我们发现,这里的变量b是可以被删除的.那么再添加一个声明呢:
b = 10;
console.log(delete b);//false
console.log(b);//10
var b;
我们发现,仅仅是多了这么一句声明,变量b就不能被删除了.
这里我们可以看得出,不使用var
不仅仅是在全局作用域中创建变量那么简单.
b = 10;
console.log(window.b);//10
console.log(delete window.b);//true
console.log(b);//ReferenceError
通过上面这个代码,我们发现,变量b可以通过全局变量window的一个属性来访问,而它也可以通过删除window这样的一个属性来删除它.
因此,我们有理由相信:
{% cq %} 不使用var
并不是在声明一个变量,而是在为全局变量window添加一个对应的属性! {% endcq %}
我们看以下代码
function foo(){
a = 10;
console.log(delete a);
console.log(a);
}
foo();//false,10
var a = 20;
虽然foo函数声明以及执行都在var之前,但因为存在变量提升的原因,所以foo内部的a只是在进行重新赋值了.
我们再看
boo();//false , 20
foo();//ture , ReferenceError
function foo(){
a = 10;
console.log(delete a);
console.log(a);
}
function boo(){
var a = 20;
console.log(delete a);
console.log(a);
}
因为boo里面的var声明只是在boo这个函数的内部执行环境中,所以并不会影响到foo函数.
我们再来测试
foo();//false , 20
function foo(){
var a = 10;
function boo(){
a = 20;
console.log(delete a);
console.log(a);
}
boo();
}
通过变量作用域名相信我们是能够很容易理解上面这段代码的执行结果的.
继续coding
var foo = {
name : "javascript",
change:function(){
name = "php";
console.log(delete window.name);
console.log(name);
}
};
foo.change();//ture , ReferenceError
我们发现,对象里面的方法,不使用var
的话也是在window对象上面添加属性来实现的.
如果在闭包中,又会是怎样的呢?
function foo(){
var a = 10;
function bar(){
console.log(a);
a=20;
console.log(delete a);
console.log(a);
}
return bar;
}
var boo = foo();
boo();//10 , false , 20
我们发现,在闭包中,因为bar中保留对foo的引用,所以虽然在外部执行了bar函数,但闭包中对a的引用会保留var a = 10
的有效性的。
我们知道,我们可以利用eval
来进行变量欺骗,那如果我们用eval
来进行变量声明,会怎样呢呢
eval("var a = 10");
console.log(window.a);//10
console.log(delete window.a);//true
console.log(a);//ReferenceError
我们发现,通过eval声明的变量,是拥有delete特性呢.我们来测试代码:
console.log(a);//ReferenceError
eval("var a = 10");
我们发现eval里面的变量声明,是不会进行声明提升的.因为引擎发初始化的时候,是不会扫描里面的具体的内容的,只有当执行到这一步的时候,才会执行里面具体的代码.也许这种情况下声明的变量也不应该叫作真正的变量声明吧.具体的原因还需要进一步深入了解.
centos6.5最小化安装/AWS centos
apr-1.5.1
apr-util-1.5.4
httpd-2.4.10
pcre-8.36
在centos最小化安装情况下,首先我们需要先安装以下几个包
yum install wget
yum install gcc
yum install gcc-c++
wget http://mirrors.cnnic.cn/apache/httpd/httpd-2.4.10.tar.gz
wget http://mirrors.cnnic.cn/apache//apr/apr-1.5.1.tar.gz
wget http://mirrors.cnnic.cn/apache//apr/apr-util-1.5.4.tar.gz
wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.36.tar.gz
tar -zxvf httpd-2.4.10.tar.gz
tar -zxvf apr-1.5.1.tar.gz
tar -zxvf apr-util-1.5.4.tar.gz
tar -zxvf pcre-8.36.tar.gz
首先把pcre安装上
cd pcre-8.36
./configure
make && make install
然后把apr移动到httpd安装包里面
cd ../
mv apr-1.5.1 httpd-2.4.10/srclib/apr
mv apr-util-1.5.4 httpd-2.4.10/srclib/apr-util
cd httpd-2.4.10
配置httpd编译参数
./configure --with-included-apr --enable-nonportable-atomics=yes --with-z
出奇的简单,因为默认就有 --enable-mods-shared=most ,模块化安装,以后自行到 httpd.conf 中决定是否开启模块,所以什么 --enable-deflate --enable-rewrite --enable-blablabla 等就完全不必要了。
默认安装的是 event mpm,如果要用 worker ,就需要--with-mpm=worker,或者干脆 --enable-mpms-shared=all,这样event、worker、prefork就会以模块化的方式安装,要用哪个就在 httpd.conf 里配置就好了
编译安装httpd
make && make install
软件已经默认安装到/usr/local/apache2里面了,对应的配置文件是conf/httpd.conf
cp /usr/local/apache2/bin/apachectl /etc/init.d/httpd
vi /etc/init.d/httpd
在首行 #!/bin/sh 下面加入两行:
# chkconfig: 35 85 15
# description: Activates/Deactivates Apache 2.4.10
加入开机自启
chkconfig --add httpd
chkconfig httpd on
接下来我们便可以启动apache了
service httpd start
打开浏览器,我们应该能够看到大大的It works!
几个字,表明我们已经正常安装了。
这是因为在默认情况,ceontos的防火墙关闭了80端口,开启方法如下
/sbin/iptables -I INPUT -p tcp --dport 80 -j ACCEPT #允许80端口
/etc/rc.d/init.d/iptables save #保存
/etc/init.d/iptables restart #重启防火墙
在前端面试题中,一般会遇到一道关于array去重的面试题。简单点来说 ,就是找出不一样的,或者说排队一样的。那么我们就可以通过生活中的一些情景来思考这道题的解法。
假如我们要表演一场千手观音的节目,我们需要高矮不一样的人来表演,于是在参选人员中,我们让他们按照高矮次序排队,一样高的就排除掉后面的那一个。
于是我们就得到了解法,先对数组进行排序,然后循环比较当前值和其后面那个值的是否相等,如果不相等,刚后面那个值认为是有效的。
var arr = [2,3,2,5,4,3];
var new_arr = [];
arr = arr.sort();
for (var i=0;i<arr.length;i++){
if(arr[i]!=arr[i+1]){
new_arr.push(arr[i])
}
}
console.log(new_arr);
假如有一大排五颜六色的球,我们要从中每个颜色选择一个球出来,于是我们就可以这样选:准备一个口袋,每看到一个球,看一下这个球的颜色在袋子里面是不是已经装得有了,如果有,就不装了,如果没有,就把它装进去。
于是在js中得到解法,使用js中的indexOf来判断数组中的每一个元素是否已经在输出结果的新数组中:
var arr = [2,3,2,5,4,3];
var new_arr = [];
for(var i=0;i<arr.length;i++){
if(new_arr.indexOf(arr[i])==-1){
new_arr.push(arr[i]);
}
}
console.log(new_arr);
相当于我们上下班打卡一样,对于同一个人在某一个时间段内重复打卡,我们会只记录他的一次打卡记录。
于是在js,我们可以把相同的值认为是同一个值在多次出现,而我们可以把值转化为一个对象的属性,这样,某一个值一旦出现过,我们就为其完成“打卡”操作。
var arr = [2,3,2,5,4,3];
var new_arr = [];
var has = {};
for(var i=0;i<arr.length;i++){
if(has[arr[i]]!== 1){
has[arr[i]]=1;
new_arr.push(arr[i]);
}
}
console.log(new_arr);
有一份名单,上面的名字可能因为在制作表的时候造成了很多名字的重复,老师为了避免重复点名,他只好每点一次名,就看下这个名字是不是在之前出现过,如果没有,就点名,否时就不点了。
于是js的解决思路还是要用到indexOf来判断当前值是否在之前的位置出现过。
var arr = [2,3,2,5,4,3];
var new_arr = [];
for(var i=0;i<arr.length;i++){
if(arr.indexOf(arr[i])==i){
new_arr.push(arr[i]);
}
}
console.log(new_arr);
假如我把数据从左往右按照次序看,前面出现过的相同的数字认为是同一只小鸟,而那个数字只是小鸟之前停留过的位置而已,所以我们只需要找到每一只小鸟最后停留的位置即可了。
解决方法来源于国外的一个人写的,参考链接.
这个函通过双重循环,顶级中的一次循环,都通过次级的循环判断后面是否还有相同的值,如果有,次级继续循环,直到找到后面没有重复值的下一个顶级i的值,接下来顶级在新的i值基础上继续循环。
Array.prototype.unique = function() {
var a = [], l = this.length;
for(var i=0; i<l; i++) {
for(var j=i+1; j<l; j++)
if (this[i] === this[j]) j = ++i;
a.push(this[i]);
}
return a;
};
我们使用git很多时候都是需要配合github同步使用的,当我们在本地使用了git管理代码,并进行修改,提交之后,接下来我们就想把代码部署到github上面去,整个流程也是非常简单的。
首先,我们登录github之后,点击右上角的new repository
,来新建一个分支,接下来就要求我们级分支起一个名字,这里比如我起一个mytest
的名字,写上分支的描述,如下图
接下来就是我最喜欢github的一点了,因为他已经非常直观地告诉了我们要怎么做了,如下图
echo "# mytest" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin https://github.com/buxuku/mytest.git
git push -u origin master
这里教我们在本地创建一个新的仓库,并建立一个README.md的文件,然后提交并推送到github上面
git remote add origin https://github.com/buxuku/mytest.git
git push -u origin master
这里就是教我们在本地已经存在的仓库里面把这个远程地址添加进来,然后把代码提交上去。
整个过程就是这么的简单。比如我提交的一份代码执行图:
从图中也看到了,github有时候可能会出现网络不稳定或者被墙的情况。
其实做为开发人员,我们都应该尊重别人的劳动成果的,但互联网的免费性,开源性,分享性,而更多的是码农的穷逼性,不知是被逼使用一些盗版软件,破解一些东西什么的。
以前手头上微信框架用的是某X力的,虽然知道也是破解的微擎的,但人家便宜呀,模块多呀。
不过真的是一分钱一分货,bug太多,更新不行,以至后来我升级到他们的新版本后根本就不能更新了。
最后想了想,还是和微擎吧,毕竟是官方的,而且免费更新框架,至少框架这一块是稳定了。
但用免费版的微擎有一个比较大的问题就是,很多模块都是要收费的,这样可免费用的模块就比较少了。
网上会有很多微擎的模块盗版出来的,但安装会有一个问题就是,微擎的模块都是在线安装的,就算我们拿到模块进行本地安装时,也会通过模块名进行云检测,如果商城中有这个模块,就会提示盗版,而站点就可能会被记录黑名单,这样就不能再使用微擎的一切云服务了,比如安装微擎商城中的模块,更新框架等。
网上主流的解决方法就是屏蔽云服务,但是这样所有模块就只能本地安装了,框架也不能更新了。
我希望能够得到完美的解决方案就是,我能够正常使用微擎的免费服务,包括更新框架,安装商城免费的模块,更新商城的免费模块,同时,我也可以安装自己盗版的模块。
通过分析微擎云服务文件,弄清楚了每个函数的用途,最后终于完美解决了我的问题,达到了我上面想要的效果。当然,具体解决方法就不公布了,毕竟这是一个不友好的行为。在此只是记录一下解决思路:让云服务不知道你安装了哪些盗版的模块。
人人商城肯定是盗版的,而微商城是正常从商城里面安装的,后期也是可以免费升级的。
分享一份自己备注的微擎cloud.mod.php函数
<?php
/**
* [WeEngine System] Copyright (c) 2014 WE7.CC
* WeEngine is NOT a free software, it under the license terms, visited http://www.we7.cc/ for more details.
*/
defined('IN_IA') or exit('Access Denied');
function cloud_client_define() {
return array(
'/framework/function/communication.func.php',
'/framework/model/cloud.mod.php',
'/web/source/cloud/upgrade.ctrl.php',
'/web/source/cloud/process.ctrl.php',
'/web/source/cloud/dock.ctrl.php',
'/web/themes/default/cloud/upgrade.html',
'/web/themes/default/cloud/process.html'
);
}
function cloud_prepare() {
global $_W;
setting_load();
if(empty($_W['setting']['site']['key']) || empty($_W['setting']['site']['token'])) {
return error('-1', "您的程序需要在微擎云服务平台注册你的站点资料, 来接入云平台服务后才能使用相应功能.");
}
return true;
}
function cloud_m_prepare($name) {//模块验证 传入模块名字,通过云平台验证是否受版权保护 仅用于模块安装 批量安装模块的时候使用
$pars['method'] = 'module.check';
$pars['module'] = $name;
$dat = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars);
if (is_error($dat)) {
return $dat;
}
if ($dat['content'] == 'install-module-protect') {
return error('-1', '此模块已设置版权保护,您只能通过云平台来安装。');
}
return true;
}
function _cloud_build_params() {//云服务变量数组
global $_W;
$pars = array();
$pars['host'] = $_SERVER['HTTP_HOST'];
$pars['family'] = IMS_FAMILY;
$pars['version'] = IMS_VERSION;
$pars['release'] = IMS_RELEASE_DATE;
$pars['key'] = $_W['setting']['site']['key'];
$pars['password'] = md5($_W['setting']['site']['key'] . $_W['setting']['site']['token']);
$clients = cloud_client_define();
$string = '';
foreach($clients as $cli) {
$string .= md5_file(IA_ROOT . $cli);
}
$pars['client'] = md5($string);
return $pars;
}
function cloud_m_build($modulename, $type = '') {//云平台安装 卸载模块 更新模块
$type = in_array($type, array('uninstall')) ? $type : '';
$sql = 'SELECT * FROM ' . tablename('modules') . ' WHERE `name`=:name';
$module = pdo_fetch($sql, array(':name' => $modulename));
$pars = _cloud_build_params();
$pars['method'] = 'module.build';
$pars['module'] = $modulename;
$pars['type'] = $type;
if (!empty($module)) {
$pars['module_version'] = $module['version'];
}
$dat = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars);
$file = IA_ROOT . '/data/module.build';
$ret = _cloud_shipping_parse($dat, $file);//云服务结果处理
if (!is_error($ret)) {
$dir = IA_ROOT . '/addons/' . $modulename;
$files = array();
if (!empty($ret['files'])) {
foreach ($ret['files'] as $file) {
$entry = $dir . $file['path'];
if (!is_file($entry) || md5_file($entry) != $file['checksum']) {
$files[] = '/' . $modulename . $file['path'];
}
}
}
$ret['files'] = $files;
$schemas = array();
if (!empty($ret['schemas'])) {
load()->func('db');
foreach ($ret['schemas'] as $remote) {
$name = substr($remote['tablename'], 4);
$local = db_table_schema(pdo(), $name);
unset($remote['increment']);
unset($local['increment']);
if (empty($local)) {
$schemas[] = $remote;
} else {
$diffs = db_table_fix_sql($local, $remote);
if (!empty($diffs)) {
$schemas[] = $remote;
}
}
}
}
$ret['upgrade'] = true;
$ret['type'] = 'module';
$ret['schemas'] = $schemas;
if (empty($module)) {
$ret['install'] = 1;
}
}
return $ret;
}
function cloud_m_query() {//非系统模块发送到服务端 a=module&do=check 已经安装模块 检查更新 安装模块 检查已经购买但没有安装的
$pars = _cloud_build_params();
$pars['method'] = 'module.query';
$pars['module'] = cloud_extra_module();//获取非系统模块
$dat = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars);
$file = IA_ROOT . '/data/module.query';
$ret = _cloud_shipping_parse($dat, $file);
return $ret;
}
function cloud_m_info($name) {//获取指定模块信息 安装模块 更新模块时进行检查
$pars = _cloud_build_params();
$pars['method'] = 'module.info';
$pars['module'] = $name;
$dat = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars);
$file = IA_ROOT . '/data/module.info';
$ret = _cloud_shipping_parse($dat, $file);
return $ret;
}
function cloud_m_upgradeinfo($name) {//在线检测模块更新 仅用于module.ctrl.php 469 $do == 'upgrade' 参数 传入模块名
$module = pdo_fetch("SELECT name, version FROM ".tablename('modules')." WHERE name = '{$name}'");
$pars = _cloud_build_params();
$pars['method'] = 'module.info';
$pars['module'] = $name;
$pars['curversion'] = $module['version'];
$pars['isupgrade'] = 1;
$dat = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars);
$file = IA_ROOT . '/data/module.info';
$ret = _cloud_shipping_parse($dat, $file);
return $ret;
}
function cloud_t_prepare($name) {//模板云检测
$pars['method'] = 'theme.check';
$pars['theme'] = $name;
$dat = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars);
if (is_error($dat)) {
return $dat;
}
if ($dat['content'] == 'install-theme-protect') {
return error('-1', '此模板已设置版权保护,您只能通过云平台来安装。');
}
return true;
}
function cloud_t_query() {//获取在线安装的模板
$pars = _cloud_build_params();
$pars['method'] = 'theme.query';
$pars['theme'] = cloud_extra_theme();
$dat = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars);
$file = IA_ROOT . '/data/theme.query';
$ret = _cloud_shipping_parse($dat, $file);
return $ret;
}
function cloud_t_info($name) {//在线获取指定模板信息
$pars = _cloud_build_params();
$pars['method'] = 'theme.info';
$pars['theme'] = $name;
$dat = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars);
$file = IA_ROOT . '/data/theme.info';
$ret = _cloud_shipping_parse($dat, $file);
return $ret;
}
function cloud_t_build($name) {//在线安装模板
$sql = 'SELECT * FROM ' . tablename('site_templates') . ' WHERE `name`=:name';
$theme = pdo_fetch($sql, array(':name' => $name));
$pars = _cloud_build_params();
$pars['method'] = 'theme.build';
$pars['theme'] = $name;
if(!empty($theme)) {
$pars['themeversion'] = $theme['version'];
}
$dat = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars);
$file = IA_ROOT . '/data/theme.build';
$ret = _cloud_shipping_parse($dat, $file);
if(!is_error($ret)) {
$dir = IA_ROOT . '/app/themes/' . $name;
$files = array();
if(!empty($ret['files'])) {
foreach($ret['files'] as $file) {
$entry = $dir . $file['path'];
if(!is_file($entry) || md5_file($entry) != $file['checksum']) {
$files[] = '/'. $name . $file['path'];
}
}
}
$ret['files'] = $files;
$ret['upgrade'] = true;
$ret['type'] = 'theme';
if(empty($theme)) {
$ret['install'] = 1;
}
}
return $ret;
}
function cloud_t_upgradeinfo($name) {//在线获取模板更新
$sql = 'SELECT `name`, `version` FROM ' . tablename('site_templates') . ' WHERE `name` = :name';
$theme = pdo_fetch($sql, array(':name' => $name));
$pars = _cloud_build_params();
$pars['method'] = 'theme.upgrade';
$pars['theme'] = $theme['name'];
$pars['version'] = $theme['version'];
$pars['isupgrade'] = 1;
$dat = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars);
$file = IA_ROOT . '/data/module.info';
$ret = _cloud_shipping_parse($dat, $file);
return $ret;
}
function cloud_sms_send($mobile, $content) {//在线短信接口
global $_W;
$log = array(
'mobile' => $mobile,
'content' => $content,
'result' => '',
'uniacid' => $_W['uniacid'],
'createtime' => TIMESTAMP
);
$row = pdo_get('uni_settings' , array('uniacid' => $_W['uniacid']), array('notify'));
$row['notify'] = @iunserializer($row['notify']);
if(!empty($row['notify']) && !empty($row['notify']['sms'])) {
$config = $row['notify']['sms'];
$balance = intval($config['balance']);
if($balance <= 0) {
$log['result'] = '发送短信失败, 请联系系统管理人员. 错误详情: 短信余额不足';
pdo_insert('core_sendsms_log', $log);
return error(-1, $log['result']);
}
$sign = $config['signature'];
if(empty($sign) && IMS_FAMILY == 'x') {
$sign = $_W['setting']['copyright']['sitename'];
}
if(empty($sign)) {
$sign = '微擎';
}
$log['content'] = $content . " 【{$sign}】";
$pars = _cloud_build_params();
$pars['method'] = 'sms.send';
$pars['mobile'] = $mobile;
$pars['content'] = $content . " 【{$sign}】";
$dat = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars);
$file = IA_ROOT . '/data/sms.send';
$ret = _cloud_shipping_parse($dat, $file);
if (is_error($ret)) {
$log['result'] = $ret['message'];
pdo_insert('core_sendsms_log', $log);
return error($ret['errno'], $ret['message']);
}
if ($ret == 'success') {
return true;
} else {
$log['result'] = $ret['message'];
pdo_insert('core_sendsms_log', $log);
return error(-1, $ret);
}
}
pdo_insert('core_sendsms_log', $log);
return error(-1, '发送短信失败, 请联系系统管理人员. 错误详情: 没有设置短信配额或参数');
}
function cloud_build() {//程序更新入口
$pars = _cloud_build_params();
$pars['method'] = 'application.build';
$pars['extra'] = cloud_extra_account();
$dat = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars);
$file = IA_ROOT . '/data/application.build';
$ret = _cloud_shipping_parse($dat, $file);
if(!is_error($ret)) {
if($ret['state'] == 'warning') {
$ret['files'] = cloud_client_define();
unset($ret['schemas']);
unset($ret['scripts']);
} else {
$files = array();
if(!empty($ret['files'])) {
foreach($ret['files'] as $file) {
$entry = IA_ROOT . $file['path'];
if(!is_file($entry) || md5_file($entry) != $file['checksum']) {
$files[] = $file['path'];
}
}
}
$ret['files'] = $files;
$schemas = array();
if(!empty($ret['schemas'])) {
load()->func('db');
foreach($ret['schemas'] as $remote) {
$name = substr($remote['tablename'], 4);
$local = db_table_schema(pdo(), $name);
unset($remote['increment']);
unset($local['increment']);
if(empty($local)) {
$schemas[] = $remote;
} else {
$sqls = db_table_fix_sql($local, $remote);
if(!empty($sqls)) {
$schemas[] = $remote;
}
}
}
}
$ret['schemas'] = $schemas;
}
if($ret['family'] == 'x' && IMS_FAMILY == 'v') {
load()->model('setting');
setting_upgrade_version('x', IMS_VERSION, IMS_RELEASE_DATE);
message('您已经购买了商业授权版本, 系统将转换为商业版, 并重新运行自动更新程序.', 'refresh');
}
$ret['upgrade'] = false;
if(!empty($ret['files']) || !empty($ret['schemas']) || !empty($ret['scripts'])) {
$ret['upgrade'] = true;
}
$upgrade = array();
$upgrade['upgrade'] = $ret['upgrade'];
$upgrade['lastupdate'] = TIMESTAMP;
cache_write('upgrade', $upgrade);
}
return $ret;
}
function cloud_schema() {//数据库整理
$pars = _cloud_build_params();
$pars['method'] = 'application.schema';
$dat = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars);
$file = IA_ROOT . '/data/application.schema';
$ret = _cloud_shipping_parse($dat, $file);
if(!is_error($ret)) {
$schemas = array();
if(!empty($ret['schemas'])) {
load()->func('db');
foreach($ret['schemas'] as $remote) {
$name = substr($remote['tablename'], 4);
$local = db_table_schema(pdo(), $name);
unset($remote['increment']);
unset($local['increment']);
if(empty($local)) {
$schemas[] = $remote;
} else {
$diffs = db_schema_compare($local, $remote);
if(!empty($diffs)) {
$schemas[] = $remote;
}
}
}
}
$ret['schemas'] = $schemas;
}
return $ret;
}
function cloud_download($path, $type = '') {//下载文件
$pars = _cloud_build_params();
$pars['method'] = 'application.shipping';
$pars['path'] = $path;
$pars['type'] = $type;
$pars['gz'] = function_exists('gzcompress') && function_exists('gzuncompress') ? 'true' : 'false';
$headers = array('content-type' => 'application/x-www-form-urlencoded');
$dat = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars, $headers, 300);
if(is_error($dat)) {
return error(-1, '网络存在错误, 请稍后重试。' . $dat['message']);
}
if($dat['content'] == 'success') {
return true;
}
$ret = @json_decode($dat['content'], true);
if(is_error($ret)) {
return $ret;
} else {
return error(-1, '不能下载文件, 请稍后重试。');
}
}
function _cloud_shipping_parse($dat, $file) {//云服务结果处理
if (is_error($dat)) {
return error(-1, '网络传输错误, 请检查您的cURL是否可用, 或者服务器网络是否正常. ' . $dat['message']);
}
$tmp = unserialize($dat['content']);
if (is_array($tmp) && is_error($tmp)) {
if ($tmp['errno'] == '-2') {
$data = file_get_contents(IA_ROOT . '/framework/version.inc.php');
file_put_contents(IA_ROOT . '/framework/version.inc.php', str_replace("'x'", "'v'", $data));
}
return $tmp;
}
if ($dat['content'] == 'patching') {
return error(-1, '补丁程序正在更新中,请稍后再试!');
}
if ($dat['content'] == 'blacklist') {
return error(-1, '抱歉,您的站点已被列入云服务黑名单,云服务一切业务已被禁止,请联系微擎客服!');
}
if (strlen($dat['content']) != 32) {
return error(-1, '云服务平台向您的服务器传输数据过程中出现错误, 这个错误可能是由于您的通信密钥和云服务不一致, 请尝试诊断云服务参数(重置站点ID和通信密钥). 传输原始数据:' . $dat['meta']);
}
$data = @file_get_contents($file);
if (empty($data)) {
return error(-1, '没有接收到服务器的传输的数据.');
}
@unlink($file);
$ret = @iunserializer($data);
if (empty($data) || empty($ret) || $dat['content'] != $ret['secret']) {
return error(-1, '云服务平台向您的服务器传输的数据校验失败, 可能是因为您的网络不稳定, 或网络不安全, 请稍后重试.');
}
$ret = iunserializer($ret['data']);
if (is_array($ret) && is_error($ret)) {
if ($ret['errno'] == '-2') {
$data = file_get_contents(IA_ROOT . '/framework/version.inc.php');
file_put_contents(IA_ROOT . '/framework/version.inc.php', str_replace("'x'", "'v'", $data));
}
}
if (!is_error($ret) && is_array($ret) && !empty($ret)) {
if ($ret['state'] == 'fatal') {
return error($ret['errorno'], '发生错误: ' . $ret['message']);
}
return $ret;
} else {
return error($ret['errno'], "发生错误: {$ret['message']}");
}
}
function cloud_request($url, $post = '', $extra = array(), $timeout = 60) {//发送云服务请求
global $_W;
load()->func('communication');
if (!empty($_W['setting']['cloudip']['ip']) && empty($extra['ip'])) {
$extra['ip'] = $_W['setting']['cloudip']['ip'];
}
return ihttp_request($url, $post, $extra, $timeout);
}
function cloud_extra_account() {//获取添加公众号信息
$data = array();
$data['accounts'] = pdo_fetchall("SELECT name, account, original FROM ".tablename('account_wechats') . " GROUP BY account");
return serialize($data);
}
function cloud_extra_module() {//获取非系统模块 只用于cloud_m_query()方法
$sql = 'SELECT `name` FROM ' . tablename('modules') . ' WHERE `type` <> :type';
$modules = pdo_fetchall($sql, array(':type' => 'system'), 'name');
if (!empty($modules)) {
return base64_encode(iserializer(array_keys($modules)));
} else {
return '';
}
}
function cloud_extra_theme() {//获取非默认模板
$sql = 'SELECT `name` FROM ' . tablename('site_templates') . ' WHERE `name` <> :name';
$themes = pdo_fetchall($sql, array(':name' => 'default'), 'name');
if (!empty($themes)) {
return base64_encode(iserializer(array_keys($themes)));
} else {
return '';
}
}
function cloud_cron_create($cron) {//添加定时任务
$pars = _cloud_build_params();
$pars['method'] = 'cron.create';
$pars['cron'] = base64_encode(iserializer($cron));
$result = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars);
return _cloud_cron_parse($result);
}
function cloud_cron_update($cron) {//更新定时任务
$pars = _cloud_build_params();
$pars['method'] = 'cron.update';
$pars['cron'] = base64_encode(iserializer($cron));
$result = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars);
return _cloud_cron_parse($result);
}
function cloud_cron_get($cron_id) {//获取定时任务
$pars = _cloud_build_params();
$pars['method'] = 'cron.get';
$pars['cron_id'] = $cron_id;
$result = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars);
return _cloud_cron_parse($result);
}
function cloud_cron_change_status($cron_id, $status) {//更改定时任务状态
$pars = _cloud_build_params();
$pars['method'] = 'cron.status';
$pars['cron_id'] = $cron_id;
$pars['status'] = $status;
$result = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars);
return _cloud_cron_parse($result);
}
function cloud_cron_remove($cron_id) {//删除定时任务
$pars = _cloud_build_params();
$pars['method'] = 'cron.remove';
$pars['cron_id'] = $cron_id;
$result = cloud_request('http://v2.addons.we7.cc/gateway.php', $pars);
return _cloud_cron_parse($result);
}
function _cloud_cron_parse($result) {//定时任务返回结果处理
if (empty($result)) {
return error(-1, '没有接收到服务器的传输的数据');
}
if ($result['content'] == 'blacklist') {
return error(-1, '抱歉,您的站点已被列入云服务黑名单,云服务一切业务已被禁止,请联系微擎客服!');
}
$result = json_decode($result['content'], true);
if (null === $result) {
return error(-1, '云服务通讯发生错误,请稍后重新尝试!');
}
$result = $result['message'];
if (is_error($result)) {
return error(-1, $result['message']);
}
return $result;
}
function cloud_auth_url($forward, $data = array()){//云端oauth权限
global $_W;
$auth = array();
$auth['key'] = '';
$auth['password'] = '';
$auth['url'] = rtrim($_W['siteroot'], '/');
$auth['referrer'] = intval($_W['config']['setting']['referrer']);
$auth['version'] = IMS_VERSION;
$auth['forward'] = $forward;
if(!empty($_W['setting']['site']['key']) && !empty($_W['setting']['site']['token'])) {
$auth['key'] = $_W['setting']['site']['key'];
$auth['password'] = md5($_W['setting']['site']['key'] . $_W['setting']['site']['token']);
}
if ($data && is_array($data)) {
$auth = array_merge($auth, $data);
}
$query = base64_encode(json_encode($auth));
$auth_url = 'http://v2.addons.we7.cc/web/index.php?c=auth&a=passwort&__auth=' . $query;
return $auth_url;
}
function cloud_module_setting_prepare($module, $binding) {//模块设置
global $_W;
$auth = _cloud_build_params();
$auth['arguments'] = array(
'binding' => $binding,
'acid' => $_W['uniacid'],
'type' => 'module',
'module' => $module,
);
$iframe_auth_url = cloud_auth_url('module', $auth);
return $iframe_auth_url;
}
function cloud_resource_to_local($uniacid, $type, $url){
global $_W;
load()->func('file');
$setting = $_W['setting']['upload'][$type];
$pathinfo = pathinfo($url);
$extension = !empty($pathinfo['extension']) ? $pathinfo['extension'] : 'jpg';
$originname = $pathinfo['basename'];
$setting['folder'] = "{$type}s/{$uniacid}/".date('Y/m/');
$originname = pathinfo($url, PATHINFO_BASENAME);
$filename = file_random_name(ATTACHMENT_ROOT .'/'. $setting['folder'], $extension);
$pathname = $setting['folder'] . $filename;
$fullname = ATTACHMENT_ROOT . $pathname;
mkdirs(dirname($fullname));
if (file_put_contents($fullname, file_get_contents($url)) == false) {
return error(1, '提取文件失败');
}
if (!empty($_W['setting']['remote']['type'])) {
$remotestatus = file_remote_upload($pathname);
if (is_error($remotestatus)) {
return error(1, '远程附件上传失败,请检查配置并重新上传');
} else {
file_delete($pathname);
}
}
$data = array(
'uniacid' => $uniacid,
'uid' => intval($_W['uid']),
'filename' => $originname,
'attachment' => $pathname,
'type' => $type == 'image' ? 1 : 2,
'createtime' => TIMESTAMP,
);
pdo_insert('core_attachment', $data);
$data['url'] = tomedia($pathname);
$data['id'] = pdo_insertid();
return $data;
}
LoadModule status_module modules/mod_status.so #去掉这个模块前面的#
如果没有这个模块,则需要编译安装这个模块
在httpd.conf中添加如下配置
<Location /server-status>
SetHandler server-status
Order Deny,Allow
#Deny from all
Allow from 60.195.252.106 60.195.252.108
</Location>
ExtendedStatus On
说明,其中Location /server-status
里面的server-status
是设置显示状态的页面
Deny from all开启的话则是禁止所有的访问,这个时候如果访问没有在Allow里面,则会出现没有权限访问
Allow from 则是配置可以访问的客户端IP,测试不能使用域名,使用花生壳把域名解析成客户机IP也不能访问,Allow需要和Deny配合使用才行.
该配置是全局配置,不能配置在某一个虚拟网站之中.
我在使用该配置中,一直出现空白页面,后检查配置文件发现,
该apache有三个虚拟主机,其中两个是tomcat,使用了ajp转发,另外一个是apache直接管理的静态资源,发现只能通过静态资源那个网址才能访问,本服务器的情况是
www.car91.cn/server-status (tomcat)不能访问,报错404
we.car91.cn/server-status (tomcat)不能访问,报错404
static.car91.cn/server-status (apache)能够访问
尝试再新建一个apache管理的虚拟主机,js.car91.cn,发现js.car91.cn/server-status依然可以访问,看来不能访问的原因是tomcat的ajp转发上.
在上篇日志里《记录2012年走过的点点滴滴》,我的梦想1是坚持写作,而转眼一到就到了四月份,自己却还没有写下一点东西,今天下午的时候的逼迫下写了一篇连自己都不知所然的《营销,像风一般拂过》,之所以是时间的逼迫是因为想参加一个会议而提交的一份写作,前天收到的写作要求邮件,因为自己对这次写作框架里面的东西理解确实还比较肤浅,实在不知道写些什么,所以一直拖到今天,朋友鼓励说随便写些什么,据内部消息说只要提交了作品都可以参加会议的。所以在今天下午还是逼了一下自己,完成了一篇一千三百多字的写作。
上次我在会议上大胆地做了一次公众承诺,我说从四月份我会每个月至少完成一本书的阅读,然后我会在我的空间里每个月写下一篇日志。在这里,我想我也应该花点时间来先写点东西了,要不然以后写什么东西都会让今天下午一样,写不出一点东西,或者写的全是自己都读不懂的东西。所以在这里决定还是把上个月走过的感悟做一次小小的记录吧。
三月份最大的收获就是学习了林伟贤的《money& you》,这也是改变我最多的一堂课。第一个就是“成功人士走的第一步就与别人不一样“,在这里分享到了一个关于乔吉拉德的故事,他的名字是被列入了世界吉尼斯记录里面的,他从1963到到1977年的十五年时间里从事汽车销售,总共卖了13001辆汽车,而他最辉煌的一年是1973年,他总共卖了一千四百多辆汽车,按照每周五个工作日来算,平均每天要买六部车,而这六部车则是从他早上起床的那一刻起就开始了,他给自己买了一个特大号的闹钟,早上闹钟一响,他就立马跳下床,告诉自己,今天一定会有人为我在别人还在睡懒觉的时候就已经起床而付出代价的。这就是他与大多数人不一样的地方,我们大家早上都爱睡懒觉,我们习惯性的动作就是早上闹钟一响,我们关上闹钟,盖上被子,继续等待闹钟第二次,第三次响起。我也是一直就这样的习惯,早上总是要等两三次闹钟才能慢慢地起来。但这个故事却深深地改变我这的一个坏毛病,从三月份以前,每天早上闹钟一响,我就告诉我自己”成都人士走的第一步就与别人不一样“,于是便立马起床了。就这么一句简单的话,就给了我无穷的动力,让我轻而易举地改变了这个坏毛病,我认为这是我三月份中取得的最大进步。诚然,许多老板,当他们赚了许多钱以后,首先换的是自己的房子,自己的车子,甚至自己的老婆,而不曾想过这一切都将有可失去,只是换掉自己的脑袋,不断去学习,才能不断地进步,不断地创造更大的财富。
有一次,和同事们玩了一个游戏,大家分别写出自己心中认为最重要的十件东西,然后通过游戏不断地一件件地划去,游戏中,大家都写出了许多自己认为最重要的东西,亲情,健康,事业,友谊等等,而我也一直认为健康和快乐是我们人生最重要的东西,然而,我们每个人每天花八个小时甚至更多的时间努力工作,却是为了什么呢?我们高尚地说我们不是为了金钱,我们是为了给家人更好的生活,给未来孩子更好的教育,当我们辛苦地沉浸于工作之中,我们透支着健康,我们透支着与父母交流的机会,我们透支着关爱孩子的时间,一切一切,当我们最终拥有无数的财富时,我们真的是在为着那一个高尚的目标吗?我们的身子垮了,我们都不知道父母早已满脸皱纹了,我们都不知道我们的孩子叛逆成什么样子了,这就是我们所想要的吗?然而,金钱对我们而言也很重要,我们需要为父母,给孩子,给自己一个更美好的生活,我们可以有很美好的梦想,我们要挣下多少钱,我们要买下什么车,我们要买下多大的房子,朋友,这一切都只是我们努力工作奋斗的一个目标,但请不要忘了,我们的目的是什么,我们的目的是为了能够过上更好的生活,能够拥有一个健康的身体,能够拥有一帮很好的朋友,在我们为着目标奋斗时,也请不要忘了我们的目的是什么,目标永远不是一条单一的线,目标是为着目的而服务的,我们脚踏实地的朝着目标前进时,同时也让我们的双手紧紧握住我们的目的。工作累了,放一放,活动一下身子,放假的时候,多出去走动走动一下,常常给家长打打电话,问声温暖,有时间尽量回去多陪陪他们,当我们有了孩子的时候,永远要记得挤也得挤出时间来陪陪他们。记得曾经在一次特训营的活动中,一个小孩子想对他们的父母说“爸爸妈妈,我不希望你们能够挣好多钱,我只希望你们能多花时间来陪陪我“,我们的这一生中,工作不是我们唯一的目的,金钱也不是我们唯一的目标,我们还有着太多太多更重要的时间,我们可以花更多的时间在工作上,但请让我们花更多的心在我们追求的人生上面。记得曾看到过一句非常有感触的话”家不是放钱的地方,家是放心的地方,只有把心放在家里,我们的家庭才会幸福美好“。我们工作再累再辛苦,工资再高,我们挣的不是钱,而是一个幸福美好的家庭。这也是我在三月份学习到感触最深的一点,关于目标与目标的关系。
3月份开始报了驾照,本来打算在外面找陪教的,但后来恰好副总把她以前在驾校报的名额让给我了,所以我也只好在驾校去学校了,不过因为教练和我一同事关系特别好,还没有报名就开始让我练了一次车,谢谢副总,也谢谢小燕,我也开始为着驾照的梦想行进了。
小悦悦的未来储蓄计划,考虑了很多,定期存款,基金,黄金等等,最终还是选定了基金定投,从三月份开始了为期20年的基金定投,也希望未来能够带来不错的收益吧。
三月份还有一件事情,就是特别想摆地摊,也和朋友们去过好几个地方考察,结果都没有找到可以摆地摊的地方,这事后来也就松懈下来了,不知道接下来有没有机会找到摆地摊的地方。
做前端开发的朋友,也许就像我一样,键盘上面的ctrl键和s健估计是磨损是严重的了。因为我们要不断地编辑器中写完代码,然后ctrl+s,接着再浏览器中按下f5刷新查看效果。不断地重复这样辛勤的工作。
之前用过chrome的一个插件,liveReload,可以实现代码保存之后,自动刷新浏览器的功能。但有一个缺点就是要在chrome中和sublime text中都要安装插件。
后面使用了gulp之后,发现了一个更加NB的node模块,browser-sync,只需要安装这样一个模块,不用在浏览器中和编辑器中再安装插件了,而且自带一个静态服务器,我们纯前端的代码就可以不依赖其它服务环境了,当然,它也可以通过代理的方式,处理我们其它服务环境下面的代码。同时支持跨浏览器,包括手机浏览器,以及同步在多个浏览器上面响应点击,滚动等等事件。一次保存,多处自动刷新。
整个安装过程也是比较简单的,这里大致记录一下:
首先,定位到我们项目的文件夹,运行:
npm init
这样通过package.json的方式来管理我们项目的依赖包。
接下来输入我们项目的相关信息 比如:
name: (browsersync)
version: (1.0.0)
description:
entry point: (gulpfile.js)
test command:
...
...
Is this ok? (yes)
这里需要注意的两点是:1.项目名字不能用我们要安装依赖模块的名字,比如我们接下来要安装browser-sync,而我们把项目的名字也写成browser-sync的话,那么我们安装broser-sync的时候,可能会报下面这样的错误
Refusing to install browser-sync as a dependency of itself
2.entry point的入口文件写成gulpfile.js,因为我们要通过gulp来进行自动化构建。
这个时候我们项目下面就会生成一个package.json文件
{
"name": "browsersync",
"version": "1.0.0",
"description": "browser-sync demo",
"main": "gulpfile.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "buxuku",
"license": "ISC",
"devDependencies": {
}
}
安装gulp,为了防止gulp升级而导致的一系列的问题,我们不采用全局的方式安装
npm install gulp --save-dev
继续安装browser-sync
npm install browser-sync
安装完毕后,创建gulpfile.js入口文件
var gulp = require('gulp');
var browserSync = require('browser-sync').create();
// 静态服务器
gulp.task('browser-sync', function() {
browserSync.init({
files: "**",
server: {
baseDir: "./"
}
});
});
这个时候,我们运行gulp broser-sync
这个任务,就会自动创建一个http服务器,并打开我们的默认浏览器,同时监听我们的项目文件
$ gulp browser-sync
[09:11:00] Using gulpfile D:\phpStudy\WWW\browser-sync\gulpfile.js
[09:11:00] Starting 'browser-sync'...
[09:11:00] Finished 'browser-sync' after 25 ms
[BS] Access URLs:
-------------------------------------
Local: http://localhost:3000
External: http://192.168.37.1:3000
-------------------------------------
UI: http://localhost:3001
UI External: http://192.168.37.1:3001
-------------------------------------
[BS] Serving files from: ./
[BS] Watching files...
我时候该我们的workspace上场了。如果你玩的是双屏,一个放浏览器,一个放编辑器,土豪,我们做朋友吧。我们穷逼孩子还是不得不来回在编辑器和浏览器之间切换一下。
我们在html文件中引入一个外部css文件,整个代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>browser-sync</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
原来的内容
我增加一句 保存一下 测试
</body>
</html>
body{
font-size: 16px;
color:green;
}
首先设置chrome的workspace,还是用一张图片展示一下:
这里需要注意的就是要指定Mappings值,这样chrome才知道和本地文件的对应关系。
当然,最后我们看到了,我通过外部引入的css,在审查元素面板是不能看到引入文件的,而且对其修改,虽然能够反映到页面上面,但不会反映到本地css文件上面。这主要是因为browser-sync对css文件采用的是注入的方式。这种方式对css进行修改可以不用重新刷新页面,而只需要重新渲染css修改的部分。这也将导致的一个问题就是,我们在审查元素面板看到不所定义的css样式的真实行号。
我尝试在browser-sync中增加配置injectChanges: false
,css依然还是采用的注入方式。并没有起什么作用。
在设置中点击Restore defaults and reload
就可以。但通过审核元素面板修改css会存在一个问题就是,在输完属性名字是没有问题的,但一开始输入属性值,就会触发workspace保存文件,而文件一更新,又会导致browser-sync重新加载,所以就会无法完整输入具体属性值。配合browser-sync,还是通过审查元素面板点开css文件,进入source面板后修改再手动保存。参看如下操作:
最后,我们打开UI地址,就可以对browser-sync进行很多丰富的设置了。
和创建对象的几个方式类似,javascript也产生了几种不同的实现继承的方式.
<script>
function SuperType(){
this.property = true;
this.color = ["yellow","red"];
this.name = "xiaodong";
}
SuperType.prototype.getSuperTypeValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
/* SubType.prototype.getSubValue = function(){ //添加属性和方法必须在替换原型之后
return this.subproperty;
}*/
SubType.prototype = new SuperType();
var instance = new SubType();
console.log(instance.getSuperTypeValue());//true
console.log(instance instanceof Object);//true 原型和实例的关系
console.log(instance instanceof SubType);//true
console.log(instance instanceof SuperType);//true
console.log(Object.prototype.isPrototypeOf(instance));//true
console.log(SuperType.prototype.isPrototypeOf(instance));//true
console.log(SubType.prototype.isPrototypeOf(instance));//true
/*console.log(SubType.getSubValue());//TypeError: SubType.getSubValue is not a function*/
SubType.prototype.getSubValue = function(){ //添加属性和方法必须在替换原型之后
return this.subproperty;
};
console.log(instance.getSubValue());//false
/* SubType.prototype = { //不能再使用对象字面量的方式来重写原型,这样会断开最初的原型链
getSubValue : function(){
return this.subproperty;
}
}
var instance2 = new SubType();
console.log(instance2.getSuperTypeValue());//TypeError: instance2.getSuperTypeValue is not a function*/
//原型链继承的问题 :包含超级类的属性值 并且对实例中引用类型的值的修改,会反映到超级类的所有实例上面 并且子类的实例没有办法向超级类传递参数 即使通过子类的参数间接传递也不行
var instance2 = new SubType();
instance.name = "tiantian";
//instance.color = ["black"];
instance.color.push("black");//如果使用instance.color=["black"],就不会影响到其它实例,因为改变了对象的引用
console.log(instance2.color);// ["yellow", "red", "black"]
console.log(instance2.name);//xiaodong
</script>
<script>
function SuperType(name){
this.color = ["yellow","green"];
this.name = name;
this.getName = function(){
return this.name;
};
}
function SubType(name){
SuperType.call(this,name);
}
var instance = new SubType("xiaodong");
instance.color.push("black");
var instance2 = new SubType();
console.log(instance.color);//["yellow", "green", "black"]
console.log(instance.name);//xiaodong
console.log(instance2.color);// ["yellow", "green"]
console.log(instance.getName == instance2.getName);//false 方法没有达到共用
//借用构造函数解决了属性值共享的问题,以及实例向超级类传递参数的问题. 缺点 方法也是在函数中定义 无法解决函数共用的问题
</script>
<script>
//最常用的继承模式
function SuperType(name){
this.name = name;
this.color = ["yellow","green"];
}
SuperType.prototype.getName = function(){
return this.name;
};
function SubType (name,age){
SuperType.call(this,name);
}
SubType.prototype = new SubType();
SubType.prototype.constructor = SubType;
SubType.prototype.getAge = function (){
return this.age;
};
var instance = new SubType("xiaodong",22);
instance.color.push("black");
var instance2 = new SubType("tiantian",11);
console.log(instance.color);// ["yellow", "green", "black"]
console.log(instance2.color);// ["yellow", "green"]
console.log(instance.getName == instance2.getName);//true 实现了方法共享
console.log(instance.getAge == instance2.getAge);//true 实现了方法共享
</script>
<script>
//基本模型 缺点:引用类型值共享
function object(o){//o传入一个基本对象
function F(){}
F.prototype = Object(o);
return new F();
}
var person = {
name:"xiaodong",
color:["yellow","green"],
getName:function(){
return this.name;
}
};
var person1 = object(person);
person1.name = "tiantian";
person1.color.push("black");
var person2 = object(person);
console.log(person1.name);//tiantian
console.log(person2.name);//xiaodong
console.log(person1.color);// ["yellow", "green", "black"]
console.log(person2.color);// ["yellow", "green", "black"]
console.log(person1.getName == person2.getName);//true 实现了函数共享
//ES5中新增了Object.create()方法来更加规范地实现这一类继承
var newperson = {
name:"xiaodong",
color:["yellow","green"],
getName:function(){
return this.name;
}
};
var newperson1 = Object.create(newperson);
newperson1.name = "tiantian";
newperson1.color.push("black");
var newperson2 = Object.create(newperson);
console.log(newperson1.name);//tiantian
console.log(newperson2.name);//xiaodong
console.log(newperson1.color);// ["yellow", "green", "black"]
console.log(newperson2.color);// ["yellow", "green", "black"]
console.log(newperson1.getName == newperson2.getName);//true 实现了函数共享
</script>
<script>
function object(o){
var F = function(){};
F.prototype = o;
return new F();
}
function creatAnother (origin) {
var clone = object(origin);
clone.sayHi = function(){
return "hi";
};
return clone;
}
var origin = {
name:"xiaodong",
color:["yellow","green"],
getName:function(){
return this.name;
}
};
var other = creatAnother(origin);
other.name = "tian";
other.color.push("black");
var other2 = creatAnother(origin);
console.log(other.name);//tian
console.log(other.color);// ["yellow", "green", "black"] 引用类型共享
console.log(other2.name);// xiaodong
console.log(other2.color);// ["yellow", "green", "black"]
console.log(other.getName == other2.getName);//true
console.log(other.sayHi == other2.sayHi);//false 使用寄生式继承来为对象添加函数,函数不能共享而降低效率
</script>
<script>
/*//组合式继承的缺点:超类会调用两次
function SuperType(name){
this.name = name;
this.color = ["yellow","green"];
this.age = 23;
};
SuperType.prototype.getName = function(){
return this.name;
}
function SubType(name,age){
SuperType.call(this,name);//第二次调用超类
this.age= age;
}
SubType.prototype = new SuperType();//第一次调用超类
SubType.constructor = SubType;
SubType.prototype.getAge = function(){
return this.age;
}
var instance = new SubType("xiaodong",23);
console.log(instance.getName());//xiaodong*/
//寄生组合式继承 只调用一次SuerType构造函数,同时避免了在SubType.prototype上面创建不必要的,多余的属性.同时原型链保值不变.
function inheritPrototype(subType,superType){
var prototype = Object(superType.prototype);//创建对象
prototype.constructor = SubType;//增强对象
subType.prototype = prototype;//指定对象
}
function SuperType(name){
this.name = name;
this.color = ["yellow","green"];
this.age = 23;
}
SuperType.prototype.getName = function(){
return this.name;
};
function SubType(name,age){
SuperType.call(this,name);
this.age = age;
}
inheritPrototype(SubType,SuperType);
SubType.prototype.getAge = function(){
return this.age;
};
var instance = new SubType("xiaodong",23);
console.log(instance.getName());//xiaodong
</script>
我们在使用hexo安装了主题之后,后续主题作者可能会更新了主题,比如修复了bug,增加了功能等。那么这个时候我们就要想办法更新主题,最粗暴的方法就是直接删除主题,重新安装最新的,但这种方式不是用hexo写博客的精神。如果主题是托管在git上面的,那么就非常好办了。我们通过简单的git命令就可以完成更新操作了。
这里我们以目前非常流行的NexT
主题为例,我们安装的时候都是通过git的方式安装的,那么我们通过git在hexo目录下面,定位到next
目录,
在git中直接运行git fetch origin master
先把远程分支摘取下来,如下图
我们来对比看一下远程和本地的差异,运行git diff master origin/master
,如下图
接下来我们就可以进行合并操作了,运行git merge origin/master
,如下图
简单的三条命令我们就完成了主题的更新操作。
需要注意的是:
我们一般都要对主题的配置文件进行修改的,所以在合并的时候一定要注意远程的修改时间和本地的修改时间,如果远程的修改时间比本地的新,比如配置文件中可能增加了新的功能,那么一定要记得备份本地的修改文件,要不然合并下来可能会被覆盖掉的。
当然,我们也可以通过更加方便的方式使用git stash
或者git rebase
方式来更新合并。
当然,更好的办法还是应该是把原作者的github进行fork过来,然后在自己的本地修改git的远程分支为自己fork过来的地址,并添加upstream分支为原作者的分支。
这样,我们保留master分支上的代码一直是和upstream以及origin上的代码是同步并一致的,而自己对主题的修改刚新建的一个分支,比如叫building
分支,这样,当主分支有了更新之后,我们也能够安全地进行合并,如果我们自己想对主题进行比较好的修改,那么我们可以在主分支上面,再新建的一个dev
分支,添加功能之后,还可以向原作者进行pull request,这样也可以贡献我们自己的代码了。
比如我的实际操作,在github上fork完毕原作者的主题之后:
因为我之前对主题文件的配置进行了修改,所以我先把配置文件保存出来,因为我不需要合并,我需要把master分支保证和源分支一致。
首先,丢弃我对主题的修改
git reset --hard HEAD
修改远程地支的地址为自己fork之后的,
git remote origin set-url [email protected]:buxuku/hexo-theme-next.git
当然,这一步我们也可以先删除远程分支再添加的方式
git remote rm origin
git remote add origin [email protected]:buxuku/hexo-theme-next.git
或者直接修改config配置文件
然后添加原作者的远程分支
git remote add upstream [email protected]:iissnan/hexo-theme-next.git
同步原作者的分支
git fetch upstream
合并到master分支
git merge upstream/master
push到自己的github上面
git push origin master
添加自己写博客需要的一个分支
git checkout -b building
然后尽情地修改自己的配置文件,主题什么的,尽情享受写作的乐趣吧
修改完毕之后,git commit之后,我们再git push origin building推送到自己的远程目录。
当我们换电脑之后,全新安装hexo,进入theme目录,克隆我们的远程仓库
git clone [email protected]:buxuku/hexo-theme-next.git next
和上面一样,添加upstream分友
下载我们的building分支
git checkout -b building origin/building
git checkout master
检查更新,合并更新,合并更新到building分支
git fetch upstream
git merge upstream/master
git push origin master
git checkout building
git merge master
git push origin building
Syncing a fork :https://help.github.com/articles/syncing-a-fork/
Configuring a remote for a fork :https://help.github.com/articles/configuring-a-remote-for-a-fork/
centos6.5最小化安装/AWS centos
cmake-3.1.0-rc2
mysql-5.7.4-m14.tar.gz
wget http://downloads.mysql.com/archives/get/file/mysql-5.7.4-m14.tar.gz
wget http://www.cmake.org/files/v3.1/cmake-3.1.0-rc2.tar.gz
tar -zxvf mysql-5.7.4-m14.tar.gz
tar -zxvf cmake-3.1.0-rc2.tar.gz
因为从mysql 5.5形如,需要使用cmake方便进行安装了,所以我们首先安装cmake
cd cmake-3.1.0-rc2
./bootstrap
gmake
gmake install
groupadd mysql #添加组
useradd mysql -g mysql -s /sbin/nologin #添加新用户,禁止登录shell
mkdir /usr/local/mysql #创建安装目录
mkdir /var/mysql
mkdir /var/mysql/data #创建数据目录
chown -R mysql:mysql /usr/local/mysql/
chown -R mysql:mysql /var/mysql/data #予数据存放目录权限
cd mysql-5.7.4
cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql/ -DMYSQL_UNIX_ADDR=/tmp/mysqld.sock -DMYSQL_USER=mysql -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_ARCHIVE_STORAGE_ENGINE=1 -DWITH_BLACKHOLE_STORAGE_ENGINE=1 -DWITH_PERFSCHEMA_STORAGE_ENGINE=1 -DWITH_READLINE=1 -DWITH_DATADIR=/var/mysql/data/ -DWITH-TCP_PORT=3306 -DENABLE_DOWNLOADS=1
make && make install
chmod +x scripts/mysql_install_db
scripts/mysql_install_db --basedir=/usr/local/mysql --datadir=/var/mysql/data --user=mysql
cp support-files/my-medium.cnf /usr/local/mysql/my.cnf
修改my.cnf参数,没有则加入如下:
basedir = /usr/local/mysql #(不配置的话默认为$PREFIX_DIR)
datadir = /var/mysql/data #(不配置的话默认为$PREFIX_DIR/data)
log-error = /usr/local/mysql/mysql_error.log #(不配置的话默认为$PREFIX_DIR/data/$hostname.err)
pid-file = /usr/local/mysql/mysql.pid #(不配置的话默认为$PREFIX_DIR/data/$hostname.pid)
user = mysql
tmpdir = /tmp #(不配置的话默认为/tmp)
方法1.
alt+f9
然后backspace
删除掉路径,接下来alt+s
选择包含文件夹就可以了。
方法2.先左右窗口相同
方法3.ctrl+→
把当前文件、文件夹显示到对面,然后到对面进行操作
方法1.按住
ctrl
的时候用鼠标点菜单上面的压缩,这个时候可以用f5压缩后的文件名
方法2.在配置文件中的[Shortcuts]
下面增加CA+F5=cm_PackFiles
,以后就可以使用ctrl+alt+f5
实现压缩了
方法3.同解压缩的方法2
方法4.同解压缩的方法3
公司的服务器mysql一直是以root用户启动起来的,运行一直正常,今天给linux重新建了一个root组的用户,以这个用户的身份登录后,重启mysql一直失败,然后就把所有mysql运程结束了,以该用户身份启动mysql时,在错误日志中一直提示如下:
150402 09:49:34 mysqld_safe mysqld from pid file /usr/local/mysql/data/iZ9423zm65xZ.pid ended
150402 09:50:44 mysqld_safe mysqld from pid file /usr/local/mysql/data/iZ9423zm65xZ.pid ended
150402 09:51:16 mysqld_safe Starting mysqld daemon with databases from /usr/local/mysql/data
150402 9:51:16 [Warning] Can't create test file /usr/local/mysql/data/iZ9423zm65xZ.lower-test
150402 9:51:16 [Warning] Can't create test file /usr/local/mysql/data/iZ9423zm65xZ.lower-test
/usr/local/mysql/bin/mysqld: Can't find file: './mysql/plugin.frm' (errno: 13)
150402 9:51:16 [ERROR] Can't open the mysql.plugin table. Please run mysql_upgrade to create it.
150402 9:51:16 InnoDB: The InnoDB memory heap is disabled
150402 9:51:16 InnoDB: Mutexes and rw_locks use GCC atomic builtins
150402 9:51:16 InnoDB: Compressed tables use zlib 1.2.3
150402 9:51:16 InnoDB: Initializing buffer pool, size = 128.0M
150402 9:51:16 InnoDB: Completed initialization of buffer pool
150402 9:51:16 InnoDB: Operating system error number 13 in a file operation.
InnoDB: The error means mysqld does not have the access rights to
InnoDB: the directory.
InnoDB: File name ./ibdata1
InnoDB: File operation call: 'open'.
InnoDB: Cannot continue operation.
150402 09:51:16 mysqld_safe mysqld from pid file /usr/local/mysql/data/iZ9423zm65xZ.pid ended
解诀办法:其实mysql配置没有问题,只是在启动时需要加是sudo来运行,sudo service mysqld start
,启动正常后,再运行sudo service mysqld restart
也是正常的。
./configure --with-apxs2=/usr/local/apache2/bin/apxs --disable-cli --enable-shared --with-libxml-dir --with-gd --with-openssl --enable-mbstring --with-mcrypt --with-mysqli --with-mysql --enable-opcache --enable-mysqlnd --enable-zip --with-zlib-dir --with-pdo-mysql --with-jpeg-dir --with-freetype-dir --with-curl --without-pdo-sqlite --without-sqlite3
在编译时报错
Sorry, I cannot run apxs. ***
Sorry, I cannot run apxs. Possible reasons follow:
1. Perl is not installed
2. apxs was not found. Try to pass the path using --with-apxs2=/path/to/apxs
3. Apache was not built using --enable-so (the apxs usage page is displayed)
configure: error: Sorry, I cannot run apxs. Either you need to install Perl or you need to pass the absolute path of apxs by using --with-apxs=/absolute/path/to/apxs
没有指明正确的perl执行程序的位置
解决办法
vi /usr/local/apache/bin/apxs
把第一行#!/replace/with/path/to/perl/interpreter -w
修改为
#!/usr/bin/perl -w
接上一步,重新编译时,又出现如下错误
mcrypt.h not found. Please reinstall libmcrypt
这是因为centos源不能安装libmcrypt-devel,由于版权的原因没有自带mcrypt的包
解决办法
cd /usr/local/src
wget http://softlayer.dl.sourceforge.net/sourceforge/mcrypt/libmcrypt-2.5.8.tar.gz
tar -zxvf libmcrypt-2.5.8.tar.gz
cd /usr/local/src/libmcrypt-2.5.8
./configure --prefix=/usr/local
make
make install
varsu@DESKTOP-V7HEGUG MINGW64 /d/temp/npm
$ npm -v
2.15.1
varsu@DESKTOP-V7HEGUG MINGW64 /d/temp/npm
$ npm install npm --global
C:\Users\varsu\AppData\Roaming\npm\npm -> C:\Users\varsu\AppData\Roaming\npm\node_modules\npm\bin\npm-cli.js
[email protected] C:\Users\varsu\AppData\Roaming\npm\node_modules\npm
varsu@DESKTOP-V7HEGUG MINGW64 /d/temp/npm
$ npm -v
3.10.9
varsu@DESKTOP-V7HEGUG MINGW64 /d/temp/npm
$ npm install forever -g
varsu@DESKTOP-V7HEGUG MINGW64 /d/temp/npm
$ npm uninstall forever -g
D:\temp\npm>npm install underscore
D:\temp\npm
`-- [email protected]
npm WARN enoent ENOENT: no such file or directory, open 'D:\temp\npm\package.json'
npm WARN npm No description
npm WARN npm No repository field.
npm WARN npm No README data
npm WARN npm No license field.
D:\temp\npm>tree
文件夹 PATH 列表
卷序列号为 000000B9 8841:2A63
D:.
└─node_modules
└─underscore
D:\temp\npm>npm uninstall underscore
- [email protected] node_modules\underscore
npm WARN enoent ENOENT: no such file or directory, open 'D:\temp\npm\package.json'
npm WARN npm No description
npm WARN npm No repository field.
npm WARN npm No README data
npm WARN npm No license field.
D:\temp\npm>npm ls
D:\temp\npm
`-- [email protected]
或者加上参数-g
查看全局范围安装的包
D:\temp\npm>npm info underscore
{ name: 'underscore',
description: 'JavaScript\'s functional programming helper library.',
'dist-tags': { latest: '1.8.3', stable: '1.8.3' },
versions:
[ '1.0.3',
'1.0.4',
'1.1.0',
'1.1.1',
'1.1.2',
'1.1.3',
'1.1.4',
'1.1.5',
'1.1.6',
'1.1.7',
'1.2.0',
'1.2.1',
'1.2.2',
'1.2.3',
'1.2.4',
'1.3.0',
'1.3.1',
'1.3.2',
'1.3.3',
'1.4.0',
'1.4.1',
'1.4.2',
'1.4.3',
'1.4.4',
'1.5.0',
'1.5.1',
'1.5.2',
'1.6.0',
.............
D:\temp\npm>npm install [email protected]
D:\temp\npm
`-- [email protected]
初始化一个项目,生成package.json 项目名不能有空格
D:\temp\npm>npm init
{
"name": "my_npm",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "buxuku",
"license": "ISC"
}
在package.json中安装npm包
D:\temp\npm>npm install underscore --save
[email protected] D:\temp\npm
`-- [email protected]
D:\temp\npm>npm install babel-cli --save-dev
[email protected] D:\temp\npm
`-- [email protected]
卸载package.json中的包
D:\temp\npm>npm uninstall underscore --save
- [email protected] node_modules\underscore
D:\temp\npm>npm install [email protected] --save-dev
检查更新
D:\temp\npm>npm outdated
Package Current Wanted Latest Location
gulp 2.0.0 2.7.0 3.9.1 my_npm
Wanted表示可以更新到的版本号,但它最新的却是3.9.1,打开package.json,我们可以看到
"devDependencies": {
"babel-cli": "^6.16.0",
"gulp": "^2.0.0"
},
包后面的^
表示只更新第二位数据的版本号,如果改成~
则表示只更新最后一位的版本号,如果版本号全部改成*
,刚表示更新全部的版本号.
改成~
D:\temp\npm>npm outdated
Package Current Wanted Latest Location
gulp 2.0.0 2.0.1 3.9.1 my_npm
改成*
D:\temp\npm>npm outdated
Package Current Wanted Latest Location
gulp 2.0.0 3.9.1 3.9.1 my_npm
注意,一般在开发项目中最好不要改成*
,因为大的版本号的更新可能会导致原来的功能不能正常使用。
cnpm的使用参见淘宝的cnpm,这里主要使用nrm来管理切换npm使用的源
安装
D:\temp\npm>npm install nrm -g
查看可以使用的源
D:\temp\npm>nrm ls
* npm ---- https://registry.npmjs.org/
cnpm --- http://r.cnpmjs.org/
taobao - https://registry.npm.taobao.org/
nj ----- https://registry.nodejitsu.com/
rednpm - http://registry.mirror.cqupt.edu.cn/
npmMirror https://skimdb.npmjs.com/registry/
edunpm - http://registry.enpmjs.org/
测试各个源的连接速度
D:\temp\npm>nrm test
* npm ---- 1520ms
cnpm --- 300ms
taobao - 445ms
nj ----- Fetch Error
rednpm - Fetch Error
npmMirror 12046ms
edunpm - Fetch Error
切换npm的源
D:\temp\npm>nrm use cnpm
Registry has been set to: http://r.cnpmjs.org/
D:\temp\npm>nrm ls
npm ---- https://registry.npmjs.org/
* cnpm --- http://r.cnpmjs.org/
taobao - https://registry.npm.taobao.org/
nj ----- https://registry.nodejitsu.com/
rednpm - http://registry.mirror.cqupt.edu.cn/
npmMirror https://skimdb.npmjs.com/registry/
edunpm - http://registry.enpmjs.org/
现象:使用文章营销插件之后,分享者把链接分享出去之后,被分享者点击链接之后,能够正常记录阅读数,但记录不到分享记录,同时也导致给分享者设置的奖励无效。
原因分析:在文章营销首页php文件addons/ewei_shop/plugin/article/core/mobile/index.php
中第52行开始代码如下
$myid = m('member')->getMid();
$shareid = intval($_GPC['shareid']);
echo $doShare = $this->model->doShare($article, $shareid, $myid);
在调用doShare
方法之前,获取到的$myid
是为空的,继续分析member
文件中的getMid()
方法可以发现,这里获取到的myid
是读取数据库ewei_shop_member
里面的信息,而如果用户之前没有进入过公众号人人商城,那么也就不会存在这个记录值的,所以返回结果是为空的。
解析办法:在这段代码之前添加方法m('member')->checkMember();
,代码如下:
m('member')->checkMember();
$myid = m('member')->getMid();
$shareid = intval($_GPC['shareid']);
echo $doShare = $this->model->doShare($article, $shareid, $myid);
现象:文章分享出去,别人点击之后,在后台查看分享记录里面的点击者,只能显示未更新用户信息
原因分析:在文件/addons/ewei_shop/core/model/member.php
中第219左右行如下:
public function checkMember($openid = '')
{
global $_W, $_GPC;
if (strexists($_SERVER['REQUEST_URI'], '/web/')) {
return;
}
if (empty($openid)) {
$openid = m('user')->getOpenid();
}
if (empty($openid)) {
return;
}
$member = m('member')->getMember($openid);
$userinfo = m('user')->getInfo($openid);
$followed = m('user')->followed($openid);
其中的$userinfo = m('user')->getInfo($openid);
写法有误,分析getInfo()
方法可以发现,这个方法只会返回openid
这一条信息的,不会返回其它如昵称之类的。
解决办法:使用$userinfo = m('member')->getInfo($openid);
方法,代码如下:
public function checkMember($openid = '')
{
global $_W, $_GPC;
if (strexists($_SERVER['REQUEST_URI'], '/web/')) {
return;
}
if (empty($openid)) {
$openid = m('user')->getOpenid();
}
if (empty($openid)) {
return;
}
$member = m('member')->getMember($openid);
$userinfo = m('member')->getInfo($openid);
$followed = m('user')->followed($openid);
现象:这个也不能说算是bug,只是程序写法中,区别上面一条的是,上面就算用户关注了公众号的,也是没办法获取到点击者信息的,而如果用户未关注公众号,在修复了上面那个问题之后,依然是获致不到用户信息的。
原因分析:在ewei_shop/core/model/member.php
中的getInfo()
方法,这个方法只会读取ewei_shop_member
表和系统用户表里面的用户信息,而如果用户没有关注的话,是没有这些信息的。
解析方法:在这个方法的原来判断代码中,如下
public function getInfo($openid = '')
{
global $_W;
$uid = intval($openid);
if ($uid == 0) {
$info = pdo_fetch('select * from ' . tablename('ewei_shop_member') . ' where openid=:openid and uniacid=:uniacid limit 1', array(
':uniacid' => $_W['uniacid'],
':openid' => $openid
));
} else {
$info = pdo_fetch('select * from ' . tablename('ewei_shop_member') . ' where id=:id and uniacid=:uniacid limit 1', array(
':uniacid' => $_W['uniacid'],
':id' => $uid
));
}
if (!empty($info['uid'])) {
echo "fda";
load()->model('mc');
这里面的if (!empty($info['uid']))
后面添加else
代码块
else{
$info = m('user')-> oauth_info();
$info['avatar'] = $info['headimgurl'];
}
通过完整的OAuth方式获取用户信息。
无聊想到写的一个小demo,整个js代码只有几行,关键思路就是乘数置前的处理手段。
我们正常的思维逻辑按照我们背的方式,一列一列的来生成:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
</body>
<script>
for(i=1;i<10;i++){
for(j=i;j<10;j++){
console.log(i+"*"+j+"="+i*j);
}
}
</script>
</html>
如果要在页面上展示出来,我们一行一行地进行处理,刚不是向上面那个一列一列地处理,可能更容易展示,于是我们就想到在循环中把乘数放在外层是更容易处理的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<table id="table"></table>
</body>
<script>
for(var i=1,htmlStr='';i<10;i++){
htmlStr += "<tr>"
for(var j=1;j<=i;j++){
htmlStr += "<td>"+j+"*"+i+"="+i*j+"</td>"
}
htmlStr += "</tr>"
}
document.getElementById("table").innerHTML=htmlStr;
</script>
</html>
有些时候,我们往往有这样的需求:
针对这些需求,我们使用在线的代码片段测试工具,也许来得更加简单和方便了。
针对前端的在线代码片段工具很多,比较常见的有jsbin和jsfiddle以及codepen.
而我最喜欢的就是jsbin了,它有着更多的特性给我带来了极大的方便:
任意控制要展示的窗口
点击这些标签,就可以控制对应的窗口的显示与隐藏,让我们获得更大的编辑区域,减少不需要的窗口的干扰。
支持console面板,因为我调试代码习惯于用console面板来调试,所以它的这个面板一下子就吸引了我。
直接支持在线用书写sublime text的快捷键来书写代码,而且是支持emmet的哦。不需要在自己的编辑器里面写好,再复制过去了,直接在线流畅地进行书写,
开启方法:Account->Editor settings->Addons 里面,Key bindings勾选Sublime就可以了。
我们在书写了代码之后,可以通过File->Save snapshot来保存当前的代码片段,但默认保存的很难让我们区分出这段代码是干什么用的。就像下面这样:
这里面的信息我们是不能编辑的,我们可以在编辑窗口,点击File->Add description来添加描述。
这下子就非常清晰了。
这个不用提了,每次必装的插件,写html.css代码如飞一般。
没办法,被逼的,只有用来它支持下中文了。
最后才装上的一个插件,支持HTML,CSS,JS,JSON的格式化插件,装上它后发现太强大了。以前一直用的tag,发现很多时候都格式化不了,这个插件不仅可以格式化html,还可以格式化css,js,json,打开压缩后的min js文件,点右键,或者ctrl+shift+h
就可以格式化了,再也不用上网找工具了,json文件也是如此。
代码成对高亮插件,找凑合标签非常方便,但不知道为什么今天装的时候在package install 里面找不到这个插件了。
PS:刚把电脑上面原来64位的卸载掉了,然后重新安装32位的,可以正常安装了,估计应该是不支持64位吧。
自定义页面模板的功能,有时候再写一套网站的时候,很多页面的模板基本上都是一样的,虽然通过复制粘贴可以实现,但还是没有用这个插件来得简洁,快捷。
插件的详细使用说明可以参考http://www.fantxi.com/blog/archives/sublime-template-engine-sublimetmpl/
自动补完文件名字,这个在插入图片文件时候非常方便的。
配合chrome F12工具中的LiveStyle插件一起使用,边浏览边在chorme里面就可以修改Css文件了。
这个软件不用说了,xbeta专门用一个网站来推荐他的一个神一般的资源管理器,因为我与电脑文件交道打得太频繁了,详细使用方法在Xbeta上面已经有了.
当年用DW做网站的时候,那个时候正好经历了从表格到DIV+CSS转变的时候,当把DIV+CSS学得差不多时候,发现DW只用得上代码模式了,其它的功能基本上都用不上了,而就为了这一点点却上用上一个庞大的DW,一直希望能够有一个记事本般小巧,但能够有代码提示功能的,直到我遇到了SB,最爱他的皮肤,他的模糊匹配,他的丰富插件,他的...
在firefox里面有一个很好用的插件xthunder,这个下载管理器非常方便,后来经常用chrome了,却一直找不到一个满意的下载管理器,直到我遇到IDM,才发现chrome不需要什么下载管理器了,它和chrome完美兼容,静默下载,多线程下载,小巧得我可以忽略它的存在,再也不用忍受每次启动都要等个十多二十秒的迅雷或者旋风,下载,就这么简单就好.
可惜它是收费的,网上比较优秀的破解版应该是Yanu的吧.附上一链接:http://www.ccav1.com/idm.html
每个月只有1G的流量,和现在动不动就几十T的网盘比起来,我却还是只用它,只因为它同步特别稳定,从来没有遇到过同步出过问题的,增量同步,多文件夹同步,方便极了.
刚刚才接触它,却立马爱上他了,以前一直用everything,同样是一款神一样的软件,秒秒搜索全盘软件,而Listary除了上搜索上同样厉害之外,还增强了许多功能,比如快捷键唤出搜索,和文件管理器结合一体,还与Totalcommander整合,增强的打开保存功能.
说实话,我用它真的是太浪费它了,因为我只用它来打开常用软件,常用文件,每天win+q打开QQ的感觉真的很爽,用上它,用上totalcommander,listray这些软件,真的是可以越来越少使用鼠标了.
我不得说,做为一个十多年的资深网民,很多下载页面我也是要找半天才能找到真实的下载地址,浏览器有很多过虑广告的插件,比如Adblock Plus,和它同样优秀的这个奶牛,优点是可以过滤到不仅仅浏览器,软件的广告也都可以过滤到.整个互联网终于清静了.看视频也不用等广告了.
以前用过CLCL,呼出加快捷选择很方便,后来用上ditto,同样非常快捷方便的剪贴板管理工具,天天和代码,文档打交道,这个是必不可少的.再也不用来回重新复制了.
现在用得比较少了,但笔记本还是一直让它自启的,因为一直登录自己的账号,远程控制无须本机确认,桌面管理,文件传输,流畅的速度,远程管理一直用的就是它了.
以前也用过一些虚拟桌面软件,但都记不得名字了,最近又看到一个虚拟桌面软件,不是virgo,虽然实际使用频率特别少,但只有7K的体积让我屈服了.不得不说算得上是超级经典的软件了.
以前一直用的是KMplayer,PotPlayer是KMplayer的新作,后来的射手同样也是做的非常优秀,我比较喜欢那些小巧得惊人,但功能却一点都不差不的软件,而且相当专注,没有太多臃肿的功能.
每次分区,修复分区表,找回误删的文件,查看丢失分区里面文件...最顺手,最信任的可能就是它了.
同样是因为小巧,但清理得却是非常彻底的.不需要动不动就是什么助手,卫士的.
我一直认为我用不上它的,因为我不是邮件达人,直到我发现它还支持RSS,每天再也不用不断去看那几个喜欢的软件有没有更新了,直接在它里面全部浏览无余.而且它收取gmail邮件比我以前用QQ代收要来得快得多.
以前用7z,因为它免费,一般我尽量找能够替代收费软件的替代品,毕竟用破解版总感觉对作者不尊重似的,后来用上bandizip主要是因为同样也是免费的,另外支持直接查看压缩包里面的图片.
折腾系统,网银不兼容,测试,测试,测试,....还是测试,而且现在VMware还非常良心地为我们推出了免费版的VMware Workstation Player,基本上我们要用的功能免费版上面都已经有了。
应该是免费VPN里面最好的了吧.
曾经研究过一段时间,什么换IP,用虚拟机,都没有它来得防查水表的强大.
备注:本篇文章是五一节的时候在家写的,当时保存在家里的电脑上面,家里的电脑没联网了,所以一直没有发布,直到端午节的时候回家再次打开一看,虽然已经有点不堪入目,本想重新修改,但想下还是算了吧,毕竟也是那个时候最真实的感悟,便还是发上来吧,以表纪念。
四月是成长的一月,也是思维上产生了巨大转变的一个的月。
曾经天天经常说我是“三分热情”,因为以前不管我产生一个什么想法,最开始是充满了无限的热情,可是过不了一段时间就放弃了。现在我发现之所以是这样的,那是因为改变自己的决定不够。当自己下定决定要去改变自己的时候,当这份决心越强烈,就越能坚持着去行动。
我相信我现在做出一份决定时,我就必将要产生强烈的一份愿望。而且我也将把它转变成一份强大的执行力,成为生活中的一部分,而逐渐成为一种习惯。四月份我看完了两本富爸爸的书,以及每天练习钢笔字。与其说是坚持下来,准确地说应该是习惯下来了。当自己决定每个月至少看完一本书,要练习钢笔字的,我便很自然地把它变成了每天晚上的一部分。正如前几天所发的微博一样,不管晚上因为其它什么原因弄得很晚,我都依然会坚持看一会儿书,然后写一页字。哪怕量可以少一点,但我会每天坚持必须去执行。当这一切逐渐形成习惯之后,每天没看书写字,反倒会觉得心中少了点什么。
我们总是感慨时间过得真快,却真没有想到时间就是在我们不经意间悄悄从指缝中流过了。当我决定每个月至少看一本书时,一个月执行下来,却是发现我是很轻松地就看完了两本书。当每天晚上我不再一吃完饭就打开电脑,无聊地刷下微博,刷下空间时,静下心来,打开台灯,翻阅几十页书纸,写上一两篇字,却是发现这短短的一两个小时,却是无限收获,无限充实的两个小时。好好地抓住一两个小时时间,我们就可以惊奇地发现,一天中就因为我们多了这短短的两个小时,却是多做了许多事情。其实说起来很简单,大家都明白我们的时间都是在无聊中慢慢地流失掉了。关键问题是我们不明白我们除了工作,我们还可以做什么。似乎工作就成了我们唯一的生活,下班后,我们可能就只能是无聊地看电视,上网,打游戏。正如网上一名老太太说的一样,现在的年轻人做着他们八十岁都还会做的事,那我们的生活色彩,我们的追求又在哪里呢?也许你可以感受一份书香墨气的清香,也许你可以收获朋友畅谈的一份轻快,也许你可以获取城市夜灯下的繁荣......时间之所以无聊,只是因为我们自己本身太无聊。当给以自己生活足够的方式时,人生也不再寂寞,成长也不再止步。
不知道是生活经历多了,还是因为慢慢在书香墨气中熏陶着一份宁静,我发现我对生活,对事情开始变得从容淡定了。很多时候,我们之所以急燥,之所以发脾气,往往都是在以这样一种无用的方式来回避而已。快乐其实很简单,就如一同事给我们分享的一个故事那样,他掉了钱,并不会觉得伤心悲痛,他会想,也许拾得这钱的人正需要这笔钱,也许他正准备犯法去偷钱去抢钱,而我这钱可能就救了他一命。我觉得这就是一种心境,一种淡定。曾经天天不小心被别人偷了几百元,她急得恨不得把那个人碎石万断,而我在电话里却是很淡定地安慰她掉了就掉了吧,钱又不是挣不来的,何必还要让自己去伤心呢。自己的工作接触的事情是比较繁多而杂乱的,以前我在内心老是犯急燥,因为我最怕杂乱了,尤其是我正在做一件事时突然又冒出另外一件事来,而现在我开始学会淡定去面对了,自己把所有的事情整理好,一件一件地去完成,我发现我会有很充足的空间时间去收获那份属于我自己的快乐。前几天和几个兄弟们一起聊天,一兄弟说我现在不敢破釜沉舟了,就是在没的找好工作前,我是不敢裸辞,我说我敢,我是真的敢,这一是一份激情,不是一份狂热,只是一份淡定,因为我相信,裸辞之后,面对接下来可能出现的各种状态,我都有足够强大的心态去面对与应付,曾经离开德阳,我是裸辞,当年在温州,天天睡地铺吃泡菜,面对雅安地震,我还活着,面对那些成功人士,我还年轻,面对那些高层管理,我还愿意去学习,我还有什么不能去面对的呢?不淡定者是因为我们在内心一直告诉自己“我可不行,我可办不到”,而我们应该告诉自己的是“我应该怎么去做?”
关于金钱的观念是我这个月思维转变最大的一次,虽然以前我也明白通过理财可以让自己的财富倍增,但我没有理解到金钱的深层次运作机制。两本富爸爸的书,以及现金流游戏深深地改变了我对金钱的看清。虽然我目前的状况是已经跳出了老鼠赛跑,但这个月的学习则是让我站在了整个人生的高度来看待我们的金钱。
我们都知道,我们金钱分为收入和支出,而我们收入其实应该是分为劳动收入和被动收入,这是很重要的一个概念,因为衡量我们是否是获得了财务自由,就是看我们的被动收入能否超过我们的支出,只有当我们的被动收入已经超出了我们的支出,我们才能称得上实现了财务自由。被动收入就是我们工作之外的收入,可以不用自己去工作就能源源不断地产生的收入,这就是一个完全自动的印钞机,比如我们的存款利息,股票,期货,版权,房租等等都是我们的被动收入。
这里讨论财务自由由我们的工资没有关系,只和我们的被动收入及支出有关,有些人也许工资很高,但他可能没有一分钱的被动收入,这是一个很容易想象的现象,万一那天公司裁员或者倒闭了,自己就将面临着没有任何收入来源了。我们又不得不马上顶都会一份压力立马去寻找其它工作,找到工作后,我们不得不小心翼翼地拼命把握住这份工作,因为一旦失去工作,我们又将失去收入来源,又不得不再去寻找下一份工作,周而复始,我们就这样不断地被工作牵着团团转了。
相反,当我们实现财务自由之后,我们则是另外一种生活状态,因为我们已经有了被动收入,而这个被动收入不是依附于自己的工作,所以哪怕自己美美地睡上一天觉,也是依然有稳定的收入流进来,而且这份收入是可以满足自己的支出需要的。这个时候,我们就不再是工作的奴隶了,因为我们有了可以选择工作的权利,我们可以很轻松去去选择自己喜欢的工作,也可以很大胆地放弃自己不喜欢的工作。
既然财务自由与我们的工作收入没有关系,那我们的工作收入代表着什么呢?工作收入就是我们的一份储蓄,市场的变化,物价的上涨,支出结构的变化等等因素都可能导致我们的支出总额不断地增多,而我们要不断地维持财务自由,我们就需要不断地提高自己的被动收入。提高自己的被动收入一方面是加大以前项目的投资额度,别一方面是增加其它被动收入来源,这就是需要寻找与把握机会,只有当我们拥有充足的储蓄时,我们才会拥有更多的机会把把握住能够产生被动收入的机会。我们不断地提高我们的工作收入,也就是在不断地储备被动收入的能力。
比如我要看下mysql启动的参数
ps aux |grep mysql
输出结果
root 21992 0.0 0.0 11340 1396 pts/2 S 09:58 0:00 /bin/sh /usr/local/mysql/bin/mysqld_safe --datadir=/usr/local/mysql/data --pid-file=/usr/local/mysql/data/iZ9423zm65xZ.pid
mysql 22070 0.0 1.1 1070516 46072 pts/2 Sl 09:58 0:00 /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data --plugin-dir=/usr/local/mysql/lib/plugin --user=mysql --log-error=/usr/local/mysql/data/iZ9423zm65xZ.err --pid-file=/usr/local/mysql/data/iZ9423zm65xZ.pid
car91 26054 0.0 0.0 103248 824 pts/2 R+ 10:12 0:00 grep mysql
1.切换到root用户:
su
2.切换回原来的用户:exit
3.切换到指定用户:su xxx
(xxx为指定的用户名)
useradd car91 -g root #添加root组用户car91
passwd car91 为car91添加密码
这样创建用户是,是不能直接使用sudo命名的,还需要如下操作
chmod u+w /etc/sudoers #添加sudo文件的写权限
vi /etc/sudoers #找到这行 root ALL=(ALL) ALL,在他下面添加xxx ALL=(ALL) ALL (这里的xxx是你的用户名)
chmod u-w /etc/sudoers #删除写权限
第二步添加的内容可以为
youuser ALL=(ALL) ALL #允许用户youuser执行sudo命令(需要输入密码).
%youuser ALL=(ALL) ALL #允许用户组youuser里面的用户执行sudo命令(需要输入密码).
youuser ALL=(ALL) NOPASSWD: ALL #允许用户youuser执行sudo命令,并且在执行的时候不输入密码.
%youuser ALL=(ALL) NOPASSWD: ALL #允许用户组youuser里面的用户执行sudo命令,并且在执行的时候不输入密码.
全局安装webpack
npm i webpack -g
mkdir webpack-step //新建一个目录并初始化它
cd webpack-step
npm init -y //加y参数全部使用默认值快速初始化
npm i [email protected] -D //项目中再安装一次webpack,webpack现在已经有2.0版本了,有些配置文件修改了,还是先安装1.0以上的版本吧
touch test.js //新建一个文件
vi test.js //编辑文件 写入console.log("hello world");并保存
webpack test.js bundle.js //运行webpack进行打包
执行到这一步,就会在项目中打包生成一个bundle.js的打包文件
这就是webpack的最简使用方法了。
touch webpack.config.js
vi webpack.config.js
输入以下内容
var webpack = require('webpack');//载入webpack模块
module.exports = {
entry :['./test.js'],//设置打包入口文件
output :{
path:__dirname,//设置打包的输出文件夹
filename:'bundle.js'//打包后的文件
}
}
这个时候再直接执行webpack
命令就可以直接打包了。
webpack有很多内置的插件及npm安装众多的插件,插件使用在webpack中的plugins配置项中,它是一个数组项,可以配置多个
var webpack = require('webpack');//载入webpack模块
module.exports = {
entry :['./test.js'],//设置打包入口文件
output :{
path:__dirname,//设置打包的输出文件夹
filename:'bundle.js'//打包后的文件
},
plugins :[
new Webpack.BannerPlugin("打包后文件的头部注释")//打包后文件的头部注释..
]
}
执行打包后就会在bundle.js文件的头部生成一条注释信息
比如我们要编译es6到es5,我们就需要用到babel这个工具,首先需要至少安装它的插件
npm i babel-core -D //babel核心库
npm i babel-loader -D //babel用于loader
npm i babel-preset-es2015 -D //babel转码规则
var webpack = require('webpack');
module.exports = {
entry :['./test.js'],
output :{
path:__dirname,
filename:'bundle.js'
},
module :{
loaders :[{
test:/\.js$/, //匹配js文件
loader:'babel-loader',//使用bable进行转码
query:{
presets:['es2015'] //转换成es5
}
}
]
},
plugins :[
new webpack.BannerPlugin("打包后文件的头部注释")//打包后文件的头部注释..
]
}
修改一下test.js文件,写入一句es6的语法
let a = "hello world!";
console.log(a);
运行webpack打包之后,打开bundle.js就会发现代码会转换成es5中的var
方法了。
这里的配置里面的'query'部分也可以提取出来,把它写入package.json中也可以。
{
"name": "webpack-step",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.22.1",
"babel-loader": "^6.2.10",
"babel-preset-es2015": "^6.22.0",
"webpack": "^1.14.0"
},
"babel": {
"presets": [
"es2015"
],
"plugins": []
}
}
这样和新建一个.babelrc
文件效果是一致的
{
"presets": [
"es2015"
],
"plugins": []
}
接下来就采用最后一种独立文件的方式吧,来得直观一些。
以上所有的运行我们都是使用webpack来运行的,接下来我们把它配置进npm的脚本中,以方便直接使用以及添加参数等
在package.json中的scripts节点中新增脚本:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
接下来运行npm run build
就可以执行打包文件了
比如我们在配置中添加-w
参数,就可以让webpack自动监听文件的修改并重新打包了
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack -w"
},
webpack-dev-server也升级到2.0以上了,安装2.0以上版本在package.json中不能使用脚本启动,所以还是先安装1.0以上的版本
npm i [email protected]
然后在package.json中添加启动脚本
"server":"webpack-dev-server"
运行npm run server
就会默认启动一个本地localhost:8080的服务
让浏览器实现热加载,只需要给webpack-dev-server添加--inline
参数就可以了
是时候让我们新建一个html文件并引入bundle.js文件在这个index.html了,这个时候,打开控制台,修改test.js文件,就能看到自动刷新的效果。
默认情况下,webpack-dev-server会采用webpack.config.js这个配置文件的,所以当文件修改之后,它会自动调用这个文件进行打包。
webpack-dev-server可以以指定文件夹来运行服务,比如我们新建一个build文件夹,然后把index.html放进去,并删除bundle.js文件,在启动脚本中新增参数--content-base build/
,变成如下:
"server": "webpack-dev-server --inline --content-base build/"
重新运行服务,会发现现在是从build目录启动服务了,这个时候并没有bundle.js文件,因为webpack-dev-server是把它打包在内存当中的。
TODO
我们修改之后,发现浏览器是全部刷新一次的,这个时候可以通过--hot
参数来实现热加载功能。
首先安装React包
npm i react --save
npm i react-dom --dave
安装babel插件
npm i babel-preset-react -D
npm i babel-preset-react-hmre -D
在.babelrc中添加react转码
{
"presets": [
"es2015",
"react"
],
"plugins": []
}
修改test.js文件,写一个最简单的无状态组件
import React from 'react';
import ReactDOM from 'react-dom';
function HelloComponent(props) {
return <div>Hello {props.name}</div>
}
ReactDOM.render(<HelloComponent name="12345" />, document.body)
运行一下,OK,我们的React也跑起来了。
webpack最强大的地方就是loaders,我们再来添加一个css-loader以支持在js中import css文件
npm i css-loader -D
npm i style-loader -D
安装好这两个插件之后,再修改webpack.config.js配置文件
module :{
loaders :[{
test:/\.js$/,
loader:'babel-loader'
},
{
test: /\.css$/,
loader: 'style-loader!css-loader'
}
]
},
这个时候我们新建一个app.css的文件,里面写入一句背景颜色的设置
body{
background-color: red;
}
在test.js中添加
import "./app.css";
运行一下,我们的css文件已效了。
继续修改一个index.html文件,添加一个容器和输入框,容器用来放我们的react组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="container"></div>
<input type="text">
</body>
<script src="bundle.js"></script>
</html>
稍微修改一下test.js里面的组件渲染位置
import React from 'react';
import ReactDOM from 'react-dom';
import "./app.css";
function HelloComponent(props) {
return <div>Hello {props.name}</div>
}
ReactDOM.render(<HelloComponent name="world" />, document.getElementById("container"))
运行之后,我们在输入框里面随便输入内容,然后修改一个app.css文件,发现热加载功能也生效了。
这是比较常用的方法。具体的操作是,选取仿制图章工具,按住Alt键,在无文字区域点击相似的色彩或图案采样, 然后在文字区域拖动鼠标复制以复盖文字。要注意的是,采样点即为复制的起始点。选择不同的笔刷直径会影响绘制的范围,而不同的笔刷硬度会影响绘制区域的边缘融合效果。
如果图片的背景色彩或图案比较一致,使用修补工具就比较方便。具体的操作是,选取修补工具,在公共栏中选择修补项为“源”,关闭“透明”选项。然后用修补工具框选文字,拖动到无文字区域中色彩或图案相似的位置,松开鼠标就完成复制。修补工具具有自动匹配颜色的功能,复制出的效果与周围的色彩较为融合,这是仿制图章工具所不具备的。
操作的方法与仿制图章工具相似。按住Alt键,在无文字区域点击相似的色彩或图案采样,然后在文字区域拖动鼠标复制以复盖文字。只是修复画笔工具与修补工具一样,也具有自动匹配颜色的功能,可根据需要进行选用。
图例中的操作的方法是,框选要处理的文字区域(防止选区以外的部分也被覆盖)执行菜单命令:滤镜→消失点,进入消失点滤镜编辑界面。然后:(1)选取左边工具栏中的创建面板工具,由地板砖缝交汇处开始,沿着缝隙,依次点四个点,连成一个有透视效果的矩形。然后拖动其边线向右方及下方扩展,令面板完全复盖文字。(2)选取左边工具栏中的图章工具, 按住Alt键点击选取源图像点,绿色十字变红后,在文字区域拖动便完成复制。
用矩形选框工具在无文字区域中作一个选区,选区不宜太宽,高度应高于文字。然后按住Ctrl+Alt键,连续按方向键(→或←),直至完全复盖文字则可。
很简单的一个拖拽效果,没有判断窗口溢出的现象,主要还是加深this的应用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
#drag{
width: 40px;
height: 40px;
position: absolute;
background-color: red;
cursor:pointer;;
}
</style>
</head>
<body>
<div id="drag">
</div>
</body>
<script>
var drag = document.getElementById("drag");
drag.onmousedown = function(ev){
var ev = ev || window.event;
var x = ev.clientX-this.offsetLeft;
var y = ev.clientY-this.offsetTop;
document.onmousemove = function(ev){
var ev = ev || window.event;
var nowx= ev.clientX-x;
var nowy= ev.clientY-y;
drag.style.left = nowx + 'px';
drag.style.top = nowy + 'px';
}
document.onmouseup = function(){
document.onmousemove = null;
document.onmouseup = null;
}
}
</script>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
#drag{
width: 40px;
height: 40px;
position: absolute;
background-color: red;
cursor:pointer;
}
</style>
</head>
<body>
<div id="drag">
</div>
</body>
<script>
function Drag(name){
this.drag = document.getElementById(name);
this.x=0;
this.y=0;
}
Drag.prototype.init = function(){
var This = this;
this.drag.onmousedown = function(ev){
var ev = ev || window.event;
This.fnDown(ev);
return false;
}
}
Drag.prototype.fnDown = function(ev){
var This = this;
this.x = ev.clientX-this.drag.offsetLeft;
this.y = ev.clientY-this.drag.offsetTop;
document.onmousemove = function(ev){
var ev = ev || window.event;
This.fnMove(ev);
};
document.onmouseup = this.fnUp;
}
Drag.prototype.fnMove = function(ev){
var nowx= ev.clientX-this.x;
var nowy= ev.clientY-this.y;
this.drag.style.left = nowx + 'px';
this.drag.style.top = nowy + 'px';
}
Drag.prototype.fnUp = function(){
document.onmousemove = null;
document.onmouseup = null;
}
var drags = new Drag('drag');
drags.init();
</script>
</html>
javascript中创建对象有几种方式,工厂模式,构造函数模式,原型模式,构造函数+原型混合模式,动态原型模式,寄生构造函数模式,稳妥构造函数模式.
针对前几种,记录一下自己的书写笔记,有时间再来整理一下文字版.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>对象</title>
<script>
//工厂方法 缺点:对象识别问题.
function CreatPerson(name,age){
var obj = new Object;
obj.name = name;
obj.age = age;
obj.fav = ["苹果","葡萄"];
obj.run = function (){
return this.name+this.age+"正在运行中...";
};
return obj;
}
/*
var person1 = CreatPerson("lxd",23);
person1.fav.push("橙子");
var person2 = CreatPerson("tian",14);
person1.name = "xiaodong";
console.log(person1.run());//xiaodong23正在运行中...
console.log(person1.fav);// ["苹果", "葡萄", "橙子"] 引用对象不共享
console.log(person2.run());//tian14正在运行中...
console.log(person2.fav);// ["苹果", "葡萄"]
console.log(person1 instanceof Object);//true
console.log(person1 instanceof CreatPerson);//false 不能获得对象标识
console.log(person1.run == person2.run);//false
*/
//构造函数模式 缺点:里面相同任务的function也会被实例化
function Person(name,age){
this.name = name;
this.age = age;
this.fav = ["苹果","葡萄"];
this.run = function(){ //这里的方法也会被实例化,等同于this.run = new Function("return this.name+this.age+'正在运行中...';")
return this.name+this.age+"正在运行中...";
}
}
var person1 = new Person("lxd",23);
person1.fav.push("橙子");
var person2 = new Person("tian",14);
person1.name = "xiaodong";
console.log(person1.run());//xiaodong23正在运行中...
console.log(person1.fav);// ["苹果", "葡萄", "橙子"] 引用对象不共享
console.log(person2.run());//tian14正在运行中...
console.log(person2.fav);// ["苹果", "葡萄"]
console.log(person1 instanceof Object);//true
console.log(person1 instanceof Person);//true 可以获得对象标识
console.log(person1.run == person2.run);//false 实例化对象,里面的方法也会实例化
Person("window",100);//当作普通函数使用 添加到window对象中
console.log(window.run());//window100正在运行中...
var o = new Object;
Person.call(o,"oooo",99);//在另外一个对象的作用域中调用
console.log(o.run());//oooo99正在运行中...
//针对构造函数的缺点,进行改造,把函数提取出来 缺点:暴躁在全局变量中的方法,却只能在对象中使用,而且如果方法很多,会创建很多这样的全局方法.
function NewPerson(name,age){
this.name = name;
this.age = age;
this.fav = ["苹果","葡萄"];
this.run = run;
}
function run(){
return this.name+this.age+"正在运行中...";
}
var newperson1 = new NewPerson("xiaodong",13);
var newperson2 = new NewPerson("tian",22);
console.log(newperson1.run == newperson2.run);//true 访问的是同一个方法
</script>
</head>
<body>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>prototype</title>
<script>
//原型模式
function Person() {};
Person.prototype.name = "xiaodong";
Person.prototype.age = 23;
Person.prototype.job = "do software";
Person.prototype.sayName = function (){
return this.name;
}
var person1 = new Person();
var person2 = new Person();
console.log(person1.sayName());//"xiaodong"
console.log(person2.sayName());//"xiaodong"
console.log(person1.sayName === person2.sayName);//访问同一个对象指针
console.log(Person.prototype.isPrototypeOf(person1));//true person1中包含有指向Person.prototype的指针
console.log(Object.getPrototypeOf(person1) == Person.prototype);//true page 149 返回prototype的值
console.log(Object.getPrototypeOf(person1).name);//xiaodong
person1.name = "tiantian"; //只能添加到实例中,不能改变原型的值
console.log(person1.name);//"tiantian" 来自实例而非原型
console.log(person2.name);//"xiaodong" 来自原型而非实例
delete person1.name;
console.log(person1.name);//"xiaodong" 来自原型
/*
console.log(person1.hasOwnProperty("name"));//false 检测是实例属性还是原型属性
person1.name = "tian";
console.log(person1.hasOwnProperty("name"));//ture 实例中包含name属性
*/
console.log("name" in person1);//true 可枚举原型中的属性 来自原型
person1.name = "okok";
console.log("name" in person1);//true //来自实例
//利用hasOwnProperty和in可以判断属性是来自实例还是原型例如
function hasPrototypeProperty(object,name){
return !object.hasOwnProperty(name) && (name in object);
}
delete person1.name;
console.log(hasPrototypeProperty(person1,"name"));//ture
person1.name = "test";
console.log(hasPrototypeProperty(person1,"name"));//false
for (var i in person1){
if (i == "age"){
console.log("1234");//"1234"
}
}
var keys = Object.keys(Person.prototype);
console.log(keys);//["name", "age", "job", "sayName"]
var keys = Object.keys(person1);
console.log(keys);// ["name"] 实例属性
Person.prototype.toString = function(){
return "ok";
}
var keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys);// ["constructor", "name", "age", "job", "sayName", "toString"] 包含不可枚举的 注意还有tostring;
var keys = Object.getOwnPropertyNames(person1);
console.log(keys);// ["name"] 证明只有构造函数才有constructor等方法
//person1.prototype.name = "dfsa"; //TypeError: person1.prototype is undefined
function NewPerson(){};
NewPerson.prototype = {//采用字面量的方式书写;
name:"xiaodong",
age: 23,
sayName: function(){
return this.name;
}
};
var newperson = new NewPerson();
console.log(newperson instanceof Object);//ture
console.log(newperson instanceof NewPerson);//ture
console.log(newperson.constructor == NewPerson);//false 重写了默认的prototype,所以其constructor属性变成了新对象的constructor属性(指向Object);
console.log(newperson.constructor == Object);//ture
function NewPerson2(){};
NewPerson2.prototype = {//采用字面量的方式书写;
constructor:NewPerson2,//显式指定
name:"xiaodong",
age: 23,
sayName: function(){
return this.name;
}
};
var newperson2 = new NewPerson2();
console.log(newperson2.constructor == NewPerson2);//ture;
NewPerson2.prototype.name = "tiantian";
console.log(newperson2.name);//原型的动态性 对其修改会在所有的对象实例中反映出来
NewPerson2.prototype = {//但如果重写的话,就会切断构造函数与最初原型的关系 page156 实例中的指针仅指向原型,而不是构造函数
name : "other"
};
console.log(newperson2.name);//tiantian
console.log(newperson2.constructor === NewPerson2)//true
//原型模式的缺点:所有的属性都是共享的.
function Friend(){};
Friend.prototype={
name:"jack",
fav:["苹果","葡萄"],
run:function(){
return this.fav;
}
};
var friend1 = new Friend();
var friend2 = new Friend();
friend1.fav.push("梨子");//引用类型
console.log(friend1.fav);// ["苹果", "葡萄", "梨子"] 引用类型
console.log(friend2.fav);// ["苹果", "葡萄", "梨子"] 引用类型
//组合使用构造函数模式和原型模式 目前最常用的方式
function Base(name,age){
this.name = name;
this.age = age;
};
Base.prototype.run = function(){
return this.name;
};
var base1 = new Base("xiaodong",23);
var base2 = new Base("tiantian",22);
console.log(base1.run());//"xiaodong"
console.log(base2.run());//"tiantian"
console.log(base1.run === base2.run);//true
</script>
</head>
<body>
</body>
</html>
实例应用:汽车点修网违章查询
应用地址:http://www.car91.cn
实现效果:对接聚合数据,实现违章查询功能,并对查询记录通过cookie保存,方便下次直接点击查询
define(["jquery", 'config', 'jquery.cookie'], function($, config) {
return {
wzInit: function() {
var history = $.cookie("queryHistory");
if (history) {
var historyJson = eval("(" + history + ")");
if (historyJson.length > 0) {//因为是采用前置插入的方式,所以在插入前先判断是否已经插入过,避免多次运行重复生成的情况
if ($(".quick-wz").length > 0) {
$(".quick-wz").remove();
}
var list = "<div class='quick-wz'><p class='quick-wz__tit'>快速查询</p><ul class='quick-wz__list clearfix'>";
for (var i = 0; i < historyJson.length; i++) {
list = list + "<li class='query' data-index='" + i + "'>" + decodeURIComponent(historyJson[i].hphm) + " " + decodeURIComponent(historyJson[i].cityName) + "</li>";
}
list = list + "</ul></div>"
$("#wzForm").before(list);
$(".query").on("click", function() {
var index = $(this).data("index");
$.ajax({
type: "POST",
dataType: "jsonp",
url: config.wzQuery,
data: {
"city": historyJson[index].city,
"hpzl": historyJson[index].hpzl,
"car_province": historyJson[index].car_province,
"hphm2": historyJson[index].hphm2,
"engineno": historyJson[index].engineno,
"classno": historyJson[index].classno,
"registno": historyJson[index].registno,
"hphm": historyJson[index].hphm
},
success: function(e) {
//var e=eval("("+e+")");
var resultcode = e.resultcode;
var info = e.reason;
if (resultcode == '200') {
var html = '<table width="98%" border="1" cellspacing="0" cellpadding="0" style="border:1px solid #f2f2f2">';
html += '<tr>' + '<td height="40"> 时间</td>' + '<td> 地点</td>' + '<td> 违章事项</td>' + '<td> 违章代码</td>' + '<td> 扣分</td>' + '<td> 罚款</td>' + '<td> 是否处理</td>' + '</tr>';
var list = e.result.lists;
if (list.length > 0) {
for (var i in list) {
html += '<tr>' + '<td height="40" width="120"> ' + list[i].date + '</td>' + '<td width="150"> ' + list[i].area + '</td>' + '<td width="280"> ' + list[i].act + '</td>' + '<td width="60"> ' + list[i].code + '</td>' + '<td width="30"> ' + list[i].fen + '</td>' + '<td width="30"> ' + list[i].money + '</td>' + '<td width="30"> ' + list[i].handled + '</td>' + '</tr>';
}
} else {
html += '<tr><td colspan=7 height="40">查询不到该车辆的违章记录</td></tr>';
}
html += '</table>';
$(".wz-result").html(html);
} else if (resultcode == '210') {
alert(resultcode + ":" + info);
} else {
alert(resultcode + ":" + info);
}
$(".wz-btn").val('违章查询').removeAttr("disabled");
}
})
})
}
}
var queryHistory = function() {
var city = $("select[name='city']").val();
var hpzl = $("select[name='hpzl']").val();
var car_province = $("select[name='car_province']").val();
var hphm2 = $("input[name='hphm2']").val();
var engineno = $("input[name='engineno']").val();
var classno = $("input[name='classno']").val();
var registno = $("input[name='registno']").val();
var hphm = $("#hphm").val();
var cityName = $(".selectCitys option:selected").text();
cityName = encodeURIComponent(cityName);
var len = 0;
var canAdd = true;
if (history) {
len = historyJson.length;
$(historyJson).each(function() {
if (this.city == city && this.hphm == hphm) {//判断相同车牌号及相同城市的查询
canAdd = false;
return false;
}
})
}
if (canAdd == true) {
var newCookie = "[";
var start = 0;
if (len > 2) {//最多记录三条历史记录
start = 1;
}
for (var i = start; i < len; i++) {
newCookie = newCookie + "{\"city\":\"" + historyJson[i].city + "\",\"hpzl\":\"" + historyJson[i].hpzl + "\",\"car_province\":\"" + historyJson[i].car_province + "\",\"hphm2\":\"" + historyJson[i].hphm2 + "\",\"engineno\":\"" + historyJson[i].engineno + "\",\"classno\":\"" + historyJson[i].classno + "\",\"registno\":\"" + historyJson[i].registno + "\",\"hphm\":\"" + historyJson[i].hphm + "\",\"cityName\":\"" + historyJson[i].cityName + "\"},";
}
newCookie = newCookie + "{\"city\":\"" + city + "\",\"hpzl\":\"" + hpzl + "\",\"car_province\":\"" + car_province + "\",\"hphm2\":\"" + hphm2 + "\",\"engineno\":\"" + engineno + "\",\"classno\":\"" + classno + "\",\"registno\":\"" + registno + "\",\"hphm\":\"" + hphm + "\",\"cityName\":\"" + cityName + "\"}]";
$.cookie("queryHistory", newCookie, {
expires: 1,
path: "/"
});
}
}
$.ajax({
type: "POST",
dataType: "jsonp",
url: config.wzCity,
data: "",
success: function(e) {
$(".selectProvince").empty();
var html = '<option value="">请选择城市</option>';
var ev = e['result'];
for (i in ev) {
html += "<option value='" + ev[i].province_code + "'>" + ev[i].province + "</option>";
//alert(e[i].city_name);
//$(".selectCitys").append("<option value='"+e[i].city_code+"'>"+e[i].city_name+"</option>");
}
$(".selectProvince").append(html);
}
})
var evc;
$("#selectProvince").change(function() {
var province = $(".selectProvince").val();
$(".selectCitys").empty().append("<option>loading...</option>");
$.ajax({
type: "POST",
dataType: "jsonp",
url: config.wzCity,
data: "province=" + encodeURIComponent(province),
success: function(e) {
$(".selectCitys").empty();
var html = '<option value="">请选择城市</option>';
ev = e['result'];
$.each(ev, function(k, v) {
evc=v;
$.each(v['citys'], function(kk, vv) {
html += "<option value='" + vv.city_code + "' engine='"+vv.engine+"' engineno='"+vv.engineno+"' abbr='" + vv.abbr + "' eclass='" + vv.class + "' eclassno='" + vv.classno + "'>" + vv.city_name + "</option>";
})
$(".selectCitys").append(html);
})
}
})
})
$(".selectCitys").change(function() {
var province = $(".selectProvince").val();
var index=$(this).get(0).selectedIndex-1;
console.log(evc['citys'][index]);
var qinfo=evc['citys'][index];
console.log(qinfo.abbr);
var city = $(this).val();
var abbr = $(".selectCitys").find("option:selected").attr("abbr");
var engine = $(".selectCitys").find("option:selected").attr("engine");
var engineno = $(".selectCitys").find("option:selected").attr("engineno");
var eclass = $(".selectCitys").find("option:selected").attr("eclass");
var eclassno = $(".selectCitys").find("option:selected").attr("eclassno");
if (typeof(abbr) != "undefined") {
$("#car_province").val(abbr);
}
if (engine == '1') {
if (engineno == '0') {
var engineinfo = '全部发动机号';
} else {
var engineinfo = '发动机号后' + engineno + '位';
}
$("input[name=engineno]").attr("placeholder", engineinfo);
$("#engineno").css({
display: ""
});
} else {
$("#engineno").css({
display: "none"
});
}
if (eclass == '1') {
if (eclassno == '0') {
var classinfo = '全部车架号';
} else {
var classinfo = '车架号后' + eclassno + '位';
}
$("input[name=classno]").attr("placeholder", classinfo);
$("#classno").css({
display: ""
});
} else {
$("#classno").css({
display: "none"
});
}
})
$(".wz-btn").click(function() {
$(".wz-btn").val('查询中...').attr("disabled", "disabled");
$(".wz-result").html('正在查询中....');
var hphm = $("#car_province").val() + $(".onlyhm").val();
hphm = encodeURIComponent(hphm);
$("#hphm").val(hphm);
queryHistory();
$.ajax({
type: "POST",
//用POST方式传输
dataType: "jsonp",
//数据格式:JSON
url: config.wzQuery,
//目标地址
data: {
"city": $("select[name='city']").val(),
"hpzl": $("select[name='hpzl']").val(),
"car_province": $("select[name='car_province']").val(),
"hphm2": $("input[name='hphm2']").val(),
"engineno": $("input[name='engineno']").val(),
"classno": $("input[name='classno']").val(),
"registno": $("input[name='registno']").val(),
"hphm": hphm
},
success: function(e) {
//var e=eval("("+e+")");
console.log("ok");
var resultcode = e.resultcode;
var info = e.reason;
if (resultcode == '200') {
var html = '<table width="98%" border="1" cellspacing="0" cellpadding="0" style="border:1px solid #f2f2f2">';
html += '<tr>' + '<td height="40"> 时间</td>' + '<td> 地点</td>' + '<td> 违章事项</td>' + '<td> 违章代码</td>' + '<td> 扣分</td>' + '<td> 罚款</td>' + '<td> 是否处理</td>' + '</tr>';
var list = e.result.lists;
if (list.length > 0) {
for (var i in list) {
html += '<tr>' + '<td height="40" width="120"> ' + list[i].date + '</td>' + '<td width="150"> ' + list[i].area + '</td>' + '<td width="280"> ' + list[i].act + '</td>' + '<td width="60"> ' + list[i].code + '</td>' + '<td width="30"> ' + list[i].fen + '</td>' + '<td width="30"> ' + list[i].money + '</td>' + '<td width="30"> ' + list[i].handled + '</td>' + '</tr>';
}
} else {
html += '<tr><td colspan=6 height="40">查询不到该车辆的违章记录</td></tr>';
}
html += '</table>';
$(".wz-result").html(html);
} else if (resultcode == '210') {
alert(resultcode + ":" + info);
} else {
alert(resultcode + ":" + info);
}
$(".wz-btn").val('违章查询').removeAttr("disabled");
}
})
})
}
}
})
1.该模块加载了jquery.cookie插件,主要是为了方便把json数据保存在cookie中。模块加载进来后,首先判断是存在历史记录,存在的话就进行相应的显示,在最开始书写的时间,生成历史记录图标,我是把所有需要的参数都通过data标签生成在DOM元素上的,例如
list = list + "<li class='query' data-city='"+historyJson[i].city+"'>" + decodeURIComponent(historyJson[i].hphm) + " " + decodeURIComponent(historyJson[i].cityName) + "</li>";
然后在接下来的ajax发送数据的时候,则通过$(this).data('city')
来获取数据,后来发现这样标签内容非常多,而我们在点击时候,其实只需要知道是点击的第一个标签,然后从原始的json数据中去获取对应的值就可以了,于是就产生了上面的data-index的写法,在获取数据时,采用historyJson[index].city就可以了。
1.在$("#selectProvince").change(function()
函数中,通过ajax获取到应用城市的json数据,数据为多层,示例如下:
jQuery19103159100429620594_1428636651525({
"resultcode": "200",
"reason": "成功的返回",
"result": {
"QH": {
"province": "青海",
"province_code": "QH",
"citys": [
{
"city_name": "西宁",
"city_code": "QH_XN",
"abbr": "青",
"engine": "0",
"engineno": "0",
"classa": "1",
"class": "1",
"classno": "0",
"regist": "0",
"registno": "0"
},
{
"city_name": "海东",
"city_code": "QH_HAIDONG",
"abbr": "青",
"engine": "1",
"engineno": "6",
"classa": "1",
"class": "1",
"classno": "0",
"regist": "0",
"registno": "0"
},
{
"city_name": "海西",
"city_code": "QH_HAIXI",
"abbr": "青",
"engine": "1",
"engineno": "6",
"classa": "1",
"class": "1",
"classno": "0",
"regist": "0",
"registno": "0"
},
{
"city_name": "海南",
"city_code": "QH_HAINAN",
"abbr": "青",
"engine": "1",
"engineno": "6",
"classa": "1",
"class": "1",
"classno": "0",
"regist": "0",
"registno": "0"
},
{
"city_name": "玉树",
"city_code": "QH_YUSHU",
"abbr": "青",
"engine": "1",
"engineno": "6",
"classa": "1",
"class": "1",
"classno": "0",
"regist": "0",
"registno": "0"
},
{
"city_name": "黄南",
"city_code": "QH_HUANGNAN",
"abbr": "青",
"engine": "1",
"engineno": "6",
"classa": "1",
"class": "1",
"classno": "0",
"regist": "0",
"registno": "0"
},
{
"city_name": "海北",
"city_code": "QH_HAIBEI",
"abbr": "青",
"engine": "1",
"engineno": "6",
"classa": "1",
"class": "1",
"classno": "0",
"regist": "0",
"registno": "0"
},
{
"city_name": "果洛",
"city_code": "QH_GUOLUO",
"abbr": "青",
"engine": "1",
"engineno": "6",
"classa": "1",
"class": "1",
"classno": "0",
"regist": "0",
"registno": "0"
}
]
}
},
"error_code": 0
})
我需要获取到最底层每个城市的信息,原来的写法是通过$.each
访求循环来获取,后来研究json格式时,明白了大括号表示对象,中括号表示数组,对象可以通过点的方式如obj.result或者key的方法如obj[result]的方法来获取,而数据则是通过下标来获取,如obj[0],上面的json中,每次选择一个省份后,只是返回一个该省份的数据,即result下面的对象只有一个,而且对象名可以通过select的值来获取,因此,不需要通过循环,直接通过下标就可以获取到值了。
ev = e['result'];
var evc=ev[province];
2.继续刚刚$.each里面的循环部分,我还是把返回的json数据写入到了dom标签中,这一步可以继续优化,因为我们可以通过$(this).get(0).selectedIndex
获取到当前选中项的索引值,因为我们可以通过该索引值再去读取json中对应的值就可以了。于是有了上面代码中的初步测试部分
var index=$(this).get(0).selectedIndex-1;
console.log(evc['citys'][index]);
var qinfo=evc['citys'][index];
console.log(qinfo.abbr);
3.再来优化,上面第2条中的返回json数据我是通过定义一个全局变量evc
来方便下面的$(".selectCitys").change(function()
方法访问,我们来继续优化减少全局变量,把$(".selectCitys").change(function()
封装成一个函数,把json返回值通过传值的方式来传递,于是便有了如下写法:
function selectCitys(evc){
$(".selectCitys").change(function() {
var province = $(".selectProvince").val();
var city = $(this).val();
var index=$(this).get(0).selectedIndex-1;
var cityInfo=evc['citys'][index];
var abbr = cityInfo.abbr;
var engine = cityInfo.engine;
var engineno = cityInfo.engineno;
var eclass = cityInfo.class;
var eclassno = cityInfo.classno;
if (typeof(abbr) != "undefined") {
$("#car_province").val(abbr);
}
if (engine == '1') {
if (engineno == '0') {
var engineinfo = '全部发动机号';
} else {
var engineinfo = '发动机号后' + engineno + '位';
}
$("input[name=engineno]").attr("placeholder", engineinfo);
$("#engineno").css({
display: ""
});
} else {
$("#engineno").css({
display: "none"
});
}
if (eclass == '1') {
if (eclassno == '0') {
var classinfo = '全部车架号';
} else {
var classinfo = '车架号后' + eclassno + '位';
}
$("input[name=classno]").attr("placeholder", classinfo);
$("#classno").css({
display: ""
});
} else {
$("#classno").css({
display: "none"
});
}
})
}
4.优化继续,我们发现在上面的代码中,记录cookie及查询的时候,两次手动读取了一样的数据,于是我们可以把这部分先独立出来,接下来传值即可。如果优化如下:
$(".wz-btn").click(function() {
$(".wz-btn").val('查询中...').attr("disabled", "disabled");
$(".wz-result").html('正在查询中....');
var hphm = $("#car_province").val() + $(".onlyhm").val();
hphm = encodeURIComponent(hphm);
$("#hphm").val(hphm);
var carInfo={
"city": $("select[name='city']").val(),
"hpzl": $("select[name='hpzl']").val(),
"car_province": $("select[name='car_province']").val(),
"hphm2": $("input[name='hphm2']").val(),
"engineno": $("input[name='engineno']").val(),
"classno": $("input[name='classno']").val(),
"registno": $("input[name='registno']").val(),
"hphm": hphm
};
queryHistory(carInfo);
$.ajax({
type: "POST",
dataType: "jsonp",
url: config.wzQuery,
data: carInfo,
success: function(e) {
var resultcode = e.resultcode;
var info = e.reason;
if (resultcode == '200') {
var html = '<table width="98%" border="1" cellspacing="0" cellpadding="0" style="border:1px solid #f2f2f2">';
html += '<tr>' + '<td height="40"> 时间</td>' + '<td> 地点</td>' + '<td> 违章事项</td>' + '<td> 违章代码</td>' + '<td> 扣分</td>' + '<td> 罚款</td>' + '<td> 是否处理</td>' + '</tr>';
var list = e.result.lists;
if (list.length > 0) {
for (var i in list) {
html += '<tr>' + '<td height="40" width="120"> ' + list[i].date + '</td>' + '<td width="150"> ' + list[i].area + '</td>' + '<td width="280"> ' + list[i].act + '</td>' + '<td width="60"> ' + list[i].code + '</td>' + '<td width="30"> ' + list[i].fen + '</td>' + '<td width="30"> ' + list[i].money + '</td>' + '<td width="30"> ' + list[i].handled + '</td>' + '</tr>';
}
} else {
html += '<tr><td colspan=6 height="40">查询不到该车辆的违章记录</td></tr>';
}
html += '</table>';
$(".wz-result").html(html);
} else if (resultcode == '210') {
alert(resultcode + ":" + info);
} else {
alert(resultcode + ":" + info);
}
$(".wz-btn").val('违章查询').removeAttr("disabled");
}
})
})
因此,最终优化完毕的全部代码如下:
define(["jquery", 'config', 'jquery.cookie'], function($, config) {
return {
wzInit: function() {
var history = $.cookie("queryHistory");
if (history) {
var historyJson = eval("(" + history + ")");
if (historyJson.length > 0) {
if ($(".quick-wz").length > 0) {
$(".quick-wz").remove();
}
var list = "<div class='quick-wz'><p class='quick-wz__tit'>快速查询</p><ul class='quick-wz__list clearfix'>";
for (var i = 0; i < historyJson.length; i++) {
list = list + "<li class='query' data-index='" + i + "'>" + decodeURIComponent(historyJson[i].hphm) + " " + decodeURIComponent(historyJson[i].cityName) + "</li>";
}
list = list + "</ul></div>"
$("#wzForm").before(list);
$(".query").on("click", function() {
var index = $(this).data("index");
$.ajax({
type: "POST",
dataType: "jsonp",
url: config.wzQuery,
data: {
"city": historyJson[index].city,
"hpzl": historyJson[index].hpzl,
"car_province": historyJson[index].car_province,
"hphm2": historyJson[index].hphm2,
"engineno": historyJson[index].engineno,
"classno": historyJson[index].classno,
"registno": historyJson[index].registno,
"hphm": historyJson[index].hphm
},
success: function(e) {
var resultcode = e.resultcode;
var info = e.reason;
if (resultcode == '200') {
var html = '<table width="98%" border="1" cellspacing="0" cellpadding="0" style="border:1px solid #f2f2f2">';
html += '<tr>' + '<td height="40"> 时间</td>' + '<td> 地点</td>' + '<td> 违章事项</td>' + '<td> 违章代码</td>' + '<td> 扣分</td>' + '<td> 罚款</td>' + '<td> 是否处理</td>' + '</tr>';
var list = e.result.lists;
if (list.length > 0) {
for (var i in list) {
html += '<tr>' + '<td height="40" width="120"> ' + list[i].date + '</td>' + '<td width="150"> ' + list[i].area + '</td>' + '<td width="280"> ' + list[i].act + '</td>' + '<td width="60"> ' + list[i].code + '</td>' + '<td width="30"> ' + list[i].fen + '</td>' + '<td width="30"> ' + list[i].money + '</td>' + '<td width="30"> ' + list[i].handled + '</td>' + '</tr>';
}
} else {
html += '<tr><td colspan=7 height="40">查询不到该车辆的违章记录</td></tr>';
}
html += '</table>';
$(".wz-result").html(html);
} else if (resultcode == '210') {
alert(resultcode + ":" + info);
} else {
alert(resultcode + ":" + info);
}
$(".wz-btn").val('违章查询').removeAttr("disabled");
}
})
})
}
}
var queryHistory = function(carInfo) {
var city = carInfo.city;
var hpzl = carInfo.hpzl;
var car_province = carInfo.car_province;
var hphm2 = carInfo.hphm2;
var engineno = carInfo.engineno;
var classno = carInfo.classno;
var registno = carInfo.registno;
var hphm = $("#hphm").val();
var cityName = $(".selectCitys option:selected").text();
cityName = encodeURIComponent(cityName);
var len = 0;
var canAdd = true;
if (history) {
len = historyJson.length;
$(historyJson).each(function() {
if (this.city == city && this.hphm == hphm) {
canAdd = false;
return false;
}
})
}
if (canAdd == true) {
var newCookie = "[";
var start = 0;
if (len > 2) {
start = 1;
}
for (var i = start; i < len; i++) {
newCookie = newCookie + "{\"city\":\"" + historyJson[i].city + "\",\"hpzl\":\"" + historyJson[i].hpzl + "\",\"car_province\":\"" + historyJson[i].car_province + "\",\"hphm2\":\"" + historyJson[i].hphm2 + "\",\"engineno\":\"" + historyJson[i].engineno + "\",\"classno\":\"" + historyJson[i].classno + "\",\"registno\":\"" + historyJson[i].registno + "\",\"hphm\":\"" + historyJson[i].hphm + "\",\"cityName\":\"" + historyJson[i].cityName + "\"},";
}
newCookie = newCookie + "{\"city\":\"" + city + "\",\"hpzl\":\"" + hpzl + "\",\"car_province\":\"" + car_province + "\",\"hphm2\":\"" + hphm2 + "\",\"engineno\":\"" + engineno + "\",\"classno\":\"" + classno + "\",\"registno\":\"" + registno + "\",\"hphm\":\"" + hphm + "\",\"cityName\":\"" + cityName + "\"}]";
$.cookie("queryHistory", newCookie, {
expires: 1,
path: "/"
});
}
}
$.ajax({
type: "POST",
dataType: "jsonp",
url: config.wzCity,
data: "",
success: function(e) {
$(".selectProvince").empty();
var html = '<option value="">请选择城市</option>';
var ev = e['result'];
for (i in ev) {
html += "<option value='" + ev[i].province_code + "'>" + ev[i].province + "</option>";
}
$(".selectProvince").append(html);
}
})
$("#selectProvince").change(function() {
var province = $(".selectProvince").val();
$(".selectCitys").empty().append("<option>loading...</option>");
$.ajax({
type: "POST",
dataType: "jsonp",
url: config.wzCity,
data: {"province" : encodeURIComponent(province)},
success: function(e) {
$(".selectCitys").empty();
var html = '<option value="">请选择城市</option>';
ev = e['result'];
var evc=ev[province];
$.each(evc['citys'], function(kk, vv) {
html += "<option value='" + vv.city_code + "'>" + vv.city_name + "</option>";
})
$(".selectCitys").append(html);
selectCitys(evc);
}
})
})
function selectCitys(evc){
$(".selectCitys").change(function() {
var province = $(".selectProvince").val();
var city = $(this).val();
var index=$(this).get(0).selectedIndex-1;
var cityInfo=evc['citys'][index];
var abbr = cityInfo.abbr;
var engine = cityInfo.engine;
var engineno = cityInfo.engineno;
var eclass = cityInfo.class;
var eclassno = cityInfo.classno;
if (typeof(abbr) != "undefined") {
$("#car_province").val(abbr);
}
if (engine == '1') {
if (engineno == '0') {
var engineinfo = '全部发动机号';
} else {
var engineinfo = '发动机号后' + engineno + '位';
}
$("input[name=engineno]").attr("placeholder", engineinfo);
$("#engineno").css({
display: ""
});
} else {
$("#engineno").css({
display: "none"
});
}
if (eclass == '1') {
if (eclassno == '0') {
var classinfo = '全部车架号';
} else {
var classinfo = '车架号后' + eclassno + '位';
}
$("input[name=classno]").attr("placeholder", classinfo);
$("#classno").css({
display: ""
});
} else {
$("#classno").css({
display: "none"
});
}
})
}
$(".wz-btn").click(function() {
$(".wz-btn").val('查询中...').attr("disabled", "disabled");
$(".wz-result").html('正在查询中....');
var hphm = $("#car_province").val() + $(".onlyhm").val();
hphm = encodeURIComponent(hphm);
$("#hphm").val(hphm);
var carInfo={
"city": $("select[name='city']").val(),
"hpzl": $("select[name='hpzl']").val(),
"car_province": $("select[name='car_province']").val(),
"hphm2": $("input[name='hphm2']").val(),
"engineno": $("input[name='engineno']").val(),
"classno": $("input[name='classno']").val(),
"registno": $("input[name='registno']").val(),
"hphm": hphm
};
queryHistory(carInfo);
$.ajax({
type: "POST",
dataType: "jsonp",
url: config.wzQuery,
data: carInfo,
success: function(e) {
var resultcode = e.resultcode;
var info = e.reason;
if (resultcode == '200') {
var html = '<table width="98%" border="1" cellspacing="0" cellpadding="0" style="border:1px solid #f2f2f2">';
html += '<tr>' + '<td height="40"> 时间</td>' + '<td> 地点</td>' + '<td> 违章事项</td>' + '<td> 违章代码</td>' + '<td> 扣分</td>' + '<td> 罚款</td>' + '<td> 是否处理</td>' + '</tr>';
var list = e.result.lists;
if (list.length > 0) {
for (var i in list) {
html += '<tr>' + '<td height="40" width="120"> ' + list[i].date + '</td>' + '<td width="150"> ' + list[i].area + '</td>' + '<td width="280"> ' + list[i].act + '</td>' + '<td width="60"> ' + list[i].code + '</td>' + '<td width="30"> ' + list[i].fen + '</td>' + '<td width="30"> ' + list[i].money + '</td>' + '<td width="30"> ' + list[i].handled + '</td>' + '</tr>';
}
} else {
html += '<tr><td colspan=6 height="40">查询不到该车辆的违章记录</td></tr>';
}
html += '</table>';
$(".wz-result").html(html);
} else if (resultcode == '210') {
alert(resultcode + ":" + info);
} else {
alert(resultcode + ":" + info);
}
$(".wz-btn").val('违章查询').removeAttr("disabled");
}
})
})
}
}
})
园子里已经有不少介绍HTTP的的好文章。对HTTP的一些细节介绍的比较好,所以本篇文章不会对HTTP的细节进行深究,而是从够高和更结构化的角度将HTTP协议的元素进行分类讲解。
在一个网络中。传输数据需要面临三个问题:
1.客户端如何知道所求内容的位置?
2.当客户端知道所求内容的位置后,如何获取所求内容?
3.所求内容以何种形式组织以便被客户端所识别?
对于WEB来说,回答上面三种问题分别采用三种不同的技术,分别为:统一资源定位符(URIs),超文本传输协议(HTTP)和超文本标记语言(HTML)。对于大多数WEB开发人员来说URI和HTML都是非常的熟悉。而HTTP协议在很多WEB技术中都被封装的过多使得HTTP反而最不被熟悉。
HTTP作为一种传输协议,也是像HTML一样随着时间不断演进的,目前流行的HTTP1.1是HTTP协议的第三个版本。
HTTP 0.9作为HTTP协议的第一个版本。是非常弱的。请求(Request)只有一行,比如:
GET www.cnblogs.com
从如此简单的请求体,没有POST方法,没有HTTP 头可以看出,那个时代的HTTP客户端只能接收一种类型:纯文本。并且,如果得不到所求的信息,也没有404 500等错误出现。
虽然HTTP 0.9看起来如此弱,但已经能满足那个时代的需求了。
随着1996年后,WEB程序的需求,HTTP 0.9已经不能满足需求。HTTP1.0最大的改变是引入了POST方法,使得客户端通过HTML表单向服务器发送数据成为可能,这也是WEB应用程序的一个基础。另一个巨大的改变是引入了HTTP头,使得HTTP不仅能返回错误代码,并且HTTP协议所传输的内容不仅限于纯文本,还可以是图片,动画等一系列格式。
除此之外,还允许保持连接,既一次TCP连接后,可以多次通信,虽然HTTP1.0 默认是传输一次数据后就关闭。
2000年5月,HTTP1.1确立。HTTP1.1并不像HTTP1.0对于HTTP0.9那样的革命性。但是也有很多增强。
首先,增加了Host头,比如访问我的博客:
GET /Careyson HTTP/1.1
Host: www.cnblogs.com
Get后面仅仅需要相对路径即可。这看起来虽然仅仅类似语法糖的感觉,但实际上,这个提升使得在Web上的一台主机可以存在多个域。否则多个域名指向同一个IP会产生混淆。
此外,还引入了Range头,使得客户端通过HTTP下载时只下载内容的一部分,这使得多线程下载也成为可能。
还有值得一提的是HTTP1.1 默认连接是一直保持的,这个概念我会在下文中具体阐述。
在Internet中所有的传输都是通过TCP/IP进行的。HTTP协议作为TCP/IP模型中应用层的协议也不例外。HTTP在网络中的层次如图1所示。
图1.HTTP在TCP/IP中的层次
可以看出,HTTP是基于传输层的TCP协议,而TCP是一个端到端的面向连接的协议。所谓的端到端可以理解为进程到进程之间的通信。所以HTTP在开始传输之前,首先需要建立TCP连接,而TCP连接的过程需要所谓的“三次握手”。概念如图2所示。
图2.TCP连接的三次握手
在TCP三次握手之后,建立了TCP连接,此时HTTP就可以进行传输了。一个重要的概念是面向连接,既HTTP在传输完成之间并不断开TCP连接。在HTTP1.1中(通过Connection头设置)这是默认行为。所谓的HTTP传输完成我们通过一个具体的例子来看。
比如访问我的博客,使用Fiddler来截取对应的请求和响应。如图3所示。
图3.用fiddler抓取请求和相应
可以看出,虽然仅仅访问了我的博客,但锁获取的不仅仅是一个HTML而已,而是浏览器对HTML解析的过程中,如果发现需要获取的内容,会再次发起HTTP请求去服务器获取,比如图2中的那个common2.css。这上面19个HTTP请求,只依靠一个TCP连接就够了,这就是所谓的持久连接。也是所谓的一次HTTP请求完成。
所谓的HTTP请求,也就是Web客户端向Web服务器发送信息,这个信息由如下三部分组成:
1.请求行
2.HTTP头
3.内容
一个典型的请求行比如:
GET www.cnblogs.com HTTP/1.1
请求行写法是固定的,由三部分组成,第一部分是请求方法,第二部分是请求网址,第三部分是HTTP版本。
第二部分HTTP头在HTTP请求可以是3种HTTP头:1.请求头(request header) 2.普通头(general header) 3.实体头(entity header)
通常来说,由于Get请求往往不包含内容实体,因此也不会有实体头。
第三部分内容只在POST请求中存在,因为GET请求并不包含任何实体。
我们截取一个具体的Post请求来看这三部分,我在一个普通的aspx页面放一个BUTTON,当提交后会产生一个Post请求,如图4所示。
图4.HTTP请求由三部分组成
虽然我们所常见的只有Get和Post方法,但实际上HTTP请求方法还有很多,比如: PUT方法,DELETE方法,HEAD方法,CONNECT方法,TRACE方法。这里我就不细说了,自行Bing。
这里重点说一下Get和Post方法,网上关于Get和Post的区别满天飞。但很多没有说到点子上。Get和Post最大的区别就是Post有上面所说的第三部分:内容。而Get不存在这个内容。因此就像Get和Post其名称所示那样,Get用于从服务器上取内容,虽然可以通过QueryString向服务器发信息,但这违背了Get的本意,QueryString中的信息在HTTP看来仅仅是获取所取得内容的一个参数而已。而Post是由客户端向服务器端发送内容的方式。因此具有请求的第三部分:内容。
当Web服务器收到HTTP请求后,会根据请求的信息做某些处理(这些处理可能仅仅是静态的返回页,或是包含Asp.net,PHP,Jsp等语言进行处理后返回),相应的返回一个HTTP响应。HTTP响应在结构上很类似于HTTP请求,也是由三部分组成,分别为:
1.状态行
2.HTTP头
3.返回内容
首先来看状态行,一个典型的HTTP状态如下:
HTTP/1.1 200 OK
第一部分是HTTP版本,第二部分是响应状态码,第三部分是状态码的描述,因此也可以把第二和第三部分看成一个部分。
对于HTTP版本没有什么好说的,而状态码值得说一下,网上对于每个具体的HTTP状态码所代表的含义都有解释,这里我说一下分类。
信息类 (100-199)
响应成功 (200-299)
重定向类 (300-399)
客户端错误类 (400-499)
服务端错误类 (500-599)
HTTP响应中包含的头包括1.响应头(response header) 2.普通头(general header) 3.实体头(entity header)。
第三部分HTTP响应内容就是HTTP请求所请求的信息。这个信息可以是一个HTML,也可以是一个图片。比如我访问百度,HTTP Response如图5所示。
图5.一个典型的HTTP响应
图5中的响应是一个HTML,当然还可以是其它类型,比如图片,如图6所示。
图6.HTTP响应内容是图片
这里会有一个疑问,既然HTTP响应的内容不仅仅是HTML,还可以是其它类型,那么浏览器如何正确对接收到的信息进行处理?
这是通过媒体类型确定的(Media Type),具体来说对应Content-Type这个HTTP头,比如图5中是text/html,图6是image/jpeg。
媒体类型的格式为:大类/小类 比如图5中的html是小类,而text是大类。
IANA(The Internet Assigned Numbers Authority,互联网数字分配机构)定义了8个大类的媒体类型,分别是:
application— (比如: application/vnd.ms-excel.)
audio (比如: audio/mpeg.)
image (比如: image/png.)
message (比如,:message/http.)
model(比如:model/vrml.)
multipart (比如:multipart/form-data.)
text(比如:text/html.)
video(比如:video/quicktime.)
HTTP头
HTTP头仅仅是一个标签而已,比如我在Aspx中加入代码:
Response.AddHeader("测试头","测试值");
对应的我们可以在fiddler抓到的信息如图7所示。
图7.HTTP头
不难看出,HTTP头并不是严格要求的,仅仅是一个标签,如果浏览器可以解析就会按照某些标准(比如浏览器自身标准,W3C的标准)去解释这个头,否则不识别的头就会被浏览器无视。对服务器也是同理。假如你编写一个浏览器,你可以将上面的头解释成任何你想要的效果微笑
下面我们说的HTTP头都是W3C标准的头,我不会对每个头的作用进行详细说明,关于HTTP头作用的文章在网上已经很多了,请自行Bing。HTTP头按照其不同的作用,可以分为四大类。
通用头即可以包含在HTTP请求中,也可以包含在HTTP响应中。通用头的作用是描述HTTP协议本身。比如描述HTTP是否持久连接的Connection头,HTTP发送日期的Date头,描述HTTP所在TCP连接时间的Keep-Alive头,用于缓存控制的Cache-Control头等。
实体头是那些描述HTTP信息的头。既可以出现在HTTP POST方法的请求中,也可以出现在HTTP响应中。比如图5和图6中的Content-Type和Content-length都是描述实体的类型和大小的头都属于实体头。其它还有用于描述实体的Content-Language,Content-MD5,Content-Encoding以及控制实体缓存的Expires和Last-Modifies头等。
请求头是那些由客户端发往服务端以便帮助服务端更好的满足客户端请求的头。请求头只能出现在HTTP请求中。比如告诉服务器只接收某种响应内容的Accept头,发送Cookies的Cookie头,显示请求主机域的HOST头,用于缓存的If-Match,If-Match-Since,If-None-Match头,用于只取HTTP响应信息中部分信息的Range头,用于附属HTML相关请求引用的Referer头等。
HTTP响应头是那些描述HTTP响应本身的头,这里面并不包含描述HTTP响应中第三部分也就是HTTP信息的头(这部分由实体头负责)。比如说定时刷新的Refresh头,当遇到503错误时自动重试的Retry-After头,显示服务器信息的Server头,设置COOKIE的Set-Cookie头,告诉客户端可以部分请求的Accept-Ranges头等。
还有一点值得注意的是,HTTP协议是无状态的,这意味着对于接收HTTP请求的服务器来说,并不知道每一次请求来自同一个客户端还是不同客户端,每一次请求对于服务器来说都是一样的。因此需要一些额外的手段来使得服务器在接收某个请求时知道这个请求来自于某个客户端。如图8所示。
图8.服务器并不知道请求1和请求2来自同一个客户端
为了解决这个问题,HTTP协议通过Cookies来保持状态,对于图8中的请求,如果使用Cookies进行状态控制,则变成了如图9所示。
图9.通过Cookies,服务器就可以清楚的知道请求2和请求1来自同一个客户端
除了Cookies之外,还可以使用表单变量来保持状态,比如Asp.net就通过一个叫ViewState的Input=“hidden”的框来保持状态,比如:
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMjA0OTM4MTAwNGRkXUfhlDv1Cs7/qhBlyZROCzlvf5U=" />
这个原理和Cookies大同小异,只是每次请求和响应所附带的信息变成了表单变量。
这个原理和上述两种状态保持方法原理是一样的,QueryString通过将信息保存在所请求地址的末尾来向服务器传送信息,通常和表单结合使用,一个典型的QueryString比如:
www.xxx.com/xxx.aspx?var1=value&var2=value2
本文从一个比较高的视角来看HTTP协议,对于HTTP协议中的细节并没有深挖,但对于HTTP大框架有了比较系统的介绍,更多关于HTTP的细节信息,请去Bing或参看相关书籍:-)
文章来源:http://www.cnblogs.com/CareySon/archive/2012/04/27/HTTP-Protocol.html
centos6.5最小化安装/AWS centos
php-5.5.5
yum install libxml2-devel gd-devel libmcrypt-devel libcurl-devel openssl-devel
cd /usr/local/src
wget http://us3.php.net/get/php-5.5.5.tar.gz/from/cn2.php.net/mirror
tar -xvf php-5.5.5.tar.gz
cd php-5.5.5
./configure --with-apxs2=/usr/local/apache2/bin/apxs --disable-cli --enable-shared --with-libxml-dir --with-gd --with-openssl --enable-mbstring --with-mcrypt --with-mysqli --with-mysql --enable-opcache --enable-mysqlnd --enable-zip --with-zlib-dir --with-pdo-mysql --with-jpeg-dir --with-freetype-dir --with-curl --without-pdo-sqlite --without-sqlite3
make && make install
我已经尽量的在参数上做了精简,用以上参数编译安装好的 php 运行 wordpress, joomla, ip board 等常见的博客、论坛程序都是没有问题的,因为有了 --disable-cli,所以就没法 make test 了,安装好以后也没法 php -v 了。安装吧:
cp php.ini-production /usr/local/lib/php.ini
vi /usr/local/apache2/conf/httpd.conf
在httpd.conf里面添加如下内容
AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps
#应该将以上两句添加在其他AddType之后。
LoadModule php5_module modules/libphp5.so
#上面那行可能在编译安装 php 的过程中已经由系统自动添加了
<FilesMatch \.php$>
SetHandler application/x-httpd-php
</FilesMatch>
接下来可以重启php查看是否安装成功了。
我们可以在apache默认目录下面新建一个php文件测试一下
vi /usr/local/apache2/htdocs/php.php
<?php
echo "php is OK";
?>
javascript的神奇之一就是其变量是松散类型的,具有动态性,所以只需要五种基本类型的和一种复杂类型,就可以轻松地保存所有的数据类型了.在使用的过程中,javascript可能会自动地转换我们的数据类型,有时我们也可能会强制地进行数据的转换.那么在转换过程中,是有一些规则需要遵循的.
对于Boolean类型,我们在使用if进行判断或者使用Boolean进行转换的时候,其结果会有true
和false
两种.
其规则是:
数据类型 | 转换为true的值 | 转换为false的值 |
---|---|---|
Boolean | true | flase |
Strin | 任何非空字符串 | ""(空字符串) |
Number | 任何非零数字(包括无穷大) | 0和NaN |
Object | 任何对象 | null |
Undefined | 不适用 | undefined |
对于Number我们可能会用到Number
,parseInt
,parseFlost
三种,我们对接用实例来展示
代码 | 结果 | 备注 |
---|---|---|
Number("Hello world") | Nan | flase |
Number("") | 0 | |
Number("0000123") | 123 | |
Number(true) | 1 | |
Number(undefined) | NaN | |
Number(null) | 0 | |
Number(1.23) | 1.23 | |
Number("1.34") | 1.34 | 只包含浮点数字 |
Number("1.34abc") | NaN | 包含了数字以外的字符 |
代码 | 结果 | 备注 |
---|---|---|
parseInt("Hello world") | NaN | 没有找到数字 |
parseInt("") | NaN | |
parseInt("123abc") | 123 | |
parseInt(true) | NaN | |
parseInt(undefined) | NaN | |
parseInt(null) | 0 | |
parseInt(1.23) | 1 | |
parseInt("1.34abc") | 1 | 只包含浮点数字 |
parseInt("10",10) | 10 | 尽量指定第二个参数,以10进制的方式转换 |
parseInt("def123abc") | NaN | 不是以数字或者(空字符加数字)开头 |
parseInt("0xf") | 15 | 16进制 |
代码 | 结果 | 备注 |
---|---|---|
parseFloat("Hello world") | NaN | 没有找到数字 |
parseFloat("") | NaN | |
parseFloat("123abc") | 123 | |
parseFloat(true) | NaN | |
parseFloat(undefined) | NaN | |
parseFloat(null) | NaN | |
parseFloat(1.23) | 1.23 | |
parseFloat("1.34abc") | 1.34 | 只包含浮点数字 |
parseFloat("def123abc") | NaN | 不是以数字或者(空字符加数字)开头 |
parseFloat("3.125e7") | 31250000 | |
parseFloat("0908.7.2") | 908.7 | |
parseFloat("0xA") | 0 | 16进制的字符串始终转换为0 |
代码 | 结果 | 备注 |
---|---|---|
String(10) | "10" | |
String(1.23) | "1.23" | |
String(00011) | 11 | |
String(0001.23) | 报错 | missing ) after argument list |
String(null) | "null" | |
String(undefined) | "undefined" |
根据网上的代码优化而来,可以实现
快捷打开TotalCommander
如果已经打开TotalCommander则激活TotalCommander
如果是未注册版本就自动点击数字1,2,3
未注册窗口设置为透明
实现的代码如下
#t::
IfWinExist ahk_class TTOTAL_CMD
{
WinActivate
}
else
{
Run "c:\totalcmd\TOTALCMD64.EXE" ;设置为自己TC所在位置
WinWait,ahk_class TNASTYNAGSCREEN,,1 ;探测NagPage,若机器慢,1可改为3、4、5
If ErrorLevel=0 ;如果有NagPage,需要模拟发送1、2、3
{
WinSet,Transparent,0,ahk_class TNASTYNAGSCREEN ;设置NagPage为透明
WinActivate,ahk_class TNASTYNAGSCREEN ;抢焦点
WinGetText,NagTextStr ;获取NagPage信息并处理
StringMid,NagSendChar,NagTextStr,1,1
WinActivate,ahk_class TNASTYNAGSCREEN ;再抢焦点
Send,%NagSendChar% ;模拟发送1、2、3
}
WinActivate,ahk_class TTOTAL_CMD
}
return
注:代码中第15行 StringMid,NagSendChar,NagTextStr,1,1
网上的代码最后数字全部设置的为10,1,经过测试,发现都无法运行,最终获取到的是全部的窗口内容,只有设置为1,1才能正确截取到相应的数字。不知道是不是和我的运行环境有关?win7 x64+TotalCommander 8.51a x64
在js中,作用域的问题算还是比较容易理解的,之前也写过一篇博文《javascript中的词法作用域》。但js的作用域规则在遇到闭包的时候可能就会出现一些问题了。最经典的就是for变量声明的问题了。
首先我们有html和js的代码片段
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div id="showClick">
<p>产品一</p>
<p>产品二</p>
<p>产品三</p>
<p>产品四</p>
</div>
</body>
</html>
var showClick = document.getElementsByTagName('p');
for(var i=0;i<showClick.length;i++){
showClick[i].onclick=function(){
console.log(i);
}
}
view code;
这个就是很经典的一个问题,当我们点击的时候,控制台输出的全部是4。出现这个问题的最根本的原因就是:
我们从词法作用域来分析这一句话,这个闭包中的i,他通过他的执行环境来获取,也就是for那一层,即全局变量,全局变量中的i是一个静态变量,在for执行完毕之后,他的值为4,也就是接下来我们执行onclick操作时的最后一个值。
之所以利用词法作用域来理解我认为有助于让我们更容易来理解解决这个问题的几种方案。
var showClick = document.getElementsByTagName('p');
for(var i=0;i<showClick.length;i++){
(function (m){
showClick[m].onclick=function(){
console.log(m);}
})(i);
}
从词法作用域来看,闭包中的m来源于匿名函数的实参,而我们知道,函数的参数是按值传递而非按照引用传递的,所以这里得到的m就是每次循环的i的实际值。
同理
var showClick = document.getElementsByTagName('p');
for(var i=0;i<showClick.length;i++){
showClick[i].onclick=(function (m){
return function(){
console.log(m);
}
})(i);
}
或者可以不传参数
var showClick = document.getElementsByTagName('p');
for(var i=0;i<showClick.length;i++){
(function (){
var tem = i;
showClick[i].onclick=function(){
console.log(tem);}
})();
}
var showClick = document.getElementsByTagName('p');
for(var i=0;i<showClick.length;i++){
showClick[i].onclick=(function (){
var tem = i;
return function(){
console.log(tem);
}
})();
}
原理都是一样的,强制让闭包在匿名函数中去找值。
var showClick = document.getElementsByTagName('p');
for(var i=0;i<showClick.length;i++){
showClick[i].i = i;
showClick[i].onclick=function(){
console.log(this.i);
}
}
这里的showClick是对象,所以我们是可以给为增加一个属性的,而对象是引用类型的,所以在增加的属性的时候,他的值已经被固定了。
var showClick = document.getElementsByTagName('p');
function returnShow(i){
return function(){
console.log(i);
}
}
for(var i=0;i<showClick.length;i++){
showClick[i].i = i;
showClick[i].onclick=returnShow(i);
}
就这相当于避开了闭包,思路同1一样的,函数的参数是按值传递的。
var showClick = document.getElementsByTagName('p');
for(let i=0;i<showClick.length;i++){
showClick[i].onclick=function(){
console.log(i);
}
}
于是我们思考一下,既然函数参数是按值传递,加上作用域链,我们可不可以这样直接通过参数来缓存变量呢?
var showClick = document.getElementsByTagName('p');
for(var i=0;i<showClick.length;i++){
showClick[i].onclick=function(i){
console.log(i);
}
}
答案是否定的,因为onclick事件传递的参数是event事件,所以打印的实际上是event事件,而不是i的值。
那么对于setTimeOut我们则可以通过这种方式来进行解决
for (var i = 0; i < 5; i++) {
setTimeout(function(){
console.log(i)
},10)
}
不出所料,以上代码会全部打印出5出来。
我们可以采用上面的匿名函数的方法来解决:
for (var i = 0; i < 5; i++) {
(function(i){
setTimeout(function(){
console.log(i)
},10)
})(i)
}
for (var i = 0; i < 5; i++) {
(function(){
var tem = i;
setTimeout(function(){
console.log(tem)
},10)
})()
}
以及我们可以直接通过函数传参的方式来实现:
function a(a){
return function(){
console.log(a);
}
}
for (var i = 0; i < 5; i++) {
setTimeout(a(i),10)
}
一个客户的项目中,在表中有个字段card_no
是保存的会员编号,客户采用的是四位数的方式来记录的,0001~9999这样的,字段采用的字符串的方式来保存的。
现在客户有一个这样的需求,在一个搜索应用中,中需要输入会员的后面的编号,比如0001
号的,只需要输入1
就可以直接搜索出这个会员。
当然,我们可以在采用sql搜索之前通过程序进行这个值的判断补全。
而更简单的方法就是通过mysql的LPAD函数。
LPAD(str,len,padstr)
使用这个函数的结果就是对str字符串判断len长度,如果超过就从左边开始截取指定len的长度,如果不足len长度就使用padstr在str前面进行补全。
对于客户这需求,我们就可以写出这样的sql语句了。
SELECT * FROM ecs_users WHERE card_no = LPAD(88,4,0);//88为传入的数值
同时还有一个函数RPAD,使用方法也是一样的。这个只是进行后补全的。
我们始终记住一句话:除了加性操作符之外,其它操作符中遇上字符的时候,javascript都会尝试在后台采用Number对字符串进行转换.于是我们可以看到如下的结果:
console.log("15"+5);//"155"
console.log("15"-5);//10
console.log("abc"+5);//"abc5"
console.log("abc"-5);//NaN
console.log("15"*5);//75
console.log("15"/5);3
console.log("5"==5);//ture
console.log("5abc"==5);//false
console.log("23"<"3");//true 没有数字,比较第一个字符"2"和"2"
console.log("23"<33);//true "23"转换为23
console.log("a"<33);//false "a"转换为NaN
在对echsop进行二开的时候,我很简单地写了一句;
$shop_style = empty(intval($_POST['shop_style'])) ? 1:intval($_POST['shop_style']);
结果一打开页面就报错:
Fatal error: Can't use function return value in write context in ...index.php on line 503
怎么看这代码都没有问题呀,上网搜索才发现,对于empty()
函数,有如下描述
Note: empty() only checks variables as anything else will result in a parse error. In other words, the following will not work: empty(trim($name)).
empty() 只检测变量,检测任何非变量的东西都将导致解析错误!
所以说上面那句正确的写法应该是
$shop_style = empty($_POST['shop_style']) ? 1:intval($_POST['shop_style']);
非常喜欢用markdown来写文章,但相信大多数在使用markdown的时候会有和我一样的困扰,那就是插入表格是非常不方便的.markdown本来就是让我们专注于写作的,而表格的书写方式确实是反人类的.今天要写一篇文章,不得不插入一些表格,于是寻找了一些解决方法,能够方便地在markdown里面插入表格.
这是这笨的方法,也是最无奈的方法,就是把表格转换成图片,当然,这也是最不想用 方法了,体积大,语义不强等.
就是借用markdown编辑器自带的插入表格功能.有一部分markdown已经带有表格插入功能了,比如MarkDownPad,但它需要pro版才带有这个功能的,还有比如小书匠,免费的,带有插入表格的功能.
因为我习惯于直接使用平时写代码的sublime text来写,所以对于markdown编辑器没有多试用,所以其它编辑器大家可以自行查看是否支持表格的插入.
如果你像我一样在使用其它编辑器来写markdown,而编辑器又不支持插入表格,同时也不想再安装一个编辑器来单独写markdown,那么可以尝试在线转换的方式.这里推荐几个网站:
PS:我用markdown表格写的一篇博文:《javascript中数据类型转换那点事 》
以前学习到“聚焦”这个词是一两年前大量阅读书籍,听讲座的时候,因为听得太多了,正处于迷茫之中,后来就了解到“聚焦”这个词,我们需要学习的东西太多太多了,而我们必须要选择性地选择自己最需要的,最重要的东西来学习。
而这两天,关于“聚焦”又感悟到了许多,其实我们的生活也需要聚焦。尤其是在这个信息化大爆炸的时代,围绕在我们身边的互联网信息太多太多了。每天我们不停地刷着微博,刷着空间,然而一天下来,我们对这个世界真的了解到了多少吗?我们真的是学习到了许多有用的知识吗?前两天看到一句话特别有感触,“不要把微博当成书来读”。
而在这里面更重要的是,我们的关注点在不断地慢慢转移,由我们的亲人,朋友,同学开始慢慢转移到互联网上那一个个从未曾谋达面的名人们。这里摘录李楠kkk很有意义的一段话:李开复的喧哗,远不及浅苍南的美
我们在微博上关注各种公知:从李开复,姚晨到薛蛮子。也关心他们的各种话题:Facebook
IPO,香港的小黄鸭和关锦鹏的八卦。
问题是,我们一生只有有限的时间。注意力花在这里,就不会花在那里。我们一生也只有有限的精力。Robin Dunbar说你只能和150人维持稳定的社交关系。再多一个,就要放弃一个。
把时间和精力花在可能一辈子都不会谋面的人上,去关心可能一辈子都和自己没有交集的事——我们为此付出了什么代价?
我们心中都有一个浅苍南:可爱温柔的邻家女孩。同校,同单位,或者同小区。认识,却并不熟悉。她的电话就躺在地址簿里,却没理由联系。
我们也都有父母亲戚,三五死党。无论是高中玩伴,还是大学室友。他们也躺在地址簿里面。却在这个“信息过载”的世界中,被我们越来越疏远。
而其实,这些人才是真正影响我们生活的“社交网络”。他们而非公知,才会在你失恋的时侯陪你聊天;在你想high的时侯凑个酒局;在你需要帮助的时侯,伸出援手。
和颠倒众生的汤唯比较,“邻家女孩”的美丽,才更可触摸。和腰缠万贯的任志强比较?父母的500块钱,才更容易借到。
我们不应更关注他们吗?哪怕只是一张略显忧郁的自拍。或者小叔孩子摇晃学步的照片。
让我们把心收回来,聚焦在我们自己的生活之上,这个世界太大,我们走不完,这个世界太远,我们看不完,更多地关心自己身边有朋友,你会得到温暖的回馈。更多地问候自己身边有朋友,你会得到无尽的帮助,更多地与自己身边的朋友沟通,你会发现有朋友一起走的路才更踏实。
离开校园,我们步入了社会的这个大圈子,我们拥有了另外一个圈子,我们的人际关系发生的巨大的变化。我们可以在一家公司工作三五年,我们可以拥有无数的客户资源,然而,我们不要忘记,昔日的同学朋友可是日日夜夜陪伴着我们走过三年,五年,甚至是十多年,而在这里面的每一天,我们都是有着最单纯的友谊,这个时间,我们是最简单的,然而,当我们融入无数争斗的这个大社会后,我们却开始把这曾经最简单的感情给忽略了。
出差到一个他乡异地,举目无亲,独自领略着这座城市的美景时,却不知道昔日的朋友就在你入住酒店背后的那一个小区;公司迅速发展,谋求合作伙伴,苦苦寻觅,价格战杀得头破血流,却不知道曾经的某位同学却是可以给你提供最佳的资源,最优的价格;想带上女朋友给她来一场最浪费的旅游,花尽心思规划无数线路终觉不满意,却不知这对于旅行社那曾经的同学来说只是小菜一碟......如果我们有着紧密的100个同学的网络,那么我们可能分布在几十个行业,几址个地方,拥有几十种不同资源,而这些,难道对于我们的生活,学习,工作不是最大的帮助吗?
细细想一下,好像在成都的同学就不少的,但认真一列,还真不知道到底有哪些在成都,闲来无聊的时候,真不知道还有谁的电话可以打过去问候一声。似乎对于曾经的同学朋友们来说,突然的一个电话,只是在告诉他,我是“无事不登三宝殿”的。
真的很想问候一下大家,知子湾村小学的同学们,临津小学的同学们,回龙中学的同学们,城北中学的同学们,电子科大的同学,你们现在都还过得好吧?不知道还有多少人还能记得我,而我真心希望能够有一种最轻快的方式把大家联系起来,如果把我们的人生写成一本书,那么我相信曾经的校园生活是我们一生中最清新的一个篇章。今天,当我们只能聊着工作,婚姻,金钱的时候,我相信,和我们曾经一起走过的同学们在一起,我们依然还可以聊起当年谁又受惩罚了,谁又偷偷爱上谁了,谁的篮球打得最棒了,谁又偷偷溜出去上网去了...这些真正属于我们自己的八卦新闻,这些真正属于我们自己最真实的生活。
朋友们,请别把我们的联系方式只是当成一种珍藏,请别把QQ的在线只是当成一种状态,请别把昨天的生活只是当成一种回忆,人生的路很长,有你有我的陪伴,我们才将不孤独,我们才将走得更远!
在javascript的五种基本数据类型中,有两和数据类型可能会经常给我们带来一些困惑,他们就是undefined
和null
了.
我们先来看一种情况:
var a;
console.log(a);//"undefined"
console.log(b);//报错
然后有
var a;
console.log(typeof a);//"undefined"
console.log(typeof b);//"undefined"
这里我们可以得出结论就是:未定义的变量,未初始化的变量,使用typeof的时候,他们的结果都是'undefined';而对于未定义的变量,他也只能使用typeof操作符,执行其它操作都将报错.(调用delete也不会报错,但没有任何意义);
从上面也可以看出,未初始化变量的默认值就是undefined
,所以console.log(a == undefined)
将会打印出true
.
因此,良好的编程习惯应该这样:没必要显式地声明a = undefined
;而对于任何变量,我们都应该显式的初始化一个除undefined
外我们想要的值,这样当我们在应用typeof的时候,我们将可以很容易地知道undefined
结果代表的是变量未声明.
我们也是先看一种情况
var a=null;
console.log(typeof a); //object;
这里我们可以看出,null
是以一种空对象的形式保存的.所以对于任何我们即将要以对象的形式保存的变量,我们都可以使用null
来进行初始化;
有一点不好理解的就是,当conslole.log(null == undefined)
的时候,结果竟然返回的是true
,其中的原因就是undefined
是派生至null
的.那么console.log(null === undefined)
的结果肯定是false
,因为他们是两种数据类型.
因为我们也就能够理解下面这段代码了:
console.log(Boolean(null));//false
console.log(Boolean(undefined));//false
对于undefined
和null
它们将存在于以下区别
1.在使用Boolean进行转换的时候,undefined
和null
都为false
,而null
所赋于的变量,拥有任何对象后都将会是true
值.
2.在使用Number进行转换的时候,Number(null)
的值为0,而Number(undefined)
为NaN
.
3.在合作String进行转换的时候,String(null)
的结果是"null"
,而String(undefined)
的结果是"undefined"
.
我们还需要注意的一点就是,typeof
操作符返回的结果是字符串.所以console.log(typeof undefined == undefined);
的结果是false
,所以typeof undefined
返回的"undefined"
非此undefined
.
我们在使用git进行远程仓库操作的时候,如果使用https的方式的话,总是会在每次操作的时候都要求输入用户名密码,如果我们固定会在某台电脑上面使用的话,就可以选择用SSH的方式来避免每次输入用户名和密码。
SSH是一个身份认证系统,它是用来识别你的身份,而不是用来识别客户端的,
因此在电脑上面,我们要用我们自己的身份来生成一份公私的密钥对。
打开git bash,运行ssh-keygen -t rsa -C '[email protected]'
,这里面的[email protected]
就是你自己的邮箱,如下图:
之后就会在我们的用户目录下面生成一个.ssh
的文件夹,里面会出现两个文件,id_rsa
和id_rsa.pub
,后面这个就是我们的公钥文件,我们需要把这个公钥文件设置到github上面。
github上面有两个设置的地方,一个是账户设置里面里面的SSH Keys
,这里面添加话,则可以通过私钥来控制整个账户下面的项目。
另一个就是具体项目设置里面的Deploy keys
,在这里面添加key则只能控制这一个项目,如果勾选里面的Allow write access
则可以对项目进行PUSH操作。
设置了key之后,我们打开本地项目中的.git
文件夹,修改里面的config
文件,把[remote "origin"]
里面的url修改成我们项目的SSH地址就可以了。接下来我们就可以轻松地使用SSH进行操作了。
需要注意的是,假如账户[email protected]在github上面注册了,并创建了仓库,我们在本地为账户[email protected]创建了SSH,而账户B是没有注册github的,我们把B的公钥添加到账户A的账户级的SSH Keys
中,那么账户B同样能够对A的所有仓库进行PUSH的操作的。而项目级的Deploy key
中,则同样对该项目具有PUSH操作权限。
一个公钥只能应用于一处,即要么是账户级,要么是项目级,项目级也只能应用于一个项目中,不能多个项目使用同个公钥。
在linux+Apache环境下开启gzip的方法:
LoadModule deflate_module modules/mod_deflate.so
<ifmodule mod_deflate.c>
DeflateCompressionLevel 6
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/atom_xml
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-httpd-php
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE application/json
</ifmodule>
查看header信息中request header
Accept:image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate, sdch
Accept-Language:zh-CN,zh;q=0.8
Cache-Control:no-cache
Connection:keep-alive
Cookie:__cfduid=d5cc4c6799d5826ebe40e22a6ce4c1ff61427275063; tq_current_visit_time=1427423496918; tq_current_source_page_url=http://we.car91.cn/exposure/exposure/index; JSESSIONID=48D7BC57B2BD6868DEDB2AFC646BEB84.jvm1
Host:www.car91.cn
Pragma:no-cache
Referer:http://www.car91.cn/default/index
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36
Accept-Encoding就表示客户端支持的压缩格式
查看header信息中response header
Accept-Ranges:bytes
Cache-Control:max-age=5356000
Connection:Keep-Alive
Content-Encoding:gzip
Content-Length:32775
Content-Type:application/javascript
Date:Fri, 27 Mar 2015 06:32:19 GMT
ETag:"169d5-5111212ad8b00-gzip"
Keep-Alive:timeout=5, max=94
Last-Modified:Thu, 12 Mar 2015 07:10:04 GMT
Server:Apache/2.4.10 (Unix)
Vary:Accept-Encoding
中看到Content-Encoding:gzip
则表示已经启用了压缩
LoadModule headers_module modules/mod_headers.so
###在httpd.conf中添加需要缓存的文件类以及设置时间
<FilesMatch ".(flv|gif|jpg|jpeg|png|ico|swf|js|css|pdf|json)$">
Header set Cache-Control "max-age=5356000"
</FilesMatch>
设置成功后,如上在response header中就可以看到Cache-Control:max-age=5356000
的效果了。
对于静态资源,我们不需要在每次请求中带上cookie信息,对于静态资源避免带上cookie的一个比较简单的方法就是单独用一个域名来保存静态资源,因为不同域的请求不会带上其它域的cookie,比如网站域名是www.abc.com,则可以另外单独用一个static.def.com的域名来保存静态资源。或者单独分配一个static.abc.com来保存,但对于这种二级域名要避免在主域名中存在.abc.com这样作用域的cookie,否则还是会把cookie请求上谨言该主域下面的所有请求上。
要降低首字节的响应时间,一个是检查dns解析时间,如果域名的dns解析时间过长,则需要考虑更换域名的dns以提高解析速度。另一个则是优化页面的响应时间了。
我们都知道,在js中闭包会一直保持对着包含函数中的变量的引用,哪怕是在其它地方返回了闭包函数,其外部函数中的变量也不会销毁的。根据这个特性,我们引用一个面试题来深入观察一下:
如何定义一个count函数,每次调用的时候,都返回这个函数被调用的次数,除了count外,不能再有其它全局变量。
这道题主要也就是考察闭包中变量引用及作用域链的问题,根据闭包的特性,于是我们可以得到这样的一个函数:
var count=(function(){
var i=0;
return function(){
console.log("这是你第"+ ++i + "次调用我");
}
})();
count();
count();
我们通过定义匿名函数来减少一次函数命名,闭包中存在对i的调用,所以函数虽然是返回了闭包函数,但其对i的引用一直存在,所以外包函数中的变量一直存在。
当时, 如果不考虑函数命名的问题,我们也可以这样写:
function count_fun(){
var i=0;
return function(){
console.log("这是你第"+ ++i + "次调用我");
}
};
var count = count_fun();
count();
count();
扩展一下,我们都知道函数也有是属性的,我们在做动画和定时器的时候,可能会经常用到这种函数属性来方法来避免定时器干扰的问题。针对这个问题,我们也同样可以通过只使用一个变量,然后通过增加属性的方法来实现。
function count(){
console.log("这是你第"+ ++count.i +"次调用");
}
count.i=0;
count();
count();
或者直接通过对象的属性来实现也可以
var count = {
i:0,
add:function(){
console.log("这是您第"+ ++this.i +"次调用");
}
}
count.add();
count.add();
count.add();
都是一些正在看和准备看的前端书籍,偶然在一位大牛的博客里找到整理出来的。
1 JavaScript权威指南(第6版):号称javascript圣经,前端必备;前端程序员学习核心JavaScript语言和由Web浏览器定义的JavaScript API的指南和综合参考手册;
2 JavaScript高级程序设计(第3版) :前端必备书,如果你想真正进入前端世界,这是一本不可多得的进阶书,没什么好说的,必须细细品读;
3 JavaScript语言精粹 :这是一本值得任何正在或准备从事JavaScript开发的人阅读,并且需要反复阅读的js书籍;
4 基于MVC的JavaScript Web富应用开发 :这不是一本适合初学者看的书籍,更适合具有一定前端开发经验的从业人员看的框架书,如果你想构建一个复杂的前端应用,你会如获至宝;
5 JavaScript DOM编程艺术(第2版):好书主要是dom操作和兼容方面的知识,值得一看;
6 JavaScript经典实例 :可以看看,主要是有关javascript一些典型小工具,有些剖析的并不深入,总体来说可以看看;
7 JavaScript设计模式 :写js不难。如何编写优美、结构化和可维护的代码呢?反复的揣摩这本书吧;
8 JavaScript编程精解 : 了解一下可以;
9 JavaScript模式 : 又一本设计模式的好书,如果你想让自身的Javascript技巧提高到一个新层次,成为专业的开发人员和程序员,反复阅读吧;
10 JavaScript高效图形编程: 是一本具有很强实操性的JavaScript图书,主要涉及JavaScript性能优化、高级UI设计、Web游戏开发、面向移动设备的开发、图形编程知识等
11 JavaScript RIA开发实战——最佳实践、性能、表现:主要介绍如何采用最合理的方式为RIA编写可靠的、易于维护的HTML、CSS和JavaScript代码,以及如何使用Ajax技术在后台实现浏览器与Web服务器的动态通信。可以细细读一下;
12 高性能JavaScript : 又一本好书,涵盖了当今JavaScript开发者需要了解的所有性能问题,毫无疑问,它已加入我的性能最佳实践列表;值得细细品读并实践;
13 悟透JavaScript(美绘本): 这是一本可以让你轻松加愉快的阅读的一本好书,读完你可能会领悟:哦原还可以这样;
14 Ajax权威指南 : 详细的展示了ajax技术的发展以及应用,对于了解ajax技术很不错的一本详尽书籍;
15 Node.js开发指南 : Node.js是一种新兴的开源技术,它将JavaScript从Web浏览器移植到常规的服务器端,使用Chrome的V8虚拟机来解释和执行JavaScript代码,能用于构建高性能、高可扩展的服务器和客户端应用,以实现真正“实时的Web应用”;
16 Node Web开发 : 雅虎架构师精准解读最炙手可热的Web开发技术;
17 jQuery Mobile权威指南:是系统学习jQuery Mobile的权威参考书;
18 数据可视化实战:使用D3设计交互式图表 :web矢量图类库d3.js的工具书;
19 jQuery权威指南 : 学习jquery的入门书籍;
20 精彩绝伦的jQuery : 名字起的不错,能忽悠不少人,入门级书籍;
21 锋利的jQuery : 不错的一本jquery应用书籍;
1 HTML 5与CSS 3权威指南 :html5和css3入级好书;详尽讲解了HTML5与CSS 3的所有功能和特性;
2 HTML5移动Web开发指南:介绍了一下移动端的web开发技术,以及一些移动端框架:QueryMobile、Sencha Touch,以及PhoneGap;
3 响应式Web设计:HTML5和CSS3实战 :有关响应式设计的知识并不是很多,大量篇幅写了html5和css3,这本书比较一般;
4 HTML5程序设计(第2版) : 很全面的介绍了一下html5技术,前端人员都应该看一看;
5 编写高质量代码:Web前端开发修炼之道 : 不可多得的一本前端开发规范书,前端开发人员的必读书;
6 精通CSS:高级Web标准解决方案 :css兼容性解决方案汇总,好书;
7 CSS禅意花园 :主要的Web设计原则以及它们运用的CSS布局技巧;
8 CSS权威指南 : css经典工具书;
9 高性能网站建设进阶指南:Web开发者性能优化最佳实践 : 好书啊!网站性能优化,浏览器加载渲染详细解析;
10 网站重构——应用Web标准进行设计 : 前端开发人员必读书,一本可以帮助网页设计师快速了解和掌握web标准设计的书;
11 变幻之美 DIV+CSS网页布局揭秘 : 详细的介绍了从效果图到web布局实现的整个过程;
12 HTML5 Canvas基础教程 : canvas入门书籍;
5月份刚过了五一,就把岳母接上来安排在华西医院进行住院治疗,这已经是第二次来华西医院,至去年十月份第一次来华西医院已经过去了大半年,而从最初发病开始,已经整整过去了八个月之久,其间去过好几个医院治疗,中药,西药,输液,无数的治疗方案都均无效果。这次是实在没办法再这样拖下去了,于是决定到华西进行住院治疗。
当初怀疑过肺脓肿,也怀疑过肺结核,甚至怀疑过肺癌,但这一切均无法确症。5月份在华西住院一个月,也是各种检查无数,而检查结果无一例外的都是各种怀疑,依然无法确症。直到最近胸外科的医师过来看完各种报告,认为是炎症,决定进行手术,而接下来还要进行各种检查,等待手术。
与此同时,当时天天也带着小悦悦跟着跑上来,陪着岳母转了一天的医院,反而把小悦悦弄得高烧不退,吃药无效,最后只得输液,那一刻,我的心是最痛的,一个不到半岁的稚嫩小生命,却第一次扎着脑袋输着那可怕的液体,在我的记忆中,好像小孩子是千万不能输液的,我真想阻止这一切,而我却无能为力。源于成都这边的各种不便,小悦悦好了后,天天又不得不带着她回到老家,而在老家,又是一会儿这儿不好,一会儿又那儿不好。
有生以来,我已经两次在病危通知书上签下了字,第一次是09年爸爸病危的时候,第二次就是岳母这次,整个5月份,我是经常往医院跑,健康两个字,又再一次深深抨击着我的思考,虽然我一直知道健康很重要,可是我真的在健康上面投资了多少呢?我又为父母他们的健康付出了多少呢?去年和天天一起做了一次健康体检后,我们约定每年我们都要带着全家做一次健康体检,然而今年已经过去了一半,我们自己都还没有去做健康体验。今年年初我给自己定下了几个目标,好几个我都已经开始在执行了,而唯有最重要的健康计划却一直没有实施起来。这,是时候思考健康投资与计划的问题了。
这是5月份最简单的词汇,但也是一直令我深入思考的一个词汇,我们每天从早到晚,从年初忙碌到年尾,一生辛劳,而我们这一切是为了什么呢?大家都可能会说为了以后过上更好的生活,可什么样的生活才是更好的生活呢?我们有过一个概念吗?我们来到这个五彩缤纷的世界上,可我们的人生色彩是什么呢?我们活着一生,目的是为自己留下什么呢?我们的人生到底是在追求着什么呢?这一切问题,也许大多数人都有了自己的答案,而我却发现我无法回答我自己。倘若让我用文字,用彩笔描绘出我未来的梦想,我真的还是不知所措。
也许以前有很多天真的遐想,现在仔细想一起,还真的有着那么一份天真劲,我也想出去旅游,但我知道哪些地方有自己喜欢的风景吗?这个世界哪些地方我应该去呢?我又对那些地点了解多少呢?我也想来一场骑行冒险,但我的路线是什么呢?我的安全知识又储备在哪里呢?倘若说这些我都还可以去规划,去学习,但除了这些,我这一生又还有哪些梦想可以去追求呢?
好像人生就是这样慢慢地由童年的天真无瑕到成年的现实麻木,昨天,我们还可以三五成群地一起聊着对未来的无数梦想,而今天,大家都开始被这个现实的生活埋没了自己的激情与梦想,工作,家庭,孩子似乎就成了我们人生的全部。而我们的人生不是应该可以更精彩一些吗?我又将怎样才能重新拾回造梦的能力呢?
在曾经的日志里面,我写到了目标与目的,我们在为着目标奋斗的同时,我们不能忘了我们所追求的目的。而我也一直在思考,正如我认为健康,快乐是我们人生中最重要的,那么我们人生中到底还有哪些是最重要的呢?除了我们各自追寻的梦想不一样,而我们人生中有那些还是我们最应该追求的呢?同样是林伟贤的一堂课堂,让我对这个疑问有了答案,因为我是非常认可这样的格局,可以说他把我曾经的以及未来的追求很完整地做了一次归纳。
什么是财富,不是我们这一生挣了多少钱,买了多少大的房子,拥有多少豪华的车,而只有我们在工作,学习,理财,健康,心灵,休闲,人脉,家庭这八大方面获得成功,我们才能算说是拥有了真正的富中之富。而我们的大多数人,却是把自己的几乎全部精力放在了工作之上,却忽视了其它几个方面的投入,所以纵然有千万金钱,那也不能算是财富。
我也开始思考自己自己,除了工作,我也仅仅是在学习,理财,心灵方面有所付出,而对健康,我却是一直没有执行,对于休闲,真的很想出去看看这个世界,去年的旅行计划,到今年都还没有开始,对于人脉的建立,确实应该开始努力了,三十岁年靠经验挣钱,三十岁后靠人脉挣钱,我不希望等到了三十岁,我才开始建立人脉,对于家庭,我认为我对父母们的关爱还远远不够,虽然与长辈们缺少交流的话语,但这不应该成为了可以少打电话,少慰问他们的理由。对于家这个摇篮,我不应该忽视太多。
回首自己的记账之路,应该可以说从去年六月算起,在此期间,我也一直保持良好的记账习惯,在此期间,记账这个东西让我收益最大的就是能够清晰地知道自己每一个账户资金情况,因为我的账户特别多,每天的流水特别多,即使两三天不记账,我的账户就会乱得毫无头绪,所以,也正是有了这各客观因素,使得我一直很好地把记账坚持下来了。
但现在发现以前自己好像是为了记账而记账,每天只是把自己的流水记下来而已,虽然两个月前开始做预算,但也没有认真地去分析它。而记账的真正目的是什么?是为了分析自己的收入支出结构,制定财务目标,分析哪些支出为不必要的开支,哪些地方可以开源节流,以便控制自己的消费行为。真正引起我重视这一点是五月份的支出报表,虽然我知道我五月份的支出相对要多一些,但却没想到一下子飙升到了这么多,五月份总共支出高达一万九百多。这也不得不让我开始认真分析自己的支出情况,开始优化自己的支出结构,开始控制自己的消费习惯。六月份我将开始学习
认真分析账单,相信六月份我会在这方面有很大的进步。期待加油。
五月份阅读完了刘克亚的《超高价营销》,他的上一本书《打造赚钱的机器》还没有看完就借给朋友去了,他的视频更多,但都没有认真地看完,所以这本书算是第一次认真地学习完的吧。确实是大师的理论体系,我相信我还得再认真地阅读好几遍才能够用自己的文字来总结出自己的学习体会。第一次阅读下来,让我对整个营销过程有了一个比较清晰的了解,包括营销前的打造自动系统,营销中的二合一促销模式,以及后续的专家,明星打造。这本书还需要继续再次阅读才能领略其中的精髓,同时也需要等待一个可以施展的舞台才能加以应用。
把“悲催”二字用在我考驾照这件事上一点也不为过。首先,我的身份证地址是成都的,因为我一直用的当时进大学后办的身份证,而自己的户口实际上已经迁回老家了,而考驾照必须要身份证上的地址和户口地址一致,无奈之下,在2月底回家重新办身份证,同时也办了一个有效期三个月的临时身份证,回来3月10号把资料交给教练,我原来有D照,而且是在德阳,本应该迁到成都来,但教练他说他会弄,我也就没管了。后来还兴冲冲地去驾校练过一次车。
交了资料后,教练什么也没有给我交代,所以等了一段时间后,到三月底才让我去体检,办完体检表后,又告之我上面的电话号码是要写另外一个电话号码的,无奈之下,又重新体检,完了后准备给他送过去,因为他回家要从我住那边过,所以他说他过来取,结果一直没有过来,这一等又是两周过去了,好不容易算是把体检表交给他了。
接下来又是等吧,这等到了五月几号,被告之得去把我的驾照迁到成都来,抽了一个空,跑去第四分局,结果搬走了,匆匆赶到新地点,被告之又要体检表,无奈,又得去体检,这次好像都跟我做对似的,中铁二局,金牛区人民医院都不给我盖章,因为我左眼的矫正视力不达标,去妇幼儿童医院,直接给我录系统,结果告之异地迁入数量超过限制,叫过一段时间。好吧,感觉妇幼儿童医院应该没问题,过了一段时间又去,结果被告之没有网络,无法联网,好吧,我又等。第三次去的时候,终于给办了下来,那个激动呀。然后赶紧跑去车管所把驾照给迁了。再一次把资料交给教练,再一次等待报名。
什么叫好事多磨,过了一周,教练又说驾照不行,是行政区域不对,连他都不明白的问题,5月28号,也就是我的临时身份证最后期限,赶紧又去车管所,重新把驾照弄好,原来是上次换证的时候,他们在录资料时把我行政区域选错了。然后领导过来赶紧把我的资料拿上去,希望能在今天把名报上。等了半天,结果又被告之:驾照显示异地迁入,要下个月3号后才能报名。好吧,我马上就是一个没有身份的人了,我只有再一次等待我的新身份证。
幸好我不是急着拿驾照,虽然整个过程比较曲折,但我一点没有抱怨教练的意思,同时我也非常感谢他一直都很友好地对待我,帮我想办法,亲自来拿资料,开车送我等等。再次感谢小燕给我介绍这样一个好教练。
tools-->new snippet会自动打开一个新建片段的文档
<snippet>
<content><![CDATA[
Hello, ${1:this} is a ${2:snippet}.
]]></content>
<!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
<!-- <tabTrigger>hello</tabTrigger> -->
<!-- Optional: Set a scope to limit where the snippet will trigger -->
<!-- <scope>source.python</scope> -->
</snippet>
content代码片段内容
tabTrigger代码的快捷键,在文档中输入该代码后,按Tab就可以输出content中的代码内容了。
scope中定义该代码在哪种类型文档中可以生效。
例如我常常要写一个jquery开源cdn库的缩写。
<snippet>
<content><![CDATA[
<script type="text/javascript" src="http://libs.useso.com/js/jquery/1.9.1/jquery.min.js"></script>
]]></content>
<!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
<tabTrigger>jq</tabTrigger>
<!-- Optional: Set a scope to limit where the snippet will trigger -->
<!-- <scope>source.python</scope> -->
</snippet>
编辑完之后保存为 C:\Users[用户]\AppData\Roaming\Sublime Text 2\Packages\User\jq.sublime-snippet (Win7下) 默认的保存路径就行。后缀必须是.sublime-snippet。
重启后打开,在代码中输入jq,然后按tab就可以直接插入jquery源了。
当然,我们还可以在代码中使用${1},1代码序号,使用了之后,在输出代码的时候会自动定位的${1}位置,按tab后会跳到第二个位置,如果数字相同,则是同时选中的效果。
如果下面一个代码片段:
<snippet>
<content><![CDATA[
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>${1}</title>
</head>
<body>
<h1>${1}</h1>
Hello, ${2:this} is a ${3:snippet}.
</body>
</html>
]]></content>
<!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
<tabTrigger>html5</tabTrigger>
<!-- Optional: Set a scope to limit where the snippet will trigger -->
<!-- <scope>source.python</scope> -->
</snippet>
保存完重启Sublime text 2,新建文件:输入html5,tab会出现如下效果:
${1}出现了两次,所以光标同时编辑图中两处。
${2:this},所以在2处出现this默认值。${1}处编辑完按tab就到${2}处。
主要是需要多思考面向对象中this的应用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.on{
background-color: red;
}
#tab div{
width: 400px;
height: 400px;
display: none;
border: 1px solid #ccc;
}
#tab .block{
display: block;
}
</style>
<script>
window.onload=function(){
var oPrent = document.getElementById('tab');
var oMenu = oPrent.getElementsByTagName('input');
var oShow = oPrent.getElementsByTagName('div');
for(i=0;i<oMenu.length;i++){
oMenu[i].index = i;
oMenu[i].onclick=function(){
for(j=0;j<oMenu.length;j++){
oMenu[j].className = '';
oShow[j].className = '';
}
this.className = 'on';
oShow[this.index].className = 'block';
}
}
var nowShow = 0;
var timmer = setInterval(function(){
if(nowShow>=oMenu.length){
nowShow = 0;
}
oMenu[nowShow].index = i;
for(j=0;j<oMenu.length;j++){
oMenu[j].className = '';
oShow[j].className = '';
}
oMenu[nowShow].className = 'on';
oShow[nowShow].className = 'block';
nowShow++;
},1000)
}
</script>
</head>
<body>
<div id="tab">
<input type="button" class="on" value="1">
<input type="button" value="2">
<input type="button" value="3">
<div class="block">111111111</div>
<div>222222222</div>
<div>333333333</div>
</div>
</body>
</html>
深刻理解this的使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.on{
background-color: red;
}
#tab div{
width: 400px;
height: 400px;
display: none;
border: 1px solid #ccc;
}
#tab .block{
display: block;
}
</style>
<script>
window.onload=function(){
function Tab(name){
this.oPrent = document.getElementById(name);
this.oMenu = this.oPrent.getElementsByTagName('input');
this.oShow = this.oPrent.getElementsByTagName('div');
}
Tab.prototype.init = function(){
var This = this;
for(i=0;i<this.oMenu.length;i++){
this.oMenu[i].index = i;
this.oMenu[i].onclick=function(){
This.change(this);
}
}
}
Tab.prototype.change = function(obj){
for(j=0;j<this.oMenu.length;j++){
this.oMenu[j].className = '';
this.oShow[j].className = '';
}
obj.className = 'on';
this.oShow[obj.index].className = 'block';
}
Tab.prototype.auto = function(){
var nowShow = 0;
var This = this;
var timmer = setInterval(function(){
if(nowShow>=This.oMenu.length){
nowShow = 0;
}
This.oMenu[nowShow].index = i;
for(j=0;j<This.oMenu.length;j++){
This.oMenu[j].className = '';
This.oShow[j].className = '';
}
This.oMenu[nowShow].className = 'on';
This.oShow[nowShow].className = 'block';
nowShow++;
},1000)
}
var tab = new Tab('tab');
tab.init();
tab.auto();
}
</script>
</head>
<body>
<div id="tab">
<input type="button" class="on" value="1">
<input type="button" value="2">
<input type="button" value="3">
<div class="block">111111111</div>
<div>222222222</div>
<div>333333333</div>
</div>
</body>
</html>
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.