Wills's blogs


  • 首页

  • 分类

  • 归档

  • 标签
Wills's blogs

babel--ES6转换成ES5!

发表于 2016-11-03 | 分类于 前端开发 |

Babel是一个广泛使用的ES6转码器,可以将ES6代码转为ES5代码,从而在现有环境执行。这意味着,你可以用ES6的方式编写程序,又不用担心现有环境是否支持。

转换步骤

1. 配置文件.babelrc

Babel的配置文件是.babelrc,存放在项目的根目录下。该文件用来设置转码规则和插件,基本格式如下:

1
2
3
4
{
"presets":[],
"plugins":[]
}

presets字段设定转码规则,官方提供以下规则集,你可以根据需要安装。

1
2
3
4
5
6
7
8
9
10
11
# ES2015转码规则
$ npm install --save-dev babel-preset-es2015
# react转码规则
$ npm install --save-dev babel-preset-react
#ES7不同阶段语法提案的转码规则(共有4个阶段),选装一个
$ npm install --save-dev babel-preset-stage-0
$ npm install --save-dev babel-preset-stage-1
$ npm install --save-dev babel-preset-stage-2
$ npm install --save-dev babel-preset-stage-3

然后将这些规则加入.babelrc

1
2
3
4
5
6
7
8
{
"presets":[
"es2015",
"react",
"stage-2",
],
"plugins":[]
}

2. 命令行转码babel-cli

1
$ npm install --global babel-cli

基本用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 转码结果输出到标准输出
$ babel example.js
# 转码结果写入一个文件
# --out-file 或 -o 参数指定输出文件
$ babel example.js --out-file compiled.js
# 或者
$ babel example.js -o compiled.js
# 整个目录转码
# --out-dir 或 -d 参数指定输出目录
$ babel src --out-dir lib
# 或者
$ babel src -d lib
# -s 参数生成source map 文件
$ babel src -d lib -s

上面的代码是在全局环境下,进行Babel转码。这意味着,如果项目要运行,全局环境必须要有babel,也就是说项目产生了对环境的依赖,另一方面,这样做也无法支持不同项目使用不同版本的Babel。

一个解决办法是将babel-cli安装在项目之中。

1
$ npm install --save-dev babel-cli

然后改写package.json。

1
2
3
4
5
6
7
8
{
"devDepandencies":{
"babel-cli":"^6.0.0"
},
"scripts": {
"build":"babel src -d lib"
},
}

转码的时候,就执行下面的命令。

1
$ npm run build

Wills's blogs

前端实用工具网站

发表于 2016-09-23 | 分类于 前端开发 |

这些是我在编程过程中经常用到的网站,将他们收录在这里,避免忘记。

  1. 图片占位工具
  2. 图片在线转换base64
  3. 腾讯雪碧图制作工具
Wills's blogs

PHP学习笔记

发表于 2016-09-18 | 分类于 后端开发 |
  1. php支持8种原始数据类型
  • 4种基本类型:
    (1) integer(整型)
    (2) float/double(浮点型)
    (3) string(字符串)
    (4) boolean(布尔型)

  • 2种复合类型:
    (5) array(数组)
    (6) object(对象)

  • 2种特殊类型:
    (7) resource(资源)
    (8) NULL

2. php中不是只有false值才为假,以下8种情况也为false

(1) 整型0
(2) 浮点型0.0
(3) 字符串”0”
(4) 空字符串””
(5) 空数组
(6) 空对象
(7) null
(8)从空标记生成的SimpleXML对象

所有其他值都被认为是true(包括任何资源)

1
2
3
4
5
6
echo 0==false;//1
echo 0.0==false;//1
echo "0"==false;//1
echo ""==false;//1
echo array()==false;//1
echo null==false;//1

3. 单引号和双引号的区别

双引号中所包含的的变量会自动被替换成实际数值,而单引号中包含的变量则按普通字符串输出。

1
2
3
$a = 'php';
echo '$a';//'$a'
echo "$a"//'php'

4. 声明一个数组

1
2
3
4
5
$arr = array(
'name' => 'phpchina',
'url' => 'www.phpchina.com',
'age' = > 13
);

5. 声明一个对象

1
2
3
4
5
6
7
8
class PHPChina{
private $name = 'phpchina';
public function sayHi(){
echo 'Hi' . $this->name;
}
}
$php = new PHPChina();
var_dump($php);

6. 输出null的情况

1
2
3
4
$a = null;
$b;
$c = 10;
unset($c);
  • unset()—— 用来销毁变量
  • is_null()—— 判断某个变量是否为null,返回一个boolean值,如果为null值,那么输出true,否则输出false

7. 数据类型转换

