lennonover


  • 首页

  • 归档

  • 标签

Node.js高性能的异步I/O

发表于 2016-10-22

Node的单线程异步非阻塞I/O模型


Node.js® is a JavaScript runtime built on Chrome’s V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.

说异步之前先了解几个名词

  • I/O的阻塞与非阻塞:阻塞模式的I/O会造成应用程序等待,直到I/O完成。同时操作系统也支持将I/O操作设置为非阻塞模式,这时应用程序的调用将可能在没有拿到真正数据时就立即返回了,为此应用程序需要多次调用才能确认I/O操作完全完成,这就是一个轮训的过程。

  • I/O的同步与异步:I/O的同步与异步出现在应用程序中。如果做阻塞I/O调用,应用程序等待调用的完成的过程就是一种同步状况。相反,I/O为非阻塞模式时,应用程序则是异步的。

  • 事件驱动:当事件被检测到发生时才回调调用会到函数,通过事件循环加事件触发的方式来运行程序。

  • 事件循环:当有大量异步操作时需要调用相应的回调函数,需要用一种机制来管理同一个队列。

Node的单线程异步非阻塞I/O模型利用单线程,远离多线程的死锁、状态同步等问题避免不必要的内存开销和上下文切换开销。 利用异步I/O,让单线程远离阻塞,更好的利CPU。

一个异步 I/O 的大致流程如下:

  • 发起 I/O 调用

    • 用户通过 Javascript 代码调用 Node 核心模块,将参数和回调函数传入到核心模块;
    • Node 核心模块会将传入的参数和回调函数封装成一个请求对象;
    • 将这个请求对象推入到 I/O 线程池等待执行;
    • Javascript 发起的异步调用结束,Javascript 线程继续执行后续操作。
  • 执行回调

    • I/O 操作完成后,会将结果储存到请求对象的 result 属性上,并发出操作完成的通知;
    • 每次事件循环时会检查是否有完成的 I/O 操作,如果有就将请求对象加入到 I/O 观察者队列中,之后当做事件处理;
    • 处理 I/O 观察者事件时,会取出之前封装在请求对象中的回调函数,执行这个回调函数,并将 result 当参数,以完成 Javascript 回调的目的。

这里面涉及到了一个设计理念:事件循环(Event Loop),它是一个类似于 while true 的无限循环,它会维护一系列的监视器,这些监视器都有对应着一种异步操作,它们注册事件监听以及相应的回调。事件循环除了维护那些观察者队列,还维护了一个 time 字段,在初始化时会被赋值为0,每次循环都会更新这个值。所有与时间相关的操作,都会和这个值进行比较,来决定是否执行。与 timer 相关的过程如下:

  • 更新当前循环的 time 字段,即当前循环下的“现在”;
  • 检查循环中是否还有需要处理的任务(handlers/requests),如果没有就不必循环了,即是否 alive。
  • 检查注册过的 timer,如果某一个 timer 中指定的时间落后于当前时间了,说明该 timer 已到期,于是执行其对应的回调函数;
  • 执行一次 I/O polling(即阻塞住线程,等待 I/O 事件发生),如果在下一个 timer 到期时还没有任何 I/O 完成,则停止等待,执行下一个 timer 的回调。如果发生了 I/O 事件,则执行对应的回调;由于执行回调的时间里可能又有 timer 到期了,这里要再次检查 timer 并执行回调。

Node.js它的单线程指的是自身 Javascript 运行环境的单线程,Node.js 并没有给 Javascript 执行时创建新线程的能力,最终的实际操作是通过事件循环来执行的。

ES6之Promise对象

发表于 2016-10-12

Promise

什么是Promise

ECMAScript 6中的Promise规范来源于Promises/A+社区。Promise是一个用于异步处理对象,其中包含了对异步进行各种操作的组件。Promise把JavaScript中的异步处理对象和处理规则进行了规范化,并按照统一的接口来编写,使用规定方法之外的写法会出现错误。

一个promise有下面三个不同状态:

  • pending待承诺 - 初始状态
  • fulfilled实现承诺 - 一个承诺成功实现状态
  • rejected拒绝承诺 - 一个承诺失败的状态

promise必须实现 then 方法

  • then必须返回一个promise
  • 可以调用多次
  • then方法接受两个参数

Promise基本语法

1、创建一个Promise对象

使用 new 来调用Promise的构造器来进行实例化

new Promise(fn) 返回一个promise对象
在fn 中指定异步等处理
处理结果正常的话,调用resolve(处理结果值)
处理结果错误的话,调用reject(Error对象)

