Imagine


  • 首页

  • 分类

  • 归档

  • 标签

  • 公益404

《javascript代码规范整理》

发表于 2016-06-18   |   分类于 规范   |  

基于参考,对参考文档进行整理,方便参照,有建议之处,欢迎留言。

无论有多少人在维护,所有在代码仓库中的代码理应看起来像同一个人写的。

变量

(1)申明变量时,必须使用 var 。如果不这么做,所申明的变量将会是一个全局变量,我们要劲量避免申明全局变量。

注:使用一个 var 打头申明一组变量,是最有效率的。

1
2
3
var items = getItems(),
goSportsTeam = true,
dragonball = 'z';

(2)请在作用域的顶部申明变量,这样做主要避免变量申明和赋值提升相关的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function() {
var name = getName();

test();
console.log('doing stuff..');

//..other stuff..

if (name === 'test') {
return false;
}

return name;
}

字符串

(1)使用单引号定义字符串

1
2
3
var name = 'Bob Parr';

var fullName = 'Bob ' + this.lastName;

字符串长度超过80个字符是,应该分成多行,通过字符串连接符(+)进行连接。

注:如果过度使用连接符,长字符串拼接会有性能问题。

1
2
3
var errorMessage = 'This is a super long error that was thrown because ' +
'of Batman. When you stop to think about how Batman had anything to do ' +
'with this, you would get nowhere fast.';

(3)使用数组方法join替代连接符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var items,
messages,
length,
i;

messages = [{
state: 'success',
message: 'This one worked.'
}, {
state: 'success',
message: 'This one worked as well.'
}, {
state: 'error',
message: 'This one did not work.'
}];

length = messages.length;


function inbox(messages) {
items = [];

for (i = 0; i < length; i++) {
items[i] = '<li>' + messages[i].message + '</li>';
}

return '<ul>' + items.join('') + '</ul>';
}

数组

(1)创建数组

1
2
3
4
var a = [x1, x2, x3];
var a2 = [x1, x2];
var a3 = [x1];
var a4 = [];

(2)数组中添加元素-push()

1
2
3
var someStack = [];

someStack.push('abracadabra');

(3)拷贝数组-slice()

1
2
3
4
5
var len = items.length;
var itemsCopy = [];
var i;

itemsCopy = items.slice();

函数

(1)定义函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 匿名 function 表达式
var anonymous = function() {
return true;
};

// 命名 function 表达式
var named = function named() {
return true;
};

// 立即执行 function 表达式
(function() {
console.log('Welcome to the Internet. Please follow me.');
})();

(2)使用函数

注:绝对不要在“非功能”块中申明方法(如,if、while等),而是通过将方法赋值给一个变量实现。

1
2
3
4
5
6
7

var test;
if (currentUser) {
test = function test() {
console.log('Yup.');
};
}

(3)参数arguments

绝对不用使用arguments作为方法的参数名。它会高优先级的覆盖掉每个方法中默认都有的arguments对象。

1
2
3
4
5
6
7
8
9
10

// bad
function nope(name, options, arguments) {
// ...stuff...
}

// good
function yup(name, options, args) {
// ...stuff...
}

(4)调用

使用锯齿状缩进将函数连分割成多行,并以 . 开头,以强调只是针对一个函数的调用,而不是一个新的语句。

1
2
3
4
5
6
('#items')
.find('.selected')
.highlight()
.end()
.find('.open')
.updateCount();
1
2
3
4
5
6
7
8
var leds = stage.selectAll('.led')
.data(data)
.enter().append('svg:svg')
.classed('led', true)
.attr('width', (radius + margin) * 2)
.append('svg:g')
.attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
.call(tron.led);

对象

(1)创建对象

1
2
3
4
5
6
7
var item = {};
var item2 = {
a: 0,
b: 1,
c: 2,
'strange key': 3
};

注:不要使用保留关键字作为key,IE8中会报错。

1
2
3
4
5
6
7
8
9
10
11
// bad
var superman = {
default: { clark: 'kent' },
private: true
};

// good
var superman = {
defaults: { clark: 'kent' },
hidden: true
};

(2) 键值命令

使用可辨识的同义词替换保留关键词

1
2
3
4
5
6
7
8
9
// bad
var superman = {
class: 'alien'
};

// good
var superman = {
type: 'alien'
};

对象属性

###(1)访问对象属性

1
2
3
4
5
var luke = {
jedi: true,
age: 28
};
var isJedi = luke.jedi;//使用 . 访问对象的属性

当属性名放在一个变量中时,使用下标 [] 的形式访问属性

1
2
3
4
5
6
7
8
9
10
11

var luke = {
jedi: true,
age: 28
};

function getProp(prop) {
return luke[prop];
}

var isJedi = getProp('jedi');

比较操作符

(1) 优先使用 === 和 !== 替换 == 和 !=

if条件判断语句,会遵循下面简单的规则,将表达式转换为布尔型的值:

  • Objects 等价于 true
  • Undefined 等价于 false
  • Null 等价于 false
  • Booleans 等价于 对应的布尔值
  • Numbers 如果是 0 或者 NaN 等价于 false,其他的等价于 true
  • Strings 除了空字符串 ‘’ 等价于 false,其他的等价于 true
1
2
3
4
if ([0]) {
// true
// Objects 等价于 true,数组也是对象
}

(2)短判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// bad
if (name !== '') {
// ...stuff...
}

// good
if (name) {
// ...stuff...
}

// bad
if (collection.length > 0) {
// ...stuff...
}

// good
if (collection.length) {
// ...stuff...
}

代码块

(1)单行代码块

1
if (test) return false;

(2)if-else

如果使用多行的if、else语句,else 应该跟在前一个 if 代码块的 } 后面。

1
2
3
4
5
6
if (test) {
thing1();
thing2();
} else {
thing3();
}

(3)for循环

1
2
3
4
5
6
var i,
length = 100;

for ( i = 0; i < length; i++ ) {
// 语句
}
1
2
3
4
var prop;
for ( prop in object ) {
// 语句
}

(4)代码块之间

在每个代码块后面保留一个空行,然后再写后面的语句。

1
2
3
4
5
6
7
8
9
var obj = {
foo: function() {
},

bar: function() {
}
};

return obj;

注释

(1)多行注释

/* … /注释内容包括描述,所有参数、返回值的具体类型和值.

sublime 下载 DocBlockr插件,帮助你创造你的代码注释

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* make() returns a new element
* based on the passed in tag name
*
* @param {String} tag
* @return {Element} element
*/
function make(tag) {

// ...stuff...

return element;
}

(2)单行注释

使用 javascript// 进行单行注释。注释时,在需要注释的语句上面新起一行,并在注释的上面保留一行空行。

1
2
3
// good
// is current tab
var active = true;
1
2
3
4
5
6
7
8
function getType() {
console.log('fetching type...');

// set the default type to 'no type'
var type = this._type || 'no type';

return type;
}

(3)FIXME和TODO前缀

使用前缀 FIXME 和 TODO 标注注释,方便开发人员快速理解需要解决的问题,或着提出解决的建议。

1
2
3
4
5
6
7
8
// 使用 FIXME 标注一个问题
function Calculator() {

// FIXME: shouldn't use a global here
total = 0;

return this;
}
1
2
3
4
5
6
7
8
// 使用 TODO 标注问题的解决方案
function Calculator() {

// TODO: total should be configurable by an options param
this.total = 0;

return this;
}

