Giter VIP home page Giter VIP logo

blog's Introduction

sidebarDepth
0

一兵的技术成长与实践之路

经历了许久的职场生活和感情生活后,平静的生活有如湖面一般沉寂,于是便思考,是不是应该在短暂有限的人生路途中做点什么事情,这样子当老的时候,再回顾自己的生平做了什么事情,才不至于没有波澜,也没有涟漪。于是乎就在这个博客记录一些自己的想法、读书心得与收获、技术上的积累、产品上的思考以及对于为人处世的方与圆。

文章目录

关于一兵

blog's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

sihunqu123

blog's Issues

图解 Flexbox

图解 Flexbox

本文图片、部分翻译来自原文 How Flexbox works — explained with big, colorful, animated gifs

属性#1 display: flex

示例如下

flex1.gif

上面4个 div 默认为 display: block

我们给其父级容器添加

#container {
  display: flex;
}

flex2.gif

其实把每个 div 添加了一个 flex context (弹性上下文)

属性#2 Flex Direction

一个 Flexbox 的容器有两个轴:主轴 交叉轴。如下:

flex3.png

默认情况下容器中的每个元素都会沿着主轴自左向右一次排列。所以容器设置为 display: flex 后所有的 div 会排列在一行上

但是 Flex-direction 属性可以让主轴旋转,如下:

flex4.gif

注意: flex-direction: column 并不是把 div 从主轴移动到交叉轴上,而是让主轴从水平变为垂直

flex-direction 还有两者值: row-reversecolumn-reverse (顾名思义 就是反转)

flex5.gif

属性#3 Justify Content

justify-content 用来控制元素在主轴上的对齐方式

先深入理解下主轴和交叉轴的区别。首先设置为 flex-direction: row

#container {
  display: flex;
  flex-direction: row;
  justify-content: flex-start;
}

justify-content 有5个可选值:

  1. flex-start
  2. flex-end
  3. center
  4. space-between
  5. space-around

flex6.gif

space-between 会使每个 div 之间产生相同的小的间隔,但在 div 和容器之间没有间隔

space-around 会在每个 div 两侧各产生一个相同的间隔,即容器和最外层的 div 之间的间隔刚好是两个 div 间隔的一半

注意:justify-content 是沿着主轴工作的。 flex-direction 是改变的主轴方向的。

属性#4 Align Items

justify-content 是沿着主轴工作的,而 align-items 则是沿着交叉轴工作。

首先重置 flex-direction: row 两个轴展示如下:

flex7.png

align-items 有5个可选值:

  1. flex-start
  2. flex-end
  3. center
  4. stretch
  5. baseline

看看后两个,其中每个 div 中的数字都包含在一个 p 标签中

align-items: stretch 时每个 div 都会充满交叉轴
align-items: baseline 时按照 p 标签的底部对齐

flex8.gif

注意:align-items: stretch 时每个 div 的 height 必须为 auto 否则 height 属性会覆盖 stretch 的效果

align-items: baseline 时如果 div 内没有 p 标签或者 div 内没有文字或者子标签内没有文字将按照每个 div 的底部对齐。如下:

flex9.png

进一步理解主轴和交叉轴的区别,把 justify-content 和 align-items 合在一起,看看在 flex-direction 两种值下的效果

flex10.gif

flex-direction: row 时每个 div 按照水平主轴排列
flex-direction: column时每个 div 按照垂直主轴向下排列

属性#5 Align Self

align-self 可以手动设置一个元素的对齐方式

它会针对一个 div 覆盖掉 align-items 属性,因为容器内元素属性都为 auto, 所以每个 div 都会使用父容器的 align-items 属性值

#container {
  align-items: flex-start;
}
.square#one {
  align-self: center;
}
/* 只有 #one 这个 div 会居中 */

将前两个 div 设置 align-self 属性,后两个使用 align-items: centerflex-direction: row 如下

flex11.gif

JavaScript设计模式的目的与概念

JavaScript设计模式的目的与概念

[toc]

我们每天都在使用设计模式,通过一个例子来了解设计模式带来的好处与概念

我们每天都在使用模式为了了解使用模式的好处,我们来研究一个非常简单的元素选择问题,我们通常是用jQuery库来解决这种问题。试想,如果我们有一个脚本,想为页面上每个具有“foo”类(class属性)的DOM元素增加一个计数器,查询列表最简单有效的方法是什么?对,有几种不同的方法可以解决这个问题。

在页面上选择所有元素并存储。接着过滤该集合并使用正则表达式(或另一种方式)来存储那些具有“foo”类的元素。

使用浏览器原生的querySelectorAll()等功能来选择所有具有“foo”类的元素。

同样地,使用原生特性getElementsByClassName()等功能来重新获得所需的集合。那么,哪种方法最快?最快的实际上是第3种方法,它比其他方法快8至10倍。但在实际应用中,IE9以下的版本不支持第3种方法,因此必须使用第1种方法,而其他方法都行不通。但使用jQuery的开发人员不必担心这个问题,因为通过使用Facade(外观)模式,它已经被抽象出来了。正如我们将在后面详细介绍的,该模式为若干更复杂的底层代码体提供了一套简单的抽象接口(例如$el.css()和$el.animate())。正如我们所看到的,这意味着我们可以在“实现级”细节上花费更少的时间。

根据现有浏览器的支持范围,jQuery会在幕后选择最佳的元素选择方式,我们只需要使用抽象层即可。我们可能都很熟悉jQuery的$(“selector”)。用它选择页面上的HTML元素比需要手动处理的getElementById()、getElementsByClassName()、getElementByTagName等容易得多。虽然我们知道querySelectorAll()可以解决这个问题,但让我们来比较一下使用jQuery的Facade(外观)模式接口与自己选择最佳路径这两种方式所花费的精力。根本就不用比!使用设计模式的抽象可以体现真实价值。

设计模式含义

编写易于维护的代码,其中一个最重要方面是能够找到代码中重复出现的主题并优化它们。这也是设计模式有价值的地方。

模式是一种可复用的解决方案,可用于解决软件设计中遇到的常见问题,如在我们编写的JavaScript应用程序的实例中。另一种模式的方式是将解决问题的方法制作成模板,并且这些模板可应用于多种不同的情况。

使用设计模式的优点或者好处

模式是已经验证的解决方案。

它们为解决软件开发中遇到的问题提供可靠的方法,也就是使用已经验证的解决方案,这些解决方案体现了开发人员的经验及见解,他们为定义和改进这些方法提供了帮助,从而形成现在的模式。

模式很容易被复用。

它们为解决软件开发中遇到的问题提供可靠的方法,也就是使用已经验证的解决方案,这些解决方案体现了开发人员的经验及见解,他们为定义和改进这些方法提供了帮助,从而形成现在的模式。

模式富有表达力。

看到模式时,通常就表示有一个设置好的结构和表达解决方案的词汇,以帮助我们非常轻松地表达出所实现的大型解决方案。

其他优点

  • 模式不是一种确切的解决方案。重要的是,我们要知道模式的作用仅仅是为我们提供一个解决问题的方案。模式无法解决所有的设计问题,也无法取代优秀软件设计师的工作,但模式确实能够支持这些工作。接下来我们将了解一下模式的其他一些优点。

  • 复用模式有助于防止在应用程序开发过程中小问题引发大问题。这意味着当我们在已经验证的模式基础上编写代码时,可以在代码结构上少花点时间,从而有更多的时间专注于整体解决方案的质量。这是因为模式支持我们用更结构化和更有组织性的方式编写代码,从而避免以后因代码的整洁性问题而重构代码。

  • 模式可以提供通用的解决方案,并且其记录方式不需要与某个特定问题挂钩。这种通用的方法意味着不管现在开发的是哪种应用程序(在许多情况下是指编程语言),设计模式都可用于改进代码的结构。 •某些模式确实能够通过避免代码复用来减少代码的总体资源占用量。通过鼓励开发人员更密切地关注解决方案中可以即刻减少代码复用的部分,例如,减少类似处理过程的函数数量,用一个广义函数取而代之,那么就可以减小代码库的总大小。这也就是所谓的使代码更加简洁。 •模式添加到开发人员的词汇中,会使沟通更快速。

  • 经常使用的模式可以逐步改进,因为其他开发人员使用这些模式后总结出的共同经验又贡献给了设计模式社区。在某些情况下,这会创造出全新的设计模式,而在其他情况下,会对有关如何更好地使用特定模式的指导做出改进。这样可

队列JavaScript实现

队列JavaScript实现

队列是一种列表,不同的是队列只能在队尾插入元素,在对首删除元素。队列用于存储按顺序排列的数据,先进先出。队列应用比较广泛,提交操作系统执行一些进程,打印任务池,日常排队买东西等等。

队列的JS实现

function Queue() {
    this.dataStore = [];
    this.enqueue = enqueue;
    this.dequeue = dequeue;
    this.front = front;
    this.back = back;
    this.toString = toString;
    this.empty = empty;
}
function enqueue(element) {
    this.dataStore.push(element)
}
function dequeue() {
	this.dataStore.shift()
}
function front() {
    return this.dataStore[0];
}
function back() {
	return this.dataStore[this.dataStore.length - 1];
}
function toString() {
	var str = '',
        i = 0,
        l = this.dataStore.length;
	for ( ; i < l; i++) {
		str += this.dataStore[i] + "\n";
    }
	return str;
}
function empty() {
    return this.dataStore.length === 0;
}

var q = new Queue();


MongDb安装与基本操作

参考教程

官方网站下载

windows版本下载

特别注意:安装过程不要勾选安装compass 相当慢,有时候甚至卡住一动不动

管理数据目录