两种方式:
(1) 在需要转换类型的变量前加上括号括起来的数据类型则名称即可:(int)$a
(2) 使用settype()函数来实现:settype($a,’int’); //将变量a转换成整型类型
基本转换:
(1) null、0和未赋值的变量或数组都会转换为false
(2) 当boolean类型转换为int类型时,true转换为1,false转换为0
(3) 字符串转换成int类型时,字符串如果以数字开头就截取到非数字部分,否则输出0;其他数据类型转换为object类型时,其中名为scalar的成员变量将包含原变量的值
强制转换:
前面转换方式的第一种就是强制转换。

8. 定义常量

常量在使用前必须先定义,常量的名字一般使用大写字母组合,不建议使用小写字母。

1
bool define ( string $name , mixed $value [, bool $case_insensitive = false ] )

最后一个参数可选,表示常量是否区分大小写,如果设定为true,表示不区分大小写,默认为false,即默认是区分大小写的。
例子:

1
define('NAME','lei');

defined() — 检查某个名称的常量是否存在,语法如下:

1
bool defined ( string $name )

9. 系统默认常量(预定义常量)

__FILE__—— 文件的完整路径和文件名
__LINE__—— 当前行号
__CLASS__—— 类的名称
__METHOD__—— 类的方法名
PHP_VERSION—— php的版本
PHP_OS—— 运行php程序的操作系统
DIRECTORY_SEPARATOR—— 返回操作系统的分隔符 windows下返回“\”,linux下返回“/”

10. php变量

变量名是区分大小写的,一个有效的变量名由字母或者下划线开头,后面跟上任意数量的字母,数字,或者下划线。按照正常的正则表达式,它将被表述为:’[a-zA-Z\x7f-\xff][a-zA-Z0-9\x7f-\xff]*’。
不需要预先定义,可以直接在使用时定义变量。同时在定义变量时,也可以不用初始化变量。
给变量赋值有两种方式,分别是传值赋值和引用赋值:
(1)传值赋值,是使用“=”直接将一个变量(或表达式)的值赋给变量,等号两边的变量互不影响,任何一个变量值的变化都不会影响到另一个变量。
(2)引用赋值同样也是使用“=”将一个变量的值赋给另一个变量,但是需要在等号右边的变量前面加上一个”&”符号。在使用引用赋值的时候,两个变量将会指向内存中同一存储空间。因此任何一个变量的变化都会引起另外一个变量的变化。

变量的作用域:
(1)局部变量
(2)全局变量
(3)静态变量

Wills's blogs

后台管理系统的主布局框架(告别传统的iframe)

发表于 2016-09-12 | 分类于 前端开发 |

实现原理:

① 整个后台管理系都是由一个完整的页面(.html | .jsp | .php)所切分的。​​

② 页面由头部、身体(左:菜单,右:内容)、尾部构成。

③ 对应着菜单项属性名称,新建同样名称的.html页面(注:这里的html不存在标签,该页面就是从主页面分出来的右边内容的一部分,它与主页面拼成一个完整的页面)。
④ 点击左边菜单项,就把当前的菜单项名称填写到Ajax的url中。

⑤ 将Ajax返回数据,填给右边内容。

代码实现:

​①.html部分(页面左:菜单):

​②.html部分(页面右:内容):​

​③.​目录结构:与菜单属性名保持一致的.html页面

④.Js部分(这里引用了jQuery库):
​

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
30
31
32
33
34
35
36
37
38
39
40
var code = $(".menulist .selected").attr("data-code");
$.ajax({
url: code + ".html",
type:"get",
data:{
code:code
},
dataType:"html",
success:function(data){
$("#content").html(data)
}
})
$(".menulist li").click(function(){
if($(this).hasClass("selected")==false){
$(".menulist li").removeClass("selected");
$(".menulist li").animate({width:"80%"},200,"swing");
$(this).addClass("selected");
$(this).animate({width:"100%"},200,"linear");
var code = $(this).attr("data-code");
hideDetail();
$.ajax({
url:code + ".html",
type:"get",
dataType:"html",
success:function(data){
$("#content").html(data);
}
})
}
});
Wills's blogs

ES6新特性

发表于 2016-09-05 | 分类于 前端开发 |

ECMAScript 6.0(以下简称ES6)是、JavaScript语音的下一代标准,已经在2015年6月正式发布。目标是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。由于ES6的第一个版本是2015年发布的,所以又称ECMAScript 2015(简称ES2015)。

##ES6常用的特性有:

  1. let
  2. const
  3. extends
  4. super
  5. arrow functions
  6. template string
  7. destrucring
  8. default
  9. rest arguments

1. let,const

这两个的用途与var类似,都是用来声明变量的,但实际运用中他两都有各自特殊的用途。
如下例:

1
2
3
4
5
6
7
var name = 'leiwensu';
while(true){
var name = 'obama';
console.log(name);//obama
break;
}
console.log(name);//obama

使用var两次输出都是obama,这是因为es5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。第一种场景就是你现在看到的内层变量覆盖外层变量。而let则实际上为JavaScript新增了块级作用域。用它所声明的变量,只在let命令所在的代码块内有效。