var promise = new Promise(function(resolve, reject) {
// 异步处理
// 处理结束后、调用resolve 或 reject
});

2、了解Promise的状态

用 new Promise 实例化的promise对象有以下三个状态。

"has-resolution" - Fulfilled

resolve(成功)时。此时会调用 onFulfilled

"has-rejection" - Rejected

reject(失败)时。此时会调用 onRejected

"unresolved" - Pending

既不是resolve也不是reject的状态。
也就是promise对象刚被创建后的初始化状态等

关于上面这三种状态的读法,其中 左侧为在 ES6 Promises 规范中定义的术语, 而右侧则是在 Promises/A+ 中描述状态的术语。

3、Promise.then()

对通过 new 生成的promise对象为了设置其值在 resolve(成功) / reject(失败) 时调用的 回调函数 可以使用 promise.then() 实例方法。

promise.then(onFulfilled, onRejected)
resolve(成功)时
onFulfilled 会被调用

reject(失败)时
onRejected 会被调用

onFulfilled、onRejected 两个都为可选参数。
promise.then 成功和失败时都可以使用。 如果只想对异常进行处理时可以采用 promise.then(undefined, onRejected) 这种方式,只指定reject时的回调函数即可。 不过这种情况下 promise.catch(onRejected) 应该是个更好的选择。

var promise = new Promise(function(resolve, reject){
    resolve("传递给then的值");
});
// 写法一
promise.then(function (value) {
    console.log(value);
}, function (error) {
    console.error(error);
});
// 写法二(使用catch)
promise.then(function (value) {
    console.log(value);
}).catch(function (error) {
    console.error(error);
});

4、使用快捷方式

静态方法 Promise.resolve(value) 和 Promise.reject(error) 可以认为是 new Promise() 方法的快捷方式。

4.1 Promise.resolve()

比如 Promise.resolve(1) 可以认为是以下代码的语法糖。

new Promise(function(resolve){
    resolve(1);
});

在这段代码中的 resolve(1)会让这个promise对象立即进入确定(即resolved)状态,并将 1 传递给后面then里所指定的 onFulfilled 函数。

方法 Promise.resolve(value) 的返回值也是一个promise对象,所以我们可以像下面那样接着对其返回值进行 .then 调用。

Promise.resolve(1).then(function(value){
    console.log(value); // 1
});

运用场景:Promise.resolve()在promise对象的初始化或者编写测试代码的时候都非常方便。

4.2 Promise.reject()

比如 Promise.reject(new Error(“出错了”)) 就是下面代码的语法糖形式。

new Promise(function(resolve,reject){
    reject(new Error("出错了"));
});

方法 Promise.reject(error) 的返回值也是一个promise对象,所以可以将错误(Error)对象传递到catch里的函数中。

Promise.reject(new Error("BOOM!")).catch(function(error){
    console.error(error);
});

5、Promise.all()

Promise.all 接收一个 promise对象的数组作为参数,当这个数组里的所有promise对象全部变为resolve或reject状态的时候,它才会去调用 .then 方法。

运用场景: 批量请求

var p1 = Promise.resolve(1),
    p2 = Promise.resolve(2),
    p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then(function (results) {
    console.log(results);  // [1, 2, 3]
});
传递给 Promise.all 的promise并不是一个个的顺序执行的,而是同时开始、并行执行的。

可以从以下例子的运行结果看出:

// `delay`毫秒后执行resolve
function timerPromisefy(delay) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(delay);
        }, delay);
    });
}
var startDate = Date.now();
// 所有promise变为resolve后程序退出
Promise.all([
    timerPromisefy(1),
    timerPromisefy(32),
    timerPromisefy(64),
    timerPromisefy(128)
]).then(function (values) {
    console.log(Date.now() - startDate + 'ms');
    // 約128ms
    console.log(values);    // [1,32,64,128]
});

6、Promise.race()

Promise.race 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。

var p1 = Promise.resolve(1),
    p2 = Promise.resolve(2),
    p3 = Promise.resolve(3);
Promise.race([p1, p2, p3]).then(function (value) {
    console.log(value);  // 1
});
进一步分析, 当第一个promise对象变为确定(FulFilled)状态后,它之后的promise对象是否还在继续运行。

// `delay`毫秒后执行resolve
function timerPromisefy(delay) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            console.log(delay)
            resolve(delay);
        }, delay);
    });
}
// 任何一个promise变为resolve或reject 的话程序就停止运行
Promise.race([
    timerPromisefy(1),
    timerPromisefy(10),
    timerPromisefy(20),
]).then(function (value) {
    console.log(value);    // => 1
});
//整体运行结果
1
1
10
20