MongoDB将数据目录存储在 db 目录下。但是这个数据目录不会主动创建,我们在安装完成后需要创建它。请注意,数据目录应该放在根目录下((如: C:\ 或者 D:\ 等 )。

cd d:

mkdir mongdb



命令行下运行 MongoDB 服务器

为了从命令提示符下运行 MongoDB 服务器,你必须从 MongoDB 目录的 bin 目录中执行 mongod.exe 文件。

C:\mongodb\bin\mongod --dbpath c:\data\db

// 执行成功后,会输出如下信息

2015-09-25T15:54:09.212+0800 I CONTROL  Hotfix KB2731284 or later update is not
installed, will zero-out data files
2015-09-25T15:54:09.229+0800 I JOURNAL  [initandlisten] journal dir=c:\data\db\j
ournal
2015-09-25T15:54:09.237+0800 I JOURNAL  [initandlisten] recover : no journal fil
es present, no recovery needed
2015-09-25T15:54:09.290+0800 I JOURNAL  [durability] Durability thread started
2015-09-25T15:54:09.294+0800 I CONTROL  [initandlisten] MongoDB starting : pid=2
488 port=27017 dbpath=c:\data\db 64-bit host=WIN-1VONBJOCE88
2015-09-25T15:54:09.296+0800 I CONTROL  [initandlisten] targetMinOS: Windows 7/W
indows Server 2008 R2
2015-09-25T15:54:09.298+0800 I CONTROL  [initandlisten] db version v3.0.6

启动服务

安装 MongoDB服务

通过执行mongod.exe,使用--install选项来安装服务,使用--config选项来指定之前创建的配置文件。

C:\mongodb\bin\mongod.exe --config "C:\mongodb\mongod.cfg" --install

要使用备用 dbpath,可以在配置文件(例如:C:\mongodb\mongod.cfg)或命令行中通过 --dbpath 选项指定。

如果需要,您可以安装 mongod.exe 或 mongos.exe 的多个实例的服务。只需要通过使用 --serviceName 和 --serviceDisplayName 指定不同的实例名。只有当存在足够的系统资源和系统的设计需要这么做。

启动MongoDB服务

net start MongoDB

关闭MongoDB服务

net stop MongoDB

移除 MongoDB 服务

C:\mongodb\bin\mongod.exe --remove

命令行下运行 MongoDB 服务器 和 配置 MongoDB 服务 任选一个方式启动就可以。

使用IndexDB存储图像和文件

[TOC]

使用IndexDB存储图像和文件

有一天,我们写了关于如何在localStorage中保存图像和文件的文章,它是关于我们今天可用的实用主义。 然而,localStorage有一些性能影响 - 我们将在稍后的博客中讨论这个问题 - 并且未来期望的方法是使用IndexedDB。 在这里,我将向您介绍如何在IndexedDB中存储图像和文件,然后通过ObjectURL呈现它们。

本文是翻译过来的,原文在这里Storing images and files in IndexedDB

关于作者: Robert Nyman [Editor emeritus]

Technical Evangelist & Editor of Mozilla Hacks. Gives talks & blogs about HTML5, JavaScript & the Open Web. Robert is a strong believer in HTML5 and the Open Web and has been working since 1999 with Front End development for the web - in Sweden and in New York City. He regularly also blogs at http://robertnyman.com and loves to travel and meet people.

使用IndexDB存储图像和文件的常规步骤

首先,我们来谈谈我们将创建一个IndexedDB数据库,将文件保存到其中然后将其读出并显示在页面中的步骤:

  • 1、创建或打开数据库
  • 2、创建一个objectStore
  • 3、将图像文件检索为blob
  • 4、初始化一个数据库事物
  • 5、保存图像blob到数据库中去
  • 6、读出保存的文件并从中创建ObjectURL并将其设置为页面中图像元素的src

1、创建或打开数据库。


// IndexedDB
window.indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.OIndexedDB || window.msIndexedDB,
    IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.OIDBTransaction || window.msIDBTransaction,
    dbVersion = 1;

/* 
    Note: The recommended way to do this is assigning it to window.indexedDB,
    to avoid potential issues in the global scope when web browsers start 
    removing prefixes in their implementations.
    You can assign it to a varible, like var indexedDB… but then you have 
    to make sure that the code is contained within a function.
*/

// Create/open database
var request = indexedDB.open("elephantFiles", dbVersion);

request.onsuccess = function (event) {
    console.log("Success creating/accessing IndexedDB database");
    db = request.result;

    db.onerror = function (event) {
        console.log("Error creating/accessing IndexedDB database");
    };
    
    // Interim solution for Google Chrome to create an objectStore. Will be deprecated
    if (db.setVersion) {
        if (db.version != dbVersion) {
            var setVersion = db.setVersion(dbVersion);
            setVersion.onsuccess = function () {
                createObjectStore(db);
                getImageFile();
            };
        }
        else {
            getImageFile();
        }
    }
    else {
        getImageFile();
    }
}

// For future use. Currently only in latest Firefox versions
request.onupgradeneeded = function (event) {
    createObjectStore(event.target.result);
};

使用它的预期方法是在创建数据库时触发onupgradeneeded事件或获取更高版本号。 目前仅在Firefox中支持此功能,但很快将在其他Web浏览器中支持。 如果Web浏览器不支持此事件,则可以使用已弃用的setVersion方法并连接到其onsuccess事件。

2、创建一个objectStore(如果它尚不存在)

// Create an objectStore
console.log("Creating objectStore")
dataBase.createObjectStore("elephants");

在这里,您创建一个ObjectStore,您将存储数据 - 或者在我们的例子中,文件 - 并且一旦创建,您不需要重新创建它,只需更新其内容即可。

3、将图像文件检索为blob

// Create XHR
var xhr = new XMLHttpRequest(),
    blob;

xhr.open("GET", "elephant.png", true);
// Set the responseType to blob
xhr.responseType = "blob";

xhr.addEventListener("load", function () {
    if (xhr.status === 200) {
        console.log("Image retrieved");
        
        // File as response
        blob = xhr.response;

        // Put the received blob into IndexedDB
        putElephantInDb(blob);
    }
}, false);
// Send XHR
xhr.send();

此代码直接将文件的内容作为blob获取。目前只支持Firefox。 收到整个文件后,将blob发送到函数以将其存储在数据库中。

4、初始化一个数据库事物


// Open a transaction to the database
var transaction = db.transaction(["elephants"], IDBTransaction.READ_WRITE);

要开始向数据库写入内容,您需要使用objectStore名称和要执行的操作类型(在本例中为read和write)启动事务。

5、保存图像blob到数据库中去

// Put the blob into the dabase
transaction.objectStore("elephants").put(blob, "image");

一旦事务到位,您将获得对所需objectStore的引用,然后将您的blob放入其中并为其提供密钥。

6、读出保存的文件并从中创建ObjectURL并将其设置为页面中图像元素的src

// Retrieve the file that was just stored
transaction.objectStore("elephants").get("image").onsuccess = function (event) {
    var imgFile = event.target.result;
    console.log("Got elephant!" + imgFile);

    // Get window.URL object
    var URL = window.URL || window.webkitURL;

    // Create and revoke ObjectURL
    var imgURL = URL.createObjectURL(imgFile);

    // Set img src to ObjectURL
    var imgElephant = document.getElementById("elephant");
    imgElephant.setAttribute("src", imgURL);

    // Revoking ObjectURL
    URL.revokeObjectURL(imgURL);
};

使用相同的事务来获取刚刚存储的图像文件,然后创建一个objectURL并将其设置为页面中图像的src。
例如,这也可以是一个附加到脚本元素的JavaScript文件,然后它将解析JavaScript。

最后完整代码

(function () {
    // IndexedDB
    var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.OIndexedDB || window.msIndexedDB,
        IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.OIDBTransaction || window.msIDBTransaction,
        dbVersion = 1.0;

    // Create/open database
    var request = indexedDB.open("elephantFiles", dbVersion),
        db,
        createObjectStore = function (dataBase) {
            // Create an objectStore
            console.log("Creating objectStore")
            dataBase.createObjectStore("elephants");
        },

        getImageFile = function () {
            // Create XHR
            var xhr = new XMLHttpRequest(),
                blob;

            xhr.open("GET", "elephant.png", true);
            // Set the responseType to blob
            xhr.responseType = "blob";

            xhr.addEventListener("load", function () {
                if (xhr.status === 200) {
                    console.log("Image retrieved");
                    
                    // Blob as response
                    blob = xhr.response;
                    console.log("Blob:" + blob);

                    // Put the received blob into IndexedDB
                    putElephantInDb(blob);
                }
            }, false);
            // Send XHR
            xhr.send();
        },

        putElephantInDb = function (blob) {
            console.log("Putting elephants in IndexedDB");

            // Open a transaction to the database
            var transaction = db.transaction(["elephants"], IDBTransaction.READ_WRITE);

            // Put the blob into the dabase
            var put = transaction.objectStore("elephants").put(blob, "image");

            // Retrieve the file that was just stored
            transaction.objectStore("elephants").get("image").onsuccess = function (event) {
                var imgFile = event.target.result;
                console.log("Got elephant!" + imgFile);

                // Get window.URL object
                var URL = window.URL || window.webkitURL;

                // Create and revoke ObjectURL
                var imgURL = URL.createObjectURL(imgFile);

                // Set img src to ObjectURL
                var imgElephant = document.getElementById("elephant");
                imgElephant.setAttribute("src", imgURL);

                // Revoking ObjectURL
                URL.revokeObjectURL(imgURL);
            };
        };

    request.onerror = function (event) {
        console.log("Error creating/accessing IndexedDB database");
    };

    request.onsuccess = function (event) {
        console.log("Success creating/accessing IndexedDB database");
        db = request.result;

        db.onerror = function (event) {
            console.log("Error creating/accessing IndexedDB database");
        };
        
        // Interim solution for Google Chrome to create an objectStore. Will be deprecated
        if (db.setVersion) {
            if (db.version != dbVersion) {
                var setVersion = db.setVersion(dbVersion);
                setVersion.onsuccess = function () {
                    createObjectStore(db);
                    getImageFile();
                };
            }
            else {
                getImageFile();
            }
        }
        else {
            getImageFile();
        }
    }
    
    // For future use. Currently only in latest Firefox versions
    request.onupgradeneeded = function (event) {
        createObjectStore(event.target.result);
    };
})();


浏览器支持

image

image

Github源码

JavaScript数据结构 - 列表、堆栈、队列

JavaScript数据结构

最近在回顾一些基础知识,在目前枝繁叶茂的前端中把基础知识弄扎实。基础扎实了上层建筑才能牢固。这是一些笔记,虽然是一些简单的东西,但还是拿出来与大家分享一下。养成一种分享的习惯。

列表

列表是一组有序的数据。每个列表中的数据项称为元素。在JavaScript中,列表中的元素可以是任意数据类型。

场景

当列表中保存的元素不多时;不需要很长的序列中查找元素;不需要对齐进行排序。

如果数据结构非常复杂,列表的作用就没那么大了。例如人们经常使用的待办事项列表、购物清单、流行榜单等就很合适使用。

实现简单的列表类

/**
 * List
 * @constructor
 */
function List() {

    // 初始化数据
    this.data = [];
    // 添加
    this.add = function (item){};
    // 删除
    this.remove = function (id){};
    // 查找
    this.find = function (id){};
    // 清空
    this.clear = function () {};
    // 获取列表数据
    this.getData = function (){};

}

var ins = new List();

栈是一种特殊的列表,栈内的元素只能通过一端访问,这一端称为栈顶。栈是后入先出的数据结构。

由于栈具有后入先出的特点,所以任何不在栈顶的元素都无法访问。为了得到栈顶的元素,必须先去掉上面的元素。

栈的JS实现

function Stack() {
    this.dataStore = [];
    this.top = 0; //栈顶
    this.push = push; // 入栈
    this.pop = pop; // 出栈并删除
    this.peek = peek; // 出栈单不删除
    this.clear = clear;
    this.getLength = getLength;
}

function push(el) {
    this.dataStore[this.top++] = el;
}

function pop() {
    return this.dataStore[--this.top];
}

function peek() {
	return this.dataStore[this.top-1];
}

function clear() {
    this.top = 0;
}

function getLength() {
    return this.top;
}

var ins = new Stack();
ins.push('a');
ins.push('b');
ins.push('c');

举2个栈实际应用的栗子

  • 数制间的相互转换
function mulBase(num, base) {
    var s = new Stack();
    do {
    	s.push(num % base);
    	num = Math.floor(num /= base);
    } while (num > 0);
    var converted = "";
    while (s.getLength() > 0) {
    	converted += s.pop();
    }
    return converted;
}
console.log(mulBase(25,2));// 11001
  • 回文判断

回文:一个单词、短语或者数字,从前往后写都是一样的。例如 abba 倒过来还是abba

function isPalindrome(word) {
    var stack = new Stack(),
    	i = 0,
        l = word.length;
    for (; i < l; i++) {
    	stack.push(word.charAt(i))
    }
    var rword = "";
    while (stack.getLength() > 0) {
    	rword += stack.pop();
    }
    return rword === word;
}
console.log(isPalindrome("rar")) //true
console.log(isPalindrome("test"))//false

队列

队列是一种列表,不同的是队列只能在队尾插入元素,在对首删除元素。队列用于存储按顺序排列的数据,先进先出。队列应用比较广泛,提交操作系统执行一些进程,打印任务池,日常排队买东西等等。

队列的JS实现

function Queue() {
    this.dataStore = [];
    this.enqueue = enqueue;
    this.dequeue = dequeue;
    this.front = front;
    this.back = back;
    this.toString = toString;
    this.empty = empty;
}
function enqueue(element) {
    this.dataStore.push(element)
}
function dequeue() {
	this.dataStore.shift()
}
function front() {
    return this.dataStore[0];
}
function back() {
	return this.dataStore[this.dataStore.length - 1];
}
function toString() {
	var str = '',
        i = 0,
        l = this.dataStore.length;
	for ( ; i < l; i++) {
		str += this.dataStore[i] + "\n";
    }
	return str;
}
function empty() {
    return this.dataStore.length === 0;
}
// 实例化
var q = new Queue();
q..enqueue('a');

JavaScript严格模式

JavaScript严格模式

回顾了一下JavaScript严格模式,贴出来与大家一起温故而知新,一起巩固。
这个里面介绍比较详细。

ECMAScript 5的严格模式是JavaScript中的一种限制性更强的变种方式。严格模式不是一个子集:它在语义上与正常代码有着明显的差异。不支持严格模式的浏览器与支持严格模式的浏览器行为上也不一样, 所以不要在未经严格模式特性测试情况下使用严格模式。严格模式可以与非严格模式共存,所以脚本可以逐渐的选择性加入严格模式。

严格模式在语义上与正常的JavaScript有一些不同。 首先,严格模式会将JavaScript陷阱直接变成明显的错误。其次,严格模式修正了一些引擎难以优化的错误:同样的代码有些时候严格模式会比非严格模式下更快。 第三,严格模式禁用了一些有可能在未来版本中定义的语法。
如果你想让你的JavaScript代码在严格模式下运行,可以参考转换成严格模式。
有时,你会看到符合规范的、非严格模式被称为"懒散模式",这不是官方术语,但你应该注意到它.

特别说明:摘自(MDN>javascript)

开启严格模式Edit

严格模式可以应用到整个script标签或个别函数中。不要在封闭大括弧({} )内这样做;在这样的上下文中这么做是没有效果的。在eval 代码,Function 代码,事件处理属性,传入 setTimeout方法的字符串和包含整个脚本的块中开启严格模式会如预期一样工作。

为某个script标签开启严格模式

为整个script标签开启严格模式, 需要在所有语句之前放一个特定语句


"use strict"; (或 'use strict';)

// 整个语句都开启严格模式的语法
"use strict";
var v ="Hi!  I'm a strict mode script!";

这种语法存在陷阱,有一个大型网站已经被它坑倒了:不能盲目的合并冲突代码。试想合并一个严格模式的脚本和一个非严格模式的脚本:合并后的脚本代码看起来是严格模式。反之亦然:非严格合并严格看起来是非严格的。合并均为严格模式的脚本或均为非严格模式的都没问题,只有在合并严格模式与非严格模式有可能有问题。建议按一个个函数去开启严格模式(至少在学习的过渡期要这样做).
您也可以将整个脚本的内容用一个函数包括起来,然后在这个外部函数中使用严格模式。这样做就可以消除合并的问题,但是这就意味着您必须要在函数作用域外声明一个全局变量。

为某个函数开启严格模式

同样的,要给某个函数开启严格模式,得把 "use strict"; (或 'use strict'; )声明一字不漏地放在函数体所有语句之前。

functionstrict(){
    // 函数级别严格模式语法
    'use strict';
    
    functionnested(){
        return "And so am I!";
    }
    return "Hi!  I'm a strict mode function!  "+ nested();
}

function notStrict(){
    return "I'm not strict.";
}

严格模式有哪些不同

严格模式同时改变了语法及运行时行为。变化通常分为这几类:

将问题直接转化为错误(如语法错误或运行时错误),简化了如何为给定名称的特定变量计算,简化了 eval 以及arguments,将写"安全“JavaScript的步骤变得更简单,以及改变了预测未来ECMAScript行为的方式。

将拼写错转成异常

在严格模式下, 先前被接受的拼写错误将会被认为是异常. JavaScript被设计为能使新人开发者更易于上手, 所以有时候会给本来错误操作赋予新的不报错误的语义(non-error semantics). 有时候这可以解决当前的问题, 但有时候却会给以后留下更大的问题. 严格模式则把这些失误当成错误, 以便可以发现并立即将其改正.

首先,严格模式下无法再意外创建全局变量。

在普通的JavaScript里面给一个拼写错误的变量名赋值会使全局对象新增一个属性并继续“工作”(尽管后面可能出错:在现在的JavaScript中有可能)。严格模式中意外创建全局变量被抛出错误替代:
"

use strict";                        
//假如有一个全局变量叫做
mistypedVariablemistypedVaraible = 17; 
// 因为变量名拼写错误                      
// 这一行代码就会抛出 ReferenceError

其次,严格模式会使引起静默失败(silently fail,注:不报错也没有任何效果)的赋值操抛出异常. 例如, NaN 是一个不可写的全局变量.

在正常模式下, 给 NaN 赋值不会产生任何作用; 开发者也不会受到任何错误反馈. 但在严格模式下, 给 NaN 赋值会抛出一个异常. 任何在正常模式下引起静默失败的赋值操作 (给不可写属性赋值, 给只读属性(getter-only)赋值赋值, 给不可扩展对象(non-extensible object)的新属性赋值) 都会抛出异常:

"use strict";

// 给不可写属性赋值
var obj1 = {};
Object.defineProperty(obj1, "x", { value: 42, writable: false });
obj1.x = 9; // 抛出TypeError错误

// 给只读属性赋值
var obj2 = { get x() { return 17; } };
obj2.x = 5; // 抛出TypeError错误

// 给不可扩展对象的新属性赋值
var fixed = {};
Object.preventExtensions(fixed);
fixed.newProp = "ohai"; // 抛出TypeError错误

第三,在严格模式下, 试图删除不可删除的属性时会抛出异常(之前这种操作不会产生任何效果):

"use strict";
delete Object.prototype; // 抛出TypeError错误

第四,在Gecko版本34之前,严格模式要求一个对象内的所有属性名在对象内必须唯一。正常模式下重名属性是允许的,最后一个重名的属性决定其属性值。因为只有最后一个属性起作用,当代码要去改变属性值而不是修改最后一个重名属性的时候,复制这个对象就产生一连串的bug。在严格模式下,重名属性被认为是语法错误:

这个问题在ECMAScript6中已经不复存在(bug 1041128)。

"use strict";
var o = { p: 1, p: 2 }; // !!! 语法错误

第五,严格模式要求函数的参数名唯一. 在正常模式下, 最后一个重名参数名会掩盖之前的重名参数. 之前的参数仍然可以通过 arguments[i] 来访问, 还不是完全无法访问. 然而, 这种隐藏毫无意义而且可能是意料之外的 (比如它可能本来是打错了), 所以在严格模式下重名参数被认为是语法错误:

function sum(a, a, c){ // !!! 语法错误
  "use strict";
  return a + b + c; // 代码运行到这里会出错
}

第六,严格模式禁止八进制数字语法. ECMAScript并不包含八进制语法, 但所有的浏览器都支持这种以零(0)开头的八进制语法: 0644 === 420 还有 "\045" === "%".在ECMAScript 6中支持为一个数字加"0o"的前缀来表示八进制数.

var a =0o10;// ES6: 八进制

有些新手开发者认为数字的前导零没有语法意义, 所以他们会用作对齐措施 — 但其实这会改变数字的意义! 八进制语法很少有用并且可能会错误使用, 所以严格模式下八进制语法会引起语法错误:

"use strict";
var sum = 015 + // !!! 语法错误
          197 +
          142;

第七, ECMAScript6中的严格模式禁止设置primitive值的属性.不采用严格模式,设置属性将会简单忽略(no-op),采用严格模式,将抛出TypeError错误

(function() {
"use strict";

false.true = "";              //TypeError
(14).sailing = "home";        //TypeError
"with".you = "far away";      //TypeError

})();

简化变量的使用

严格模式简化了代码中变量名字映射到变量定义的方式. 很多编译器的优化是依赖存储变量X位置的能力:这对全面优化JavaScript代码至关重要. JavaScript有些情况会使得代码中名字到变量定义的基本映射只在运行时才产生. 严格模式移除了大多数这种情况的发生, 所以编译器可以更好的优化严格模式的代码.

首先, 严格模式禁用 with. with 所引起的问题是块内的任何名称可以映射(map)到with传进来的对象的属性, 也可以映射到包围这个块的作用域内的变量(甚至是全局变量), 这一切都是在运行时决定的: 在代码运行之前是无法得知的. 严格模式下, 使用 with 会引起语法错误, 所以就不会存在 with 块内的变量在运行是才决定引用到哪里的情况了:

"use strict";
var x = 17;
with (obj) // !!! 语法错误
{
  // 如果没有开启严格模式,with中的这个x会指向with上面的那个x,还是obj.x?
  // 如果不运行代码,我们无法知道,因此,这种代码让引擎无法进行优化,速度也就会变慢。
  x;
}

一种取代 with 的简单方法是,将目标对象赋给一个短命名变量,然后访问这个变量上的相应属性.

第二, 严格模式下的 eval 不再为上层范围(surrounding scope,注:包围eval代码块的范围)引入新变量. 在正常模式下, 代码 eval("var x;") 会给上层函数(surrounding function)或者全局引入一个新的变量 x . 这意味着, 一般情况下, 在一个包含 eval 调用的函数内所有没有引用到参数或者局部变量的名称都必须在运行时才能被映射到特定的定义 (因为 eval 可能引入的新变量会覆盖它的外层变量). 在严格模式下 eval 仅仅为被运行的代码创建变量, 所以 eval 不会使得名称映射到外部变量或者其他局部变量:

var x = 17;
var evalX = eval("'use strict'; var x = 42; x");
console.assert(x === 17);
console.assert(evalX === 42);

相应的, 如果函数 eval 被在严格模式下的eval(...)以表达式的形式调用时, 其代码会被当做严格模式下的代码执行. 当然也可以在代码中显式开启严格模式, 但这样做并不是必须的.

function strict1(str){
  "use strict";
  return eval(str); // str中的代码在严格模式下运行
}
function strict2(f, str){
  "use strict";
  return f(str); // 没有直接调用eval(...): 当且仅当str中的代码开启了严格模式时
                 // 才会在严格模式下运行
}
function nonstrict(str){
  return eval(str); // 当且仅当str中的代码开启了"use strict",str中的代码才会在严格模式下运行
}

strict1("'Strict mode code!'");
strict1("'use strict'; 'Strict mode code!'");
strict2(eval, "'Non-strict code.'");
strict2(eval, "'use strict'; 'Strict mode code!'");
nonstrict("'Non-strict code.'");
nonstrict("'use strict'; 'Strict mode code!'");

因此,在 eval 执行的严格模式代码下,变量的行为与严格模式下非 eval 执行的代码中的变量相同。

第三, 严格模式禁止删除声明变量。delete name 在严格模式下会引起语法错误:

"use strict";

var x;
delete x; // !!! 语法错误

eval("var y; delete y;"); // !!! 语法错误

语法错误

让eval和arguments变的简单

严格模式让arguments和eval少了一些奇怪的行为。两者在通常的代码中都包含了很多奇怪的行为: eval会添加删除绑定,改变绑定好的值,还会通过用它索引过的属性给形参取别名的方式修改形参. 虽然在未来的ECMAScript版本解决这个问题之前,是不会有补丁来完全修复这个问题,但严格模式下将eval和arguments作为关键字对于此问题的解决是很有帮助的。

  • 首先, 名称 eval 和 arguments 不能通过程序语法被绑定(be bound)或赋值. 以下的所有尝试将引起语法错误:
"use strict";
eval = 17;
arguments++;
++eval;
var obj = { set p(arguments) { } };
var eval;
try { } catch (arguments) { }
function x(eval) { }
function arguments() { }
var y = function eval() { };
var f = new Function("arguments", "'use strict'; return 17;");
  • 第二,严格模式下,参数的值不会随 arguments 对象的值的改变而变化。在正常模式下,对于第一个参数是 arg 的函数,对 arg 赋值时会同时赋值给 arguments[0],反之亦然(除非没有参数,或者 arguments[0] 被删除)。严格模式下,函数的 arguments 对象会保存函数被调用时的原始参数。arguments[i] 的值不会随与之相应的参数的值的改变而变化,同名参数的值也不会随与之相应的
function f(a){
  "use strict";
  a = 42;
  return [a, arguments[0]];
}
var pair = f(17);
console.assert(pair[0] === 42);
console.assert(pair[1] === 17);
  • 第三,不再支持 arguments.callee。正常模式下,arguments.callee 指向当前正在执行的函数。这个作用很小:直接给执行函数命名就可以了!此外,arguments.callee 十分不利于优化,例如内联函数,因为 arguments.callee 会依赖对非内联函数的引用。在严格模式下,arguments.callee 是一个不可删除属性,而且赋值和读取时都会抛出异常:
"use strict";
var f = function() { return arguments.callee; };
f(); // 抛出类型错误

"安全的" JavaScript

严格模式下更容易写出“安全”的JavaScript。现在有些网站提供了方式给用户编写能够被网站其他用户执行的JavaScript代码。在浏览器环境下,JavaScript能够获取用户的隐私信息,因此这类Javascript必须在运行前部分被转换成需要申请访问禁用功能的权限。没有很多的执行时检查的情况,Javascript的灵活性让它无法有效率地做这件事。一些语言中的函数普遍出现,以至于执行时检查他们会引起严重的性能损耗。做一些在严格模式下发生的小改动,要求用户提交的JavaScript开启严格模式并且用特定的方式调用,就会大大减少在执行时进行检查的必要。

第一,在严格模式下通过this传递给一个函数的值不会被强制转换为一个对象。对一个普通的函数来说,this总会是一个对象:不管调用时this它本来就是一个对象;还是用布尔值,字符串或者数字调用函数时函数里面被封装成对象的this;还是使用undefined或者null调用函数式this代表的全局对象(使用call, apply或者bind方法来指定一个确定的this)。这种自动转化为对象的过程不仅是一种性能上的损耗,同时在浏览器中暴露出全局对象也会成为安全隐患,因为全局对象提供了访问那些所谓安全的JavaScript环境必须限制的功能的途径。所以对于一个开启严格模式的函数,指定的this不再被封装为对象,而且如果没有指定this的话它值是undefined:

"use strict";
function fun() { return this; }
console.assert(fun() === undefined);
console.assert(fun.call(2) === 2);
console.assert(fun.apply(null) === null);
console.assert(fun.call(undefined) === undefined);
console.assert(fun.bind(true)() === true);

第二,在严格模式中再也不能通过广泛实现的ECMAScript扩展“游走于”JavaScript的栈中。在普通模式下用这些扩展的话,当一个叫fun的函数正在被调用的时候,fun.caller是最后一个调用fun的函数,而且fun.arguments包含调用fun时用的形参。这两个扩展接口对于“安全”JavaScript而言都是有问题的,因为他们允许“安全的”代码访问"专有"函数和他们的(通常是没有经过保护的)形参。如果fun在严格模式下,那么fun.caller和fun.arguments都是不可删除的属性而且在存值、取值时都会报错:

function restricted()
{
  "use strict";
  restricted.caller;    // 抛出类型错误
  restricted.arguments; // 抛出类型错误
}
function privilegedInvoker()
{
  return restricted();
}
privilegedInvoker();

第三,严格模式下的arguments不会再提供访问与调用这个函数相关的变量的途径。在一些旧时的ECMAScript实现中arguments.caller曾经是一个对象,里面存储的属性指向那个函数的变量。这是一个安全隐患,因为它通过函数抽象打破了本来被隐藏起来的保留值;它同时也是引起大量优化工作的原因。出于这些原因,现在的浏览器没有实现它。但是因为它这种历史遗留的功能,arguments.caller在严格模式下同样是一个不可被删除的属性,在赋值或者取值时会报错:

"use strict";
function fun(a, b)
{
  "use strict";
  var v = 12;
  return arguments.caller; // 抛出类型错误
}
fun(1, 2); // 不会暴露v(或者a,或者b)

为未来的ECMAScript版本铺平道路

未来版本的ECMAScript很有可能会引入新语法,ECMAScript5中的严格模式就提早设置了一些限制来减轻之后版本改变产生的影响。如果提早使用了严格模式中的保护机制,那么做出改变就会变得更容易。
首先,在严格模式中一部分字符变成了保留的关键字。这些字符包括implements, interface, let, package, private, protected, public, static和yield。在严格模式下,你不能再用这些名字作为变量名或者形参名。

function package(protected){ // !!!
  "use strict";
  var implements; // !!!

  interface: // !!!
  while (true)
  {
    break interface; // !!!
  }

  function private() { } // !!!
}
function fun(static) { 'use strict'; } // !!!

两个针对Mozilla开发的警告:第一,如果你的JavaScript版本在1.7及以上(你的chrome代码或者你正确使用了)并且开启了严格模式的话,因为let和yield是最先引入的关键字,所以它们会起作用。但是网络上用或者加载的代码,let或者yield都不会作为关键字起作用;第二,尽管ES5无条件的保留了class, enum, export, extends, import和super关键字,在Firefox 5之前,Mozilla仅仅在严格模式中保留了它们。
其次,严格模式禁止了不在脚本或者函数层面上的函数声明。在浏览器的普通代码中,在“所有地方”的函数声明都是合法的。这并不在ES5规范中(甚至是ES3)!这是一种针对不同浏览器中不同语义的一种延伸。未来的ECMAScript版本很有希望制定一个新的,针对不在脚本或者函数层面进行函数声明的语法。在严格模式下禁止这样的函数声明对于将来ECMAScript版本的推出扫清了障碍:

"use strict";
if (true){
  function f() { } // !!! 语法错误
  f();
}

for (var i = 0; i < 5; i++){
  function f2() { } // !!! 语法错误
  f2();
}

function baz() { // 合法
  function eit() { } // 同样合法
}

这种禁止放到严格模式中并不是很合适,因为这样的函数声明方式从ES5中延伸出来的。但这是ECMAScript委员会推荐的做法,浏览器就实现了这一点。
浏览器的严格模式Edit
主流浏览器现在实现了严格模式。但是不要盲目的依赖它,因为市场上仍然有大量的浏览器版本只部分支持严格模式或者根本就不支持(比如IE10之前的版本)。严格模式改变了语义。依赖这些改变可能会导致没有实现严格模式的浏览器中出现问题或者错误。谨慎地使用严格模式,通过检测相关代码的功能保证严格模式不出问题。最后,记得在支持或者不支持严格模式的浏览器中测试你的代码。如果你只在不支持严格模式的浏览器中测试,那么在支持的浏览器中就很有可能出问题,反之亦然。

学习Node.js从express开始

学习Node.js从express开始

[toc]

安装依赖

http://www.expressjs.com.cn/starter/installing.html

// 进入常用代码目录或者本地web服务器根目录

启动nodejs基本应用 开启Hello world!

mkdir myapp

  • 通过 npm init 命令为你的应用创建一个 package.json 文件。需要配合输入相关选项然后按enter下一步就好。 欲了解 package.json 是如何起作用,百度一下,你就知道
npm init

  • 全局安装express
npm install express

  • 本地工作目录安装
npm install express --save

  • 进入 myapp 目录,创建一个名为 app.js 的文件,然后将下列代码复制进去:
var express = require('express');
var app = express();

app.get('/', function (req, res) {
  res.send('Hello World!');
});

var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;

  console.log('Example app listening at http://%s:%s', host, port);
});
  • 启动nodejs应用,开启hello world