1
2
3
4
5
6
7
var name = 'leiwensu';
while(true){
let name = 'obama';
console.log(name);//obama
break;
}
console.log(name);//leiwenxiu

另外一个var带来的不合理场景就是用来计数的循环变量泄露为全局变量,看下面的例子:

1
2
3
4
5
6
7
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10

上面代码中,变量i是var声明的,在全局范围内都有效。所以每一次循环,新的i值都会覆盖旧值,导致最后输出的是最后一轮的i的值。而使用let则不会出现这个问题。

1
2
3
4
5
6
7
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6

再来看一个更常见的例子,了解下如果不用ES6,而用闭包如何解决这个问题。

1
2
3
4
5
6
var clickBoxs = document.querySelectorAll('.clickBox')
for (var i = 0; i < clickBoxs.length; i++){
clickBoxs[i].onclick = function(){
console.log(i)
}
}

我们本来希望的是点击不同的clickBox,显示不同的i,但事实是无论我们点击哪个clickBox,输出的都是5。下面我们来看下,如何用闭包搞定它。

1
2
3
4
5
6
7
8
9
10
// 闭包
var clickBox = document.querySelectorAll('.clickBox');
for (var i = 0; i < clickBox.length; i++) {
clickBox[i].onclick = function (i) {
return function(){
console.log(i);
}
}(i)
}

const也用来声明变量,但是声明的是常量。一旦声明,常量的值就不能改变。

1
2
const PI = Math.PI
PI = 23 //Module build failed: SyntaxError: /es6/app.js: "PI" is read-only

当我们尝试去改变用const声明的常量时,浏览器就会报错。
const有一个很好的应用场景,就是当我们引用第三方库的时声明的变量,用const来声明可以避免未来不小心重命名而导致出现bug:

1
const monent = require('moment')

总结:

  • let所声明的变量,只在let命令所在的代码块内有效
  • let所声明的变量,不存在变量提升,变量一定要在声明后使用,否则报错。
  • 暂时性死区,ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错
  • 不允许重复声明,let不允许在相同作用域内,重复声明同一个变量
  • let实际上为JavaScript新增了块级作用域,块级作用域的出现,实际上使得获得广泛应用的立即执行匿名函数(IIFE)不再必要了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // IIFE写法
    (function () {
    var tmp = ...;
    ...
    }());
    // 块级作用域写法
    {
    let tmp = ...;
    ...
    }
  • 块级作用域与函数声明。ES5规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。ES6引入了块级作用域,明确允许在块级作用域之中声明函数。

全局对象的属性
ES5之中,全局对象的属性与全局变量是等价的,ES6为了改变这一点,一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,依旧是全局对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于全局对象的属性。也就是说,从ES6开始,全局变量将逐步与全局对象的属性脱钩。

1
2
3
4
5
6
7
var a = 1;
// 如果在Node的REPL环境,可以写成global.a
// 或者采用通用方法,写成this.a
window.a // 1
let b = 1;
window.b // undefined

变量的解构赋值:

  1. 数组的解构赋值
    ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

以前,为变量赋值,只能直接指定值。

1
2
3
var a = 1;
var b = 2;
var c = 3;

ES6允许写成这样:

1
var [a, b, c] = [1, 2, 3];

  1. 解构赋值允许指定默认值。

    1
    2
    3
    4
    5
    var [foo = true] = [];
    foo // true
    [x, y = 'b'] = ['a']; // x='a', y='b'
    [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
  2. 对象的解构赋值
    解构不仅可以用于数组,还可以用于对象。

    1
    2
    3
    var {foo,bar} = {foo:"aaa",bar:"bbb"};
    foo//"aaa"
    bar//"bbb"

对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

2. class, extends, super

这三个特性涉及了ES5中最令人头疼的的几个部分:原型、构造函数,继承…你还在为它们复杂难懂的语法而烦恼吗?你还在为指针到底指向哪里而纠结万分吗?

有了ES6我们不再烦恼!

ES6提供了更接近传统语言的写法,引入了Class(类)这个概念。新的class写法让对象原型的写法更加清晰、更像面向对象编程的语法,也更加通俗易懂。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Anlimal{
constructor(){
this.type = 'animal'
}
says(say){
console.log(this.type+'says'+say)
}
}
let animal = new Animal()
animal.says('hello')//animal says hello
class Cat extends Animal{
constructor(){
super()
this.type = 'cat'
}
}
let cat = new Cat()
cat.says('hello')//cat says hello

上面代码首先用class定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。简单地说,constructor内定义的方法和属性是实例对象自己的,而constructor外定义的方法和属性则是所有实例对象可以共享的。
Class之间可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。上面定义了一个Cat类,该类通过extends关键字,继承了Animal类的所有属性和方法。

super关键字,它指代父类的实例(即父类的this对象)。子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。

ES6的继承机制,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。

3. arrow function

这个恐怕是ES6最最常用的一个新特性了,用它来写function比原来的写法要简洁清晰很多;

1
2
function(i){return i + 1}//ES5
(i)=>i+1//ES6

简直是简单的不像话对吧…
如果方程比较复杂,则需要用{}把代码包起来:

1
2
3
4
5
6
function(x, y) {
x++;
y--;
return x + y;
}
(x, y) => {x++; y--; return x+y}

除了看上去更简洁以外,arrow function还有一项超级无敌的功能!
长期以来,JavaScript语言的this对象一直是一个令人头痛的问题,在对象方法中使用this,必须非常小心。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Animal{
constructor(){
this.type = 'animal'
}
says(say){
setTimeout(function(){
console.log(this.type+'says'+say)
},1000)
}
}
var animal = new Animal()
animal.says('hi')//undefined says hi

运行上面的代码会报错,这是因为setTimeout中的this指向的是全局对象。所以为了让它能够正确的运行,传统的解决方法有两种:

1.第一种是将this传给self,再用self来指代this

1
2
3
4
5
says(say){
var self = this;
setTimeout(function(){
console.log(self.type + ' says ' + say)
}, 1000)

2.第二种方法是用bind(this),即

1
2
3
4
says(say){
setTimeout(function(){
console.log(this.type + ' says ' + say)
}.bind(this), 1000)

但现在我们有了箭头函数,就不需要这么麻烦了:

1
2
3
4
5
6
7
8
9
10
11
12
class Animal {
constructor(){
this.type = 'animal'
}
says(say){
setTimeout( () => {
console.log(this.type + ' says ' + say)
}, 1000)
}
}
var animal = new Animal()
animal.says('hi') //animal says hi

当我们使用箭头函数时,函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,它的this是继承外面的,因此内部的this就是外层代码块的this。

4. template string

这个东西也是非常有用,当我们要插入大段的html内容到文档中时,传统的写法非常麻烦,所以之前我们通常会引用一些模板工具库,比如mustache等等
大家可以先看下面一段代码:

1
2
3
4
5
6
$("#result").append(
"There are <b>" + basket.count + "</b> " +
"items in your basket, " +
"<em>" + basket.onSale +
"</em> are on sale!"
);

我们要用一堆的’+’号来连接文本与变量,而使用ES6的新特性模板字符串``后,我们可以直接这么来写:

1
2
3
4
5
$("#result").append(`
There are <b>${basket.count}</b> items
in your basket, <em>${basket.onSale}</em>
are on sale!
`);

用反引号(\)来标识起始,用${}`来引用变量,而且所有的空格和缩进都会被保留在输出之中,是不是非常爽?!

5. destructuring

ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

看下面的例子:

1
2
3
4
let cat = 'ken'
let dog = 'lili'
let zoo = {cat: cat, dog: dog}
console.log(zoo) //Object {cat: "ken", dog: "lili"}

用ES6完全可以像下面这么写:

1
2
3
4
let cat = 'ken'
let dog = 'lili'
let zoo = {cat, dog}
console.log(zoo) //Object {cat: "ken", dog: "lili"}

反过来可以这么写:

1
2
3
let dog = {type: 'animal', many: 2}
let { type, many} = dog
console.log(type, many) //animal 2

###default, rest
default很简单,意思就是默认值。大家可以看下面的例子,调用animal()方法时忘了传参数,传统的做法就是加上这一句type = type || ‘cat’ 来指定默认值。

1
2
3
4
5
function animal(type){
type = type || 'cat'
console.log(type)
}
animal()

如果用ES6我们而已直接这么写:

1
2
3
4
function animal(type = 'cat'){
console.log(type)
}
animal()

最后一个rest语法也很简单,直接看例子:

1
2
3
4
5
6
7
8
9
10
11
// arguments变量的写法
function animals() {
console.log(Array.prototype.slice.call(arguments));
}
animals('cat', 'dog', 'fish') //["cat", "dog", "fish"]
// rest参数的写法
function animals(...types){
console.log(types)
}
animals('cat', 'dog', 'fish') //["cat", "dog", "fish"]
1
2
3
4
5
6
7
// arguments变量的写法
function sortNumbers() {
return Array.prototype.slice.call(arguments).sort();
}
// rest参数的写法
const sortNumbers = (...numbers) => numbers.sort();

而如果不用ES6的话,我们则得使用ES5的arguments。

ES6生成器(Generators)

什么是生成器?
我们从一个实例开始:

1
2
3
4
5
6
7
8
function* quips(name) {
yield "你好 " + name + "!";
yield "希望你能喜欢这篇介绍ES6的译文";
if (name.startsWith("X")) {
yield "你的名字 " + name + " 首字母是X,这很酷!";
}
yield "我们下次再见!";
}

看起来很像一个函数,我们称之为生成器函数,它与普通函数有很多共同点,但是二者有如下区别:

  • 普通函数使用function声明,而生成器函数使用function*声明
  • 在生成器函数内部,有一种类似return的语法:关键字yield。二者的区别是,普通函数只可以return一次,而生成器函数可以yield多次(当然也可以只yield一次)。在生成器的执行过程中,遇到yield表达式立即暂停,后续可恢复执行状态。
    这就是普通函数和生成器函数之间最大的区别,普通函数不能自暂停,生成器函数可以。

生成器做了什么?
当你调用quips()生成器函数时发生了什么?

1
2
3
4
5
6
7
8
9
10
> var iter = quips("jorendorff");
[object Generator]
> iter.next()
{ value: "你好 jorendorff!", done: false }
> iter.next()
{ value: "希望你能喜欢这篇介绍ES6的译文", done: false }
> iter.next()
{ value: "我们下次再见!", done: false }
> iter.next()
{ value: undefined, done: true }

字符串的扩展

  1. 字符串的遍历器接口
    ES6为字符串添加了遍历器接口(详见《Iterator》一章),使得字符串可以被for…of循环遍历。
    1
    2
    3
    4
    5
    6
    for (let codePoint of 'foo') {
    console.log(codePoint)
    }
    // "f"
    // "o"
    // "o"
Wills's blogs

前端性能优化的14个规则!

发表于 2016-09-02 | 分类于 前端开发 |

规则01:尽量减少HTTP请求

前端优化的黄金准则指导着前端页面的优化策略:只有10%-20%的最终用户响应时间花在接受请求的HTML文档上,剩下的80%-90%时间花在为HTML文档所引用的所有组件(图片、脚本、样式表等)进行的HTTP请求上。因此,改善响应时间的最简单途径就是减少组件的数量,并由此减少HTTP请求的数量。当然很多人就会说,既然这样,那我们就减少页面组件的数量不就OK了吗?那你试试,你会掀起一场性能优化和产品设计之间的大PK。所以,我们要减少HTTP请求是要平衡性能和设计的。如果找到这个平衡点呢?书中从以下几个方面做了介绍,我逐一说明:

① 图片地图

初看“图片地图”四个字,对非专业的前端人员来说一头雾水,我的第一印象就是这样的,咱们以京东的移动站点为例,右侧用户和购物车的图标,正常实现我会选择如下方式:

1
2
3
4
5
6
<a href="用户跳转页面URL">
<div class=”定义用户icon显示的样式表”></div>
</a>
<a href="购物车跳转页面URL">
<div class="定义用户icon显示的样式表"></div>
</a>

这种方式无可厚非的,但是两张图片就有两个HTTP请求,这明显是增加了页面中的HTTP请求。

那么我们可以把这两个HTTP请求变成一个吗?答案当然是可以的,这就是图片地图:允许在一张图片上关联多个URL,而目标URL的选择取决于用户单击了图片上的哪个位置。这样上面京东两个图标合并成一张图片,这样图片的HTTP请求就减少了一个。

示例代码如下:

1
2
3
4
5
6
<img src="合并后的图片">
<map name="map1">
<area shape="rect" coords="0,0,40,40" href="用户跳转页面URL">
<area shape="rect" coords="50,0,90,40" href="购物车跳转页面URL">
</map>

不过图片地图只支持矩形形状,其他形状不支持。

② 请CSS喝“雪碧”(CSS Sprites)

CSS Sprites一句话:将多个图片合并到一张单独的图片,这样就大大减少了页面中图片的HTTP请求。

③ 内联图片和脚本

使用data:URL(Base64编码)模式直接将图片包含在Web页面中而无需进行HTTP请求。

但是此种方法存在明显缺陷:

  • 不受IE的欢迎;

  • 图片太大不宜采用这种方式,因为Base64编码之后会增加图片大小,这样页面整体的下载量会变大;

  • 内联图片在页面跳转的时候不会被缓存。(大图片可以使用浏览器的本地缓存,在首次访问的时候保存到浏览器缓存中,典型的是HTML5的manifest缓存机制以及LocalStorage等)。

④ 样式表的合并

将页面样式定义、脚本、页面本身代码严格区分开,但是样式表、脚本也不是分割越细越好,因为没多引用一个样式表就增加一次HTPP请求,能合并的样式表尽量合并。一个网站有一个公用样式表定义,每个页面只要有一个样式表就OK啦。

通过以上四个努力之后,你会发现你的网页响应时间最多能减少一半,这不是作者说大话,也不是我狂吹,我亲手用我的移动网站首页做了一个尝试,本地测试之后响应时间能减少40%左右。所以减少页面HTTP请求数量,是一个很重要的原则。遵循此原则可以同时改善首次访问和后续访问的响应时间,而每一个网站的首次响应时间会决定用户之后还来不来的重要原因。

规则02:使用内容发布网络(CDN的使用)

什么叫内容发布网络(CDN)?它是一组分布在多个不同地理位置的Web服务器,用于更加有效地向用户发布内容。主要用于发布页面静态资源:图片、css文件、js文件等。如此,能轻易地提高响应速度。

关于CDN的具体详细原理以及优缺点,各位可以自行询问度娘或者google。

规则03:添加Expires头

浏览器使用缓存来减少HTTP请求的数据,并减小HTTP响应的大小,使页面加载更快。Web服务器使用Expires头来告诉浏览器它可以使用一个组件的当前副本,直到指定的deadline为止。HTTP规范中称此头为:在这一时间之后响应被认为失效。

个人对这块表示不想使用,其实就是一句话,把一些css、js、图片在首次访问的时候全部缓存到浏览器本地,从我做移动网站的过程中发现,其实没有这么复杂,完全可以使用HTML5提供的本地缓存机制就OK了。关于HTML5本地缓存机制,各位可以查阅相关资料。后续我也会对HTML5的缓存机制进行介绍的。

规则04:压缩组件(使用Gzip方式)

书中关于压缩从gzip压缩方式到如何压缩讲了很多,我想直接跳过,对于做PC网站或者移动网站来说,急需要压缩的是css文件和js文件,至于如何压缩,网上有很多在线工具,去挑选一个自己用的顺手看的顺眼的就好,当然也有人选择对HTML进行压缩,这样也可以。但是实际工作中我没有这么做。之所谓没有这么做,是因为我觉得很麻烦。不要鄙视我,毕竟我不是一个真正意义上的前端工程师,哈哈!

规则05:将CSS样式表放在顶部

如果将css样式定义放在页面中或者页面底部,会出现短暂白屏或者某一区域短暂白板的情况,这和浏览器的运营机制有关的,不管页面如何加载,页面都是逐步呈现的。所以在每做一个页面的时候,用Link标签把每一个样式表定义放在head中。

规则06:将javascript脚本放在底部

浏览器在加载css文件时,页面逐步呈现会被阻止,直到所有css文件加载完毕,所以要把css文件的引用放到head中去,这样在加载css文件时不会组织页面的呈现。但是对于js文件,在使用的时候,它下面所有也页面内容的呈现都会被阻塞,将脚本放在页面越靠下的地方,就意味着越多的内容能够逐步呈现。

规则07:避免使用CSS表达式

CSS表达式是动态玩CSS的一种很强大的方式,但是强大的同时也存在很高的危险性。因为css表达式的频繁求值会导致css表达式性能低下。如果真想玩css表达式,可以选用只求值一次的表达式或者使用事件处理来改变css的值。

规则08:使用外部javascript和CSS

内联js和css其实比外部文件有更快的响应速度,那为什么还要用外部呢?因为使用外部的js和css可以让浏览器缓存他们,这样不仅HTML文档大小减少,而且不会增加HTTP请求数量。

另外,使用外部js和css可以提高组件的可复用性。

规则09:减少DNS查询

DNS查询有时间开销,通常一个浏览器查找一个给定主机名的IP地址需要20-120ms。

缓存DNS:缓存DNS查询可以很好地提高网页性能,一旦缓存了DNS查询,之后对于相同主机名的请求就无需进行再次的DNS查找,至少短时间内不需要。

所以在使用页面中URL、图片、js文件、css文件等时,不要使用过多不同的主机名。

规则10:精简javascript

如何精简?最初始的精简方式就是移除不必要的字符减小js文件大小,改善加载时间。包括所有的注释、不必要的空白字符。

高级一点的精简方式就是:混淆。它不但会移除不必要的字符,还会改写代码,比如函数和变量的名字会被改成很短的字符串,这样使js代码更简练更难阅读。

但是我一般很少使用混淆,一个现在互联网时代,代码没有必要整的那么神秘,大可以大家一起share,天下代码一起抄,只要抄出自己的特色就ok了。而且一旦使用混淆,对于js代码的维护和调试都很复杂,因为有时候混淆之后的js代码完全看不懂。

其实实际开发过程中,从文件大小和代码可复用性来说,不仅仅是js代码需要精简,css代码一样也很需要精简。

规则11:避免重定向

重定向的英文是Redirect,用于将用户从一个URL重新跳转到另一个URL。最常见的Redirect就是301和302两种。

关于重定向的性能影响这里就不说了,自行查阅相关资料吧。

在我们实际开发中避免重定向最简单也最容易被忽视的一个问题就是,设置URL的时候,最后的“/”,有些人有时候会忽略,其实你少了“/”,这时候的URL就被重定向了,所以在给页面链接加URL的时候切记最后的“/”不可丢。

规则12:删除重复脚本

重复的js代码除了有不必要的HTTP请求之外,还会浪费执行js的时间。

将你使用的js代码模块化,可以很好地避免这个问题,至于js模块化如何实现,现在有很多可以使用的开源框架,我用的比较多的是我们公司玉伯的Sea.js。

规则13:配置ETag

Etag(EntityTag),实体标签,是Web服务器和浏览器用户确认缓存组件的有效性的一种机制。

写的很复杂,对我这种非专业的前端开发人员来说,有点过了,关于这个原则有兴趣的自己看吧。

规则14:使Ajax可缓存

针对页面中主动的Ajax请求返回的数据要缓存到本地,当然这个是针对短期内不会变化的数据。如果不确定数据变化周期的话,可以增加一个修改标识的判断,我正常处理过程中会给一些Ajax请求返回的数据增加一个MD5值的判断,每次请求会判断当前MD5是否变化,如果变化了取最新的数据,如果不变化,则不变。

噼里啪啦说了一堆,14个规则啊,那我们开发过程中要针对这每一个规则对自己的前端代码做check吗?我反正不这么干,做前端页面,尤其是移动网站的页面,我所记住的准则就是:尽量减少页面大小,尽量降低页面响应时间。在页面性能和交互设计之中找平衡点。

Wills's blogs

编程过程中的一些笔记

发表于 2016-09-02 | 分类于 前端开发 |

这是一些我在前端代码编写过程中的一些笔记,有一些平常经常会遇到的,但是又容易忘记,所以做了以下笔记。

1. 关于表格

在一些数据的展示方面通常都要使用表格,这时候,表格的大小样式方面显得非常重要。因为一旦表格大小不合适,容易造成变形。以下是一般表格的模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<table class="dp-tab">
<thead>
<tr>
<th width="65"> 店铺类型</th>
<th width="120">店铺名称</th>
<th width="100">提交日期</th>
<th width="100">申请成功日期</th>
<th width="60">状态</th>
<th>备注</th>
<th width="50">操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>@item.ShopTypeName</td>
<td>@item.ShopName</td>
<td></td>
<td></td>
<td class="dp-red">@item.ApplyStatus</td>
<td>@item.Remark</td>
<td><a href="/shop/input>详情</a></td>
</tr>
</tbody>
</table>

如上图所示,先把能确定最大大小的单元格宽度全部固定下来,剩下一个可能内容比较多且宽度不固定的单元格不加宽度,使其自适应。

2. 使用z-index改变元素层级

使用z-index改变元素层级时,要对该元素加定位,一般使用

1
position:relative

3. 清除浮动

在平常我们coding时候经常会发生子元素浮动而引起父级元素不能完全撑开的状况,解决办法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1. 对父级元素添加以下样式:
.parent{
overflow:hidden;
zoom:1;
}
2. 对父级元素添加以下样式:
.parent:after{
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}

4. 文本模糊效果

1
2
3
4
.blur {
color: transparent;
text-shadow: 0 0 5px rgba(0,0,0,0.5);
}

5. jquery遍历函数

  1. filter(expression)
    过滤掉与expression参数值指定的标准不匹配(返回值为false)的元素集合,例子:
1
2
3
$('td').filter(function(){
return this.innerHTML.match(/^\d+$/);
});
  1. map(callback)
    将元素集合中的每一个元素调用回调函数,并将返回值收集到jQuery对象实例中,例子:
1
2
3
var allIds = $('div').map(function(){
return (this.id==undefined)?null:this.id;
}).get();
  1. each(callback)
    遍历匹配集里所有的元素,为每一个元素调用传入的迭代函数,例子:
1
2
3
$([1,2,3]).each(function(){
alert(this);
});

6. iframe的bug

使用iframe标签时,如果iframe标签不闭合,即

7. 鼠标放在图片上,图片放大显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var imgWid = 0 ;
var imgHei = 0 ; //变量初始化
var big = 1.1;//放大倍数
$('.sw-con1,.sw-con2,.sw-con3').hover(function() {
$(this).find("img").stop(true,true);
var imgWid2 = 0;
var imgHei2 = 0;//局部变量
imgWid = $(this).find("img").width();
imgHei = $(this).find("img").height();
imgWid2 = imgWid * big;
imgHei2 = imgHei * big;
$(this).find('img').animate({
width: imgWid2,
height: imgHei2
},"slow"
);
}, function() {
$(this).find("img").stop().animate({"width":imgWid,"height":imgHei});
});

8. 解决img标签间距问题

关于img标签间距问题:
多个img之间有间距,包含img标签的div之间有间距块级元素包含内联元素如图片文字等时,内联元素默认是和父级元素的baseline(基线)对齐的,而baseline又和父级元素底边有一定的距离(这个距离和font有关,不一定是5px),所以以上代码的效果中不同div之间有间隙,这是因为图片与父元素的底边有距离。说到baseline呢,其实它是vertical-align属性的默认值,vertical-align属性是设置元素的垂直排列的,用来定义行内元素的基线相对于该元素所在行的基线的垂直对齐,除了baseline对齐方式之外,还可以是sub | super | top | text-top | middle | bottom | text-bottom |inherit(任何的版本的Internet Explorer (包括 IE8)都不支持属性值 “inherit”)。

  知道了问题产生的原因,就好对症下药解决问题了,其实就是要消除baseline对齐方式产生的距离。所以,
  方法1:很容易想到,把对齐方式改一下不就好了,于是设置img的vertical-align属性为bottom;

  方法2:就是上文说的给父元素加上font-size:0的属性,既然这个距离和font有关,那么把字体大小设为0,总该没有距离了吧;

  方法3:可由方法二想到,既然为0可以,那把行高设的很小可不可以呢?经试验发现,本例图片大小为200px,设line-height不大于12就能够消除间隙了,鉴于这个距离一般是5px,所以可以把line-height设为5px左右;

  另外一个间隙是多个img标签的左右间隙,是由于img标签是行内元素,而事实是当行内元素之间有“回车”、“tab”、“空格”时就会出现间隙。

  所以方法就是上文提到的,去掉img标签之间所有的空格,如果又不想把所有连续的行内元素写到一行,可以多行注释,把空格回车什么的注释掉,就像下图这样;当连续的行内元素不是img时,也可以通过设置父元素的font-size为0来消除左右间隙。
方法四:把img变成块级元素

9. 移动端字体设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
body {
font-family: "Helvetica Neue", Helvetica, STHeiTi, sans-serif;
}
//使用rem单位时,html的字体一般设置
@media only screen and (min-width: 641px)
html {
font-size: 125%!important;
}
@media only screen and (min-width: 561px)
html {
font-size: 109%!important;
}
@media only screen and (min-width: 481px)
html {
font-size: 94%!important;
}
html {
font-size: 62.5%;
}

10. chrome浏览器,将html网页中input [file] 元素css样式中的’cursor’属性设置为’pointer’,但是鼠标移上去后的形状还是箭头。

解决办法:

为input [file]元素添加css样式:

1
2
font-size:0;
cursor:pointer;

11. 模拟input[file]上传按钮,以及文件名获取方法如下:

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
30
31
32
33
34
35
36
<a class="file-con" href="#">
选择文件
<input id="orderFile" type="file" />
</a>
<style>
.file-con {
vertical-align: middle;
text-align: center;
line-height: 32px;
color: #666;
text-decoration: none;
display: inline-block;
width: 80px;
height: 32px;
background: #f6f6f6;
position: relative;
overflow: hidden;
border: 1px solid #ccc;
border-radius: 5px;
}
.file-con:hover {
background: #efefef;
}
.file-con input {
position: absolute;
right: 0;
top: 0;
opacity: 0;
filter: alpha(opacity=0);
width: 80px;
height: 32px;
outline: none;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
/**
*获取文件名
*/
function getFileName(path) {
var pos1 = path.lastIndexOf('/');
var pos2 = path.lastIndexOf('\\');
var pos = Math.max(pos1, pos2)
if (pos < 0)
return path;
else
return path.substring(pos + 1);
}
</script>

12. js判断电话号码运营商

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var isChinaMobile = /^1(3[4-9]|5[012789]|8[23478]|4[7]|7[8])\d{8}$/; //移动
var isChinaUnion = /^1(3[0-2]|5[56]|8[56]|4[5]|7[6])\d{8}$/; //联通
var isChinaTelcom = /^1(3[3])|(8[019])\d{8}$/; //电信
function check(telphone){
if(telphone.length !== 11){
alert("未检测到正确的手机号码");
}else{
if(isChinaMobile.test(telphone)){
alert("移动");
}else if(isChinaUnion.test(telphone)){
alert("联通");
}else if(isChinaTelcom.test(telphone)){
alert("电信");
}else{
alert("未检测到正确的手机号码");
}
}
}
check("1324****322");

###13. 纯css小三角形
html:

1
2
3
<ul class="dp_nav">
<li><a href="">退出</a></li>
</ul>

样式:

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
30
31
32
33
34
35
36
37
38
39
40
.dp_nav {
display:none;
position: absolute;
min-width: 100px;
padding: 5px 0;
background-color: #fff;
border: 1px solid #ccc;
border: 1px solid rgba(0,0,0,0.2);
line-height: 1.5em;
text-align:center;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
border-radius: 6px;
-webkit-box-shadow: 0 5px 10px rgba(0,0,0,0.2);
-moz-box-shadow: 0 5px 10px rgba(0,0,0,0.2);
box-shadow: 0 5px 10px rgba(0,0,0,0.2);
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
}
.dp_nav:before {
content: '';
display: inline-block;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
position: absolute;
top: -7px;
left: 9px;
}
.dp_nav:after {
content: '';
display: inline-block;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
position: absolute;
top: -6px;
left: 10px;
}

12
Wills

Wills

wills blogs,前端开发博客,技术博客,前端学习笔记

17 日志
2 分类
14 标签
GitHub
© 2017 Wills
由 Hexo 强力驱动
主题 - NexT.Pisces