执行上面代码,我们会看到setTimeout 方法都会执行完毕, console.log 也会分别输出它们的信息。也就是说, Promise.race 在第一个promise对象变为Fulfilled之后,并不会取消其他promise对象的执行。

Promise实例练习

考虑本文一开始的场景,进行小进阶练习,实现每隔一秒依次输出1-10。

//利用for循环 避免一长串的.then写法
function log(i) {
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log(i)
            resolve();
        }, 1000)
    })
}
function printN(n) {
    var p = log(1);
    for (var i = 2; i <= n; i++) {
        var a = function(x) { // 注意回调函数闭包的问题
            return function() {
                return log(x);
            }
        }
        p = p.then(a(i));
    }
}
printN(10);

对于前后端开发模式一点点思考

发表于 2016-09-19

问题的起因

很多东西真的是用起来才会发现问题,问题是前两天交互需要改一个业务,另个部门A同事发我让我去修改,这页面静态确实是我写的,我也抽时间把它写好重新发给A同事。

现状

我们公司目前的开发模式是:交互根据PM出高保真,视觉出视觉稿、切图,然后交给前端出静态,最后交给另一个部门同事把它转换成服务端模板。

这样问题就来了当出现上述交互要改业务的情况,A同事改不了只能发回前端改,如果A把他已经搞成服务端模板的代码返回给前端,前端没环境也没法下手,只能去改以前的静态,改好后重新发给A,这时候A又麻烦了,他要对比两次修改的部分,把自己前一阵的修改合并进去。这样开发效率会慢很多。

基于NodeJS的全栈式开发

这是2014年杭JS,淘宝赫门做的分享(那时候我还在玩泥巴o(╯□╰)o),特意去找了当年的文档,中途岛(Midway Framework)的架构,淘宝的前端团队真的很厉害。

Alt text

多加了一层中间层。
前后端共享模板,前端来决定某个模板是服务端渲染还是客户端渲染,当首屏的时候,就在服务器渲染,次屏和局部刷新的时候,就在浏览器端渲染展示。
加入NodeJs还有很多好处,比如NodeJs的高并发特性,请求合并等。同时使用nodeJs做桥梁,前端可以自己决定获取什么格式的数据。

Alt text

这样还分清了职责

我们该怎么做

在公司产品的角度,觉得我们公司并不需要。其实适合自己就好,没必要占领中间层。相对来说我们公司的前端比较弱,产品线又横向发展并没太深入的产品,分离的还是业务逻辑JS该谁写,把这些都应该交给前端前,端负责展现交互逻辑,去掉A那部分,后端专注于数据接口就好,对于接口接口风格是面向业务 还是面相页面有待统一。这样必然导致前端人员工作量的增加,和水平的相应自我提升。对于数据Mock,可以采用RAP之类接口自动化工具。

编码规范

发表于 2016-09-13

前端编码规范

最近写的项目越来越大,代码堆积的太多,自己看着都头疼下决心整理了下,  
着实一个浩大的工程hh,花了我三个晚上,效果还是有那么点的,
顺便把遇到的整理下,写博客在于记忆,以后回来看看好知道我曾经这么菜。

JS篇

  • 三元条件判断
    用三元操作符分配或返回语句替换简单的if语句

不推荐

if(x === 0) {    
    return '00';   
    } else {    
     return x; 
}       

推荐

return x === 4 ? '00' : x;      
  • for循环
    数组的长度,使用一个变量来接收

不推荐

for(var i=0;i<arr.length,i++){
}       

推荐

for(var i=0,len=arr.length;i<len,i++){
}    
  • DOM操作
    对于重复的dom操作,使用一个变量来进行接收

不推荐

$('.onSelect').text('1');
$('.onSelect').text('2');
$('.onSelect').text('3');
$('.onSelect').text('4');

推荐

var sel=$('.onSelect');
sel.text('1');
sel.text('2');
sel.text('3');
sel.text('4');
  • 立即执行函数
    推荐在立即执行函数开始跟结尾都添加上分号,避免在合并时因为别人的代码不规范而影响到我们自己的代码,如果有用到全局变量应该通过变量传递的方式,让立即执行函数的函数体在调用时,能以局部变量的形式调用,在一定程度上提升程序性能。

不推荐

(function(){
'use strict';
var x = 10,
    y = 100,
    c,
    elem=$('body');
console.log(window.x + ' ' + window.y);
$(document).on('click',function(){
});
if(typeof c==='undefined'){
    //你的代码
}
}());

推荐