node app.js

使用Nodejs生成器,进入正常开发

  • 全局安装express生成器
npm install express-generator -g

  • 创建express应用
express myapp

  • 然后安装所有依赖包:
cd myapp 

npm install

  • 启动这个应用:
// (MacOS 或 Linux 平台)
DEBUG=myapp npm start

// windows平台

set DEBUG=myapp & npm start

image

image

javascript继承

javascript继承

[toc]

原型链去继承

function SuperType() {
    this.property = true;
}
SuperType.prototype.getSuperValue = function () {
    return this.property;
};

function SubType() {
        this.subproperty = false;
    } //继承了SuperType
SubType.prototype = new SuperType(); //添加新方法 

SubType.prototype.getSubValue = function () {
    return this.subproperty;
};
//重写超类型中的方法 
SubType.prototype.getSuperValue = function () {
    return false;
};

借用构造函数

function SuperType() {
    this.colors = ["red", "blue", "green"];
}

function SubType() { //继承了SuperType    
    SuperType.apply(this,this.arguments);
}

组合继承

function SuperType(name) {
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function () {
    alert(this.name);
};

//继承方法
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function () {
    alert(this.age);
};

原型式继承

Object.create(proto)
function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};

var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie"); //"Shelby,Court,Van,Rob,Barbie"alert(person.friends);

寄生组合式

function SuperType(name) {
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function () {
    alert(this.name);
};

function SubType(name, age) {
    //第二次调用SuperType()    
    SuperType.call(this, name);
    this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function () {
    alert(this.age);
};

ECMAScript 6 继承extend

Node.js数据库操作

Node.js数据库操作

[toc]

参考文档

安装

npm install mysql

基础使用

连接数据库,在app.js使用

var mysql = require('mysql');
var conn = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: 'root',
    database:'nodejs',
    port: 3306
});
conn.connect();
conn.query('SELECT * from table', function(err, rows, fields) {
    if (err) throw err;
    console.log('The solution is: ', rows[0].solution);
});
conn.end();

增删改查

var mysql = require('mysql');
var conn = mysql.createConnection({
    host: 'localhost',
    user: 'nodejs',
    password: 'nodejs',
    database: 'nodejs',
    port: 3306
});
conn.connect();

var insertSQL = 'insert into t_user(name) values("conan"),("fens.me")';
var selectSQL = 'select * from t_user limit 10';
var deleteSQL = 'delete from t_user';
var updateSQL = 'update t_user set name="conan update"  where name="conan"';

//delete
conn.query(deleteSQL, function (err0, res0) {
    if (err0) console.log(err0);
    console.log("DELETE Return ==> ");
    console.log(res0);

    //insert
    conn.query(insertSQL, function (err1, res1) {
        if (err1) console.log(err1);
        console.log("INSERT Return ==> ");
        console.log(res1);

        //query
        conn.query(selectSQL, function (err2, rows) {
            if (err2) console.log(err2);

            console.log("SELECT ==> ");
            for (var i in rows) {
                console.log(rows[i]);
            }

            //update
            conn.query(updateSQL, function (err3, res3) {
                if (err3) console.log(err3);
                console.log("UPDATE Return ==> ");
                console.log(res3);

                //query
                conn.query(selectSQL, function (err4, rows2) {
                    if (err4) console.log(err4);

                    console.log("SELECT ==> ");
                    for (var i in rows2) {
                        console.log(rows2[i]);
                    }
                });
            });
        });
    });
});

//conn.end();

使用连接池配置

var mysql = require('mysql');
var pool = mysql.createPool({
    host: 'localhost',
    user: 'nodejs',
    password: 'nodejs',
    database: 'nodejs',
    port: 3306
});

var selectSQL = 'select * from t_user limit 10';

pool.getConnection(function (err, conn) {
    if (err) console.log("POOL ==> " + err);

    conn.query(selectSQL,function(err,rows){
        if (err) console.log(err);
        console.log("SELECT ==> ");
        for (var i in rows) {
            console.log(rows[i]);
        }
        conn.release();
    });
});

在express项目中使用

  • 这是express目录结构

这是目录结构

  • model中是用来存储业务逻辑代码
var mysql = require('mysql');
var mysqlConfig = require('../conf/mysql');
// 使用DBConfig.js的配置信息创建一个MySQL连接池
var pool = mysql.createPool( mysqlConfig );

var userSql = {
	insert:'INSERT INTO node_user(name,age) VALUES(?,?)',
	queryAll:'SELECT * FROM node_user',
	getUserById:'SELECT * FROM node_user WHERE id = ? '
};

var main = {
	add: function (request,callback) {
		// 从连接池获取连接
		pool.getConnection(function(err, connection) {
			var d = {};
			if ( err ) {
				d.connectError = err;
				return callback(d);
			}

			// 获取前台页面传过来的参数
			var param = request.query || request.params;

			// 建立连接 增加一个用户信息
			connection.query(userSql.insert, [param.name,param.age], function(err, result) {
				d.response = { err: err, success: result };
				callback(d);
				// 释放连接
				connection.release();

			});
		});
	},
	list: function (request,callback) {
		// 从连接池获取连接
		pool.getConnection(function(err, connection) {
			var d = {};
			if ( err ) {
				d.connectError = err;
				return callback(d);
			}
			// 建立连接 增加一个用户信息
			connection.query(userSql.queryAll,  function(err, result) {

				d.response = { err: err, success: result };
				callback(d);
				// 释放连接
				connection.release();

			});
		});
	}
};
module.exports = main;

然后在路由routes/user.js里面引进来使用,如下。

var express = require('express');
var router = express.Router();
var userModel = require('../model/userModel');

// 添加用户
router.post('/addUser', function(req, res, next){
	userModel.add(req,function (data) {

		if (data.connectError) {
			return res.json('connect database error')
		}

		if (data.response) {
			res.json(data.query.success);
		}


	});
});

router.get('/list', function(req, res, next){
	userModel.list(req,function (data) {
		if (data.connectError) {
			return res.json({code:'101',desc: 'db connect error!'})
		}

		if (data.response) {
			return res.json({code:'200',desc: 'success',data:data.response.success})
		}

	});
});

module.exports = router;

测试

vagrant常用命令

[toc]

初始化 vagrant

vagrant init  

启动 vagrant


vagrant up    

关闭vagrant

vagrant halt  

通过ssh远程登录vagrant

vagrant ssh   

更改配置项重启

cd ~/Homestead && vagrant provision && vagrant reload

laravel实战进阶知识导航

[TOC]

laravel实战进阶

Git操作书籍

homestead配置

Homestead 是 Laravel 官方推荐的开发环境。在本书里,我们会强制读者使用 Homestead,原因主要有以下:

  • Homestead 是本书很重要的技能点之一,学完此书,你必须学会 Homestead;
  • Homestead 统一了环境,避免歧义,减少新手在学习中遇到不必要的卡顿;
  • 统一环境带来的好处还有:当你遇到问题的时候,其他同学能很容易的帮助到你;
    最大程度接近线上生产环境,为后续的课程做铺垫;
    这是最佳实践,是需要从一开始培养起来的好习惯。

Carbon

Carbon 是 PHP 知名的 DateTime 操作扩展,Laravel 将其默认集成到了框架中。diffForHumans 是 Carbon 对象提供的方法

参考Carbon文档

Laravel 项目开发规范

Laravel 5.x Scaffold Generator 代码生成器

模型观察者

excerpt 字段存储的是话题的摘录,将作为文章页面的 description 元标签使用,有利于 SEO 搜索引擎优化。摘录由文章内容中自动生成,生成的时机是在话题数据存入数据库之前。我们将使用 Eloquent 的 观察器 来实现此功能。