空格与Tab

(1)永远都不要混用空格和Tab;

(2)编辑中设置两个空格代替Tab;

(3)在 { 前放着一个空格;

(4)在 if 、while等语句的 ( 前放置一个空格。

(5)用空格将操作符隔开

1
var x = y + 5;

分号

需要在每个语句结束的时候都添加分号。

1
2
3
4
(function() {
var name = 'Skywalker';
return name;
})();

类型转换

(1)parseInt

使用parseInt转换成数字是需要标注出进制;

1
2
3
4
5
6
7
var inputValue = '4';

var val = Number(inputValue);

//or

var val = parseInt(inputValue, 10);

(2)布尔

1
2
3
4
5
6
7
var age = 0;

var hasAge = Boolean(age);

//or

var hasAge = !!age;

命名规范

(1) 避免使用单个字母进行命名。尽量描述清楚你的命名;

(2)使用全字母大写,下划线分隔的方式命名不变量。不要使用 const 关键词命名常量,ie不支持。

1
var MD5_key = 'ya23adfuodf4sd';

(3)使用驼峰命名法(camelCase),命名你的对象、方法和实例

1
2
var thisIsMyObject = {};
function thisIsMyFunction() {}

(4) 正则表达式变量使用 r 作为前缀

1
rNumber = /(\d)+/;

(5)使用帕斯卡命名法(PascalCase),命名你的构造函数或类

1
2
3
4
5
6
7
8

function User(options) {
this.name = options.name;
}

var good = new User({
name: 'yup'
});

(6)用下划线打头命名私有属性

1
this._firstName = 'Panda';

(7)用 _this 保存 this 的引用

1
2
3
4
5
6
function() {
var _this = this;
return function() {
console.log(_this);
};
}

jquery

(1)命名jQuery对象的变量时,用 $ 作为前缀

1
var $sidebar = $('.sidebar');

闭包

闭包会在它封闭的作用域中保存变量引用。其结果是,附带着会创建DOM对象的循环引用,从而产生内存泄露。 例如下面的代码:

1
2
3
4
5

// bad
function foo(element, a, b) {
element.onclick = function() { /* uses a and b */ };
}

在这个匿名方法中,闭包保存了element、a和b的引用,即使它不会用到element。我们构建了一个闭环,导致不会被垃圾清理回收。 在这种情况下,可以做如下重构:

1
2
3
4
5
6
7
8
// good
function foo(element, a, b) {
element.onclick = bar(a, b);
}

function bar(a, b) {
return function() { /* uses a and b */ };
}

delete

尽量避免使用delete删除键

1
2
3
4
5
6
7
8
9
// bad
Foo.prototype.dispose = function() {
delete this.property_;
};

// good
Foo.prototype.dispose = function() {
this.property_ = null;
};

使用JSON.parse替代eval

1
2
3
4
5
6
7
8
9
{
"name": "Alice",
"id": 31502,
"email": "looking_glass@example.com"
}

var userInfo = JSON.parse(feed);

var email = userInfo['email'];

不要使用with(){}

使用with语句速度要比不使用with语句的等价代码的速度慢得多,90%(或者更高比例)的with应用场景都可以用其他更好的方式代替。

参考

  • https://github.com/rwaldron/idiomatic.js/tree/master/translations/zh_CN (idiomatic代码风格)

  • https://github.com/hbxeagle/JavaScriptStyleGuide(代码规范指导)

  • https://github.com/adamlu/javascript-style-guide

  • http://javascript.ruanyifeng.com/grammar/style.html(阮一峰-代码风格)

《MongoDB常用命令&基本操作》

发表于 2016-05-22   |   分类于 数据库   |  

MongoDB安装

  • 1、下载安装(http://www.mongodb.org/downloads),安装目录:c:\MongoDB

  • 2、在c:\MongoDB下新建data\db目录

  • 3、命令行运行服务端程序mongod.exe:

C:>cd C:\MongoDB\bin
C:\MongoDB\bin>mongod.exe –dbpath=C:\MongoDB\data\db –directoryperdb –logpath=C:\MongoDB\data\logs –logappend

  • 4、新建命令行,运行客户端程序mongo.exe (不要关闭mongod.exe这个命令行窗口)

MongoDB概念

  • databases: 数据库;

  • collections:表;(colloections组成了databases)

  • documents:行;(documents组成了collections)

MongoDB没有新建数据库的命令,只要进行insert或其它操作,MongoDB就会自动帮你建立数据库和collection。当查询一个不存在的collection时也不会出错,Mongo会认为那是一个空的collection。

一个对象被插入到数据库中时,如果它没有ID,会自动生成一个“_id”字段,为12字节(24位)16进制数。
当然如果插入文档不带_id,则系统会帮你自动创建一个,如果自己指定了就用自己指定的。

命名

字段名限制:不能以“$”开头;不能包含“.”;“_id”是系统保留的字段,但用户可以自己储存唯一性的数据在字段中。

客户端语法:

  • show dbs // 列出所有数据库

  • use memo // 使用数据库memo。即使这个数据库不存在也可以执行,但该数据库不会立刻被新建,要等到执行了insert等的操作时,才会建立这个数据库。

  • show collections // 列出当前数据库的collections(当前数据库下的表)

  • db // 显示当前数据库

  • show users // 列出用户

  • db.表名.insert({name:”jack”,addr:”fujian”});//向表插入字段

  • db.表名.find();//查询-作用相当于select * from 表名

查询数据

  • $lt ->less then 小于

  • $lte ->less than and equal 不大于

  • $lt ->less then 小于

  • $gt ->greater then 大于

  • $gte ->greater then and equal 不小于)

  • $ne ->not equal 不等于

db.foo.find() // select from foo
db.foo.find().limit(10) // select
from foo limit 10
db.foo.find().sort({x:1}) // select from foo order by x asc 1:升序 -1:降序
db.foo.find().sort({x:1}).skip(5).limit(10) // select
from foo order by x asc limit 5, 10
db.foo.find({x:10}) // select from foo where x = 10
db.foo.find({x: {$lt:10}}) // select
from foo where x <= 10
db.foo.find({}, {y:true}) // select y from foo
一些SQL不能做的,MongoDB也可以做:
db.foo.find({“address.city”:”gz”}) // 搜索嵌套文档address中city值为gz的记录
db.foo.find({likes:”math”}) // 搜索数组
db.foo.ensureIndex({“address.city”:1}) // 在嵌套文档的字段上建索引

更新数据

  • db.foo.update({},{}) //更新对象,第一个参数是查询对象,第二个是替代的,可以在第二个对象里指定更新哪些字段,要使用$set。”$set”用来指定一个键的值。如果这个键不存在,则创建它,如果存在则更新

    db.foo.update({name:”jack”},{$set:{name:”zky”}});

    删除记录

  • db.foo.remove({});//第一个参数要删除的记录,只删除匹配的对象

删除数据库表

  • db.foo.drop();//删除foo这个表

删除当前数据库

  • db.dropDatebase();

字段操作

  • $push:增加数组元素;

db.foo.update({name:’xxx’},{$push:{age:”10”}});

  • $pop:减少数组元素;

  • $or,$and和$exists

db.people.find({“name”:”yhb”,$or:[{“age”:18},{“age”:20}]}) //找出name为yhb,age为18或者20的;

db.people.find({“addr”:{$exists:false}}) //找出不存在addr field

db.people.find({$or:[{“name”:”yhb”,”age”:18},{“name”:”lwy”,”age”:19}]})
//找出people中name为yhb,年龄为18,或者name为lwy,name为19的

小结

如果是or: ->> {$or:[{条件1},{条件2}]}
如果是and: ->> {$and:[{条件1},{条件2}]}

索引

1
2
3
4
5
6

db.foo.ensureIndex({productid:1}) // 在productid上建立普通索引

db.foo.ensureIndex({district:1, plate:1}) // 多字段索引

db.foo.ensureIndex({productid:1}, {unique:true}) // 唯一索引

备份与恢复

(1)、备份工具 mongodump;

命令行执行:

1
c:\MongoDB\bin\mongodump.exe --help 查看帮助命令

示例-备份数据库:

命令行执行:

1
c:\MongoDB\bin\mongodump -o ../data/backup/test (备份数据到backuo/test目录下)

(2)、恢复数据 mongorestore:

示例-恢复数据库:

命令行执行:

1
c:\MongoDB\bin\mongorestore -d test -c t002 c:\MongoDB\data\backup\test\t002.bson

数据 导入& 导出

每次只导入\导出一个表,json或csv格式;

(1)、导入 mongoimport

示例:

1
mongoimport -d test -c t004 drop c:\MongoDB\data\backup\test_t001.json

(2)、导出 mongoexport

示例:

1
mongoexport -d test -c t001 -o c:\MongoDB\data\backup\test_t001.json

安全与认证

MongoDB本身是没有开启安全性检查的,在开启之前,需要至少一个管理员账号。

开启安全性检查,只有数据库认证用户才能执行读或写的操作。

增加用户

注:V3版本mongoDB已经不再使用addUser,而是采用了db.createUser;

示例:

1
2
3
4
5
6
7
8
9
use admin

db.createUser(
{
user: "accountUser",
pwd: "password",
roles: [ "readWrite", "dbAdmin" ]
}
)

对用户开启安全认证

1
2
3
4
5
6
7
8
9
10
11

1、重启客户端与服务器;

2、然后切换到客户端命令行:


use admin

db.auth("root","root")

这样,某种权限的用户只有切换到对应的数据库才能执行某些操作。

参考

http://www.open-open.com/lib/view/open1335003001358.html

http://www.runoob.com/mongodb/mongodb-tutorial.html

MongoDB可视化客户端工具

robomongo https://robomongo.org/

《js闭包之块级作用域》

发表于 2016-05-07   |   分类于 javascript   |  

在js中我们应该尽量避免使用全局变量和全局函数,以防止发生命名冲突,那么要如何避免呢?js中有一个闭包的概念,现在我们使用闭包中块级作用域来讲解,这也是闭包中最重要的概念。

js本身是不支持像C/C#/java…这样的语言中有块级作用域的,即在语法块中定义的变量,在语法块外面是无法访问到的。

那么,js要怎么去模拟像C这样的块级作用域呢?我们先来看一个栗子:

例如:

1
2
3
4
5
6
7
8
9
 test();
function test() {
(function(){
for(var i=0;i<3;i++){
console.log(i);//这个时候i就在**匿名函数里**面有效了,在**匿名函数外**无效,在匿名函数外,i就被销毁了
}
})();
console.log(i);//输出:undefined
}

在函数内部创建一个匿名函数,就相当于在函数内部创建了一个私有作用域,这对于在较大/多人开发的项目中是很好用的,这样每个程序员能在自己的功能函数中使用自己的变量不至于混乱、冲突。

《AngularJS实例-01》

发表于 2016-05-05   |   分类于 javascript   |  

初探四大特性

angular四大核心特性:

  • MVC

  • 模块化

  • 指令系统(ng特有)

  • 双向数据绑定(ng特有)

MVC

首先,来了解ng的第一个特性:MVC

Model:数据模型层;

View :视图层,负责展示,我们眼睛能看到的那些东西都属于视图;

Controller:业务逻辑和控制逻辑放的地方;

MVC的好处:

职责清晰,代码模块化,便于复用,例如像Model、View这种东西,能够经常的复用,因为很多地方的界面展示是一模一样的;

MVC属于架构模式,它是观察者模式、策略模式、组合模式的组合;

在ng的语境下,你要很熟悉代码中哪些是Model,哪些是View,哪些是Controller;

例如:有这么一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<html ng-app>
<head>
<head>
<meta charset="utf-8">
</head>
<title></title>
<script type="text/javascript" src="http://cdn.staticfile.org/angular.js/1.3.0-beta.13/angular.js"></script>
</head>
<body>
<!--Controller: 给div放置一个控制器testController -->
<div ng-controller="testController">
<!-- Model、View:数据模型{{}}、View:<p>标签 -->
<p>{{greeting.text}}</p>
</div>

<script type="text/javascript">
function testController($scope) {//控制器,负责业务逻辑与控制逻辑
$scope.greeting={
text:'Hello ng'
};
}
</script>

</body>
</html>

模块化

上面的代码中我们看到,我们定义了一个全局的函数testController,这显然不好。现在我们对这个function进行改进:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<html ng-app="test_ng">
<head>
<head>
<meta charset="utf-8">
</head>
<title></title>
<script type="text/javascript" src="http://cdn.staticfile.org/angular.js/1.3.0-beta.13/angular.js"></script>
</head>
<body>
<!--Controller: 给div放置一个控制器testController -->
<div ng-controller="testController">
<!-- Model、View:数据模型{{}}、View:<p>标签 -->
<p>{{greeting.text}}</p>
</div>

<script type="text/javascript">
var myModule=angular.module("test_ng",[]);//定义一个模块
//用模块生成一个控制器testController
myModule.controller("testController",['$scope',
function testng($scope){//第一个参数$scope,在告诉ng,请再testng里注入$scope————即,依赖注入
$scope.greeting={
text:'Hello ng'
};
}
]);
</script>

</body>
</html>

下面我们具体看一下ng的模块化Module概念,并且在Module下有很多的概念。

需要注意的是:angular是从模块开始的,你在做东西的时候,需要首先想到Module,只有在Module下你才能使用ng的一些概念;

我们来看一张图:

alt text

指令系统

指令系统,是angular最吸引人的部分。

首先上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<html ng-app="MyNg">
<head>
<head>
<meta charset="utf-8">
</head>
<title></title>
<script type="text/javascript" src="http://cdn.staticfile.org/angular.js/1.3.0-beta.13/angular.js"></script>
</head>
<body>
<!-- 浏览器不是识别hello标签 -->
<hello></hello>

<script type="text/javascript">
var myModule=angular.module("MyNg",[]);//定义一个模块
// 为了让浏览器解析hello这个自定义标签,我们需要借助ng的directive特性
myModule.directive("hello",function(){//hello 对应指令名称,即<hello></hello>标签名
return {
restrict:'E',
template:'<div>Hello angular</div>',//hello标签被替换为该模板
replace:true
}
});
</script>

</body>
</html>

这就是angular的指令系统,有了这个指令系统我们能够做很多事情,比如我们将一个具体的指令进行封装,在其他地方我们就能够进行调用。

当然,指令不仅仅是用来使用自定义标签,它还有其他神奇的功能,例如:

ng-app:这是一个ng指令,ng-app=”MyNg”,表示告诉angular去使用MyNg这个模块。ng-app这个指令可以看成是一个angular的启动指令,或是入口,相当于C语言中得Main()函数。故,在任意一个单页中只能出现一次ng-app。

双向数据绑定

在单向数据绑定中,通常是把模板和数据模型都写好,这个数据模型可能来自后端数据,然后将模板与数据模型合并输出到视图进行展示,这样有一个缺点:即一旦模板与数据模型写好之后,View就无法改变了,如果要改变,那么只能重新再来一次。

在JqueryUi\Backbone\Flex这样的前端框架中都是这么处理的,这样是否优雅呢?

Angular看到了,就不喜欢了,它就弄了一个双向数据绑定,它认为:

  • 视图View和数据Model是对应的,当View上面的内容发生变化的时候,Model也发生变化;当Model发生变化,View会自动更新;——这样的思路需要借助一种事件机制;

例如:在HTML中,表单常常会发生变化,因为表单是用来搜集用户的输入,故常常容易发生变化。在angular中,如果数据/表单发生变化,就会同步到Model上面;

下面看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<html ng-app>
<head>
<head>
<meta charset="utf-8">
<title></title>
<script type="text/javascript" src="http://cdn.staticfile.org/angular.js/1.3.0-beta.13/angular.js"></script>
</head>
<body>

<form>
<!-- 在input中绑定一个ng-model -->
<input type="text" placeholder="请输入" ng-model="greeting.text">
<!-- 在p标签中去获取值,双花括弧在angular中表示取值 -->
<p>{{greeting.text}}</p>
</form>

</body>
</html>

在上面这段代码中,你无需写任务angular代码就能实现一个简单的双向数据绑定。

思考

  • 为什么其他的前端框架不实现双向数据绑定?
  • 双向数据绑定有什么缺点?

搭建开发、调式、测试工具

前端开发中需要什么样的开发环境?

  • 代码编辑工具:sublime编辑器
  • 断点调式工具:chrome 插件Batarang
  • 版本管理工具: git,在从github上克隆项目前需要先fork(相当于建立一个自己的分支),然后再Clone.
  • 代码合并和混淆工具:gulp、grunt
  • 依赖管理工具:bower(自动安装依赖组件、组件之间的依赖检测、版本兼容性自动检测)
  • 轻量级Server—http-server(一款简单的http-server,能将电脑上得任意一个目录变成在浏览器上能访问的;它基于NodeJS的HTTP接口,通过npm 安装,主要用来模拟与后端交互的数据)
  • 单元测试工具:karma配合jasmine(通过npm安装)。jasmine有四个核心概念:分组、用例、期望、匹配,分别对应jasmine四种函数。

    (1)describe(string,function)分组,一组测试用例;

    (2)it(string,function)表示测试用例

    (3)expect(expression)表示期望expression这个表达式具有某个值或者具有某种行为;

    (4)to*(arg)表示匹配;

  • 集成测试工具:Proctactor-专门为Angular应用而设计;
    Proctactor基于webDriverJs;
    利用webDriverJS,能借助于NodeJS直接调用浏览器的接口;
    https://github.com/angular/protractor
    https://code.google.com/p/selenium/wiki/WebDriverJs
    angular官方提供angular-phonecat的例子做为测试;

最后我们来看一个完整的项目目录结构实例:

alt text

《楚亡·从项羽到韩信》——重新梳理楚汉之争01___项羽为什么失败

发表于 2016-04-24   |   分类于 阅读   |  

最近看了一本讲历史的文学书,书名《楚亡·从项羽到韩信》,作者李开元。为什么会去看这本书呢?说来也凑巧,在我上高中的时候偶然间在电视上看到《秦时明月》这部国产动漫,我本身对历史记得不太牢,很多历史在读书的时候杂揉在一起,于是就没有太多印象了,但是看这部动漫的时候,诶~感觉还不错,于是就看到现在。哈哈。但我也不是什么秦迷啦~我感觉自己只是对这段历史是想了解的。虽然,这部动漫很多剧情是虚构的。

OK,既然说要“重新梳理楚汉之争”,那么我们就来聊聊楚汉之争。

首先,想问问楚汉争霸中都有哪些主要人物?(不得不赞叹,历史是为天下豪杰写的)项羽、刘邦肯定的了,然后你从这本书名还可以找到韩信这个人,还有呢?————————我们从项羽的楚军和刘邦的汉军来找找这些人。我们根据这几个主要人物来讲楚汉之争。

从两位头头开始吧。

项羽。这个人很厉害,西楚霸王,在项羽败亡之前吟唱的《垓(gai)下歌》中,他都这么自夸自己:‘力拔山兮气盖世,时不利兮骓(zhui,’马’)不逝.骓不逝兮可奈何,虞兮虞兮奈若何!’在开头第一句,他就说了’力拔山兮气盖世’,意思是“老子是盖世英雄,力大无穷能开山”,但是项羽的厉害不止是力气大。——————谁能说说项羽还有哪里厉害的地方呢?取了一位美貌天仙的虞姬?还是他在乌江自刎时那句豪言壮语?

————————项羽是位军人,身上满满军人的血气,自幼跟着叔父项梁打战,在对秦战争中从未有过败仗,多次以少胜多,全歼秦军,在大规模野战中更是得心应手,有如神助。例如:在一次单刀中,项羽骑着坐骑乌骓,对方将领看到项羽瞪了自己一眼,立马吓得魂不守舍,回到阵营不敢出战。在几个著名的战役——如“巨鹿之战大破秦军,项羽破斧沉舟以5万楚军大胜40万秦军,从此确立项羽在反秦联盟中得地位;彭城之战大败刘邦,使刘邦从此不敢与项羽正面交锋”,可以看得出项羽在大战的时候是很有自信的。

但是,唯有这一次——垓下之战,让项羽犹如困在牢笼中感到很不舒服,项羽对部下直言:“这是上天要亡我,而非征战之过错啊!”。项羽很是无奈啊~对了,我们前面说到过项羽在临死前说了一句豪言壮语,这句话也能体现出他的勇气。项羽站在乌江边,对着迎面而来的吕马童喊道:“我听说汉王赏金千金,封邑(yi)万户,要我的头,我成全你!”说完挥剑自刎,年仅31岁。其实,在乌江边上,早有船家在等候项羽过江,但是项羽拒绝了,他说道:“既然天要亡我,又何必渡江!遥想当年八千江东子弟随我渡江西进,如今无一人生还,纵使江东父老兄弟怜爱不弃,仍以我为王,我又有何脸面去见他们?纵使江东父老兄弟默默不言语,我项籍岂能问心无愧啊!”。

哈哈~英雄,往往因为几句气壮山河的话语让世人铭记。。。

前面我们说了项羽的厉害之处,有人会问,项羽这么屌,最终为什么会败呢?

我们来说说项羽失败的几处不完全原因:

在荥(xing)阳之战中,两军对峙,项羽一心想引诱刘邦出城会战,刘邦可不干,但是这么一直对峙下去也不是办法,就对项羽喊话:“我刘季宁愿斗志,不能斗力。”,接着高声宣读项羽的十大罪状,我列举主要几条:

  • 第一条:项羽违背与刘邦定下的约定:“先入关中者作关中王”;
  • 第二条:违背与楚怀王“进入秦国不得暴掠”的约定,项羽进入关中后,烧毁了秦国的宫殿,破坏秦始皇墓地上的建筑,搜刮财宝据为己有;
  • 第三条:暴虐欺诈,在新安坑杀20万秦军将士;
  • 第四条:驱逐义帝楚怀王,并派人秘密杀害义帝于江南;

从上面几条来看,我们能看到项羽的性格缺失:不守信、不敬上、大逆无道、功高自傲;

史太公司马迁在项羽败亡的百年之后,曾总结过项羽失败的原因:

  • 战略地理错误:背弃关中,定都彭城;
  • 政治伦理错误:驱逐楚怀王,以下克上;
  • 居功自傲:固执己见,不向历史学习;
  • 以武力经营天下;

几千年以来,历史学家们都对项羽的失败做出了不同的评价,可是于己所见,综上项羽所做过的事,可以见得项羽在楚汉之争中渐渐失去了人心,在垓下之战后诸侯国纷纷离他而去,项羽最终失去了帮手,孤军奋战。可见“失人心者失天下”、“水能载舟,亦能覆舟”。

当然,从用人这个角度看,也是项羽失败的原因之一:

项羽身边的文武官,大都是由项羽的家族人士来担当,例如项梁、项伯、项庄,亚父范增,发小龙且等寥寥数人;

《史记》中记载陈平的一句话,陈平向刘邦献计说:“项王的忠臣,只有范增、钟离眛、龙且、周殷几个人,如果你能用万金买通说客,去离间他们的君臣关系,再出兵攻打,项王必败。”刘邦遂用此计。项羽果然对忠臣疑忌,致使范增、钟离眛、周殷等忠臣纷纷离去,几乎只有龙且没有离开。”

由此可见,人才计划在一个国家或是企业尤为重要!

那么,刘邦为什么能胜?这个我们放到下期来讲~ 回见!

《分成两半的子爵》

发表于 2016-04-09   |   分类于 阅读   |  

卡尔维诺经典,《分成两半的子爵》一书中有这么两段话让我印象深刻:

“如果能够将一切东西都一劈为二的话,那么人人都可以摆脱他那愚蠢的完整概念的束缚了。我原来是完整的人。那时什么东西在我看来都是自然而混乱的,像空气一样简单。我以为什么都已看清,其实只看到皮毛而已。假如你将变成你自己的一半的话,孩子,我祝愿你如此,你便会了解用整个头脑的普通智力所不能了解的东西。你虽然失去了你自己和世界的一半,但是留下的这一半将是千倍的深刻和珍贵。你也将会愿意一切东西都如你所想象的那样变成半个,因为美好、智慧、正义只存在于被破坏之后。”


“这就是做半个人的好处:理解世界上每个人由于自我不完整而感到的痛苦,理解每一事物由于自身不完全而形成的缺陷。我过去时完整的,那时我还不明白这些道理,我走在遍地的痛苦和伤痕之中却视而不见、充耳不闻,一个完整的人不敢相信这样的事实。”

《分成两半的子爵》讲述的人物不仅仅是在战场上被土耳其人用炮火炸成两半的子爵,还有造绞刑架的工匠,子爵的奶妈,英格兰大夫,他们在这场故事中的表现如现实中存在的人物一样,讲述的是一场关于道德价值的故事。

HTML5缓存的那些事

发表于 2016-03-26   |   分类于 性能优化   |  

关于存储

说道存储,你可能会想到这是服务器端的一种设置。

服务器端的存储介质大体上分为4种:

  • cache:缓存,它可以让从数据库、磁盘上输出的东西/数据放置在缓存里,从而减少数据库或是磁盘的读取与写入(IO)操作;
  • 磁盘文件:如,我们常常会将图片、视频等文件存放在磁盘上;
  • 数据库:mySql\mongoDB…关系\非关系数据库;
  • 内存:通常放置频繁要使用到的东西,能够提高读取效率;缓存(cache)也是存放在内存里的;

HTML的存储-cookies

在HTML5出生之前,通常在浏览器(客户端)使用cookies来存储客户端的内容;

cookies的特点:

  • 每次的http请求头中,都会带有cookies——缺点;
  • 每个域名只能存储4K大小的cookies;
  • 主域名污染:如果我们使用cookies存储主域名的东西,那么子域名下得Http请求都会带上主域名的东西;

如果关联上网络,那么将带来安全问题。

所以,通常我们会使用cookies用在如购物车、身份验证等问题上。

下面,我们来看一下百度首页的cookies在浏览器端的一个存储形态:

如图:

alt text

HTTP这一列,如果在setCookie的时候,这里就会打钩,这与HTTPOnly相关。

HTTPOnly:

如果把HTTPOnly设置为true,那么cookies只能被server服务器端来读取或是修改,客户端没有权限进行读取和修改。例如,我们在进行身份验证的时候,就可以使用这个。

Secure:与安全相关,如果设置了,那么请求只能是来自HTTP加密请求。

HTML的存储-UserData

  • 只有IE支持,有微软提供API,但不符合W3C标准;
  • 存储在XML文件中;

HTML5的存储

针对以上问题,HTML5的出现,需要解决以下问题:

  • 解决4K的大小问题;
  • 解决请求头常带存储信息的问题;
  • 解决关系型存储的问题;
  • 跨浏览器平台问题;

##HTML5存储形式

  • 本地存储——localstorage \sessionstorage
  • 离线缓存——application cache
  • IndexedDB、Web SQL

本地存储

API:

localstorage 、sessionstorage

存储形式:

key–>value

过期时间:

localstorage:永久存储,永不失效,除非手动删除
sessionstorage:重新打开页面,或是关闭浏览器,sessionstorage才会消失;

存储大小:
每个域名能存5M;

支持情况:

IE8+,safari3.2+,chrome,firefox等主流浏览器都支持;

使用方法——localstorage\sessionstorage

主要涉及到5个方法:

  • getItem:获取localstorage\sessionstorage
  • setItem:设置localstorage\sessionstorage
  • removeItem:移除localstorage\sessionstorage
  • key:获取某一个位置上的key值,按值从0开始索引;
  • clear:全部清除localstorage\sessionstorage

例如:我们打开www.baidu.com

在控制台Console输出面板,输入:

localStorage.setItem("test1","test");
那么在Resources面板的Local Storage下,将出现Key=test1,value=test的记录

localStorage.getItem("test1"); //输出test

localStorage.key(0);//输出BDSUGSTORED

sessionstorage的API与localstorage一样,但是你要注意一点:

sessionStorage需要在浏览器关闭或是重新打开页面,才会消失;

本地存储可以存储什么?

  • 数组(需要将其序列化为字符串才能存储);
  • json数据——将其转化为字符串存储;
  • 图片
  • 脚本、样式文件:通过ajax

只要能被转化为字符串的数据,都能被localstorage存储;

本地存储如何存储图片

先来看一段代码:

var src="demo.jpg";

function set(key){
    var img=document.createElement('img');

    img.addEventListener("load",function(){
        //创建一个canvas
        var imgCanvas=document.createElement("canvas"),
        imgContext=imgCanvas.getContext("2d");
        //确保canvas元素的大小和图片的尺寸一致
        imgCanvas.width=this.width;
        imgCanvas.height=this.height;
        //渲染图片到canvas中,使用canvas的drawImage()方法
        imgContext.drawImage(this,0,0,this.width,this.height);
        //用canvas的dataUrl的形式取出图片,imgAsDataURL是一个base64的字符串
        var imgAsDataURL=imgCanvas.toDataURL("image/png");
        //保存到本地存储中
        //使用try-catch()查看是否支持localstorage
        try{
            localStorage.setItem(key,imgAsDataURL);//将取出的图片存放到localStorage 
        }
        catch(e) {
         console.log("Storage failed:"+e);//存储失败
        }

    },false);
    img.src=src;
}    
function get(key) {//从本地缓存获取图片并且渲染
var srcStr=localStorage.getItem(key);//从localStorage中取出图片
var imgObj=document.createElement('img');//创建一个img标签
imgObj.src=srcStr;
document.body.appendChild(imgObj);
}

    注释:
    (1)、这个比较适合用在不常更改的图片,但是如果图片的base64大小比较大的话,将比较耗费localStorage的资源;

    (2)、canvas有一个安全策略的问题:如果图片和你本身请求的域名不在同一个域名下,浏览器会报出一个安全问题,这个时候我们要给我们的服务器加一个“允许跨域”访问的响应头————Access Orign=*,这样来保证你的图片可进行跨域被canvas来画;

HTML5本地存储需要注意的:

  • 使用前判断浏览器是否支持localStorage;(IOS浏览器在无痕模式浏览下,是无法打开localStorage;以及,其他奇葩浏览器,在存储localstorage的时候报错)

做法:根据前面代码,我们在检查是否支持,先进行setItem()一次,然后对setItem进行异常捕获;

  • 写数据的时候,需要异常处理,避免超出容量抛出错误;
    localStorage本身只有5M;

  • 避免把敏感的信息存入localStorage;

  • key的唯一性;重复写,将会覆盖之前的key;

HTML5本地存储使用限制:

  • 存储更新策略,过期控制:localStorage是永不过期的,业务上如果想实现一些过期策略,需要在localStorage上加一层处理过期的机制;
  • 各个子域名之间不能共享存储数据;(借助H5的postMessage()这个API做一些跨域上得处理)
  • 超出存储大小之后如何存储——使用一些如LRU、FIFO的算法去淘汰一些旧的数据;
  • server端如何取到数据——使用post/get参数

处理过期控制

先来看一下代码:

function set(key,y){
    var curTime=new Date().getTime();
    //存储一个当时存储时候的时间
    localStorage.setItem(key,JSON.stringify({data:v,time:curTime}));

}
function get(key,exp) {
    var data=localStorage.getItem(key);
    var dataObj=JSON.parse(data);
    if(new Date().getTime()-dataObj.time>exp) {//get出来的时间减去当时存储的时间大于过期时间,那么就认为过期
       console.log("过期");
    }else {
    //否则,返回值
     console.log("data="+dataObj.data);
    }
}

本地存储使用场景

  • 本地数据存储,减少网络传输
  • 在弱网络的环境下,会发生高延迟,低带宽,应该尽量把数据(如脚本、样式)本地化;

我们来看一张图,显示的是本地存储和网络拉取耗时的对比:

alt text

IndexedDB

概念

IndexedDB,是一种能做浏览器中持久地存储结构化数据的数据库,并且为web应用提供了丰富的查询能力;

支持情况

chrome11+\opera不支持\firefox 4+\IE 10+,移动端浏览器支持能力弱

存储结构

IndexedDB是按域名分配独立空间,一个独立域名下可以创建多个数据库,每个数据库可以创建对个对象存储空间(表/table),一个对象存储空间可以存储多个对象数据;

如图:

alt text

使用IndexedDB实现离线数据库

这里我们主要从IndexedDB 的四大功能入手:

  • 增删改
  • 事务处理
  • 游标
  • 索引

下面我们通过一段代码来讲解,请关注里面的注释:

 <!DOCTYPE html>
<html>

<div class="form-group">
    <label for="name">姓名:</label><input type="text" id="name" value="" />    
    <label for="phone">电话:</label><input type="text" id="phone" value="" />    
    <label for="address">地址:</label><input type="text" id="address" value="" />    
    <input type="button" id="seletBtn" value="查询" />    
    <input type="button" id="add" value="添加" />    
    <input type="button" id="deleteDB" value="删除数据库" />    
</div>


<script type="text/javascript">
    var db;
    var arrayKey=[];
    var openRequest;
    var lastCursor;
    var indexedDB=window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;//indexedDB在不同的浏览器下不同

    var dbName="person";//数据库名称
    var tableName="testTable";//表名称
    function init() {
        openRequest=indexedDB.open(dbName);//页面加载时先打开一个DB,如果该DB存在,则打开;不存在,则新建

        //触发事件——当一个“新的数据库”被创建或者数据库的“版本号”被更改时触发
        openRequest.onupgradeneeded=function(e){
            console.log("onupgradeneeded");
            var thisDb=e.target.result;
            console.log(thisDb.version);

            //检查这个数据库中是否包含我们要查找的表
            if(!thisDb.objectStoreNames.contains(tableName)){
                //不包含——创建一个表
                console.log("需要创建一个objectStore");
                //keyPath:主键,autoIncrement:主键自增
                var objectStore=thisDb.createObjectStore(tableName,{keyPath:"id",autoIncrement:true});
                //创建表的时候,指定哪些字段是能被索引的
                objectStore.createIndex("name","name",{unique:false});//创建索引
                objectStore.createIndex("phone","phone",{unique:false});
            }

        }
        //触发事件——成功打开一个数据库时触发
        openRequest.onsuccess=function(e){
            db=e.target.result;
            console.log(db.version);
            db.onerror=function(event){
                alert("数据库错误:"+event.target.errorCode);
                console.dir(event.target);
            };
            //判断该数据库中有没有这个表
            if(db.objectStoreNames.contains(tableName)){
                //存在这个表
                console.log("包含表:"+tableName);
                //通过事物机制操作一个表的读写,从而保证数据的一致性和可靠性
                var transaction=db.transaction([tableName],"readwrite");
                //事物的事件
                transaction.oncomplete=function(event){
                    console.log("完成");
                };

                transaction.onerror=function(event){
                    console.dir(event);
                };
                var objectStore=transaction.objectStore(tableName);//通过事物获取表中一个objectStore对象,即表的对象

                //遍历表的记录——游标-openCursor,这是indexedDb的重点
                objectStore.openCursor().onsuccess=function(event){
                    var cursor=event.target.result;
                    if(cursor){
                        console.log(cursor.key);
                        console.dir(cursor.value);
                        render({key:cursor.key,name:cursor.value["name"],phone:cursor.value["phone"],address:cursor.value["address"]});
                        lastCursor=cursor.key;//如果不设置lastCursor,那么游标默认是下一条接着下一条来遍历;设置了lastCursor,游标将循环遍历
                        cursor.continue();
                    }else {
                        console.log("请使用游标来搞定");
                    }
                };
                objectStore.openCursor().onerror=function(event){
                    console.dir(event);
                };

            }

        }


        //添加新记录
        document.querySelector("#add").addEventListener("click",function(){
            var name=document.querySelector("#name").value();
            var phone=document.querySelector("#phone").value();
            var address=document.querySelector("address").value();
            var person={"name":name,"phone":phone,"address":address};//设置对象
            //通过事务——操作表
            var transaction=db.transaction([tableName],"readwrite");
            transaction.oncomplete=function(event){
                console.log("事务处理完成");
            };
            transaction.onerror=function(event){
                console.dir(event);
            };
            var objectStore=transaction.objectStore(tableName);//创建一个表对象
            objectStore.add(person);//添加对象到表中——add()
            //将新增的记录显示处理
            objectStore.openCursor().onsuccess=function(event){
                cursor=event.target.result;
                var key;
                if(lastCursor==null){
                    key=cursor.key;
                    lastCursor=key;
                }else {
                    key=++lastCursor;
                }
                render({key:key,name:name,phone:phone,address:address});
                console.log("成功添加新记录:"+key);
                console.dir(person);
            }

        });

        //删除指定ID
        function deleteRecord(id){
            var transaction=db.transaction([tableName],"readwrite");
            transaction.oncomplete=function(event){
                console.log("事务处理完成");
            };
            transaction.onerror=function(event){
                console.dir(event);
            };
            var objectStore=transaction.objectStore(tableName);
            var removeKey=parseInt(id);
            var getRequest=objectStore.get(removeKey);//获取索引值---get()
            getRequest.onsuccess=function(e){
                var result=getRequest.result;
                console.dir(result);
            }
            var request=objectStore.delete(removeKey);//删除——delete
            request.onsuccess=function(e){
                console.log("删除成功");
            };
            request.onerror=function(e){
                console.log("删除错误"+e);
            };
            //隐藏删除的DOM
            document.getElementById(removeKey).style.display="none";
        }

        //查询记录
        document.querySelector("#seletBtn").addEventListener("click",function(){
            var curName=document.getElementById("selname").value;
            var transaction=db.transaction([tableName],"readwrite");
            transaction.oncomplete=function(event){
                console.log("事务处理完成");
            };
            transaction.onerror=function(event){
                console.dir(event);
            };
            var objectStore=transaction.objectStore(tableName);
            var boundKeyRange=IDBKeyRange.only(curName);//生成一个表示范围的Range对象---IDBKeyRange,有4个方法,only\lowerBound\upperBound\bound
            objectStore.index("name").openCursor(boundKeyRange).onsuccess=function(event){
                //从indexedDb中找到name
                var cursor=event.target.result;
                if(!cursor){
                    return;
                }
                var rowData=cursor.value;
                console.log(rowData);
                document.getElementById('content').innerHTML="";
                render({key:cursor.value.id,name:cursor.value["name"],phone:cursor.value["phone"],address:cursor.value["address"]});
                cursor.continue();
            };

        });
        //删除数据库
        document.querySelector("#deleteDB").addEventListener("click",function(){
            //使用deleteDatabase()
            var deleteDB=indexedDB.deleteDatabase(dbName);
            var content=document.querySelector("#content");
            while(content.firstChild){
                content.removeChild(content.firstChild);
            }
            deleteDB.onsuccess=function(event){
                console.log("删除成功");
            };
            deleteDB.onerror=function(event){
                console.dir(event.target);
            };
        });

        //渲染
        function render(opt){
            var child_node = document.createElement("div");
            var child_node_child1 = document.createElement("div");
            var child_node_child2 = document.createElement("div");
            var child_node_child3 = document.createElement("div");
            var child_node_child4 = document.createElement("div");
            child_node_child1.setAttribute("class","table_child");
            child_node_child2.setAttribute("class","table_child");
            child_node_child3.setAttribute("class","table_child");
            child_node_child4.setAttribute("class","table_child");
            child_node_child1.setAttribute("style","float:left");
            child_node_child2.setAttribute("style","float:left");
            child_node_child3.setAttribute("style","float:left");
            child_node_child4.setAttribute("style","float:left");
            child_node_child1.innerHTML = name;
            child_node_child2.innerHTML = opt.phone;
            child_node_child3.innerHTML = opt.address;
            child_node_child4.innerHTML = "<input type='button' value='删除'>"
            child_node.appendChild(child_node_child1);
            child_node.appendChild(child_node_child2);
            child_node.appendChild(child_node_child3);
            child_node.appendChild(child_node_child4);
            child_node.setAttribute("class","table_tr");
            child_node.setAttribute("id",opt.key);
            var content = document.getElementById('content');
            content.appendChild(child_node)

        }

    }
</script>

</body>
</html>

离线缓存——application Cache

何为离线缓存

它是能让web应用在离线的情况下继续使用,通过一个叫manifest的文件指明需要缓存的资源;你可以通过navigator.online检测是否在线;

原理

如图:

alt text

解释:

(1)用户通过浏览器(browser)去访问应用,首先检测浏览器是否有一个叫做“App cache”的东西存在,如果存在,则从中检索出app cache所要缓存的list,然后把资源(缓存在浏览器中)拉取出来,返回给用户;

(2)在访问的同时,会检查server上一个叫做manifest的文件,如果该文件有更新,就把manifest指定的文件从server端重新拉取一次,然后把这些缓存在浏览器中,并更新相应的app cache文件;如果manifest这个文件没有更新,那么就啥也不做。

从上图,我们总结2点:

  • 缓存机制的改变,会更新app cache.但是,用户访问,会返回上一次的结果。这样一来,会有一个麻烦,即如果你的业务发生更改,你就需要去更新一次manifest。

注意:更改完,第一次是不生效的,只有第二次刷新才会生效;

  • 如果有一个文件要更新,你就要去更新manifest,而更新manifest文件,它会把server上的文件全部重新拉取一次,而非只是拉取你需要更改的那个文件,这就会造成损耗;

浏览器支持情况

safari on ios 3.2+\android 1,5+\window phone 9+

应用

例子:cache.appcache

CACHE MANIFEST

#version 1.0

CACHE:

#需要缓存的文件
/css/a.css
/js/a/js
/images/a.png

NETWORK:

#每次重新拉取的文件

*

FALLBACK

#离线状况下代替的文件

/404.html

在页面上引入manifest文件:

<html manifest="cache.appcache">

在服务器添加mime-type text/cache-manifest

如果在服务器上添加:

找到你的xampp/apache/conf目录,找到mime.types文件,在最后面添加一条记录:

text/cache-manifest appcache (appcache是后缀名,你可以选择其他的)

我们来看一个例子:

<html lang="en" manifest="cache.appcache">
  <head>
    <meat charset="utf-8" />
  </head>
  <body>
     <h1><demo1/h1>

  <script type="text/javascript">
    window.addEventListener('load',function(e){
    //监听app cache的updateready事件
     window.applicationCache.addEventListener('updateready',function(e)){

        console.log(window.applicationCache.status);
        if(window.applicatioinCache.status==window.applicationCache.UPDATEREADY){
        //application cache的版本号发生改年
          window.applicationCache.swapCache();
          window.location.reload();
        }else {
          console.log("manifest没有更改");
        }
    },false);
  },false);
  </script>
  </body>

</html>

注意:

  • app cache会自动地将本页当做一个静态页缓存;

  • 如果你要更新,请更新server端的manifest文件的版本;

  • 如果你不想启用app cache,或者说现在app cache不适合你现在的应用,那么有一个做法:

更改server端上manifest文件的名称,例如cache1.appcache,这个时候再去刷新浏览器,首先,浏览器还是会从app cache缓存中读取缓存,到第二次刷新的时候,浏览器会到server端查找manifest文件,发现这个文件不存在,那么浏览器会走网络从Server上重新拉取文件;

app cache优势:

  • 完全离线
  • 资源缓存,加载更快
  • 降低服务器负载

app cache缺陷:

  • 含有manifest属性的当前请求页无论如何都会被缓存;
  • 更新需要建立在manifest文件的更新,文件更新后是需要页面再次刷新的,并且在第2次刷新才能获取新资源;
  • 更新是全局性的,无法单独更新某个文件;
  • 对于链接的参数变化的敏感的,任何一个参数的修改都会被重新缓存,例如:index.html和index.html?v=1会被认为是不同文件,分别缓存;

app cache适用场景

  • 单地址页面
  • 对实时性要求不要的业务
  • 离线web应用

总结

在实际应用中,我们需要根据业务的需要来采取相应的缓存措施,如上所述,html5的几种缓存都有各自的优缺点和适用场景,有时我们也需要组合使用。

关于HTML5缓存我们就介绍到这里。

参考

HTML5之IndexedDB使用详解

fis-plus【01】——前端开发环境配置

发表于 2016-03-20   |   分类于 自动化工具   |  

fis-plus【01】——前端开发环境配置

前端自动化工具确实给前端同学带来的极大的方便,这一篇中我们将通过介绍fis-plus来配置前端的开发环境。

av8d,请坐好哦~

alt text

一、fis-plus引入

fis-plus能够把前端当做整个PHP的开发环境来渲染,包括它能对smarty的开发环境能够很好的支持。
官网网站:
fis-plus

首先我们进行安装

在命令行输入:npm install -g fis-plus

如果你在安装过程中遇到网络问题,安装不成功,你可以通过淘宝镜像进行安装。
淘宝镜像安装教程参照:
[http://npm.taobao.org/]

安装完cnpm,之后执行 cnpm install -g fis-plus

查看是否安装成功:
在命令行输入: fisp -v

如有下图所示,则表示成功:

alt text

在fisp的官网首页,我们看到fisp提供能很完善的解决方案,这个比gulp或是grunt都要来的出色。在平时,如果要写这些PHP smaty的解决方案,是需要花费很大成本的,现在通过fisp来节约我们的开发和优化时间。

注释: smarty是一个使用PHP写出来的模板PHP模板引擎,它提供了逻辑与外在内容的分离,简单的讲,目的就是要使用PHP程序员同美工分离,使用的程序员改变程序的逻辑内容不会影响到美工的页面设计,美工重新修改页面不会影响到程序的程序逻辑,这在多人合作的项目中显的尤为重要。

alt text

二、fis-plus是做什么的?

alt text

FIS-PLUS 是基于 FIS,应用于后端是 PHP,模板是 Smarty 的场景,是扩展自FIS的前端集成解决方案。其提供 后端框架、前端框架、自动化工具、辅助开发工具等开发套件。

在安装fis-plus之前,fis-plus对环境有要求:

  • (1)安装node ,>=0.8.0的版本;
  • (2)jre版本,>=1.5.0,用于本地调式
  • (3)php-cgi版本,>=5.0.0,用于本地调式

说一下php-cgi的安装,这里我们通过Brew来安装,
首先你需要安装brew,官网:homebrew。

在命令行输入:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"进行安装。

安装成功之后,在命令行输入:brew install php55 --with-cgi安装php-cgi。

上面的三个环境都安装完成之后,我们在命令行输入:

fisp server start你就能看到如下结果,这时会启动fisp 的调式服务器。

alt text

浏览器也会打开:

alt text

在上图,你可以看到这个目录的路径。

现在我们进入到这个文件夹中 cd /Users/imaginexie/.fis-plus-tmp/www

然后看一下这个目录下有什么文件:dir,然后打开这个目录open .

alt text

当然啦~这个目录只是本地server目录,但是其实,当我们在线上开发的时候,我们有一个真正的server。

fis-plus支持把线下(本地)的资源直接放到线上。

2.1、lights

说道这里,你是不是睡着了???让你睡~

alt text

lights 是 fis 提供的包管理工具,托管了 fis 所有资源。是使用 fis 的时候,必不可少的利器。

首先我们安装lights:npm install -g lights

然后下载一个PC端案例:我们在桌面上安装 lights install pc-demo

安装成功之后,你的桌面上会出现一个pc-demo的文件夹。

OK,现在我们来打开这个pc-demo目录,看看它都包含了哪些东西。

alt text

在这个文件夹中,我们看到,common和home目录下都有一个fis-conf.js配置文件。

在common(common文件夹是所有静态资源的结合)的fis-conf.js中我们看到:

配置文件对css和js进行了打包(pack),打包到了static/pkg这个目录下。
home目录下也是同理。

然后我们再看看home目录下,page/index.tpl这个模板文件:

alt text

我们看到这里面使用了require对静态资源进行加载。这个require模块加载主要用到的是fis自己写的mod.js,它也是一个类似于require.js或sea.js的模块化脚本。mod.js属于cmd规范。这个源码理解起来不然,你可以看下,因为mod.js主要是用来加载资源的。

然后我们再来看下common/page/layout.tpl

alt text

page其实主要是用来组装widge文件下的widget组件。只是对页面进行渲染,而不负责输出。

再来看下package.json这个文件:

alt text

这些文件在编译的时候会编译成smaty对应的文件夹,以及一个静态的文件夹。

现在我们来发布这个PC-demo。

在pc-demo目录下,命令行分别输入fisp release -r common

fisp release -r home,即,对common和home这两个目录进行发布(release)。

发布完成之后,命令行输入:fisp server start来启动服务器。

这个时候我们在浏览器上看到的还是这个:

alt text

你可能会疑惑,怎么啥变化没有呀???

hold住~我们进入到pc-demo 的www目录下查看下发生了什么:

cd /Users/imaginexie/.fis-plus-tmp/www

我们看到如下:

alt text

alt text

这些文件把我们前端的东西都打包了过来。对比之前我们在进行fisp server start的时候,是不是发生了很大的变化。

这里生成了一个static文件夹,对应生成了common和home目录。如图:

alt text

啊哈~马上要大功告成啦~感动啊

alt text

现在,我们把www这个文件夹拽到sublime中。然后对默认的首页index.php进行重命名:index_w.php。即> 把默认的首页去除。

然后,我们在命令行输入cd ..先退出www目录,返回到上级,然后执行fisp server init初始化模拟线上环境。

我们再到浏览器来刷新一下 127.0.0.1:8080。这个时候你会看到:

alt text

这就是fis给我提供的pc-demo的网站。

下面我们来了解下fis-plus发布(release)的时候用到的命令。
输入fisp release -h可以看到fis-plus release为我们提供了如下:

alt text

其中,都做了详细的解释。你可以按着上面的命令选项进行尝试,同时这些命令可以同时组合使用,例如fisp release -Domupld。

Ok ,以上是关于fis-plus在前端开发中得环境配置,但是我们还需要配置后端的开发环境,下一节我们将深入> 讲解fis-plus和后端的环境配置。

alt text

start 2016.03.19

发表于 2016-03-19   |   分类于 随笔   |  

从今天起,慢慢将blog从CSDN、豆瓣迁移到这里,在CSDN、豆瓣上的blog会陆续更新这里的blog,以后就在这里静心记录我的生活、工作、学习。

还是那句话,脚踏实地,技术很纯粹,生活很简单。我们一起加油!

welcome to imagine-life, nice to meet you.

imagineXie

imagineXie

this is imagine life

9 日志
7 分类
17 标签
RSS
github csdn
Links
  • bbplus
© 2016 imagineXie
由 Hexo 强力驱动
主题 - NexT.Pisces