;(function($,window,document,undefined){
'use strict';
var x = 10,
    y = 100,
    c,
    elem=$('body');
console.log(window.x + ' ' + window.y);
$(document).on('click',function(){});
if(typeof c==='undefined'){
    //你的代码
}
}(jQuery,window,document));
  • 类型转换

number to string的转换,建议使用 1 + ‘’或String(1),不使用new String(1)或1.toString()的方式。
string to number的转换,建议使用parseInt,必须显式指定第二个参数的进制。下面的例子展示了不指定进制的风险:

parseInt('08'); // 0 
parseInt('08', 10); //8

float to integer的转换,建议使用Math.floor/Math.round/Math.ceil方法,不使用parseInt。

  • 使用命名空间

使用多个命名空间,其中的内容互不干扰

//全局对象
var Wrox = {};
//一个命名空间
Wrox.ProAjax = {};
Wrox.ProAjax.EventUtil = {};
Wrox.ProAjax.CookieUtil = {};
//另一个命名空间
Wrox.ProJS = {};
Wrox.ProJS.EventUtil = {};
Wrox.ProJS.CookieUtil = {};
  • 字符串拼接
    字符串拼接,应使用数组保存字符串片段,使用时调用join方法。避免使用+或+=的方式拼接较长的字符串,每个字符串都会使用一个小的内存片段,过多的内存片段会影响性能。如:

不推荐

var str = ''; 
for (var i = 0, len = list.length; i < len; i++) { 
  str+= '<div>' + list[i] + '</div>'; 
} 
dom.innerHTML = str;

推荐

var str = []; 
for (var i = 0, len = list.length; i < len; i++) { 
  str.push('<div>'+ list[i] + '</div>'); 
} 
dom.innerHTML = str.join('');
  • 尽量避免全局变量和函数

不推荐

var name = "";
function setName(value) {
    name = value;
}
function sayName() {
    console.log(name);
}

推荐

var setPersonName = {
    name: "",
    setName: function (value) {
        this.name = value;
    },
    sayName: function () {
        console.log(this.name);
    }
};
setPersonName.setName("Oli");
setPersonName.sayName();

JS继承

发表于 2016-08-29

继承

一种减少重复性代码的一种设计模式,尽量弱化对象间耦合。在学习JS中继承过程中,遇到坑比较多,抽点时间记录下。由于javascript的语言特性,它的继承也被分为了3中实现方式。

对象创建

说道继承先来看看对象的创建

//第一种,手动创建
var a={'name':'hahaha'};   
//第二种,构造函数
function A(){
    this.name='hahaha';
}
var a=new A();
//第三种,class (ES6标准写法)
class A{
    constructor(){
        super();
        this.name='hahaha';
    }
}
var a=new A()
//其实后面两种方法本质上是一种写法

这三种写法创建的对象的原型(父对象)都是Object,需要提到的是,ES6通过引入class ,extends等关键字,以一种语法糖的形式把构造函数包装成类的概念,更便于大家理解。是希望开发者不再花精力去关注原型以及原型链,也充分说明原型的设计意图和类是一样的。

构造函数继承

在子类型构造函数的内部调用超类构造函数,通过使用call()和apply()方法可以在新创建的对象上执行构造函数。

function Father(name){
    this.name =name;
    this.age=5;
}
function Children(name){
    Father.call(this,name);
}
let jc = new Children("jicheng");
let jc2 = new Children("jicheng2");
jc2.age = 10;
console.log(jc.name + jc.age);  //jicheng 5
console.log(jc2.name + jc2.age);  //jicheng2 10

当我们new这个构造函数的时候,就会生成一个Children对象的实例。
但是通过上面的例子你会发现用构造函数生成实例对象,它有一个缺点,那就是无法共享属性和方法,没有涉及到原型。
因为这两个对象的age属性是独立的,修改其中一个,不会影响到另一个。
这样做的坏处就是会造成资源浪费,那么我们要如何来解决这件事呢,那就需要prototype出场了。

组合继承

就是融合了原型和构造器的一种继承方法。 这个应该算是一种比较稳妥的继承方式.即能排除引用类型造成的问题,又可以实现共享的效果。

function Father(name){
    this.name =name;
}
Father.prototype.age=5;
function Children(name){
    Father.call(this,name);
}
Children.prototype = new Father();
Children.prototype.constructor = Children;
let jc = new Children("jicheng");
let jc2 = new Children("jicheng2");
Father.prototype.age = 10;
console.log(jc.name + jc.age);  //jicheng 10
console.log(jc2.name + jc2.age);  //jicheng2 10