Eloquent 模型会触发许多事件(Event),我们可以对模型的生命周期内多个时间点进行监控: creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored。事件让你每当有特定的模型类在数据库保存或更新时,执行代码。当一个新模型被初次保存将会触发 creating 以及 created 事件。如果一个模型已经存在于数据库且调用了 save 方法,将会触发 updating 和 updated 事件。在这两种情况下都会触发 saving 和 saved 事件。

Eloquent 观察器允许我们对给定模型中进行事件监控,观察者类里的方法名对应 Eloquent 想监听的事件。每种方法接收 model 作为其唯一的参数。代码生成器已经为我们生成了一个观察器文件,并在 AppServiceProvider 中注册。接下来我们要定制此观察器,在 Topic 模型保存时触发的 saving 事件中,对 excerpt 字段进行赋值

laravel-mix

参考文档Laravel 的资源任务编译器 Laravel Mix

HTMLPurifier for Laravel 5

HTMLPurifier for Laravel 是对 HTMLPurifier 针对 Laravel 框架的一个封装。本章节中,我们将使用此扩展包来对用户内容进行过滤。

composer require "mews/purifier:~2.0"

HTMLPurifier 本身就是一个独立的项目,运用『白名单机制』对 HTML 文本信息进行 XSS 过滤。

『白名单机制』指的是使用配置信息来定义『HTML 标签』、『标签属性』和『CSS 属性』数组,在执行 clean() 方法时,只允许配置信息『白名单』里出现的元素通过,其他都进行过滤。

消息通知

聊一聊css的注释

聊一聊css的注释

很少会有人会特别注意CSS的注释,要么就快捷键注释,要么就没有注释,只有少部分的开发人员会特别留意CSS注释。看了BAT系的css注释,有规范,但无风格。
国外的开发人员对注释就有着比较好的思考,值得我们国内的开发者去学习。
从CSS注释当中可以看出开发人员的编码风格与习惯,进而判断出个人或者团队行事风格。对于有完美主义的开发者,对于自己的代码注释不重视,那是相当难受的。

主要内容:基本注释、变量注释(可选)、块级注释、群组注释、具体样式注释、继承注释、mixin注释(可选)、函数注释(可选)
其他注释待补充。如果未使用sass,less的可以忽略可选。

花费时间:1分钟即可阅读完毕。

css注释风格参考

下面的注释参考了Bootstrap源码的风格、normalize等的注释,给大家参考。

基本注释

/* 单行注释 */


/**
 * 多行注释
 * 1. Correct the line height in all browsers.
 * 2. Prevent adjustments of font size after orientation changes in
 *    IE on Windows Phone and in iOS.
 */
 

变量注释

//
// Variables
// --------------------------------------------------



块级注释:集中存放某个功能关联的css代码

/* 块级注释
   ========================================================================== */
 

群组注释:即类似的样式应该声明在一组相关的注释下面,以便及时调整

//== 颜色
//
//## 用途范围

具体样式注释


//** Background color for `<body>`.
$body-bg: #fff !default;


继承注释

/**  
 * 继承注释
 * Extend `.foo` in theme.css  
 */  

mixins注释

bootstrap风格


// CSS image replacement
//
// Heads up! v3 launched with only `.hide-text()`, but per our pattern for
// mixins being reused as classes with the same name, this doesn't hold up. As
// of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`.
//
// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757

// Deprecated as of v3.0.1 (has been removed in v4)
@mixin hide-text() {
  font: 0/0 a;
  color: transparent;
  text-shadow: none;
  background-color: transparent;
  border: 0;
}

js系


/**  
 * mixin name and use
 * @param
 * @return 
 */  

@mixin hide-text() {
  font: 0/0 a;
  color: transparent;
  text-shadow: none;
  background-color: transparent;
  border: 0;
}

其他风格的注释...

编写可维护的javascript 一 编程风格

为什么讨论编程风格?

组件一个团队,团队每个人都各自一套编程习惯。

  • 任何开发者都不会在乎某个文件的作者是谁,没有必要花费时间和精力再去理解代码逻辑,并重新排版,这样节约时间。

  • 当代码风格不同时,就很容易发现风格不一致的代码

基本格式

  • 缩进层级

    使用四个空格,设置tab键 为四个空格

  • 语句结尾

    要么独占一行,要么以分号结尾。

// 合格代码
var team = "omwteam";

function sayTeam () {
    
    return {
        name: "freddy",
        data: []
    }
}

// 不合格代码

var team = "omwteam"

function sayTeam () {
    
    return 
    {
        name: "freddy",
        data: []
    }
}

// 这段代码会被编译器解析成如下这段代码
// 原意结果是返回一个对象,实际上却返回 undefined;

var team = "omwteam";

function sayTeam () {
    
    return ;
    {
        name: "freddy",
        data: []
    }
}
  • 行的长度

    单行长度不应超过80行,超过80行应强制换行。

  • 适当的地方换行

    当一行超过80个时,需要手动换行,换行部分应使用两个缩进;

// 正确做法
callback(this,document,'test',[],'xxx','dsds',windows,
        'ddsds');
        
// 不正确 换行部分只有一个缩进
callback(this,document,'test',[],'xxx','dsds',windows,
    'ddsds');
    
// 不正确 换行部分带上 ","运算符前面
callback(this,document,'test',[],'xxx','dsds',windows
    ,'ddsds');       
        
  • 在合适的地方加上空行,以增加代码的可读性

    • 在方法之间。

    • 在方法中的局部变量和第一条语句

    • 在多行或者单行注释之前

    • 在方法内的逻辑片段之间插入空行,提高可读性

// 合理的写法
var list = [];

if (list && list.length) {
    
    for (i = 0, l = list.length; i < l; i++) {
        item = list[i];
        type = object[item]
        
        if (Object.hasOwnProperty(item)) {
            
            if (type && type === 'object') {
                return true;
            } else {
                return false;
            }
        }
    }
}

// 不合理的
var list = [];

if (list && list.length) {
    for (i = 0, l = list.length; i < l; i++) {
        item = list[i];
        type = object[item]
        if ( Object.hasOwnProperty(item) ) {
            if (type && type === 'object') {
                return true;
            } else {
                return false;
            }
        }
    }
}
  • 变量与函数命名

    一般采用驼峰法,语义化准则以增强代码的可读性

var anotherVarible;
var thisIsMyName;


// 好的写法
var count = '';
var myName = '';

// 不好的写法 变量写起来像函数
var getCount = '';
var isFound = '';

// 好的写法

function getName () {
    return myName;
}

// 不好的写法: 函数看起来像变量

function theName () {
    return count;
}

JavaScript中巧用位运算

JavaScript中巧用位运算

日常前端开发中我们很少用到位运算,容易让人遗忘,让我们一起回顾下一下js中的位运算。

位运算详细说明查看JavaScript|MDN

下面主要回顾一下一些常用的位运算的巧用。

将十进制转化为二进制

var number = 3;
var result = number.toString(2);

var result2 = 14..toString(2); // "1110"

我们使用位运算来代替Math.floor()来向下取整

var data = 2.2352524535;
var result = data | 0; // 2


var re2 = ~~data; // 2

将颜色从RGA转换为Hex格式


var color = {r: 186, g: 218, b: 85};

// RGB to HEX
var rgb2hex = function(r, g, b) {
    return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).substr(1);
}
rgb2hex(color.r, color.g, color.b);//"#bada55"

区分两个数的大小

// variables
var a = 9285;
var b = 3569;

// 取大
var max = a ^ ((a ^ b) & -(a < b));//9285;

// 取小
var min =  b ^ ((a ^ b) & -(a < b);//3569

交换变量

var a = 10;
var b = 99;

a = (b^=a^=b)^a;

console.log(a) // 99
console.log(b) // 10

判断正负

function isPos(n) {
  return (n === (n >>> 0)) ? true : false;  
}
isPos(-1); // false
isPos(1); // true

回顾ES5新增的数组方法

回顾ES5新增的数组方法

1、every(currentValue[当前元素],index[索引],arr[引用数组]) 测试断言函数是否对每个数组元素都为真。

方法使用指定函数检测数组中的所有元素:
如果数组中检测到有一个元素不满足,则整个表达式返回 false,且剩余的元素不会再进行检测。
如果所有元素都满足条件,则返回 true。

示例: 检测两个数据中的元素是否都为数字


var numberArray= [2,3,4],
	stringArray = [1,3333,"c",2];

//

var a = numberArray.every(function (p) {
	return typeof p === "number"
});

var b = stringArray.every(function (p) {
	return typeof p === "number"
});

console.log(a); // 返回 true

console.log(b);// 返回 false

2、filter(callback[, thisArg])返回满足断言函数的数组元素。

示例:返回数组中为字符串类型的元素


var	stringArray = [1,3333,"c",2];


var a = stringArray.every(function (p) {
	return typeof p === "string"
});

console.log(a) // 返回["c"]

3、forEach(currentValue, index, array)为数组的每一个元素调用指定函数。

示例:打印数组中的元素

var numberArray= [2,3,4];
numberArray.forEach(function (value) {
	console.log(value) //依次 2,3,4
});


4、lastIndexOf() 方法返回指定元素(也即有效的 JavaScript 值或变量)在数组中的最后一个的索引,如果不存在则返回 -1。从数组的后面向前查找,从 fromIndex 处开始。

语法

arr.lastIndexOf(searchElement[, fromIndex = arr.length - 1])

示例:查找数组中字符串a出现最后的索引。

var arr = [1,"a",2,"b","a"];



console.log(arr.lastIndexOf("a") //第一次出现的位置所以是 1 第二次即最后一次是 4 所以结果是4

5、map() 方法创建一个新数组,其结果是该数组中的每个元素调用一个提供的函数。

语法

详细参考MDN

const new_array = arr.map(callback()[, thisArg])

示例:

var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt);
/* roots的值为[1, 2, 3], numbers的值仍为[1, 4, 9] */

var a = [1,2,3];
var newArr = a.map(function(v){
    return v*2
})
console.log(newArr) // [2,4,6]

6、reduce() 方法对累加器和数组中的每个元素 (从左到右)应用一个函数,将其减少为单个值,返回值为函数累计处理的结果

参考MDN

语法

arr.reduce(callback,[initialValue])

示例1 : 返回数组所有数字的和

var sum = [0, 1, 2, 3].reduce(function(acc, val) {
  return acc + val;
}, 0);

console.log(sum);
// 6

7、some() 方法测试数组中的某些元素是否通过了指定函数的测试。

语法

详细参考

arr.some(callback[, thisArg])

示例:检测在数组中是否有元素大于 10

var re = [2, 5, 8, 1, 4].some(function(e){
    return (e >= 10);
})

console.log(re);  //false

8、indexOf()方法返回在数组中可以找到给定元素的第一个索引,如果不存在,则返回-1。

示例

var a = [2, 9, 9]; 
a.indexOf(2); // 0 
a.indexOf(7); // -1

if (a.indexOf(7) === -1) {
  // element doesn't exist in array
}

对于不支持此方法的兼容代码摘自mdn

if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function(searchElement, fromIndex) {

    var k;

    // 1. Let O be the result of calling ToObject passing
    //    the this value as the argument.
    if (this == null) {
      throw new TypeError('"this" is null or not defined');
    }

    var O = Object(this);

    // 2. Let lenValue be the result of calling the Get
    //    internal method of O with the argument "length".
    // 3. Let len be ToUint32(lenValue).
    var len = O.length >>> 0;

    // 4. If len is 0, return -1.
    if (len === 0) {
      return -1;
    }

    // 5. If argument fromIndex was passed let n be
    //    ToInteger(fromIndex); else let n be 0.
    var n = +fromIndex || 0;

    if (Math.abs(n) === Infinity) {
      n = 0;
    }

    // 6. If n >= len, return -1.
    if (n >= len) {
      return -1;
    }

    // 7. If n >= 0, then Let k be n.
    // 8. Else, n<0, Let k be len - abs(n).
    //    If k is less than 0, then let k be 0.
    k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);

    // 9. Repeat, while k < len
    while (k < len) {
      // a. Let Pk be ToString(k).
      //   This is implicit for LHS operands of the in operator
      // b. Let kPresent be the result of calling the
      //    HasProperty internal method of O with argument Pk.
      //   This step can be combined with c
      // c. If kPresent is true, then
      //    i.  Let elementK be the result of calling the Get
      //        internal method of O with the argument ToString(k).
      //   ii.  Let same be the result of applying the
      //        Strict Equality Comparison Algorithm to
      //        searchElement and elementK.
      //  iii.  If same is true, return k.
      if (k in O && O[k] === searchElement) {
        return k;
      }
      k++;
    }
    return -1;
  };
}

H5与APP交互原理解析

[TOC]

APP与H5交互原理解析

Hybrid App,俗称混合应用,即混合了 Native技术 与 Web技术 进行开发的移动应用。现在比较流行的混合方案主要有三种,主要是在UI渲染机制上的不同:

行业方案

  • 1、基于 WebView UI 的基础方案,市面上大部分主流 App 都有采用,例如微信JS-SDK,通过 JSBridge 完成 H5 与 Native 的双向通讯,从而赋予H5一定程度的原生能力。

  • 2、基于 Native UI 的方案,例如 React-Native、Weex。在赋予 H5 原生API能力的基础上,进一步通过 JSBridge 将js解析成的虚拟节点树(Virtual DOM)传递到 Native 并使用原生渲染。

  • 3、近期比较流行的小程序方案,通过更加定制化的 JSBridge,并使用双 WebView 双线程的模式隔离了JS逻辑与UI渲染,形成了特殊的开发模式,加强了 H5 与 Native 混合程度,提高了页面性能及开发体验。

因此,JSBridge 也是整个混合应用最关键的部分,例如我们在设置微信分享时用到的 JS-SDK,wx对象 便是我们最常见的 JSBridge:

Hybrid技术原理

Hybrid App的本质,其实是在原生的 App 中,使用 WebView 作为容器直接承载 Web页面。因此,最核心的点就是 Native端 与 H5端 之间的双向通讯层,其实这里也可以理解为我们需要一套跨语言通讯方案,来完成 Native(Java/Objective-c/...) 与 JavaScript 的通讯。这个方案就是我们所说的 JSBridge,而实现的关键,便是作为容器的 WebView,一切的原理都是基于 WebView 的机制。

hybrid渲染H5

(一) JavaScript 调用 Native接口方式

基于 WebView 的机制和开放的 API, 实现这个功能有三种常见的方案:

  • 1)、API注入,原理其实就是 Native 获取 JavaScript环境上下文,并直接在上面挂载对象或者方法,使 js 可以直接调用,Android 与 IOS 分别拥有对应的挂载方式。

  • 2)、WebView 中的 prompt/console/alert 拦截,通常使用 prompt,因为这个方法在前端中使用频率低,比较不会出现冲突;

  • 3)、WebView URL Scheme 跳转拦截;

第二三种机制的原理是类似的,都是通过对 WebView 信息冒泡传递的拦截,从而达到通讯的,接下来我们主要从 原理-定制协议-拦截协议-参数传递-回调机制 5个方面详细阐述下第三种方案 -- URL拦截方案。

方案3 - URL拦截方案

1、实现原理

在 WebView 中发出的网络请求,客户端都能进行监听和捕获

2、协议的定制

我们需要制定一套URL Scheme规则,通常我们的请求会带有对应的协议开头,例如常见的 https://xxx.com 或者 file://1.jpg,代表着不同的含义。我们这里可以将协议类型的请求定制为:

xxcommand://xxxx?param1=1&param2=2

  • (1)、xxcommand:// 只是一种规则,可以根据业务进行制定,使其具有含义,例如我们定义 xxcommand:// 为公司所有App系通用,为通用工具协议:

xxcommand://getProxy?h=1

而定义 xxapp:// 为每个App单独的业务协议。

xxapp://openCamera?h=2

不同的协议头代表着不同的含义,这样便能清楚知道每个协议的适用范围。

  • (2)、 这里不要使用 location.href 发送,因为其自身机制有个问题是同时并发多次请求会被合并成为一次,导致协议被忽略,而并发协议其实是非常常见的功能。我们会使用创建 iframe 发送请求的方式。

  • (3)、 通常考虑到安全性,需要在客户端中设置域名白名单或者限制,避免公司内部业务协议被第三方直接调用。

3.协议的拦截

客户端可以通过 API 对 WebView 发出的请求进行拦截:

  • IOS上: shouldStartLoadWithRequest
  • Android: shouldOverrideUrlLoading

当解析到请求 URL 头为制定的协议时,便不发起对应的资源请求,而是解析参数,并进行相关功能或者方法的调用,完成协议功能的映射。

4、协议回调