age属性放在prototype对象里,是两个实例对象共享的。只要修改了prototype对象,就会同时影响到两个实例对象。
这样会两次调用到父类型,对内存影响还是比较大的。但是es6 class 的出现,解决了这一问题。

class

在使用class的使用,你完全可以不去写,constructor, prototype之类的东西了

//原生继承方式   
function Father(name){
    this.name =name;
}
Father.prototype.age=function(){
    return "4"
};
var father = new Father("张三")
//使用class
class Father(){
    constructor(name){
        this.name = name;
    }
    age(){
        return "4"
    }
}
var father = new Father("张三")

DOM编程总结

发表于 2016-08-02

节点操作

获取节点

  • 父子关系
    • parentNode
    • firstChild/lastChild/childNodes
    • childNodes/children
  • 兄弟关系
    • previousSibling/nextSibling
    • previousElementSibling/nextElementSibling
  • 选择器
    • element=document.getElementById(id)
    • collection=element.getElementByTagName(tarName)
      • collection 是个动态的随着节点改变而改变
      • tarName=*代表获取所有
    • collection=element.getElementByClassName(className)
    • list=element.querySelect/All(selector)
  • 现代浏览器中内置的可以等效替代jQuery的功能

    • 创建全局的 ‘$’ 变量

      1
      2
      3
      window.$ = function(selector) {   
      return document.querySelector(selector);
      };

创建节点

  • element=document.createElement(tagName)

修改节点

  • element.textContent 获取节点以及后面的文本内容
  • element.textContent = “修改内容”
  • element.innerText 不规范不推荐使用

插入节点

  • element.appendChild(a) 在指定元素后追加a
  • element.insertBefore(h,a) 在指定元素h之前添加a

删除节点

  • element.removeChild

innerHtml

  • 节点的HTML内容

设置样式

  • element.style.cssText=’color:red;’ 一条语句设置样式
  • element.className =’clasname’ 编辑类名一次修改多个样式
  • element.style.color 获取颜色样式 不一定是它实际样式 当元素上有设置颜色的才获取到实际的
    * window.getComputedStyle(element).color 获取到的是实际
    

事件

事件注册

  • elem.addEventListener(type,function,false/true)

浏览器兼容型事件注册

1
2
3
4
5
6
7
8
9
function addEvent(element,type,handler){
if (element.addEventListener) {
element.addEventListener(type,handler,false);
}else if (element.attachEvent) {
element.attachEvent('on'+type,handler);
}else{
element['on'+type]=handler;
}
}

取消事件注册

  • elem.removeEventListener(type,function,false/true)

事件触发

  • elem.dispatchEvent(type)

事件对象

  • 属性

    • type
    • target

      1
      2
      3
      4

      //兼容
      var event=e || window.event;
      var target=event.target || event.srcElement;
    • currentTarget

  • 方法
    • stopPropagation(W3C) 阻止事件传播 cancelBubble(IE低版本)
    • preventDefault(W3C) 阻止默认行为 returnValue=false(IE低版本)
    • stopImmediatePropagation

事件类型

  • Event
    • load
    • unload
    • error
    • select
    • abort
  • UIEvent //继承自Event
    • resize
    • scroll
  • FocusEvent //继承自UIevent
    • blur
    • focus
    • focusin
    • focusout
  • InputEvent //继承自UIevent
    • beforeinput
    • input
  • KeyboarEvent //继承自UIevent
    • keydown
    • keyup
  • MouseEvent //继承自UIevent
    • click
    • dbclick
    • mousedown
    • mouseleave
    • mousemove
    • mouseout
    • mouseup
  • wheelEvent //继承自MouseEvent
    • wheel

CSS布局

发表于 2016-07-27

CSS布局

布局–display

block

  • 默认宽度是父元素宽度
  • 可设置宽高
  • 换行显示
  • 常见块状元素 div p h1-h6 ul from

inline

  • 默认宽度是内容宽度
  • 不可设置宽高
  • 同行显示
  • 常见行内元素 span a label cite em

inline-block

  • 默认宽度是内容宽度
  • 可设置宽高
  • 同行显示
  • 常见的行内块状元素 input textarea select button

none

  • 隐藏不占位置
  • visiblity:hidden隐藏占位置

布局–position

relative

  • 在文档流中
  • 参照物是本身

absolute

  • 默认宽度是内容宽度
  • 脱离文档流
  • 参照物是第一个定位的祖先元素

fixed

  • 默认宽度是内容宽度
  • 脱离文档流
  • 参照物是视窗

布局–float

  • 默认宽度为内容宽度
  • 半脱离文档流,对元素脱离文档流,对内容在文档流
  • 向指定方向移动

清除浮动

1
2
3
4
5
6
7
8
9
10
11
.fix:before,.fix:after{
content: "";
display: table;
}
.fix:after{
clear: both;
overflow: hidden;
}
.fix{
zoom:1; /*for IE6 IE7*/
}

居中布局

水平居中

1
2
3
4
<div class="parent">
<div class="child">DEMO</div>
</div>
body{margin:20px;}
  • inline-block + text-align

    优点:兼容性好;缺点:子元素会继承父元素的居中
    
    1
    2
    3
    4
    5
    6
    .parent{
    text-align: center;
    }
    .child{
    display: inline-block;
    }
  • table + margin

    1
    2
    3
    4
    .child{
    display: table;
    margin: 0 auto;
    }
  • absolute + transform

    优点:脱离文档流对其它元素没有影响。缺点:兼容性问题
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    .parent{
    height:1.5em;
    position: relative;
    }
    .child{
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    }
  • flex + justify-content

    1
    2
    3
    4
    .parent{
    display: flex;
    justify-content: center;
    }

垂直居中

1
2
3
4
5
6
<div class="parent">
<div class="child">DEMO</div>
</div>
body{margin:20px;}
.parent{width:4em;height:500px;}
.child{width:100%;}
  • table-cell + vertical-align

    1
    2
    3
    4
    .parent{
    display: table-cell;
    vertical-align: middle;
    }
  • absolute + transform

    1
    2
    3
    4
    5
    6
    7
    8
    .parent{
    position: relative;
    }
    .child{
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    }
  • flex + align-items

    1
    2
    3
    4
    .parent{
    display: flex;
    align-items: center;
    }

垂直水平居中

1
2
3
4
5
6
<div class="parent">
<div class="child">DEMO</div>
</div>

body{margin:20px;}
.parent{width:200px;height:300px;}
  • inline-block + text-aligin + table-cell + vertical-align

    1
    2
    3
    4
    5
    6
    7
    8
    .parent{
    text-align: center;
    display: table-cell;
    vertical-align: middle;
    }
    .child{
    display: inline-block;
    }
  • absolute + transform

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    .parent{
    position: relative;
    }
    .child{
    position: absolute;
    left: 50%;
    top: 50%;
    transform:
    translate(-50%,-50%);
    }
  • flex + justify-content + align-items

    1
    2
    3
    4
    5
    .parent{
    display: flex;
    justify-content: center;
    align-items: center;
    }

多列布局

定宽自适应

1
2
3
4
5
6
7
8
9
<div class="parent">
<div class="left">
<p>left</p>
</div>
<div class="right">
<p>right</p>
<p>right</p>
</div>
</div>
左侧定宽右侧自适应
  • float + margin

    1
    2
    3
    4
    5
    6
    7
    8
    .left{
    float: left;
    width: 100px;
    }
    .right{
    margin-left: 120px;
    }
    //在IE6中因为right是没有浮动的所以right里面的文字会往右边缩进3像素
  • float + overflow

    1
    2
    3
    4
    5
    6
    7
    8
    9
    .left{
    float: left;
    width: 100px;
    margin-right: 20px;
    }
    .right{
    overflow: hidden;
    }
    //IE6不支持
  • table

    table默认宽度内容宽度、且对margin无效、响应padding
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    .parent{
    display: table;
    width: 100%;
    table-layout: fixed;//布局优先、且加速table渲染
    }
    .left,.right{
    display: table-cell;
    }
    .left{
    width: 100px;
    padding-right: 20px;
    }
  • flex

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    .parent{
    display: flex;
    }
    .left{
    width: 100px;
    margin-right: 20px;
    }
    .right{
    flex: 1;
    }
左侧不定宽右侧自适应
  • float + overflow
  • table
  • flex

等分布局

  • float

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <div class="parent">
    <div class="column"><p>1</p></div>
    <div class="column"><p>2</p></div>
    <div class="column"><p>3</p></div>
    <div class="column"><p>4</p></div>
    </div>
    .parent{
    margin-left: -20px;
    }
    .column{
    float: left;
    width: 25%;
    padding-left: 20px;
    box-sizing: border-box;
    }
  • table

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <div class="parent-fix">
    <div class="parent">
    <div class="column"><p>1</p></div>
    <div class="column"><p>2</p></div>
    <div class="column"><p>3</p></div>
    <div class="column"><p>4</p></div>
    </div>
    </div>
    .parent-fix{
    margin-left: -20px;
    }
    .parent{
    display: table;
    width:100%;
    table-layout: fixed;//平分单元格
    }
    .column{
    display: table-cell;
    padding-left: 20px;
    }
  • flex

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <div class="parent">
    <div class="column"><p>1</p></div>
    <div class="column"><p>2</p></div>
    <div class="column"><p>3</p></div>
    <div class="column"><p>4</p></div>
    </div>
    .parent{
    display: -flex;
    }
    .column{
    flex:1;
    }
    .column+.column{
    margin-left:20px;
    }