由于协议的本质其实是发送请求,这属于一个异步的过程,因此我们便需要处理对应的回调机制。这里我们采用的方式是JS的事件系统,这里我们会用到 window.addEventListener 和 window.dispatchEvent这两个基础API;

  • 发送协议时,通过协议的唯一标识注册自定义事件,并将回调绑定到对应的事件上。

  • 客户端完成对应的功能后,调用 Bridge 的dispatch API,直接携带 data 触发该协议的自定义事件。

原生的事件机制是我们非常熟悉的,例如我们常用的

window.addEventListener('DOMContentLoaded', () => {});

通过事件的机制,会让开发更符合我们前端的习惯,例如当你需要监听客户端的通知时,同样只需要在通过 addEventListener 进行监听即可。

Tips: 这里有一点需要注意的是,应该避免事件的多次重复绑定,因此当唯一标识重置时,需要removeEventListener对应的事件。

5、参数传递方式

由于 WebView 对 URL 会有长度的限制,因此常规的通过 search参数 进行传递的方式便具有一个问题,既 当需要传递的参数过长时,可能会导致被截断,例如传递base64或者传递大量数据时。‘’

因此我们需要制定新的参数传递规则,我们使用的是函数调用的方式。这里的原理主要是基于:

Native 可以直接调用 JS 方法并直接获取函数的返回值。

我们只需要对每条协议标记一个唯一标识,并把参数存入参数池中,到时客户端再通过该唯一标识从参数池中获取对应的参数即可。

(二) Native 调用 Javascript接口方式

由于 Native 可以算作 H5 的宿主,因此拥有更大的权限,上面也提到了 Native 可以通过 WebView API直接执行 Js 代码。这样的权限也就让这个方向的通讯变得十分的便捷。

  • IOS: stringByEvaluatingJavaScriptFromString
// Swift
webview.stringByEvaluatingJavaScriptFromString("alert('NativeCall')")
  • Android: loadUrl (4.4-)
// 调用js中的JSBridge.trigger方法
// 该方法的弊端是无法获取函数返回值;
webView.loadUrl("javascript:JSBridge.trigger('NativeCall')")

Tips: 当系统低于4.4时,evaluateJavascript 是无法使用的,因此单纯的使用 loadUrl 无法获取 JS 返回值,这时我们需要使用前面提到的 prompt 的方法进行兼容,让 H5端 通过 prompt 进行数据的发送,客户端进行拦截并获取数据。

  • Android: evaluateJavascript (4.4+)
// 4.4+后使用该方法便可调用并获取函数返回值;
mWebView.evaluateJavascript"javascript:JSBridge.trigger('NativeCall')",      new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String value) {
        //此处为 js 返回的结果
    }
});

基于上面的原理,我们已经明白 JSBridge 最基础的原理,并且能实现 Native <=> H5 的双向通讯机制了。

(三)JSBridge 的接入

接下来,我们来理下代码上需要的资源。实现这套方案,从上图可以看出,其实可以分为两个部分:

JS部分(bridge): 在JS环境中注入 bridge 的实现代码,包含了协议的拼装/发送/参数池/回调池等一些基础功能。

  • Native部分(SDK):在客户端中 bridge 的功能映射代码,实现了URL拦截与解析/环境信息的注入/通用功能映射等功能。

我们这里的做法是,将这两部分一起封装成一个 Native SDK,由客户端统一引入。客户端在初始化一个 WebView 打开页面时,如果页面地址在白名单中,会直接在 HTML 的头部注入对应的 bridge.js。这样的做法有以下的好处:

双方的代码统一维护,避免出现版本分裂的情况。有更新时,只要由客户端更新SDK即可,不会出现版本兼容的问题;

  • App的接入十分方便,只需要按文档接入最新版本的SDK,即可直接运行整套Hybrid方案,便于在多个App中快速的落地;

  • H5端无需关注,这样有利于将 bridge 开放给第三方页面使用。

这里有一点需要注意的是,协议的调用,一定是需要确保执行在bridge.js 成功注入后。由于客户端的注入行为属于一个附加的异步行为,从H5方很难去捕捉准确的完成时机,因此这里需要通过客户端监听页面完成后,基于上面的回调机制通知 H5端,页面中即可通过window.addEventListener('bridgeReady', e => {})进行初始化。

(四) App中 H5 的接入方式

将 H5 接入 App 中通常有两种方式:

(1) 在线H5,这是最常见的一种方式。我们只需要将H5代码部署到服务器上,只要把对应的 URL地址 给到客户端,用 WebView 打开该URL,即可嵌入。该方式的好处在于:

  • 独立性强,有非常独立的开发/调试/更新/上线能力;
  • 资源放在服务器上,完全不会影响客户端的包体积;
  • 接入成本很低,完全的热更新机制。

但相对的,这种方式也有对应的缺点:

  • 完全的网络依赖,在离线的情况下无法打开页面;
  • 首屏加载速度依赖于网络,网络较慢时,首屏加载也较慢;

但相对的,这种方式也有对应的缺点:

  • 完全的网络依赖,在离线的情况下无法打开页面;
  • 首屏加载速度依赖于网络,网络较慢时,首屏加载也较慢;

通常,这种方式更适用在一些比较轻量级的页面上,例如一些帮助页、提示页、使用攻略等页面。这些页面的特点是功能性不强,不太需要复杂的功能协议,且不需要离线使用。在一些第三方页面接入上,也会使用这种方式,例如我们的页面调用微信JS-SDK。

(2) 内置包H5,这是一种本地化的嵌入方式,我们需要将代码进行打包后下发到客户端,并由客户端直接解压到本地储存中。通常我们运用在一些比较大和比较重要的模块上。其优点是:

  • 由于其本地化,首屏加载速度快,用户体验更为接近原生;
  • 可以不依赖网络,离线运行;

但同时,它的劣势也十分明显:

  • 开发流程/更新机制复杂化,需要客户端,甚至服务端的共同协作;
  • 会相应的增加 App 包体积;

这两种接入方式均有自己的优缺点,应该根据不同场景进行选择。

总结整理了网上的资料

H5性能监控设计

[TOC]

H5性能监控设计

背景

随着业务量的增加,用到H5页面的越来越多;H5页面的性能怎么样?用户访问的体验怎么样?H5的出错率以及兼容性怎么样?目前来说,处于一个模糊状态,只能是通过公司测试的设备在不同的网络条件下体验一次,心里才有一个初步的印象。

没有一个数据系统支撑,仅仅凭内部人员测试和体验,这是远远不够的,我们之前在一些页面上加了百度统计做了一个初步的统计,就显示每天有数十万的访问量,而这是在一些个别页面测试得到的数据,不够精确。经过我们团队讨论决定要把H5性能监控系统做起来。

目的

  • 通过收集页面在用户设备的性能数据分析,可以方便知道页面在用户设备运行的性能情况,从而为开发人员提供选择合适的技术提供数据支撑。

  • 通过错误监控与收集,降低用户遇到问题的概率,分析问题原因,减少问题重复出现概率,能够在用户碰到问题前发现并解决。

  • 统计数据分析,根据数据分析做出相应的策略,从而选择合适的技术选型,从而提升页面性能和开发可维护性可扩展性

  • 用户行为分析,给产品运营人员提供运营数据支持

采集数据

性能数据

  • wbTotal(webview加载H5页面时间)
  • wbInit(webview初始化时间)
  • wbResponse(webView初始化加载url到第一次webView start回调时间)
  • loadPageComplete(页面所有资源加载完成时间)
  • renderPage(用户基本可以看到基础页面的时间)
  • domReady(解析DOM树结构的时间),ttfb(用户拿到Html5页面第一个资源)
  • pageRequest(请求h5页面文档所需的时间)
  • ttfb(用户拿到Html5页面第一个资源)
  • netType(网络类型)

内存信息(performance.memory)

  • jsHeapSizeLimit: 内存大小限制

  • totalJSHeapSize: 可使用的内存

  • usedJSHeapSize: JS对象(包括V8引擎内部对象)占用的内存,不能大于totalJSHeapSize,如果大于,有可能出现了内存泄漏

错误数据

语法错误

  • msg: 错误描述,
  • url: 错误页面地址,
  • line: 错误函数
  • 语法错误

Http请求错误(下个版本做)

参考以下资料

使用性能API快速分析web前端性能

初探 performance – 监控网页与程序性能

使用window.performance分析web前端性能

PerformanceTiming API参考文档

Using the Resource Timing API

阮一峰 Performance API

数据处理上报

通过APP大数据埋点接口数埋点和上报

大数据平台界面展示、数据分析与报警

界面展示

分两个维度统计

1、分别统计IOS设备和安卓设备下

  • 每个页面各个阶段的近两周(这个时间可以讨论)的平均时间。包括
    wbTotal(webview加载H5页面时间),wbInit(webview初始化时间)loadPageComplete(页面所有资源加载完成时间),renderPage(用户基本可以看到基础页面的时间),domReady(解析DOM树结构的时间),ttfb(用户拿到Html5页面第一个资源),pageRequest(请求h5页面文档所需的时间)

2、统计在不同网络条件下,wifi条件下、4G和3G以下网络条件

阀值报警 (待做)

Node.js下jwt实现请求验证

[TOC]

Node.js下jwt实现请求验证

1、JWT基本使用

JWT是我们用来实现基于token认证的一种实现方式,是目前实现web开发中跨域认证的一种常用方式。当然,你也可以基于这种方式实现自己签发和认证处理方式,这里首先介绍一下使用方式。

jsonwebtoken官方网站

  • 安装jsonwebtoken
 npm install jsonwebtoken 
  • 在登录的时候签发token,前端在拿到token后存储在本地(cookie、web storage都可以)
const jwt = require('jsonwebtoken');
const secret = '秘钥abcABC!@#'; 

   //jwt生成token
 const token = jwt.sign({
     name: web
  }, secret, {
     expiresIn:  60*60*2 //2个小时到期时间
  });
console.log(token);

  • 服务器在客户端请求需要认证的接口时进行解密认证
//解密token
jwt.verify(token, secret, function (err, decoded) {
    if (!err){
          console.log(decoded.name);  //如果超时则验证出错
     }
})


2、JWT实现原理

客户端登录时请求服务器,签发一个token, json web token官方规定由三部分组成
Header(头部).Payload(负载).Signature(签名),并且以 " . "分隔,

2.1)Header 中由两部分组成一个是加密方法,另一个是令牌类型

  • 例如
{
  "alg": "HS256",
  "typ": "JWT"
}

2.2)Payload 这个指的是有效负载,简单的说就存放的实体数据,官方给了一些建议声明。

这些是一组预定义声明,不是强制性的,但建议使用,以提供一组有用的,可互操作的声明。其中一些是:iss(签发人),exp(到期时间),sub(主题),aud(观众)等。

{
    iss: "",
    exp: "",
    sub: "",
    aud: ""
}

也可以自己定义一些字段

{
    iss: "",
    exp: "",
    sub: "",
    aud: "" ,
    jid: ""//序号
    effect: "",//生效时间
}

然后使用Base64Url对Payload进行编码,形成令牌token的第二部分

2.3)Signature(签名)要创建签名部分,必须采用标头中指定的算法对Header,Payload,秘钥secret,对其进行签名,如下

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

2.4)Base64URL

前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。

JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法。

3、顺带聊一下web开发中的一些认证机制

3.1)HTTP基本认证(HTTP Basic Auth)

在HTTP中,HTTP基本认证是一种允许Web浏览器或者其他客户端在请求时提供用户名和密码形式的身份凭证的一种登录验证方式。

3.2)Cookie与session认证机制

用户登录以后服务器会用session来保存用户的登录状态信息,用户下次请求时通过session来认证。

3.3)OAuth(开放授权)

OAuth 是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表等),而无需将用户名和密码提供给第三方应用。

3.4)基于Token认证机制

客户端登录时,服务器会签发一个token给客户端,这个toekn有过期时间和使用范围,客户端保存在本地,客户端每次请求需要认证的接口,都要带上token,服务器判断是否有权限访问。

这种方式最大的缺点就是,在token未过期时,其他人拿到token也能使用,所以使用这种方式最好考虑清楚应对方式,

3.5)WebAuthn认证 一 浏览器上一种新的认证方式

W3C的WebAuthn API是一种可融入浏览器和相关Web平台基础架构的标准WebAPI,可为每个站点提供强大、唯一且基于公钥的凭证,消除了从某一站点窃取密码后被用于其他站点的风险。 使用FIDO身份验证器加载到设备上的在浏览器中运行的Web应用程序,可以通过密码操作代替密码交换,或除了密码交换之外,还可为服务提供者和用户带来诸多益处:

  • 更简单的身份验证:用户只需使用一种手势登录PC、笔记本电脑和/或移动设备中的内部或内置认证器(如指纹或面部生物识别技术)使用CTAP进行设备到设备认证的外部认证器(如安全密钥和移动设备),一个由FIDO联盟开发的用于补充WebAuthn的外部认证器协议

  • 更强的身份验证:FIDO身份验证比单纯依赖密码和相关身份验证方式要强大得多,并具有以下优点用户证书和生物识别模板永远不会离开用户的设备,也不会存储在服务器上帐户可以免受网络钓鱼,中间人攻击和使用被盗密码的反复攻击

  • 开发人员可以开始在FIDO新的开发者资源页面上创建利用FIDO身份验证的应用程序和服务。

查看FIDO项目组官方网站

参考文档

jwt官方网站

Web开发中常见的认证机制 - 陈杰

JSON Web Token 入门教程 - 阮一峰

HTML5 一 history API

[TOC]

HTML5 一 history API

简介

history:接口允许操作浏览器的曾经在标签页或者框架里访问的会话历史记录。

兼容性支持

pushState(H5新增)

pushState:pushState用来新增历史记录。

  • pushState() 带有三个参数:一个状态对象,一个标题(现在被忽略了),以及一个可选的URL地址。下面将对这三个参数进行细致的检查:

    • state object — 状态对象是一个由 pushState()方法创建的、与历史纪录相关的JS对象。当用户定向到一个新的状态时,会触发popstate事件。事件的state属性包含了历史纪录的state对象。(译者注:总而言之,它存储JSON字符串,可以用在popstate事件中。)state 对象可以是任何可以序列化的东西。由于 火狐 会将这些对象存储在用户的磁盘上,所以用户在重启浏览器之后这些state对象会恢复,我们施加一个最大640k 的字符串在state对象的序列化表示上。如果你像pushState() 方法传递了一个序列化表示大于640k 的state对象,这个方法将扔出一个异常。如果你需要更多的空间,推荐使用sessionStorage或者localStorage。

    • title — 火狐浏览器现在已经忽略此参数,将来也许可能被使用。考虑到将来有可能的改变,传递一个空字符串是安全的做法。当然,你可以传递一个短标题给你要转变成的状态。(译者注:现在大多数浏览器不支持或者忽略这个参数,最好用null代替)

    • URL — 这个参数提供了新历史纪录的地址。请注意,浏览器在调用pushState()方法后不会去加载这个URL,但有可能在之后会这样做,比如用户重启浏览器之后。新的URL不一定要是绝对地址,如果它是相对的,它一定是相对于当前的URL。新URL必须和当前URL在同一个源下;否则,pushState() 将丢出异常。这个参数可选,如果它没有被特别标注,会被设置为文档的当前URL。


history.pushState({'history':'history'},'history','#hisory')


replaceState(H5新增)

replaceState:pushState用来替换当前的增历史记录。

history.repalceState({'history':'repalceState'},'repalceState','#hisory')

带有三个参数:一个状态对象,一个标题(现在被忽略了),以及一个可选的URL地址。下面将对这三个参数进行细致的检查:

state object :— 状态对象是一个由 pushState()方法创建的、与历史纪录相关的JS对象。当用户定向到一个新的状态时,会触发popstate事件。事件的state属性包含了历史纪录的state对象。(译者注:总而言之,它存储JSON字符串,可以用在popstate事件中。)state 对象可以是任何可以序列化的东西。由于 火狐 会将这些对象存储在用户的磁盘上,所以用户在重启浏览器之后这些state对象会恢复,我们施加一个最大640k 的字符串在state对象的序列化表示上。如果你像pushState() 方法传递了一个序列化表示大于640k 的state对象,这个方法将扔出一个异常。如果你需要更多的空间,推荐使用sessionStorage或者localStorage。

title :— 火狐浏览器现在已经忽略此参数,将来也许可能被使用。考虑到将来有可能的改变,传递一个空字符串是安全的做法。当然,你可以传递一个短标题给你要转变成的状态。(译者注:现在大多数浏览器不支持或者忽略这个参数,最好用null代替)

URL :— 这个参数提供了新历史纪录的地址。请注意,浏览器在调用pushState()方法后不会去加载这个URL,但有可能在之后会这样做,比如用户重启浏览器之后。新的URL不一定要是绝对地址,如果它是相对的,它一定是相对于当前的URL。新URL必须和当前URL在同一个源下;否则,pushState() 将丢出异常。这个参数可选,如果它没有被特别标注,会被设置为文档的当前URL。

popstate事件(H5新增)

触发历史记录改变的事件,比如用户前进、后退等操作 会触发popstate事件。进而获取到通过pushState和replaceState传递的值。

 // 监听state事件
window.addEventListener('popstate',function (event) {
	
    console.log(event.state);

});

history.back 回退

  • 回退到上一个history记录

history.forward 前进

  • 前进一个history记录

history.go 去往第n条记录

  • 去往第几个history记录

history.length 长度

  • 获取浏览history记录的长度

history.state 当前地址

  • 获取当前的记录

图解 Flexbox 2 - 深入理解

图解 Flexbox 2 - 深入理解

[toc]

本文图片、部分翻译来自原文 Even more about how Flexbox works — explained in big, colorful, animated gifs

上一篇 53.图解 Flexbox 介绍了 Flexbox 的基本属性,这次来深入理解下。

属性 #1 flex-basis

上一篇主要看了容器元素的属性,这次来看下子元素

flex-basis 控制元素的默认尺寸,然后再由其他 Flexbox 属性控制

下图表示可以和 width 属性互换

flex-more-1.gif

flex-basiswidth 又有什么区别呢,因为 Flexbox 的容器有两个轴 如下:

flex-more-2.png

flex-basis 是通过主轴 (main axis) 来影响元素尺寸的

我们保持 flex-basis 不变,然后切换主轴方向 如下:

flex-more-3.gif

注意,我们必须从手动设置高度切换成手动设置高度。因此通过 flex-direction 的不同,flex-basis 会交替的影响 widthheight

属性 #2 flex-grow

首先设置每个元素 width 为 120px ,如下:

flex-more-4.png

flex-grow 的默认值为 0,意味着每个元素不能在容器内增长 (grow) 他们的宽度

如果设置 flex-grow: 1 呢,如下图:

flex-more-5.png

每个元素在容器内均匀的充满了整个空间,flex-grow 的值覆盖了 width

flex-grow: 1 到底意味着什么呢,如果我们改为 flex-grow: 999

flex-more-6.png

完全一样,没有变化。那是因为 flex-grow 不是个绝对值,而是一个相对值。

起作用的不是一个元素的 flex-grow 的值,而是他相对于其他元素值。比如,我们改变三个元素的flex-grow: 其他元素不变

flex-more-7.gif

这个其实很容易理解,就是按照百分比来占据空间:

当每个元素都为 flex-grow: 1 ,一共6块, 每个元素占据空间的 1/6 宽度

当第三个元素 flex-grow: 2 时,整个父级空间切成7份 1+1+2+1+1+1。 第三个元素占据 2/7 ,其他元素为 1/7

同理,第三个元素 flex-grow: 3 第三个元素占据 3/8 ,其他元素为 1/8

flex-grow 的值是按比例来计算的,比如设置每个元素 flex-grow: 4 ,第三个元素 flex-grow: 12, 其实和 1:3 一样的

flex-more-8.png

flex-growflex-basis 一样是作用于主轴的。除非改变 flex-direction: column 否则元素只是改变宽度

属性 #3 flex-shrink

flex-shrink 的作用和 flex-grow 恰恰相反,来确定一个元素的缩小比例。(顾名思义,shrink 和 grow 就是一个缩小一个增长)

flex-shrink 的作用是指定哪些元素缩小哪些不缩小。默认情况每个元素 flex-shrink: 1,意味着他们会随着父级容器的缩小而缩小。

下图中每个元素都是 flex-grow: 1 他们会充满容器,同时每个元素 flex-shrink: 1, 他们是可以收缩的

flex-more-9.gif

如果把第三个元素设为 flex-shrink: 0 禁止缩小,所以他可以增长宽度适应容器,但是不会下降到 120px 以下

flex-more-10.gif

每个元素默认 flex-shrink: 1

flex-shrink 也是相对值,按比例来计算的,若果一个元素 flex-shrink 的值为6,其他的为2,当容器空间压缩时,该元素会以 3x 的速度缩小。这里的 3x 指的是将缩小 3倍,而不是缩小为 1/3 的宽度。

后面会有 flex-shrinkflex-grow 更详细的说明,先来个最后一个属性

属性 #4 flex

flex 属性是把 flex-grow flex-shrink flex-basis 综合到了一起

默认值为 0 (flex-grow) 1 (flex-shrink) auto (flex-basis)

最后一个例子,我们设置2个元素

.square#one {
  flex: 2 1 300px;
}
.square#two {
  flex: 1 2 300px;
}

flex-basis 的值相同,他们都占据相同的空间 300px (容器算上 margin padding 是 600px)

但是 元素1 将会2x速度增长宽度, 元素2 将会2x速度缩小宽度 如下

flex-more-11.gif

##flex-growflex-shrink 是如何工作的

当 元素1 (.square#one) 增长时他不会增长到 元素2 (.square#two) 的两倍;同样缩小时 元素2 (.square#two) 也不会缩小为 元素1 (.square#one) 的一半

因此这不是他们的大小比例为 1:2 或者 2:1 ,而是他们的收缩和增长速度

简单计算下

容器初始宽度为 640px ,除去两边的各 20px 的 padding 后,两个元素各有 flex-basis 为 300px

当容器设置为 430px 时,我们失去了 210px 的空间 元素1 flex-shrink: 1 减少了 70px ; 元素2 flex-shrink: 2 少了 140px

当容器设置为 340px 时,我们失去了 300px 的空间 元素1 减少了 100px ; 元素2 少了 200px

他们减少的比例正好也是 2:1

flex-grow 也是同样道理。当容器设置为 940px 时,增长的 300px 空间分别来自 元素1 的 200px 和 元素2 的 100px

直接用 flex 即可表达:

flex-more-12.gif

上图很容易看出元素的宽度是如何根据比例进行调整的

最后

flex-basis 控制着元素在沿着主轴上 grow 和 shrik 之前最终能够变化多大

flex-grow 决定了他将与兄弟元素成比例的增长多少,flex-shrink 决定了缩小多少

javaScript设计模式的应用 (一)

对象字面量

使用对象字面量,对象字面量不需要使用new运算符进行实例化,但不能用在一个语句的开头,因为开始的可能被解读为一个块的开始。

var myObjectLiteral = {
    variableKey: variableValue,
    functionKey: function () {}
};

模块模式

Module模式最初被定义为一种在传统软件工程中为类提供私有和公有封装的方法。

模拟 私有方法、属性

Module模式使用闭包封装“私有”状态和组织。它提供了一种包装混合公有/私有方法和变量的方式,防止其泄露至全局作用域,并与别的开发人员的接口发生冲突。通过该模式,只需返回一个公有API,而其他的一切则都维持在私有闭包里。

var myNamespace = (function () {
    // 私有计数器变量
    var myPrivateVar = 0;
    // 记录所有参数的私有函数
    var myPrivateMethod = function (foo) {
        console.log(foo);
    };
    return {
        // 公有变量  
        myPublicVar: "foo",
        // 调用私有变量和方法的公有函数  
        myPublicFunction: function (bar) {
            // 增加私有计数器值  
            myPrivateVar++;
            // 传入bar 调用私有方法  
            myPrivateMethod(bar);
        }
    };
})();

引入引出的问题

  • 引入
// 全局模块
var myModule = (function (jQ, _) {
function privateMethod1() {
    jQ(".container").html("test");
}

function privateMethod2() {
    console.log(_.min([10, 5, 100, 2, 1000]));
}
return {
    publicMethod: function () {
        privateMethod1();
    }
}; // 引入jQuery 和Underscore
})(jQuery, _));
myModule.publicMethod();
  • 引出扩展
// 全局模块
var myModule = (function () {
    // 模块对象
    var module = {},
        privateVariable = "Hello World";

    function privateMethod() {
        // ...
    }
    module.publicProperty = "Foobar";
    module.publicMethod = function () {
        console.log(privateVariable);
    };
    return module;
})();

工具包和特定框架的Module模式实现

命名空间

var store = window.store || {};
if (!store["basket"]) {
    store.basket = {};
}
if (!store.basket["core"]) {
    store.basket.core = {};
}
store.basket.core = {
    // ...剩余的逻辑
};

揭示模块模式

  • 优点

该模式可以使脚本语法更加一致。在模块代码底部,它也会很容易指出哪些函数和变量可以被公开访问,从而改善可读性。

var myRevealingModule = function () {
    var privateCounter = 0;

    function privateFunction() {
        privateCounter++;
    }

    function publicFunction() {
        publicIncrement();
    }

    function publicIncrement() {
        privateFunction();
    }

    function publicGetCount() {
            return privateCounter;
        } // 将暴露的公有指针指向到私有函数和属性上
    return {
        start: publicFunction,
        increment: publicIncrement,
        count: publicGetCount
    };
}();
myRevealingModule.start();

单例模式

Singleton(单例)模式被熟知的原因是因为它限制了类的实例化次数只能一次。

var mySingleton = (function () {
    // 实例保持了Singleton 的一个引用    
    var instance;

    function init() {
        // Singleton       
        // 私有方法和变量       
        function privateMethod() {
            console.log("I am private");
        }
        var privateVariable = "Im also private";
        var privateRandomNumber = Math.random();
        return {
            // 公有方法和变量          
            publicMethod: function () {
                console.log("The public can see me!");
            }, publicProperty: "I am also public",
            getRandomNumber: function () {
                return privateRandomNumber;
            }
        };
    };
    return {
        // 获取Singleton 的实例,如果存在就返回,不存在就创建新实例   
        getInstance: function () {
            if (!instance) {
                instance = init();
            }
            return instance;
        }
    };
})();

观察者模式、发布订阅模式

一个或多个观察者对目标的状态感兴趣,它们通过将自己依附在目标对象上以便注册所感兴趣的内容。目标状态发生改变并且观察者可能对这些改变感兴趣,就会发送一个通知消息,调用每个观察者的更新方法。当观察者不再对目标状态感兴趣时,它们可以简单地将自己从中分离。

var pubsub = {};   // 定义发布者

(function (q) {

    var list = [],  //回调函数存放的数组,也就是记录有多少人订阅了我们东西
        subUid = -1;

    // 发布消息,遍历订阅者
    q.publish = function (type, content) {
        // type 为文章类型,content为文章内容
        
        // 如果没有人订阅,直接返回
        if (!list[type]) {

            return false;
        }

        setTimeout(function () {
            var subscribers = list[type],
                len = subscribers ? subscribers.length : 0;

            while (len--) {
                // 将内容注入到订阅者那里
                subscribers[len].func(type, content);
            }
        }, 0);

        return true;

    };
    //订阅方法,由订阅者来执行
    q.subscribe = function (type, func) {
        // 如果之前没有订阅过
        if (!list[type]) {
            list[type] = [];
        }

        // token相当于订阅者的id,这样的话如果退订,我们就可以针对它来知道是谁退订了。
        var token = (++subUid).toString();
        // 每订阅一个,就把它存入到我们的数组中去
        list[type].push({
            token: token,
            func: func
        });
        return token;
    };
    //退订方法
    q.unsubscribe = function (token) {
        for (var m in list) {
            if (list[m]) {
                for (var i = 0, j = list[m].length; i < j; i++) {
                    if (list[m][i].token === token) {
                        list[m].splice(i, 1);
                        return token;
                    }
                }
            }
        }
        return false;
    };

} (pubsub));

//将订阅赋值给一个变量,以便退订
var girlA = pubsub.subscribe('js类的文章', function (type, content) {
    console.log('girlA订阅的'+type + ": 内容内容为:" + content);
});
var girlB = pubsub.subscribe('js类的文章', function (type, content) {
    console.log('girlB订阅的'+type + ": 内容内容为:" + content);
});
var girlC = pubsub.subscribe('js类的文章', function (type, content) {
    console.log('girlC订阅的'+type + ": 内容内容为:" + content);
});

//发布通知
pubsub.publish('js类的文章', '关于js的内容');  
// 输出:
// girlC订阅的js类的文章: 内容内容为:关于js的内容
// test3.html:78 girlB订阅的js类的文章: 内容内容为:关于js的内容
// test3.html:75 girlA订阅的js类的文章: 内容内容为:关于js的内容


//girlA退订了关于js类的文章 
setTimeout(function () {
    pubsub.unsubscribe(girlA);
}, 0);

//再发布一次,验证一下是否还能够输出信息
pubsub.publish('js类的文章', "关于js的第二篇文章");
// 输出:
// girlB订阅的js类的文章: 内容内容为:关于js的第二篇文章
// girlC订阅的js类的文章: 内容内容为:关于js的第二篇文章

HTML5系列文章之音频audio操作

最近因为项目上用到音乐播放,就仔细研究了一下html5 audio API,利用国庆休息的时间,进行了一些总结,有些坑还没填好已经备注文档中。
这篇文章主要是介绍一些基本使用,下一篇将主要与大家分享audio在各个浏览器和设备上存在的问题以及如何去解决。

查看demo演示以及源码使用

常用属性

属性 | 作用 |
---|---|----
src | 设置或返回是否在就绪(加载完成)后随即播放音频
currentSrc | 返回当前音频的 URL。
currentTime | 设置或返回音频中的当前播放位置(以秒计)。
duration | 返回音频的长度(以秒计)。
readyState | 返回音频当前的就绪状态。
networkState | 返回音频的当前网络状态。

功能播放设置

属性 作用
paused bool 设置或返回音频是否暂停。
ended bool 返回音频的播放是否已结束。
muted bool 设置或返回是否关闭声音。
controls bool 设置或返回音频是否应该显示控件(比如播放/暂停等)。
loop bool 设置或返回音频是否应在结束时再次播放。
autoplay bool 设置或返回是否在就绪(加载完成)后随即播放音频。
preload bool 设置或返回音频的 preload 属性的值。
volume 范围 0-1 设置或返回音频的音量。
playbackRate 1.0/2.0倍速度 -2后退两倍速度 设置或返回音频/视频播放的速度(留下一个坑 负值不起作用)

常用方法

名称 作用
canPlayType() 查浏览器是否可以播放指定的音频类型 "probably" - 浏览器最可能支持该音频/视频类型,"maybe" - 浏览器也许支持该音频/视频类型,"" - (空字符串)浏览器不支持该音频/视频类型
fastSeek() 在音频播放器中指定播放时间
load() 重新加载音频元素
play() 开始播放音频
pause() 暂停当前播放的音频

常用事件

事件名称 事件描述
loadstart 客户端开始请求数据
progress 客户端正在请求数据(或者说正在缓冲)
play 播放中
pause 暂停
ended 播放结束
timeupdate 当前播放时间发生改变的时候。常用作显示进度
canplaythrough 歌曲已经载入完全完成
canplay 缓冲至目前可播放状态。
error 播放发生错误时。

发布平台开发中Vue相关问题积累与解决

[toc]

发布平台开发中Vue相关问题积累与解决

Vue的底层原理实现

Vue组件的生命周期示例图

打包构建资源引用问题

根据环境变量的不同构建不同环境的代码

1、在公共Js中根据生产环境、测试环境和开发环境域名不同来定义不同的域名

// 示例
const production = host.indexOf("static.watch.okii.com") > -1;

// 定位接口域名
export const location_domain = production
    ? `${protocol}//location.watch.okii.com`
    : `${protocol}//location.module.okii.com`;
    

2、在构建的时候根据node环境变量来判断来构建打包

示例参考
webpack生产环境官方配置说明

CSS引用字体路径不正确的问题

http://deploy.test/front/static/css/static/fonts/ionicons.24712f6.ttf

http://deploy.test/front/static/fonts/ionicons.24712f6.ttf

在 webpack 出现之前,前端开发人员会使用 grunt 和 gulp 等工具来处理资源,并将它们从 /src 文件夹移动到 /dist 或 /build 目录中。同样方式也被用于 JavaScript 模块,但是,像 webpack 这样的工具,将动态打包(dynamically bundle)所有依赖项(创建所谓的依赖图(dependency graph))。这是极好的创举,因为现在每个模块都可以_明确表述它自身的依赖,我们将避免打包未使用的模块。

webpack 最出色的功能之一就是,除了 JavaScript,还可以通过 loader 引入任何其他类型的文件。也就是说,以上列出的那些 JavaScript 的优点(例如显式依赖),同样可以用来构建网站或 web 应用程序中的所有非 JavaScript 内容。让我们从 CSS 开始起步,或许你可能已经熟悉了这个设置过程。

示例说明参考官方配置

计算属性

  • 计算属性的初衷

设计的初衷是用于简单运算的,避免在模板中有太多的计算。

vue中的render渲染函数

vuex全局状态管理的问题

vuex官方网站

状态管理的问题

路由设置

子路由、嵌套路由