等高布局

  • table
  • flex
  • float
    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
    //float的伪等高
    <div class="parent">
    <div class="left">
    <p>left</p>
    </div>
    <div class="right">
    <p>right</p>
    <p>right</p>
    </div>
    </div>
    body{margin:20px;}
    p{background: none!important;}
    .left,.right{background: #444;}
    .parent{
    overflow: hidden;
    }
    .left,.right{
    padding-bottom: 9999px;
    margin-bottom: -9999px;
    }
    .left{
    float: left; width: 100px;
    margin-right: 20px;
    }
    .right{
    overflow: hidden;
    }

用Gulp优化项目

发表于 2016-07-11

需求

公司的项目性能很差,由于刚来实习老大让我尝试优化下就当给我练练手

原因

发现这个项目是JSP写的动态网页并没有模块化预加载什么的单纯的引入JS/CSS,每次访问请求用太多时间

解决

想到就先合并请求,并压缩代码之类的 工具是基于流的Gulp
//然并没用过

首先安装Gulp

1
npm install -g gulp

安装相应的插件

为了解决插件和模块依赖被重复的引入进来我用了ulp-load-plugins
为了以后项目的升级我也进行了参数配置全局化在gulp.config.js

1
npm install gulp-minify-css gulp-uglify gulp-concat gulp-rename ulp-load-plugins through2 vinyl-source-stream gulp-marked --save-dev gulp

由于是jsp写的路径useref方法无法识别

用到filesystem模块先读到index.jsp然后用正则替换路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
gulp.task('readBuffer', function() {
return gulp.src('app/index.jsp')
.pipe(modify(swapStuff))
.pipe(modify(swapStuff_))
.pipe(gulp.dest(config.build));

})
function modify(modifier) {
return through2.obj(function(file, encoding, done) {
var content = modifier(String(file.contents));
file.contents = new Buffer(content);
this.push(file);
done();
});
}
//正则
function swapStuff(data) {
return data.replace(/\$\{pageContext\.request\.contextPath\s*\}/g, '..');
}
function swapStuff_(data) {
return data.replace(/window\.ContextPath \= \"\.\.\/\"/, 'window.ContextPath = "${pageContext.request.contextPath}/"')
.replace(/base\shref\=\"\.\.\/\"/,'base href="dist/"');
}

然后用useref方法读到所有内容,进行压缩合并等步骤

这里需要在index.jsp根据你的需求页面配置

1
2
3
4
5
6
7
8
<!-- build:css css/lib.css -->
<!-- endbuild -->
<!-- build:css css/app.css -->
<!-- endbuild -->
<!-- build:js js/lib.js -->
<!-- endbuild -->
<!-- build:js js/app.js -->
<!-- endbuild -->

1
2
3
4
5
6
7
8
9
10
11
12
13
gulp.task('optimize',['readBuffer'],function() {
return gulp
.src('dist/index.jsp')
.pipe($.useref()) // 解析jsp中build,将里面引用到的文件合并传过来
.pipe($.if('js/app.js', $.ngAnnotate()))
.pipe($.if('js/*.js', $.uglify()))
.pipe($.if('css/*.css', $.minifyCss()))
.pipe($.if('!index.jsp', $.rev()))
.pipe($.revReplace())
.pipe(gulp.dest(config.build))
.pipe($.rev.manifest())
.pipe(gulp.dest('dist/rev/'));
});

给资源文件加时间戳

1
2
3
4
5
gulp.task('rev', ['optimize'],function() {
return gulp.src(['dist/rev/*.json', 'dist/index.jsp'])
.pipe($.revCollector())
.pipe(gulp.dest(config.build));
});

由于所有图片都合并到一起、路径就会发生变化这里对路径做个统一

1
2
3
4
5
6
7
8
9
gulp.task('images', function() {
return gulp
.src(config.images)
.pipe($.rename(function(path) {
path.dirname = path.dirname.replace(/.*\\images(.*)/, 'images$1');
path.dirname = path.dirname.replace(/.*\\img(.*)/, 'img$1');
}))
.pipe(gulp.dest(config.build + 'images/'));
});

最后调用任务

1
gulp.task('default', ['rev','images']);

结束

刚接触gulp目前只是实现了更能,代码还有很多不足和优化地方欢迎指正O(∩_∩)O哈哈~
源码

注:任务依赖要处理好

JS作用域

发表于 2016-07-08

JavaScript的作用域链

JavaScript需要查询一个变量x时,首先会查找作用域链的第一个对象,如果以第一个对象没有定义x变量,JavaScript会继续查找有没有定义x变量,如果第二个对象没有定义则会继续查找,以此类推。下面的代码涉及到了三个作用域链对象,依次是:inner、rainman、window。

1
2
3
4
5
6
7
8
9
10
11
var rain = 1;
function rainman(){
var man = 2;
function inner(){
var innerVar = 4;
//var rain = 5;//函数体内部,局部变量的优先级比同名的全局变量高。
alert(rain);
}
inner(); //调用inner函数 1
}
rainman(); //调用rainman函数

JavaScript没有块级作用域

比如if条件语句,就不算一个独立的作用域.
为了解决块级作用域,
ES6引入了新的关键字let,用let替代var可以申明一个块级作用域的变量.
ES6标准引入了新的关键字const来定义常量,const与let都具有块级作用域:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function func() {
if (true) {
var tmp = 123;
}
console.log(tmp); // 123 var关键字声明的变量有一个变量提升的过程
}

function func() {
if (true) {
let tmp = 123;
}
console.log(tmp); // ReferenceError: tmp is not defined
}

const PI = 3.14;
PI = 3; // 某些浏览器不报错,但是无效果!
PI; // 3.14

函数中声明的变量在整个函数中都有定义

由于在函数rain内局部变量x在整个函数体内都有定义( var x= ‘rain-man’,进行了声明),所以在整个rain函数体内隐藏了同名的全局变量x。这里之所以会弹出’undefined’是因为,第一个执行alert(x)时,局部变量x仍未被初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var x = 1;
function rain(){
alert( x ); //弹出 'undefined',而不是1
var x = 'rain-man';
alert( x ); //弹出 'rain-man'
}
rain();
//等效于下面
var x = 1;
function rain(){
var x;
alert( x );
x = 'rain-man';
alert( x );
}
rain();

未使用var关键字定义的变量都是全局变量、全局变量都是window对象的属性

用Hexo快速搭建站

发表于 2016-07-08

准备

首先电脑里需要安装以下两个软件

  • git
  • Node.js

Git绑定Github账号

  • 通过ssh key与github网站帐户绑定
    使用命令:
1
ssh-keygen -C '你的GitHub账号' -t rsa
  • 会在用户目录 ~/.ssh/下建立相应的密钥文件然后打开这个文件复制密钥

  • 打开GithHub设置 增加密钥

  • 通过以下命令测试

1
ssh -v 你的GitHub账号

搭建本地Hexo博客

  • 安装Hexo
1
npm install -g hexo
  • 创建hexo文件夹
    随便一个文件夹下(如E:\hexo),在E:\hexo内点击鼠标右键,选择Git bash,执行以下指令
1
2
3
hexo init
npm install
npm install hexo-deployer-git --save(这步很重要)
  • 生成和查看本地博客
    执行以下命令,然后到浏览器输入localhost:4000查看本地博客。
1
2
hexo generate
hexo server

部署到GitHub

  • 编辑_config.yml
1
2
3
4
deploy:
type: git
repository: https://github.com/你的用户名/你的用户名.github.io.git
branch: master
  • 执行下列指令即可完成部署
1
2
hexo generate
hexo deploy

复制主题

看的一些大神酷炫的主题,是不是很想要(有能力自己写也可以)

  • 复制主题
    例如复制next主题
1
git clone https://github.com/iissnan/hexo-theme-next themes/next
  • 启用主题
    修改Hexo目录下的config.yml配置文件中的theme属性
1
theme: next
  • 更新主题
1
2
cd themes/next
git pull
  • 主题其他修改配置可以去看作者文档

  • 增加一些Hexo常用命令

1
2
3
4
5
6
7
hexo new "文件名" #创建新文章,名称可自己定义
hexo n == hexo new #创建新文章,默认名称为 my new post
hexo g == hexo generate #生成
hexo s == hexo server #启动本地服务,进行文章预览调试
hexo d == hexo deploy #部署到GitHub
hexo d -g == hexo generate hexo deploy
hexo new page "about"
1…67
lennonover

lennonover

一丿口石砳磊

70 日志
28 标签
© 2022 lennonover
由 Hexo 强力驱动
主题 - NexT.Muse