路有监听

路由设置的问题

axios设置问题

详细查看中文文档

  • 全局设置
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
  • 拦截器

在请求或响应被 then 或 catch 处理前拦截它们。

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
  });

如果你想在稍后移除拦截器,可以这样:

var myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);

可以使用 validateStatus 配置选项定义一个自定义 HTTP 状态码的错误范围。

axios.get('/user/12345', {
  validateStatus: function (status) {
    return status < 500; // 状态码在大于或等于500时才会 reject
  }
})

布局Flex

Flex参考文档

promise异步返回问题

promise基本使用

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});
  • 介绍一下Then方法

Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。

getJSON("/posts.json").then(function(json) {
  return json.post;
}).then(function(post) {
  // ...
});
  • catch方法

Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。
另外,then方法指定的回调函数,如果运行中抛出错误,也会被catch方法捕获。

p.then((val) => console.log('fulfilled:', val))
  .catch((err) => console.log('rejected', err));

// 等同于
p.then((val) => console.log('fulfilled:', val))
  .then(null, (err) => console.log("rejected:", err));
  • finally方法

用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。

  • Promise.all()

Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

Promise实现原理

  • Promise 状态

Promise 必须为以下三种状态之一:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。一旦Promise被resolve或reject,不能再迁移至其他任何状态(即状态 immutable)。
为保持代码清晰,暂无异常处理。同时为表述方便,约定如下:

fulfilled 使用 resolved 代替
onFulfilled 使用 onResolved 代替

  • Promise 构造函数

从构造函数开始,我们一步步实现符合 Promsie A+ 规范的 Promise。大概描述下,Promise构造函数需要做什么事情。

  • 初始化 Promise 状态(pending)
  • 初始化 then(..) 注册回调处理数组(then 方法可被同一个 promise 调用多次)
  • 立即执行传入的 fn 函数,传入Promise 内部 resolve、reject 函数
  • ...
function Promise (fn) {
  // 省略非 new 实例化方式处理
  // 省略 fn 非函数异常处理

  // promise 状态变量
  // 0 - pending
  // 1 - resolved
  // 2 - rejected
  this._state = 0;
  // promise 执行结果
  this._value = null;
 
  // then(..) 注册回调处理数组
  this._deferreds = [];

  // 立即执行 fn 函数
  try {
    fn(value => {
      resolve(this, value);
    },reason => {
      reject(this, reason);
    })
  } catch (err) {
    // 处理执行 fn 异常
    reject(this, err);
  }
}

_state 和 _value 变量很容易理解,_deferreds变量做什么?规范描述:then 方法可以被同一个 promise 调用多次。为满足多次调用 then 注册回调处理,内部选择使用 _deferreds 数组存储处理对象。具体处理对象结构,见 then 函数章节。
最后执行 fn 函数,并调用 promise 内部的私有方法 resolve 和 reject。resolve 和 reject 内部细节随后介绍。

  • then 函数

Promise A+提到规范专注于提供通用的 then 方法。then 方法可以被同一个 promise 调用多次,每次返回新 promise 对象 。then 方法接受两个参数onResolved、onRejected(可选)。在 promise 被 resolve 或 reject 后,所有 onResolved 或 onRejected 函数须按照其注册顺序依次回调,且调用次数不超过一次。
根据上述,then 函数执行流程大致为:

实例化空 promise 对象用来返回(保持then链式调用)
构造 then(..) 注册回调处理函数结构体
判断当前 promise 状态,pending 状态存储延迟处理对象 deferred ,非pending状态执行 onResolved 或 onRejected 回调
...

Promise.prototype.then = function (onResolved, onRejected) {

  var res = new Promise(function () {});
  // 使用 onResolved,onRejected 实例化处理对象 Handler
  var deferred = new Handler(onResolved, onRejected, res);

  // 当前状态为 pendding,存储延迟处理对象
  if (this._state === 0) {
    this._deferreds.push(deferred);
    return res;
  }

  // 当前 promise 状态不为 pending
  // 调用 handleResolved 执行onResolved或onRejected回调
  handleResolved(this, deferred);
  
  // 返回新 promise 对象,维持链式调用
  return res;
};

Handler 函数封装存储 onResolved、onRejected 函数和新生成 promise 对象。

function Handler (onResolved, onRejected, promise) {
  this.onResolved = typeof onResolved === 'function' ? onResolved : null;
  this.onRejected = typeof onRejected === 'function' ? onRejected : null;
  this.promise = promise;
}

  • 链式调用为什么要返回新的 promise
    如我们理解,为保证 then 函数链式调用,then 需要返回 promise 实例。但为什么返回新的 promise,而不直接返回 this 当前对象呢?看下面示例代码:
var promise2 = promise1.then(function (value) {
  return Promise.reject(3)
})

复制代码假如 then 函数执行返回 this 调用对象本身,那么 promise2 === promise1,promise2 状态也应该等于 promise1 同为 resolved。而 onResolved 回调中返回状态为 rejected 对象。考虑到 Promise 状态一旦 resolved 或 rejected就不能再迁移,所以这里 promise2 也没办法转为回调函数返回的 rejected 状态,产生矛盾。
handleResolved 函数功能为根据当前 promise 状态,异步执行 onResolved 或 onRejected 回调函数。因在 resolve 或 reject 函数内部同样需要相关功能,提取为单独模块。往下翻阅查看。

ECMAScript 6 - 阮一峰

Promises/A+ 规范

参考文章掘金上的文章

JavaScript数据结构之集合

JavaScript数据结构之集合

集合是一组无序但彼此之间又有一定相关性的成员构成的,每个成员在集合中只能出现一次。

- 不包含任何成员的空集,全集则是包含一切的成员的集合。
- 如果两个集合的成员完全相同,则称为两个集合相等
- 如果一个集合中所有的成员都属于另外一个集合则称前一集和为另一集合的子集

集合操作

  • 并集
  • 交集
  • 补集
  • 子集

JS实现

资源参考与完善:https://github.com/debingfeng/javascript

/**
 * 集合类JavaScript描述
 * @constructor
 */
function Set() {

    this.dataStore = [];
    
    this.add = add;
    
    this.remove = remove;
    
    this.size = size;
    
    this.union = union;
    
    this.intersect = intersect;
    
    this.subset = subset;
    
    this.difference = difference;
    
    this.show = show;
    
    this.has = has;

}
/**
 * 添加元素
 * @param data
 * @returns {boolean}
 */
function add(data) {
	if (this.dataStore.indexOf(data) < 0) {
		this.dataStore.push(data);
		return true;
	} else {
		return false;
	}
}
/**
 * 移除数据
 * @param data
 * @returns {boolean}
 */
function remove(data) {
	var index = this.dataStore.indexOf(data);
	if (index > -1) {
		this.dataStore.splice(index,1);
		return true;
	} else {
		return false;
	}
}
/**
 * 获取集合所有数据
 * @returns {Array}
 */
function show() {
	return this.dataStore;
}
/**
 * 检测是否含有某元素
 * @param data
 * @returns {boolean}
 */
function has(data) {
	return this.dataStore.indexOf(data) >= 0;
}
/**
 * 获取元素的长度
 * @returns {Number}
 */
function size() {
	return this.dataStore.length;
}

/**
 * 并集
 * @param set
 * @returns {Set}
 */
function union(set) {
	var tempSet = new Set();
	var i = 0,
		l = this.dataStore.length;
	for (; i < l; i++) {
		tempSet.add(this.dataStore[i]);
	}

	var j = 0,
		ol = set.dataStore.length;
	for ( ; j < ol; j++) {
		if (!(tempSet.has(set.dataStore[j]))) {
			tempSet.dataStore.push(set.dataStore[j]);
		}
	}

	return tempSet;
}
//当一个元素属于一个集合,同时也属于另一个集合时,则把该元素加入到一个新集合。
/**
 * 交集
 * @param set
 * @returns {Set}
 */
function intersect(set) {
	var tempSet = new Set();
	var i = 0,
		l = this.dataStore.length;
	for (; i < l; i++) {
		if (set.has(this.dataStore[i])) {
			tempSet.add(this.dataStore[i]);
		}
	}
	return tempSet;
}
// 首先判断这个集合的长度是否大于待比较的集合,如果大于则不可能是他的子集,进而判断该集合的元素是否有不存在待比较的集合中,如果有则说明不是他的子集。
/**
 * 子集
 * @param set
 * @returns {Set}
 */
function subset(set) {
	if(this.size() > set.size()) {
		return false;
	}
	var i = 0,
		l = this.dataStore.length;
	for (; i < l; i++) {
		if (!set.has(this.dataStore[i])) {
			return false
		}
	}
	return true;
}

/**
 * 补集
 * @param set
 * @returns {Set}
 */
function difference(set) {
	var tempSet = new Set();
	var i = 0,
		l = this.dataStore.length;
	for (; i < l; i++) {
		if (!(set.has(this.dataStore[i]))) {
			tempSet.add(this.dataStore[i]);
		}
	}
	return tempSet;
}

var a = new Set();
a.add("a");
a.add("b");
var b = new Set();
b.add("b");
b.add("c");
console.log(a.union(b).show());
console.log(a.difference(b).show());
console.log(a.intersect(b).show());

移动web开发像素知识

移动web开发像素知识

  • px : css逻辑像素,浏览器使用的抽象单位

  • dp,pt: 设备独立像素

  • dpr: 设备缩放比

计算公式: 1 px = dpr的平方 * dp

  • DPI: 打印机每英寸可以喷的墨汁点
  • PPI:屏幕上每英寸的像素数量

PPI = 根号(物理像素的长^2 + 宽^2) / 4

PPI对应的缩放比参考
image

设置视口viewport

viewport详细参考

<meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">

设计移动web方案

移动页面参考模板

  • 以iphone6 640*1167宽度 320 来设计

  • 缩放比0.5来设计 这时1px = pt

布局

  • 弹性布局 为了兼容安卓4.4以下的设备,可以使用flexbox,-webkit-box,其他最新的浏览器使用flex.

  • 响应式布局

    • 媒体查询,百分比

    • 弹性图片

    • 重新布局,显示与隐藏

    • 自适应性

    • 多行文本溢出

特别样式处理

  • 一像素问题
    使用缩放transform:scaleY(0.5)
    image

  • 使用相对单位:rem 使用html的字体的相对单位

设置基础值,screen.width/20

终端交互优化

移动端web开发技巧

常见问题及解决方法参考

  • 触摸事件 单击事件 300ms延迟

使用zepto 有点透的bug

自己模拟tap效果

  • 开启硬件加速
-webkit-transform:translateZ(0);
  • 弹性滚动

局部滚动开启弹性滚动:

body {
    overflow: scroll;
    -webkit-overflow-scrolling: touch;
}
  • 下拉刷新

使用typescript和gulp快速开始Angular 2开发(翻译)

使用typescript和gulp快速开始Angular 2开发

Angular2出来了一段时间,通过更简单和更简洁的概念,如基于组件的架构,新的依赖注入或内置模块化,新版本的框架要学习得简单得多。在这个分步教程中,您将了解如何使用TypeScript和Gulp开始使用Angular2。源代码在Github上可用。

本文是翻译codeleak.pl上的一篇文章。(英语很挫,错误在所难免)

原文地址:http://blog.codeleak.pl/2016/03/quickstart-angular2-with-typescript-and.html

[toc]

通用说明

更新日志

15/09/2016升级到2.0.0重新angular2文章,添加新的代码库中的代码来反映。添加快速入门指南。

  • 06/09/2016 - 更新至 Angular2 to 2.0.0-rc.6
  • 05/06/2016 - typings updated to 1.0.4. Section related to that part changed too.
  • 05/05/2016 - Upgraded Angular2 to 2.0.0-rc.1
  • 22/04/2016 - Upgraded Angular2 to 2.0.0-beta.15
  • 26/03/2016 - Upgraded Angular2 to 2.0.0-beta.12
  • 21/03/2016 - Upgraded Angular2 to 2.0.0-beta.11 (the post itself as well as the source code)

快速入门

如果你不感兴趣的一步一步的教程,只需按照以下步骤迅速启动。

1、环境要求

Nodejs 必须安装在您的系统和下面的全局节点包必须安装:

gulp
npm i -g gulp

gulp-cli
npm i -g gulp-cli

typings
npm i -g [email protected]

typescript
npm i -g [email protected]

ts-node
npm i -g [email protected]

2、克隆仓库

git clone https://github.com/kolorobot/angular2-typescript-gulp.git

3、定位至angular2-typescript-gulp目录

cd angular2-typescript-gulp

4、安装依赖

npm install

5、构建工程

npm run clean & npm run build
// 构建后会产生Build目录

6、启动应用

    npm start

介绍

至今,Angular 1.X可能仍然是最流行的前端框架,毫无疑问Angular 1.X是一个伟大的框架。然而,这是非常难以掌握。复杂的API和许多概念推出以来,使人了解框架和有效地使用它确实很难。

angular2,另一方面,是一个新的开放。新版本的框架要简单得多,学习更简单和更简洁的概念,如基于组件的体系结构,新的依赖注入或内置模块化。

如果你想找一个比 angular.io 更好地 练习和开始学习Angular 2的地方,如果正在寻找用gulp构建来使用Angular 2的方式,那么这篇文档正适合你!

Note: 文章所使用的代码资源在github
https://github.com/kolorobot/angular2-typescript-gulp.

工程预览

初始化的工程是基于Angular2 Quickstart:
https://angular.io/docs/ts/latest/quickstart.html 做了一些改变。
最重要的变化是将源文件与生成文件分离:SRC目录包含所有源文件并生成包含所有已编译和处理的文件。
服务器使用build目录作为基本目录来资源文件。

angular2-typescript-gulp
|   .gitignore
|   bs-config.json  -> BrowserSync configuration
|   gulpfile.ts     -> Gulp in TypeScript
|   package.json    -> npm configuration
|   tsconfig.json   -> TypeScript configuration
|   typings.json    -> TypeScript typings definitions
|   tslint.json     -> tslint configuration
|
\---src
│   │   index.html                 -> Starting point for the application
│   │   systemjs.config.js         -> SystemJS configuration
│   │
│   \---app                       -> Application modules
│       │   app.component.ts          -> Main application component
│       │   app.html              -> Main application template 
│       │   app.module.ts         -> Application module definition       
│       │   app.routing.ts        -> Routing configuration      
│       │   main.ts               -> Application bootstrap   
│       │
│       \---about 
│       │   └───components
│       │           about.components.ts
│       │           about.html
│       │
│       \---todo
│           ├───components
│           │       task-list.component.ts
│           │       task-list.css
│           │       task-list.html
│           │       task.component.ts
│           │       task.html
│           │
│           \---models
│           │       task.ts
│           │
│           \---services
│                   task-service.ts

NPM全局依赖

假设Node和NPM已经安装,你可以通过调用下面的命令安装全局依赖:

npm i -g <dependency>

为了运行工程必须安装的全局依赖:

gulp and gulp-cli
npm i -g gulp 
npm i -g gulp-cli

typings
npm i -g [email protected]

typescript
npm i -g [email protected]

ts-node
npm i -g [email protected]

// Note: To check global dependencies use the following command:

npm -g –depth 0 ls

创建工程目录和文件

创建如上文所述的目录和文件结构

构建配置

1、typescript配置 - tsconfig.ts

编译文件会被保存至build/app;请注意gulpfile.ts要排除编译。

{
  "compilerOptions": {
    "outDir": "build/app",
    "target": "es5",
    "module": "system",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false
  },
  "exclude": [
    "gulpfile.ts",
    "node_modules"
  ]
}

说明:如果你想导入你的工程到IDE(如IntelliJ),让这个IDE也使用这个文件。

2、Typings - typings.json

To get started we need some definitions to be installed. Run the following commands:
为了能够正常运行开始,我们需要的一些定义被安装。运行一下命令:

typings install –global –save dt~core-js 
typings install –global –save dt~node

这些会被添加至typings.json

{
  "globalDependencies": {
    "core-js": "registry:dt/core-js#0.0.0+20160725163759",
    "node": "registry:dt/node#6.0.0+20160909174046",
  }
}

Typings will be download to typings directory and they will be downloaded on npm install.

Typings 会被下载到typings目录并且他们会通过npm instal的方式被下载下来。

3、npm包和脚本配置 - package.json

关于脚本命令的一些命令:

  • clean - 清除build目录
  • compile - TypeScript compilation (with sourcemaps)
  • build - 打包构建工程
  • start - 运行使用来自bs-config.json配置的lite服务器,它还使用监视任务同步源目录的任何更改
{
  "name": "angular2-typescript-gulp",
  "version": "1.0.0",
  "description": "Angular2 with TypeScript and Gulp QuickStart",
  "scripts": {
    "clean": "gulp clean",
    "compile": "gulp compile",
    "build": "gulp build",
    "start": "concurrent --kill-others \"gulp watch\" \"lite-server\"",
    "postinstall": "typings install"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/kolorobot/angular2-typescript-gulp.git"
  },
  "author": "Rafał Borowiec",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/kolorobot/angular2-typescript-gulp/issues"
  },
  "dependencies": {
    "@angular/common": "2.0.0",
    "@angular/compiler": "2.0.0",
    "@angular/core": "2.0.0",
    "@angular/forms": "2.0.0",
    "@angular/http": "2.0.0",
    "@angular/platform-browser": "2.0.0",
    "@angular/platform-browser-dynamic": "2.0.0",
    "@angular/router": "3.0.0",
    "@angular/upgrade": "2.0.0",

    "core-js": "^2.4.1",
    "reflect-metadata": "^0.1.3",
    "rxjs": "5.0.0-beta.12",
    "systemjs": "0.19.27",
    "zone.js": "^0.6.23"
  },
  "devDependencies": {
    "concurrently": "^2.2.0",
    "del": "^2.2.0",
    "gulp": "^3.9.1",
    "gulp-sourcemaps": "^1.6.0",
    "gulp-tslint": "^6.1.1 ",
    "gulp-typescript": "^2.13.6",
    "lite-server": "^2.2.2",
    "tslint": "^3.5.0",
    "typescript": "^2.0.2",
    "typings": "^1.3.3",
    "ts-node": "^1.3.0"
  }
}

4、BrowserSync configuration - lite-server

默认情况下,内容从当前目录提供,因此需要更改;而且,由于Lite Server使用BrowserSync,足以提供配置服务器的bs-config.json来从构建目录提供内容。

{
  "port": 8000,
  "files": [
    "build/**/*.{html,htm,css,js}"
  ],
  "server": {
    "baseDir": "build"
  }
}

5、tslint(typescript语法检测配置) - tslint.json

TSLint检查TypeScript代码是否可读性,可维护性和功能错误,而Gulp可以与gulp-tslint插件一起使用。

tslint.json用于配置哪些规则可以运行。只需将文件添加到项目的根目录。您应该根据需要调整规则。您可以在这里找到有关规则的更多信息:http://palantir.github.io/tslint/usage/tslint-json/

{
  "rules": {
    "class-name": true,
    "curly": true,
    "eofline": false,
    "forin": true,
    "indent": [
      true,
      4
    ],
    "label-position": true,
    "label-undefined": true,
    "max-line-length": [
      true,
      140
    ],
    "no-arg": true,
    "no-bitwise": true,
    "no-console": [
      true,
      "debug",
      "info",
      "time",
      "timeEnd",
      "trace"
    ],
    "no-construct": true,
    "no-debugger": true,
    "no-duplicate-key": true,
    "no-duplicate-variable": true,
    "no-empty": false,
    "no-eval": true,
    "no-string-literal": false,
    "no-trailing-whitespace": true,
    "no-unused-variable": false,
    "no-unreachable": true,
    "no-use-before-declare": true,
    "one-line": [
      true,
      "check-open-brace",
      "check-catch",
      "check-else",
      "check-whitespace"
    ],
    "radix": true,
    "semicolon": true,
    "triple-equals": [
      true,
      "allow-null-check"
    ],
    "variable-name": false,
    "whitespace": [
      true,
      "check-branch",
      "check-decl",
      "check-operator",
      "check-separator"
    ]
  }
}

6、Gul构建配置- gulpfile.ts

要开始,我们需要一个编译TypeScript文件,将资产和依赖关系复制到构建目录的任务。为了实现这些任务需要几个任务。

注意:Gulp文件是以TypeScript而不是JavaScript创建的。它需要ts-node执行,如本教程开头所述。

"use strict";

const gulp = require("gulp");
const del = require("del");
const tsc = require("gulp-typescript");
const sourcemaps = require('gulp-sourcemaps');
const tsProject = tsc.createProject("tsconfig.json");
const tslint = require('gulp-tslint');

/**
 * Remove build directory.
 */
gulp.task('clean', (cb) => {
    return del(["build"], cb);
});

/**
 * Lint all custom TypeScript files.
 */
gulp.task('tslint', () => {
    return gulp.src("src/**/*.ts")
        .pipe(tslint({
            formatter: 'prose'
        }))
        .pipe(tslint.report());
});

/**
 * Compile TypeScript sources and create sourcemaps in build directory.
 */
gulp.task("compile", ["tslint"], () => {
    let tsResult = gulp.src("src/**/*.ts")
        .pipe(sourcemaps.init())
        .pipe(tsc(tsProject));
    return tsResult.js
        .pipe(sourcemaps.write(".", {sourceRoot: '/src'}))
        .pipe(gulp.dest("build"));
});

/**
 * Copy all resources that are not TypeScript files into build directory.
 */
gulp.task("resources", () => {
    return gulp.src(["src/**/*", "!**/*.ts"])
        .pipe(gulp.dest("build"));
});

/**
 * Copy all required libraries into build directory.
 */
gulp.task("libs", () => {
    return gulp.src([
            'core-js/client/shim.min.js',
            'systemjs/dist/system-polyfills.js',
            'systemjs/dist/system.src.js',
            'reflect-metadata/Reflect.js',
            'rxjs/**',
            'zone.js/dist/**',
            '@angular/**'
        ], {cwd: "node_modules/**"}) /* Glob required here. */
        .pipe(gulp.dest("build/lib"));
});

/**
 * Watch for changes in TypeScript, HTML and CSS files.
 */
gulp.task('watch', function () {
    gulp.watch(["src/**/*.ts"], ['compile']).on('change', function (e) {
        console.log('TypeScript file ' + e.path + ' has been changed. Compiling.');
    });
    gulp.watch(["src/**/*.html", "src/**/*.css"], ['resources']).on('change', function (e) {
        console.log('Resource file ' + e.path + ' has been changed. Updating.');
    });
});

/**
 * Build the project.
 */
gulp.task("build", ['compile', 'resources', 'libs'], () => {
    console.log("Building the project ...");
});

7、安装依赖关系并检查构建

现在是安装所有依赖关系的时候了。运行:

npm install

应该在安装期间创建node_modules和typing目录。

构建工程

npm run clean & npm run build

build目录应在构建期间创建

注意:如果在编译期间看到以下内容,请确保至少有ts-node 1.3.0:

[00:49:42] Failed to load external module ts-node/register
[00:49:42] Failed to load external module typescript-node/register
[00:49:42] Failed to load external module typescript-register
[00:49:42] Failed to load external module typescript-require

工程配置

1. Index - src/index.html

库是在构建任务期间创建的lib目录的引用

<html>
<head>
    <title>Angular 2 TypeScript Gulp QuickStart</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- 1. Load libraries -->
    <!-- Polyfill(s) for older browsers -->
    <script src="lib/core-js/client/shim.min.js"></script>

    <script src="lib/zone.js/dist/zone.js"></script>
    <script src="lib/reflect-metadata/Reflect.js"></script>
    <script src="lib/systemjs/dist/system.src.js"></script>

    <!-- 2. Configure SystemJS -->
    <script src="systemjs.config.js"></script>
    <script>
        System.import('app')
                .then(null, console.error.bind(console));
    </script>

</head>

<!-- 3. Display the application -->
<body>
<app>Loading...</app>
</body>

</html>

2. SystemJS 配置 - src/systemjs.config.js

function (global) {
    System.config({
        paths: {
            // paths serve as alias
            'npm:': 'lib/'
        },
        // map tells the System loader where to look for things
        map: {
            // our app is within the app folder
            app: 'app',
            // angular bundles
            '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
            '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
            '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
            '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
            '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
            '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
            '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
            '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
            // other libraries
            'rxjs': 'npm:rxjs'
        },
        // packages tells the System loader how to load when no filename and/or no extension
        packages: {
            app: {
                main: './main.js',
                defaultExtension: 'js'
            },
            rxjs: {
                defaultExtension: 'js'
            }
        }
    });
})(this);

3、应用组件

1. 主入口组件 - src/app/app.component.ts

import {Component, OnInit} from "@angular/core";

@Component({
    selector: "app",
    templateUrl: "./app/app.html"
})
export class AppComponent implements OnInit {
    ngOnInit() {
        console.log("Application component initialized ...");
    }
}

他的模板 (src/app/app.html):

<p>Angular 2 is running ... </p>

2. About 模块

关于模块由两个基本构件 - 组件及其模板组成。

src/app/about/components/about.component.ts

import {Component} from "@angular/core";
import {OnInit} from "@angular/core";

@Component({
    templateUrl: './app/about/components/about.html'
})
export class AboutComponent implements OnInit {

    ngOnInit() {

    }
}

src/app/about/components/about.html:

<h1>About</h1>
<p>这是about</p>

3. Todo 模块

Todo模块有点复杂 - 它包含组件,模型和服务。

src/app/todo/components/task.component.ts

import {Component} from "@angular/core";
import {Input} from "@angular/core";

import {Task} from "../models/task";
import {Output} from "@angular/core";
import {EventEmitter} from "@angular/core";

@Component({
    selector: 'task',
    templateUrl: './app/todo/components/task.html'
})
export class TaskComponent {
    @Input() task:Task;
    @Output() statusChanged:any = new EventEmitter<any>();

    toggleDone() {
        this.task.toggleDone();
        this.statusChanged.emit(null);
    }
}

src/app/todo/components/task.html

<form role="form">
    <input title="Name" type="text" [(ngModel)]="task.name" name="name" [disabled]="task.done" />
    <input title="Done" type="checkbox" (click)="toggleDone()" [checked]="task.done" />
</form>

src/app/todo/components/task-list.component.ts

import {Component} from "@angular/core";
import {Task} from "../models/task";
import {OnInit} from "@angular/core";
import {TaskService} from "../services/task-service";
import {TaskComponent} from "./task.component";

@Component({
    selector: 'task-list',
    templateUrl: './app/todo/components/task-list.html',
    styleUrls: ['./app/todo/components/task-list.css'],
    providers: [TaskService]
})
export class TaskListComponent implements OnInit {

    todoCount:number;
    selectedTask:Task;
    tasks:Array<Task>;

    constructor(private _taskService:TaskService) {
        this.tasks = _taskService.getTasks();
        this.calculateTodoCount();
    }

    ngOnInit() {
        console.log("Todo component initialized with " + this.tasks.length + " tasks.");
    }

    calculateTodoCount() {
        this.todoCount = this.tasks.filter(t => !t.done).length;
    }

    select(task:Task) {
        this.selectedTask = task;
    }
}

src/app/todo/components/task-list.css

li.selected {
    background-color: #8a8a8a;
}

src/app/todo/components/task-list.html

<h1>Todo tasks ({{todoCount}})</h1>
<ul>
    <li *ngFor="let task of tasks" [class.selected]="task == selectedTask">
        <a href="javascript:void(0)" (click)="select(task)">
            {{task.name}}
            <span [ngSwitch]="task.done">
                <template [ngSwitchCase]="true">[Done]</template>
                <template [ngSwitchCase]="false">[Todo]</template>
            </span>
        </a>
    </li>
</ul>

<task *ngIf="selectedTask" [task]="selectedTask" (statusChanged)="calculateTodoCount()"></task>

src/app/todo/models/task.ts

export class Task {

    constructor(public name:string, public done:boolean) {
    }

    toggleDone() {
        this.done = !this.done;
    }
}

src/app/todo/services/task-service.ts

import {Injectable} from "@angular/core";
import {Task} from "../models/task";

@Injectable()
export class TaskService {

    private tasks:Array<Task> = [
        new Task("Task 1", false),
        new Task("Task 2", false),
        new Task("Task 3", false),
        new Task("Task 4", false),
        new Task("Task 5", false)
    ];

    getTasks():Array<Task> {
        return this.tasks;
    }

    addTask(name:string) {
        this.tasks.push(new Task(name, false));
    }

}

4. Angular2 路由 - src/app/app.routing.ts

import {Routes, RouterModule} from '@angular/router';
import {TaskListComponent} from "./todo/components/task-list.component";
import {AboutComponent} from "./about/components/about.component";
import {ModuleWithProviders} from "@angular/core";

const appRoutes: Routes = [
    {path: 'tasks', component: TaskListComponent, data: {title: 'TaskList'}},
    {path: 'about', component: AboutComponent, data: {title: 'About'}}
];

export const appRoutingProviders: any[] = [];
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes, { useHash: true });

5. 模块定义 - src/app/app.module.ts

Angular2模块有助于将应用程序整合到一个功能性的集合中。

import {NgModule}      from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';

import {AppComponent} from "./app.component";
import {TaskListComponent} from "./todo/components/task-list.component";
import {AboutComponent} from "./about/components/about.components";
import {TaskComponent} from "./todo/components/task.component";

import {routing, appRoutingProviders} from './app.routing';
import {FormsModule} from "@angular/forms";

@NgModule({
    imports: [
        BrowserModule,
        FormsModule,
        routing
    ],
    declarations: [
        AppComponent,
        TaskComponent,
        TaskListComponent,
        AboutComponent
    ],
    providers: [
        appRoutingProviders
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
}

6. Application bootstrap - src/app/main.ts

//<reference path="../../typings/index.d.ts"/>

import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppModule} from './app.module';

const platform = platformBrowserDynamic();

platform.bootstrapModule(AppModule);

构建运行

npm run clean & npm run build 
npm start

您应该看到应用程序在浏览器中运行

关于IntelliJ的注意事项

IntelliJ处理建议的设置没有问题。如果IntelliJ中使用TypeScript编译器,则应使用该项目的tsconfig.json。在这种情况下,ts文件的更改将立即反映在构建目录中。

代码资源

https://github.com/kolorobot/angular2-typescript-gulp

下一步做什么?

需要更多的实际使用这个项目。因此,很快就会添加更多功能。如果您有任何建议,评论或添加Github问题。

angular2-seed

如果您需要更成熟的启动器,请查看angular2-seed项目

https://github.com/mgechev/angular2-seed
angular2-seed以更先进的方式使用gulp,并且已经支持生产和开发构建,单元和集成测试等等。

Node.js模板的引擎的选择

Node.js使用

[toc]

ejs - Node.js模板的引擎的选择

常用语法

  • 用<%...%>包含js代码
  • 用<%=...%>输出变量 变量若包含 '<' '>' '&'等字符 会被转义
  • 用<%-...%>输出变量 不转义
  • 用<%- include('user/show') %>引入其他模板 包含 ./user/show.ejs
  • 用<%# some comments %>来注释,不执行不输出
  • <%% 转义为 '<%'
  • <% ... -%> 删除新的空白行模式?
  • <%_ ... _%> 删除空白符模式

为什么选择ejs

参考了这个讨论

看过了pug(以前叫jade)的语法,确实简洁,

class提示功能等不方便,对于编写前端Html的人来说,真的不习惯。

格式要求比较严格

ejs语法比较符合 我们的需求,在Html里面嵌套,符合长期编写前端页面的人。

基本功能使用

安装

npm install ejs

在APP.js配置


var ejs = require('ejs');

//注册ejs模板为html页。简单的讲,就是原来以.ejs为后缀的模板页,现在的后缀名可以//是.html了

app.engine('.html', ejs.__express);

//设置视图模板的默认后缀名为.html,避免了每次res.Render("xx.html")的尴尬
app.set('view engine', 'html');

//设置模板文件文件夹,__dirname为全局变量,表示网站根目录
app.set('views', __dirname + '/views');

// 设置可访问静态资源目录
app.use(express.static(__dirname + '/'));


在路由中index.js中设置

var express = require('express');
var router = express.Router();

router.get('/', function(req, res, next) {

    var users = [
    	{name: 'tobi', email: '[email protected]'},
    	{name: 'loki', email: '[email protected]'},
    	{name: 'jane', email: '[email protected]'}
    ];
    
    //渲染模板
    res.render('index', {
    	list: users,
    	title: "EJS example",
    	header: "Some users"
    });

});

module.exports = router;

在模板中 views/index.html中设置

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
    <meta content="telephone=no" name="format-detection">
    <meta content="email=no" name="format-detection">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <meta name="apple-mobile-web-app-capable" content="yes"/>
    <link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
    <meta name="apple-touch-fullscreen" content="yes"/>
    <title> <%= title %></title>
</head>
<body>
<header class="navbar navbar-collapse">
    <div class="container"><h2><%= header %></h2></div>
</header>
<div class="container">

    <table class="table table-responsive table-hover">
        <tr>
            <th>名字</th>
            <th>邮箱</th>
        </tr>

        <% for(var i in list) { %>
        <tr>
            <td><%= list[i].name  %></td>
            <td><%= list[i].email  %></td>
        </tr>
        <% } %>
    </table>
</div>

</body>
</html>

效果图

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.