Showing preview only (958K chars total). Download the full file or copy to clipboard to get everything.
Repository: hstarorg/HstarDoc
Branch: master
Commit: 0e54b7123b0e
Files: 168
Total size: 618.9 KB
Directory structure:
gitextract_j4qofkey/
├── .Net Platform/
│ ├── 01_Dotnet Core尝鲜.md
│ ├── 02_Dotnet Core V2.md
│ ├── C#中处理耗时任务的几种方式.md
│ ├── [20140913]可替代反射的几种方式.md
│ ├── 实战Asp.Net Core:DI生命周期.md
│ ├── 实战Asp.Net Core:中间件.md
│ ├── 实战Asp.Net Core:构建一个Core Lib.md
│ └── 实战Asp.Net Core:部署应用.md
├── .gitignore
├── AngularJS相关/
│ ├── Angular1.x升级指南.md
│ ├── AngularJS官方FAQ.md
│ ├── AngularJS教程:1W字综合指南.md
│ ├── AngularJS:Looking under the hood.md
│ ├── Angular从0到1:function(上).md
│ ├── Angular从0到1:function(下).md
│ ├── Angular再回首(1)-Component组件.md
│ ├── Angular再回首(2)-那些容易忽略的Component细节.md
│ ├── Angular再回首(3)-我们来实现一个组件.md
│ ├── Angular开发Tips.md
│ ├── Angular:指令、Controller数据共享.md
│ ├── [20140917]Angular:如何编写一个指令.md
│ ├── 用AngularJS开发Web应用程序.md
│ └── 详解angular之$q.md
├── Angular系列/
│ ├── 01_Angular2初体验.md
│ ├── 02_Angular2组件生命周期.md
│ ├── 03_Angular2的那些Decorator.md
│ ├── 04_Angular2指令简析.md
│ ├── 05_Angular2组件简析.md
│ ├── 06_Angular2管道(Pipe)简析.md
│ ├── 07_Angular2使用路由.md
│ ├── 08_Angular2动态加载组件.md
│ ├── 09_Angular2使用ui-router-ng2.md
│ ├── Angular2知识点.xmind
│ ├── Angular2踩坑大全.md
│ ├── 利用Angular实现多团队模块化SPA开发框架.md
│ └── 跟我学Angular2(1-初体验).md
├── CSS3学习之路/
│ ├── CSS3入门之文本与字体.md
│ ├── CSS3入门之转换.md
│ └── CSS3入门之边框与背景.md
├── Canvas学习札记/
│ └── 01_初识Canvas,绘制简单图形.md
├── ES6入门/
│ ├── ES6入门系列一(基础).md
│ ├── ES6入门系列三(特性总览下).md
│ ├── ES6入门系列二(特性总览上).md
│ └── ES6入门系列四(测试题分析).md
├── GoLang学习笔记/
│ └── 01_开始GO.md
├── JS札记/
│ ├── ES6 Class如何管理私有数据.md
│ ├── JS实现继承的几种方式.md
│ ├── JavaScript之毒瘤.md
│ ├── JavaScript之糟粕.md
│ ├── JavaScript的深拷贝的实现.md
│ ├── [20141121]JavaScript之Array常用功能汇总.md
│ ├── 那些不常见的JavaScript题目(上).md
│ └── 那些不常见的JavaScript题目(下).md
├── LICENSE
├── MongoDB入门基础/
│ ├── 01_记一次MongoDB裸奔.md
│ └── 02_Mongo权限探索.md
├── Other/
│ ├── Go Go.md
│ ├── NPM使用详解(上).md
│ ├── NPM使用详解(下).md
│ ├── Thrift简单实践.md
│ ├── TypeScript札记:初体验.md
│ ├── Windows下把Nginx,PM2包装为服务.md
│ ├── 开发者讨厌你API的十个原因.md
│ ├── 浅析12306前端优化点.md
│ └── 程序集强签名.md
├── PHP学习之路/
│ ├── 01_PHP简易安装环境.md
│ └── 02-PHP基础语法(上).md
├── README.md
├── React Native Cookbook/
│ ├── 章节一-new.md
│ └── 章节一.md
├── React Native 开发笔记/
│ ├── RN Aspect-01-环境准备.md
│ ├── RN Aspect-02-Hello React Native.md
│ ├── RN Aspect-03-修改名称与icon.md
│ ├── RN Aspect-04-打包App.md
│ └── React Native开发之多屏适配.md
├── React面面观/
│ ├── JSX中的那些小细节.md
│ ├── sources/
│ │ └── Redux交互流程.vsdx
│ ├── 【译】参考手册-React组件.md
│ ├── 【译】快速起步-JSX简介.md
│ ├── 【译】快速起步-事件处理.md
│ ├── 【译】快速起步-列表与KEY.md
│ ├── 【译】快速起步-条件渲染.md
│ ├── 【译】快速起步-渲染元素.md
│ ├── 【译】快速起步-状态和生命周期.md
│ ├── 【译】快速起步-状态提升.md
│ ├── 【译】快速起步-组件与属性.md
│ ├── 【译】快速起步-组合与继承.md
│ ├── 【译】快速起步-表单.md
│ ├── 【译】高级指南-不受控组件.md
│ ├── 【译】高级指南-深入JSX.md
│ └── 【译】高级指南-高阶组件.md
├── RxJS小记/
│ └── 02_RxJS之Observable.md
├── Sass学习之路/
│ ├── 01_Sass学习之路:Sass、Compass安装与命令行.md
│ └── 02_Sass学习之路:注释、变量以及导入.md
├── Vue实践之路/
│ ├── 01_认识Vue.md
│ └── 02_Vue组件(上).md
├── catalog_builder.js
├── jQuery拆解/
│ ├── 01-目录篇.md
│ ├── 02-模块化加载&防冲突处理.md
│ ├── 03-基础结构.md
│ └── jQuery中那些有趣的代码.md
├── 从0开始Stylus/
│ └── 01_Stylus简介&基本使用.md
├── 从零开始H5/
│ ├── HTML5探索一(那些新增的标签和属性).md
│ ├── 从零开始H5(一):升级你的HTML到HTML5.md
│ └── 从零开始H5(二):HTML5新技术点.md
├── 前端相关/
│ ├── CORS详解.md
│ ├── CSS布局(上).md
│ ├── CSS布局(下).md
│ ├── Google JavaScript Style Guide(上).md
│ ├── Iframe跨域通信的几种方式.md
│ ├── JSONP详解.md
│ ├── JWT详解.md
│ ├── Nginx常规用法解析.md
│ ├── TypeScript配置文件tsconfig简析.md
│ ├── VsCode简易配置手册.md
│ ├── Web API接口之FileReader.md
│ ├── Web API接口之Geolocation.md
│ ├── Webpack In Angular2.md
│ ├── Webpack初体验.md
│ ├── Webpack小抄.md
│ ├── Web前端基础测试题.md
│ ├── Yarn vs. Npm.md
│ ├── [20140311]前端构建之gulp与常用插件.md
│ ├── [20141025]从0开始Grunt.md
│ ├── [20150107]Web离线存储的几种方式.md
│ ├── 一个元素实现3个回图形.md
│ ├── 再说Promise.md
│ ├── 前端模块化:RequireJS.md
│ ├── 如何用Node编写命令行工具.md
│ ├── 探索Decorator.md
│ ├── 浏览器 Pointer Events.md
│ ├── 浏览器关闭事件分析.md
│ ├── 浏览器内容安全策略解析.md
│ ├── 浏览器历史history对象.md
│ ├── 简单学ES6.md
│ ├── 认识AMD、CMD、UMD、CommonJS.md
│ ├── 记一次Bug排查(Spider).md
│ ├── 说说如何部署node程序.md
│ ├── 这些年我们处理过的跨域.md
│ ├── 那些容易出错的Dom操作.md
│ └── 那些年我们认识的iframe.md
├── 微信小程序/
│ └── 微信小程序.xmind
├── 数据库之路/
│ ├── [20141114]这些年你需要注意的SQL.md
│ └── 说说你所熟知的MSSQL中的substring函数.md
├── 最佳实践系列/
│ └── Express异步进化史.md
├── 正则表达式/
│ └── 你真的理解正则修饰符吗.md
├── 测试相关/
│ ├── MOCHA测试代码汇总.md
│ ├── 使用chai-http实现API测试.md
│ ├── 利用Karma、Mocha搭建测试环境.md
│ └── 利用Nightwatch.js实现e2e测试.md
├── 编写高质量JS代码的68个有效方法-读书笔记/
│ ├── [20140926]编写高质量JS代码的68个有效方法(一).md
│ ├── [20141011]编写高质量JS代码的68个有效方法(二).md
│ ├── [20141030]编写高质量JS代码的68个有效方法(三).md
│ ├── [20141129]编写高质量JS代码的68个有效方法(四).md
│ ├── [20141205]编写高质量JS代码的68个有效方法(五).md
│ ├── [20141213]编写高质量JS代码的68个有效方法(六).md
│ ├── [20141220]编写高质量JS代码的68个有效方法(七).md
│ ├── [20141227]编写高质量JS代码的68个有效方法(八).md
│ ├── [20150110]编写高质量JS代码的68个有效方法(九).md
│ ├── [20150123]编写高质量JS代码的68个有效方法(十).md
│ ├── [20150214]编写高质量JS代码的68个有效方法(十一).md
│ ├── [20150304]编写高质量JS代码的68个有效方法(十二).md
│ └── [20150312]编写高质量JS代码的68个有效方法(十三).md
└── 运维&部署/
├── Docker容器管理平台Humpback进阶-私有仓库.md
├── Docker部署:Mysql Master-Slave.md
├── 一个简单易用的容器管理平台-Humpback.md
├── 前端监控系统实现.md
└── 记一次Zookeeper数据找回.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .Net Platform/01_Dotnet Core尝鲜.md
================================================
---
title: 01_Dotnet Core尝鲜
date: 2017/02/21 14:47:10
---
# 0、About Dotnet Core
`Dotnet Core` 是新一代的 `.Net Framework`,是一个具有跨平台能力的应用程序开发框架。它本身是由多个子项目组成的。包括 ``Core Fx``、``Core CLR``、``.Net Compiler Platform`` 等等。
`Dotnet Core` 具有高效的开发效率,高性能和跨平台能力,是 ``.Net平台`` 的一次大跃进。
# 1、尝试 Dotnet Core
### 1.1、Install
``Dotnet Core`` 从发布至今,已经有很长一段时间了。期间也发布了beta,rc等版本。就在前不久,正式版也已经发布了,经过了之前大量的api变化,现在core已经非常稳定了。这个阶段,已经值得我们去尝试、去使用它了。
要尝试 ``Dotnet core``, 我们先进入它的网站[https://dotnet.github.io/](https://dotnet.github.io/),[https://www.microsoft.com/net/core#windows](https://www.microsoft.com/net/core#windows) 。
根据我们的操作系统版本,选择合适的开发包。我这里是Windows下开发,理所当然的下载 [the .NET Core SDK for Windows](https://go.microsoft.com/fwlink/?LinkID=809122) 。
安装好之后,在命令行输入 ``dotnet --version`` ,如果输出了版本信息,那就表示安装成功了。
**注意:如果之前尝试过Dotnet Core,请保证在安装最新版本的SDK之前,先卸载干净。**
### 1.2、Console App
在安装好SDK之后,我们就可以开始创建项目了。新建一个文件夹,进入控制台,执行 ``dotnet new``,即可看到在目录下生成了如下文件结构:
```
Program.cs
project.json
```
内容如下:
```csharp
//Program.cs
using System;
namespace ConsoleApplication
{
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
```
```json
//project.json
{
"version": "1.0.0-*",
"buildOptions": {
"debugType": "portable",
"emitEntryPoint": true
},
"dependencies": {},
"frameworks": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0"
}
},
"imports": "dnxcore50"
}
}
}
```
此时我们可以通过 ``dotnet restore`` 来安装依赖。
**注意:就算project.json中的dependencies属性为空对象,我们也要执行 ``dotnet restore``,该命令会生成project.lock.json文件。**
**注意2:如果我们的网络环境的走的代理,那么可能会在安装依赖这个步骤遇到407错误,此时我们需要配置Nuget的代理设置,找到 ``%AppData%/NuGet/NuGet.Config`` 文件,然后添加如下配置项:**
```xml
<configuration>
<config>
<add key="http_proxy" value="s1firewall:8080" />
<add key="http_proxy.user" value="jh3r" />
<add key="http_proxy.password" value="xxx" />
</config>
</configuration>
```
**通过这样的设置,就可以使用代理来安装依赖了。**
当我们安装好依赖之后,通过执行 ``dotnet run`` 来编译和运行我们的程序,此时可以在控制台看到输出: ``Hello World!``
以上,就是我们使用 ``Dotnet Core`` 的一般步骤了。
### 1.3、Web App
在尝试了Console App之后,我们也来试试Web App 在 ``Dotnet Core`` 下是如何运行的。
首先,基本步骤如上,先创建一个项目模板。
接着,首先要使用Web功能,我们需要指定依赖,在project.json中的dependencies属性中增加依赖:
```
"dependencies": {
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0"
}
```
**注意:在设定依赖的时候,一定要注意版本号,如果依赖库的版本号不兼容Core的版本,那么很可能会出现一些莫名其妙的错误,而找不到原因。**
增加依赖之后,我们再次通过 ``dotnet restore`` 来安装依赖。
这个时候,我们来编写Web宿主程序,内容如下:
```csharp
//Program.cs
using System;
using Microsoft.AspNetCore.Hosting;
namespace WebApplication
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseStartup<Startup>() //此处使用了类型Startup,来自于Startup.cs
.UseUrls("http://localhost: 10000")
.Build();
host.Run();
}
}
}
```
```csharp
//Startup.cs
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace WebApplication
{
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Run((context) =>
{
return context.Response.WriteAsync("Hello, From Core.");
});
}
}
}
```
编写好如上代码之后,我们在执行 ``dotnet run``,访问 ``http://localhost:10000`` 就可以看到我们的Web程序已经成功运行起来了。
以上则是一个简单的Web程序所需要的代码。
# 2、Other
### 2.1、``Dotnet Core`` 与 ``Node.js`` 的性能测试。
在 ``Dotnet Core`` 的官方宣传中,号称比 ``Node.js`` 快8倍。实际上,通过计算从0到100000000的累加来看,两者的差距并不大(几十ms的差别,Node稍弱)。
由于时间环境关系,没有进行更复杂测试,但我想在高并发下,Node的机制可能会有更高的性能。在大量IO操作处理中,``Dotnet Core``会有绝对的优势【后续可验证】。
### 2.2、如何直接运行Dotnet Core程序?
在开发模式下,我们通过 ``dotnet run`` 来运行程序,那我们如何来运行发布好的程序呢?
首先,我们可以通过 ``dotnet publish`` 来生成好我们的应用程序(在Windows下生成的是dll,其他平台未测试)。
在发布模式,我们的程序所依赖的包,也会被一同发布到目录下,我们可以在 ``/appRoot/bin/Debug/netcoreapp1.0/publish`` 中找到我们发布好的文件。
此时,我们就可以将publish目录拷贝到其他电脑运行了。
由于发布好的文件入口点是 ``.dll`` 文件,我们要运行它的话,需要通过 ``dotnet xxx.dll`` 来进行启动。
**注意:在publish的时候,我们可以使用参数``dotnet publish -c Release`` 生成Release版本的发布包,目录对应变更为 ``bin/Release/``**
**注意2:请不要删除publish目录下的文件,否则可能导致无法运行。经测试,<appName>.deps.json 和 <appName>.pdb可以删除,但依赖和 <appName>.runtimeconfig.json是绝对不能删除的。**
### 2.3 后续
此文为 ``Dotnet Core`` 系列第一篇,后续计划将Web开发所需要用到的一些基本知识点,库等均在 ``Dotnet Core`` 调试通,且成文。
**加油,``Dotnet!``**
**【6.30号更新】**
### 2.4、如何创建web项目
在 1.3 中,我们知道如何把一个Console App 改造为一个Web项目,但这对应开发一个Web应用来说还不够。
其实,``dotnet new`` 可以默认创建 Web 项目开发模板。
通过 ``dotnet new -t --help`` 我们可以看到 ``dotnet new`` 能帮我们创建的项目类型有如下四种:
1. Console
2. Web
3. Lib
4. xunittest
我们可以直接通过 ``dotnet new -t Web`` 来创建一个 Web 项目模板,简单快捷。
### 2.5、如果在Linux下发布(CentOS7)
``Dotnet Core`` 开发的程序,具有跨平台能力,那如何在非Windows上发布呢?各大操作系统方式并不同。
在CentOS7(仅支持7+)上发布非常简单。
首先是在CentOS7上安装 ``Dotnet Core``,不知道如何安装?请查阅 [https://www.microsoft.com/net/core#centos](https://www.microsoft.com/net/core#centos) 。
安装好之后,只需要在Windows把开发好的程序,通过 ``dotnet publish`` 生成发布目录,然后将该目录拷贝到CentOS上即可。
最后,在CentOS上执行 ``dotnet xxx.dll`` 即可运行项目了。
**注意: xxx.dll是你开发的项目的主程序**
================================================
FILE: .Net Platform/02_Dotnet Core V2.md
================================================
---
title: 02_Dotnet Core V2
date: 2017-05-20 09:14:21
---
# 前言
时隔三个月,再来看 `.Net Core`,已经到 `2.x preview` 了,虽然这个版本有赶工的嫌疑,但并不妨碍它成为当前的主力版本(可用API翻倍,API相对更稳定)。
在 `.Net Core` 的开发中,可以采用 `VSC` 或者是 `VS`,在我个人体验来看,如果要开发稍大的(一个解决方案,多个子项目)项目,还是选择VS吧。对于`.Net Core` ,一个简单的编辑器,体验还是太弱了。
**注意:要在VS中使用Core2.x Preview,请安装 [VS 15.3 Preview](https://www.visualstudio.com/vs/preview/) 版本。**
# V2命令行
在安装好 [.Net Core 2.0 Preview 1](https://www.microsoft.com/net/core/preview#windowscmd) 之后,通过 `dotnet new -h` 可以发现可以创建更多的模板了,其中一部分是项目模板:
1. Console Application (控制台应用程序)
2. Class Library (类库)
3. Unit Test Project (使用微软测试库的单元测试)
4. xUnit Test Project (使用xUnit作为测试库的单元测试)
5. ASP.NET Core Empty (Core的空项目)
6. ASP.NET Core Web App(MVC) (网站项目-Application)
7. ASP.NET Core Web App(Razor Pages) (网站项目 - WebPage,和Application不一样的是一个cshtml对应一个.cs,两者之前的区别类似.Net的WebApplication和WebPage)
8. ASP.NET Core Web API (Web API项目)
还有另外一部分是一些其他的单个文件,如:
1. Nuget Config
2. Web Config
3. Solution Config
4. Razor Page
5. MVC ViewImports
6. MVC ViewStart
我们最常用的还是上面的一些项目模板(如果用VS,这都不是事~)
除了这些,还提供了不少的命令行,不用去死记硬背,用的时候通过帮助命令查看使用说明即可,如 `dotnet -h` 列出所有的命令, `dotnet build -h` 列出命令的具体用法。
# Console App
在升级到2.0之后,曾今的 `project.json` 就不再使用了,回归到了 `.csproj` 这样的项目文件,但这个和 `.Net` 中的 `.csproj` 并不一样,最大的一个区别是,`.Net Core` 中,并不会将文件路径映射到 `.csproj` 中。这对于开发Web非常有好处(现代Web有构建等一大套工具链)。
通过 `dotnet new console -o ConsoleDemo` (创建一个控制台项目,并输出到ConsoleDemo目录中)创建一个项目,我们可以看到一个基本的 `ConsoleDemo.csproj` 内容如下:
```
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
</Project>
```
仅仅是标记一下我们的Framework版本。
**Console不做过多尝试,相信和.Net差不多。更多的,我们还是使用 `.Net Core` 来做Web应用。``
# Web Application
我们还是先使用常用的MVC方式创建一个WebApplication,`dotnet new mvc -o MvcDemo`,创建好之后,可以看到如下结构:
```
Controllers/ --标准MVC三大目录
Models/ --标准MVC三大目录
Views/ --标准MVC三大目录
wwwroot/ --此处用来放置静态资源
.bowerrc --Bower配置文件
appsettings.Development.json 特定环境配置文件(Development环境)
appsettings.json 标准配置文件
bower.json -- Bower项目文件
bundleconfig.json -- 资源打包配置
MvcDemo.csproj -- 项目文件
Program.cs -- 自托管入口文件
Startup.cs -- 启动配置
```
其中,`bower` 相关和 `bundleconfig`,并没有什么大用,直接干掉。至于 `bower` 是什么,查看这里:[Bower官网](https://bower.io/)。
抛开MVC三大目录不说,我是建议创建一个src目录,用于放置相关的前端资源文件(JS,CSS,LESS,TS等),然后通过构建打包工具(gulp,webpack这类),将代码生成到 `wwwroot` 中。
另,需要注意项目配置文件 `appsettings.json` 是采用的类似继承的方式读取值,也就是说:最终的配置 = (appSettings.json).extend(appsettings.<Env>.json)。如果将环境设置为 `Development`,那么会根据节点,先从 `appsettings.Development.json` 中找,找不到则从 `appsettings.json` 中找。
注意看 `Program.cs` 和 `Startup.cs` 的内容。其中 `Program.cs` 是非常独立的一个文件,仅依赖 `Startup.cs`,而 `Startup.cs` 则全是Web相关的配置文件。
看一下 `Program.cs` 的关键代码:
```csharp
namespace MvcDemo
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
```
为什么是这样的结构呢?`.Net Core` 支持IIS托管和自托管。在 `.Net` 中,我们知道Web程序是不会有 `Program.cs`,也就没有 `Main` 方法的。
实际上,这个文件在 `.Net Core` 中的作用就是自托管Web程序,从它的内容中,可以理解到自托管就是利用控制台程序创建了一个WebHost,然后将我们Web程序的真正入口文件 `Startup` 传递到Web容器中运行。
在IIS中托管程序的话,这个 `Program.cs` 通过调试,发现也会执行,但其中的一些配置则不会生效,如:`.UseUrls("http://*:5000")`。
**注意:具体在IIS中是如何执行的,还有待分析。**
## Web Application QA
1、如何设置环境?
在上面的内容中,有提到实际配置和环境是有关系的,那我们如何设定环境呢?最简单的方式,就是在 `Program.cs` 中,通过如下方式设置:
```csharp
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseEnvironment("Development") // 设置环境
.UseStartup<Startup>()
.Build();
```
2、如果在自托管的时候设置多个IP/端口绑定?
同上,也是在 `Program.cs` 中进行配置:
```csharp
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseUrls("http://*:5000;http://*:6000") // 多个用分号隔开
.UseStartup<Startup>()
.Build();
```
3、如何在视图中根据不同的环境加载不同的内容?
MVC为我们提供了相关的Helper,所以我们可以在View(一般是Layout视图)中,通过 `<environment></environment>` 来实现不同的内容加载,如下:
```html
<environment include="Development">
<!-- 当环境是Development时加载 -->
<link rel="stylesheet" href="~/static/mdl/material.css">
</environment>
<environment exclude="Development">
<!-- 当环境不是Development时加载 -->
<link rel="stylesheet" href="~/static/mdl/material.css">
</environment>
```
4、既然有配置文件,那我们就需要读取配置,应该如何读取呢?
在 `.Net Core` 中,配置是以JSON字符串的方式存在的,那么我们怎么来读取呢?
在 `Startup.cs` 中,构造函数中,注入了一个 `IConfiguration` 对象,并赋值到了这个类的 `Configuration` 属性上,这个属性就类似于 `.Net` 下的 `ConfigurationManager`。所以我们可以用它来进行读取,
假设有如下配置文件:
```json
{
...忽略默认配置,
"v1": "xxx",
"s2": {
"v2": 1
},
"s3": {
"Key1": "key1",
"Key2": "key2"
}
}
```
那么读取方式如下:
```csharp
Configuration.GetValue<string>('v1'); // 读取指定的配置项,返回 "xxx"
Configuration.GetSection('s2').GetValue<int>('v2'); // 从嵌套对象中读取配置项, 返回 1
```
当配置项过多的时候,这种方式可以比较麻烦,这个时候,我们可以创建一个配置类来映射,如下:
```csharp
public class S3
{
public string Key1{ get; set; }
public string Key2{ get; set; }
}
// 然后我们可以获取到s3这个节点,然后绑定到S3上。
var s3 = new S3();
Configuration .GetSection("s3").Bind(s3);
Console.WriteLine(s3.Key1); // 可以打印出 "key1" 了。
```
# 总结
`.Net Core` 正在变得越来越好,赶紧跟上吧~
================================================
FILE: .Net Platform/C#中处理耗时任务的几种方式.md
================================================
---
title: C#中处理耗时任务的几种方式
date: 2017/02/21 14:47:10
---
## 0、准备
首先,我们先创建几个耗时任务:
public class TestTasks
{
//无参、无返回值任务
public void Task1()
{
Console.WriteLine("task1.");
Thread.Sleep(5000);
Console.WriteLine("task1 completed.");
}
//有参数、无返回值任务
public void Task2(int x)
{
if (x < 2000)
{
x += 2000;
}
Console.WriteLine("task2.");
Thread.Sleep(x);
Console.WriteLine("task2 completed.");
}
//有参数,有返回值任务
public int Task3(int x)
{
if (x < 2000)
{
x += 2000;
}
Console.WriteLine("task3.");
Thread.Sleep(x);
Console.WriteLine("task3 completed.");
return x;
}
}
## 1、创建新线程执行方法
var tt = new TestTasks();
new Thread(tt.Task1).Start();
//针对有参数的任务,需要用Lambda进行包装或者使用ParameterizedThreadStart对象
new Thread(x=>tt.Task2((int)x)).Start((object)1000);
//使用ParameterizedThreadStart,要求要执行的方法参数必须为object,同时无返回值。
//new Thread(new ParameterizedThreadStart(tt.Task2)).Start((object)1000);
**注意:使用该方式无法执行带返回值的方法。**
**推荐指数:★★**
## 2、使用异步调用方式执行方法
var tt = new TestTasks();
Action ac = tt.Task1;
Action<int> ac2 = tt.Task2;
ac.BeginInvoke(null, null);
ac2.BeginInvoke(1000, null, null);
//以下是调用有参数,有返回值的方法
//代码一
private delegate int AsyncCaller(int x); //该代码放在方法体外部
AsyncCaller ac = new AsyncCaller(tt.Task3);
var asyncResult = ac.BeginInvoke(1000,null,null);
int result = ac.EndInvoke(asyncResult); //接收返回值
//代码二,使用Func简化代码
Func<int,int> ac = tt.Task3;
var asyncResult = ac.BeginInvoke(1000,null,null);
int result = ac.EndInvoke(asyncResult);
**注意:通过这种方式生成新线程是运行在后台的(background),优先级为normal**
**推荐指数:★★**
## 3、通过ThreadPool(线程池)执行方法
var tt = new TestTasks();
ThreadPool.QueueUserWorkItem(o => tt.Task1());
ThreadPool.QueueUserWorkItem(o => tt.Task2(1000));
**注意:该方式不支持返回值,可以将返回值保存在引入类型的参数上,然后进行迂回实现**
**推荐指数:★★★**
## 4、通过BackgroundWorker(后台Worker)执行方法
var tt = new TestTasks();
var bw = new BackgroundWorker();
bw.DoWork += (sender, e) => tt.Task1();
bw.DoWork += (sender, e) => tt.Task2(1000);
//要接收返回值,必须将返回值赋值给Result。
bw.DoWork += (sender, e) => e.Result = tt.Task3(1000);
bw.RunWorkerAsync();
//注册事件使用返回值
bw.RunWorkerCompleted += (sender, e) => Console.WriteLine(e.Result);
**注意:使用BackgroundWorker注册DoWork事件的任务只能挨个执行,如果要同时执行多个任务,需要多个BackgroundWorker。要使用返回值,一定要记得赋值给Result。**
**推荐指数:★★**
## 5、同时Task执行方法
var tt = new TestTasks();
var t1 = Task.Factory.StartNew(tt.Task1);
var t2 = Task.Factory.StartNew(() => tt.Task2(1000));
var t3 =Task.Factory.StartNew(() => tt.Task3(1000));
//等待t1,t2,t3执行完成
Task.WaitAll(t1,t2,t3);
Console.WriteLine(t3.Result);
**注意:Task具有灵活的控制能力,同时可以单个等待,多个等待。**
**推荐指数:★★★★★**
## 6、使用async/await执行方法
private async void AsyncRunTask()
{
var tt = new TestTasks();
await Task.Factory.StartNew(tt.Task1);
await Task.Factory.StartNew(() => tt.Task2(1000));
var result = await Task.Factory.StartNew(() => tt.Task3(1000));
Console.WriteLine(result);
}
AsyncRunTask();
Console.WriteLine("不用等待,我先执行了。");
**注意:需要Framework4.5的支持**
**推荐指数:★★★★**
## The End
**没有原理,没有言语,相信以大家聪明的大脑,已经学会如何在C#中执行耗时任务和使用多线程了。**
================================================
FILE: .Net Platform/[20140913]可替代反射的几种方式.md
================================================
---
title: C#可替代反射的几种方式
date: 2014/09/13
---
##标准的反射代码##
var type = obj.GetType();
var fieldInfo = type.GetField("age", BindingFlags.Instance | BindingFlags.NonPublic);
fieldInfo.SetValue(obj, 20);
// Console.WriteLine("设置年龄成功:{0}", (obj as ModelTest).Age);
var s1 = type.InvokeMember("TestMethod1", BindingFlags.InvokeMethod, null, obj, null);
var s2 = type.InvokeMember("TestMethod2", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, obj, null);
// Console.WriteLine(s1);
// Console.WriteLine(s2);
###说明###
1. 能动态获取对象属性、方法、字段等信息
2. 能访问对象的私有字段,属性,方法等成员
3. 能动态修改对象属性
4. **注:**访问public方法使用BindingFlags.InvokeMethod,访问私有方法时,必须加上BindingFlags.NonPublic|BindingFlags.Instance,否则会出现找不到方法的异常
##采用dynamic对象达到发射的效果##
dynamic d = obj;
var s1 = d.TestMethod1();
Console.WriteLine(s1);
###说明###
1. 可使用公开的属性,字段,方法等成员
2. 代码足够简洁
3. **注:**不能访问非公开的成员
##依赖Microsoft的测试组件Microsoft.VisualStudio.TestTools.UnitTesting来达到反射的效果##
var privateObj = new PrivateObject(obj);
privateObj.SetField("age", 20);
var age = privateObj.GetProperty("Age");
Console.WriteLine(age);
privateObj.Invoke("TestMethod1");
privateObj.Invoke("TestMethod2");
###说明###
1. 采用第三方组件实现
##性能说明##
测试代码如下:
private static void Main(string[] args)
{
RunSpecialTest(new SimpleReflection(), 1000);
RunSpecialTest(new DynamicReflection(), 1000);
RunSpecialTest(new PrivateObjectReflection(), 1000);
Console.ReadKey();
}
private static void RunSpecialTest(ITest test, int runCount)
{
var modelTest = new ModelTest();
var stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < runCount; i++)
{
test.TestRun(modelTest);
}
stopwatch.Stop();
Console.WriteLine("运行{0} {1}次,共耗时:{2}ms", test.Name, runCount, stopwatch.ElapsedMilliseconds);
}
结果:
运行SimpleReflection 1000次,共耗时:2ms
运行DynamicReflection 1000次,共耗时:717ms
运行PrivateObjectReflection 1000次,共耗时:14ms
##疑问##
1. 采用标准的反射用法,除了第一次耗时较慢意外,后面耗时都很短,可以说是最快的方式,猜测是缓存,具体未知。
2. PrivateObjectReflection这个和发射有类似的情况,初次慢,后面快。何解?
================================================
FILE: .Net Platform/实战Asp.Net Core:DI生命周期.md
================================================
---
title: 实战Asp.Net Core:DI生命周期
date: 2018-11-30 21:54:52
---
# 1、前言
`Asp.Net Core` 默认支持 `DI(依赖注入)` 软件设计模式,那使用 `DI` 的过程中,我们势必会接触到对象的生命周期,那么几种不同的对象生命周期到底是怎么样的呢?我们拿代码说话。
**关于 DI 与 IOC:**
个人理解:`IOC(控制反转)` 是目的(降低代码、服务间的耦合),而 `DI` 是达到该目的的一种手段(具体办法)。
# 2、DI生命周期
DI的生命周期,根据框架、库的不同,会略有差异。此处,我们就以微软的DI扩展为例,来说下DI中常用的几种生命周期。
首先,我们想象一个这样一个场景。假设我们有寄快递的需求,那么我们会致电快递公司:“我们要寄快递,派一个快递员过来收货”。接着,快递公司会如何做呢?
1. 一直派遣同一个快递员来收货。
2. 第一周派遣快递员A、第二周派遣快递员B收货。
3. 每次都派遣一个新的快递员收货。
这对应到生命周期就是:
1. 单例(Singleton),单一实例,每次使用都是该实例。
2. 作用域实例(Scoped),在一个作用域(比如单次请求)内是同一个实例,不同的作用域实例不同。
3. 瞬时实例(Transient),每次使用都创建新的实例。
快递公司也就是我们在DI中常说的容器(Container)了。
## 2.1、验证准备
首先,我们需要三个Services(Service1\Service2\Service3)内容一致,如下:
```c#
// Service1.cs,Service2、Service3除类名以外,内容一致
public class Service1
{
private int value = 0;
public int GetValue()
{
this.value++;
return this.value;
}
}
```
然后,我们需要一个业务类,再一次注入这三个Service,内容如下:
```c#
// DefaultBusiness.cs
public class DefaultBusiness
{
private readonly Service1 s1;
private readonly Service2 s2;
private readonly Service3 s3;
public DefaultBusiness(Service1 s1, Service2 s2, Service3 s3)
{
this.s1 = s1;
this.s2 = s2;
this.s3 = s3;
}
public int GetS1Value()
{
return this.s1.GetValue();
}
public int GetS2Value()
{
return this.s2.GetValue();
}
public int GetS3Value()
{
return this.s3.GetValue();
}
}
```
最后,还需要在Startup.cs进行注入
```c#
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// 单例模式
services.AddSingleton<Service1>();
// 作用域模式
services.AddScoped<Service2>();
// 瞬时模式
services.AddTransient<Service3>();
// 为了保证结果,将Business类注册为瞬时模式,每次注入都是全新的。
services.AddTransient<Business.DefaultBusiness>();
}
```
## 2.2、验证单例(Singleton)
对于单例来说,我们期望,在整个程序启动期间,都是同一个实例,所以,我们只需要在Service中,增加一个局部变量,做累加就可以验证。
```c#
// DefaultController.cs
[Route("singleton")]
public IActionResult SingletonTest()
{
this.defaultBiz.GetS1Value();
return Json(new
{
s1 = s1.GetValue()
});
}
```
然后我们访问 `http://localhost:5000/singleton` 多次,输入如下:
```c#
// 第一次
{ s1: 2 }
// 第二次
{ s1: 4 }
// 第三次
{ s1: 6 }
```
可以发现,每请求一次,value都会增加2。分析下怎么来的呢?
1. 首先是执行 `defaultBiz.GetValue()` 中,根据内部代码,此处会对注入的实例,value + 1。
2. 之后,`Json()`中的,`s1 = s1.GetValue()`,此处再一次增加了1。
3. 综上,一次请求,s1 中的value值会增加2,由于是单例模式,在整个服务运行期间,都是一个实例,所以,每次请求都会累加2。
## 2.3、验证作用域实例(Scoped)
```c#
// DefaultController.cs
[Route("scoped")]
public IActionResult ScopedTest()
{
this.defaultBiz.GetS2Value();
return Json(new
{
s2 = s2.GetValue()
});
}
```
然后我们访问 `http://localhost:5000/scoped` 多次,输入如下:
```c#
// 第一次
{ s2: 2 }
// 第二次
{ s2: 2 }
// 第三次
{ s2: 2 }
```
从结果可以看出,每次请求的返回值是固定的,都为2,也就是证明了Service2中,value++执行了两次。对于执行value++的代码,只有 `defaultBiz.GetS2Value()` 和 `s2 = s2.GetValue()`,所以这两处操作的是同一个实例。这也就证明了,对于 `Scoped` 生命周期,在作用域(可以简单理解为单次请求,实际上并不准确,**注意,此处为考虑多线程的情况**)内,都是使用的同一个实例。在不同的请求之间,则是不同的实例。
## 2.4、验证瞬时实例(Transient)
```c#
// DefaultController.cs
[Route("transient")]
public IActionResult TransientTest()
{
return Json(new
{
s3 = s3.GetValue()
});
}
```
然后我们访问 `http://localhost:5000/transient` 多次,输入如下:
```c#
// 第一次
{ s3: 1 }
// 第二次
{ s3: 1 }
// 第三次
{ s3: 1 }
```
从结果来看,每次请求的都是相同的返回值,`s3 = 1`,这说明了,两次操作的value++,是针对的不同实例。也就是每次使用 Service1,都是全新的实例。
# 3、扩展(Autofac DI 类库)
Asp.Net Core中默认的DI,相对还是比较简单的,只有三个生命周期。对于时下比较的依赖注入库,一般都会有更多的生命周期,有些还会有生命周期事件可以监控。
以 `Autofac` 为例,该类库提供了如下一些生命周期,可以做到更精细化的控制:
1. 单次依赖(Instance Per Dependency)- 也就是Transient,每次获取实例都是全新的。
2. 单例(Single Instance) - 也就是单例,整个服务周期都是一个实例。
3. 作用域隔离的实例(Instance Per Lifetime Scope) - 也就是一个作用域一个,示例代码如下:
```c#
// 先创建作用域
using(var scope1 = container.BeginLifetimeScope())
{
for(var i = 0; i < 100; i++)
{
// 在作用域内,Resolve 的都是同一个实例
var w1 = scope1.Resolve<Worker>();
}
}
// 创建另一个作用域
using(var scope2 = container.BeginLifetimeScope())
{
for(var i = 0; i < 100; i++)
{
// 在作用域内,Resolve 的都是同一个实例,但是这个实例和 scope1 作用域中的 w1 不是同一个。
var w2 = scope2.Resolve<Worker>();
}
}
```
4. 带标签的作用域隔离实例(Instance Per Matching Lifetime Scope)
5. 单次请求作用域实例(Instance Per Request) - 每个请求作为一个作用域。
6. 指定Owner的作用域实例(Instance Per Owned)- 对于同一个Owner,实例保持一致
7. 线程作用域实例(Thread Scope)
**更多 Autofac 生命周期相关内容,请参考:[https://autofac.readthedocs.io/en/latest/lifetime/instance-scope.html](https://autofac.readthedocs.io/en/latest/lifetime/instance-scope.html)**
# 4、总结
本文主要简单演示了 Asp.Net Core 中默认的几种服务生命周期效果,也抛砖引玉的说了下 Autofac 中的服务生命周期。合理的利用生命周期,可以减少对象的创建,提交程序性能。但是,用错了生命周期,则容易产生隐含的bug。在使用 DI 类库的时候,一定要理解清楚不同的生命周期的应用场景。
本文示例代码:[https://github.com/hstarorg/HstarDemoProject/tree/master/dotnet_demo/servicelifttime-demo/ServicelifttimeDemo](https://github.com/hstarorg/HstarDemoProject/tree/master/dotnet_demo/servicelifttime-demo/ServicelifttimeDemo)
================================================
FILE: .Net Platform/实战Asp.Net Core:中间件.md
================================================
================================================
FILE: .Net Platform/实战Asp.Net Core:构建一个Core Lib.md
================================================
================================================
FILE: .Net Platform/实战Asp.Net Core:部署应用.md
================================================
# 1、前言
某一刻,你已经把 .Net Core 的程序写好了。接下来,还可以做什么呢?那就是部署了。
作为一名开发工程师,如果不会部署自己开发的应用,那么这也是不完整的。接下来,我们就来说说,如何部署我们的 .Net Core 应用程序(主要是 Asp.Net Core 应用)。
# 2、Asp.Net Core 的部署方式
对于虚拟机中执行的语言来说,大都会有 SDK(Software Development Kit) 以及 XRE(X Runtime Environment)。对于 C#来说,也不例外。在 C#中,这两者的区别在于:
- .Net Core SDK 用于开发者构建 App,包含有较多开发相关的工具包(实际上,SDK 也是包含 Runtime)
- .Net Core Runtime 仅作为运行时使用,不包含开发工具包,体积较小。
既然要部署 Asp.Net Core,自然离不开 Runtime(如果装 SDK 也能跑,不过不推荐在运行环境装 SDK)。以下的部署方式的前提都是已经安装 Runtime 环境。
**下载地址:[https://dotnet.microsoft.com/download](https://dotnet.microsoft.com/download)**
## 2.1、控制台直接运行
Asp.Net Core 程序在发布后,会产生一个入口 dll 文件,要运行该程序,只需要通过 dotnet 命令执行该 dll 文件。所以,第一种方式就是直接找到 dll 文件,并使用 dotnet 命令来运行。(你说 dotnet 命令哪来的?安装了 Runtime 就有了。)
```bash
# 进行控制台执行
dotnet xxx.dll
```
**优势:**
1. 足够简单,拷贝文件就开整。
2. 兼容多平台部署。
**缺陷:**
1. 想象一下,如果控制台关掉了,服务器重启了会怎样?此时需要手动重新去重新执行。(你说,可不可以设置为开机启动?如果创建一个批处理,放在启动项中,还是能做到的)
## 2.2、IIS 部署
用 .Net Framework 开发的应用,大家都比较熟悉用 IIS 来部署。那 .Net Core 呢?虽然两者的运行模式并不相同,但微软为了减少迁移难度,自然也提供了用 IIS 的部署方法。
与 Asp.Net 不同,ASP.NET Core 不再是由 IIS 工作进程(w3wp.exe)托管,而是使用自托管 Web 服务器(Kestrel)运行,IIS 则是作为反向代理的角色转发请求到 Kestrel 不同端口的 ASP.NET Core 程序中,随后就将接收到的请求推送至中间件管道中去,处理完你的请求和相关业务逻辑之后再将 HTTP 响应数据重新回写到 IIS 中,最终转达到不同的客户端(浏览器,APP,客户端等)。
如果要使用 IIS 部署 Asp.Net Core 程序,步骤如下:
1. 首先,需要安装 .Net Core 托管捆绑包,[点此下载捆绑包](https://www.microsoft.com/net/permalink/dotnetcore-current-windows-runtime-bundle-installer)
2. 进行 IIS 控制台,确保能在模块中找到 AspNetCoreModule 托管模块。(如果执行了步骤 1,未找到,可以尝试控制台执行 `iisreset`)
3. 按照常规部署 .Net Framework 程序的方式,创建应用,在选择应用程序池的时候,注意,一定要选择`无托管代码`,如图:
4. 至此,就算部署成功了。
**优势:**
1. 学习成本低,和 .Net Framework 应用的部署方式保持类似。
2. 能够自动开机自启。
3. 可以在可视化界面中,配置端口域名绑定。
**劣势:**
1. 该方式仅能在 Windows 服务器上使用。
2. 通过 IIS 桥接一层,有性能损失。
**了解更多,请参考:[IIS 部署.Net Core 应用](https://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/iis/?view=aspnetcore-2.2#install-the-net-core-hosting-bundle)**
## 2.3、部署为 Windows Service
在 2.2 的部署方式中,较大的缺陷就是性能损失。那么,有没有什么办法能够可以避开这个问题呢?。答案就是 Windows Service,通过 Windows Service,我们能够解决 2.1 中的开机启动和持久运行问题,也能避开 2.2 中的性能损失。具体如何做呢?如下提供一种方式(当然,也可以用其他方式来部署 Windows Service):
1. 借助 nssm 来管理 Windows Service,[Nssm](https://nssm.cc/),用法,请参考:[https://nssm.cc/usage](https://nssm.cc/usage)
2. 配置 Service 开机启动。
**优势:**
1. 高性能部署,稳定性好。
2. 支持开机启动。
**劣势:**
1. 仅能用于 Windows 服务器。
2. 引入了一个外包依赖 NSSM。
## 2.4、Linux 部署
由于 .Net Core 天生支持跨平台,如果在廉价又稳定的 Linux 上部署 .Net Core 程序逐渐成为主流。对于 Linux 上的部署,和 Windows 上并没有什么区别。首先是安装 Runtime 环境,然后拷贝程序,并通过命令行运行。
再进一步,可以使用后台模式,让程序在后台运行。
更进一步,也可以效仿 Windows,把程序启动管理作为一个服务,来达到开机启动和灵活管理的目的。
## 2.5、Docker 部署
作为当前个人认为的最棒的 .Net Core 应用部署方式,建议大家都了解下。
首先,是 Docker 的基本使用:
1. 编写 Dockerfile
2. 使用 `docker build` 构建镜像
3. 使用 `docker run` 创建容器并运行
好,我们来依次说明,对于 Docker 来说,需要先安装 Docker 环境。
接着,我们假设发布包路径如下:
```bash
root-folder/
app/ # 发布包目录
xxx.dll # 程序入口点
Dockerfile # Dockerfile文件
```
然后针对该程序,编写如下 Dockerfile:
```bash
# 根镜像
FROM microsoft/dotnet:2.2-runtime
# 拷贝程序发布包
COPY app /app
# 设置工作目录
WORKDIR /app
# 导出的端口
EXPOST 80
# 程序运行命令
CMD ["dotnet", "xxx.dll"]
```
接下来,通过在 `root-folder` 中执行 `docker build -t xxx:0.0.1 .` 来构建一个镜像。
接着,再通过 `docker run -it -p 8000:80 --name xxx-demo xxx:0.0.1` 来创建并运行容器。
这样,就可以通过 `http://localhost:8000` 来访问到你的应用程序了。
此处只是大概写下 Docker 部署的步骤,抛砖引玉。真正需要将其用于产线,还需要去学习下足够的 Docker 知识。
**额外提一下,如何选择基础镜像**
> 对于 .Net Core 来说,一般有如下几类基础镜像:
* sdk -- 相信这个都比较容易理解,就是包含了 .Net Core SDK。
* runtime -- 这个也相对容易理解,包含了.Net Core Runtime。
* runtime-deps --这个就不是很好理解, runtime? deps? 什么意思呢?就是说,这个连 Runtime都不是全的,需要你在打包的时候,选择自寄宿模式,把Runtime也打进去。
**综上,我个人推荐大家选择 runtime 这类作为基础镜像。**
# 参考文档
1. [https://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/iis/?view=aspnetcore-2.2#install-the-net-core-hosting-bundle](https://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/iis/?view=aspnetcore-2.2#install-the-net-core-hosting-bundle)
2. [https://hub.docker.com/r/microsoft/dotnet](https://hub.docker.com/r/microsoft/dotnet)
3. [https://docs.microsoft.com/en-us/dotnet/core/docker/building-net-docker-images?view=aspnetcore-2.2](https://docs.microsoft.com/en-us/dotnet/core/docker/building-net-docker-images?view=aspnetcore-2.2)
================================================
FILE: .gitignore
================================================
.DS_Store
Thumbs.db
db.json
*.log
node_modules/
public/
.deploy*/
================================================
FILE: AngularJS相关/Angular1.x升级指南.md
================================================
---
title: Angular1.x升级指南
date: 2017/02/21 14:47:10
---
## 0、导言
Angular从1.2.x到1.3.x是一个大的跳跃。在1.3.x之后,也有1.4.x,1.5.x这么两个大版本。
Newkit从1.2.x跳跃到1.3.x经历过一次大的变化,在之后的较大版本升级中,基本上没有大多大改动。但是其中的差异点,我们也需要去了解。
1.3.x以前的版本,我们就不去深究了,毕竟现在不是主流,我们就从1.3.x开始,来看看Angular到底有了哪些变化。
## 1、从1.3.x到1.4.x
从1.3到1.4,Angular的变化涉及到很多个方面,一一列举如下。
### 1.1、动画
在1.4中,动画功能进行了很大的重构,但是API基本保持一致。在新的版本中,我们通过注入 ``$animationCss`` 来实现用JS创建CSS动画。
**1.1.1、定义CSS动画如下:**
```javascript
angular.module('app', ['ngAnimate'])
.animation('.slide-animation', ['$animateCss', function ($animateCss) {
return {
enter: function (element, doneFn) {
var animation = $animateCss(element, {
from: { background: 'black' },
to: { background: 'blue' },
duration: 10 // one second
});
animation.start().done(doneFn);
},
leave: function(element, doneFn){
var animation = $animateCss(element, {
from: { fontSize: '12px' },
to: { fontSize: '25px' },
duration: 10 // one second
});
animation.start().done(doneFn);
}
}
}]);
```
如何使用?
```html
<div class="test slide-animation" ng-if="vm.ck1"> AAAAAAAAAAA</div>
```
只需要在元素上设置一个class,然后当元素从隐藏到显示,则会执行 ``enter`` 动画,从显示到隐藏,则会执行 ``leave`` 动画。
**1.1.2、监听动画事件**
```javascript
// < 1.4
element.on('$animate:before', function(e, data) {
if (data.event === 'enter') { ... }
});
element.off('$animate:before', fn);
// 1.4+
$animate.on('enter', element, function(data) {
//...
});
$animate.off('enter', element, fn);
```
在1.4版本之前,我们需要通过在element上去监听动画开始和结束,另外还必须要通过 ``data.event`` 来判断动画类型。
在1.4及以后,我们可以直接通过 ``$animate`` 来监控了。
**1.1.3、触发动画**
```javascript
var $el = $('.slide-animation');
$animate.enter($el, $el.parent()).then(function(){
console.log('enter');
});
```
我们需要使用如上的方式,来手动启动动画,此时会触发元素的对应动画事件。
**注意:触发动画的回调中,如果要操作$scope,在<1.4中会失败,需要借助$apply,但在1.4+,就不需要了。示例如下:**
```javascript
$animate.enter(element, elementParent).then(function() {
$scope.$apply(function() {
$scope.explode = true;
});
});
// 1.4+
$animate.enter(element, elementParent).then(function() {
$scope.explode = true;
});
```
**1.1.4、启用/禁用动画**
```javascript
// < 1.4
$animate.enabled(false, element);
// 1.4+
$animate.enabled(element, false);
```
实现该操作的方法参数1.4+刚好和小于1.4相反。
### 1.2、表单
**1.2.1、ngMessages**
```javascript
<!-- AngularJS 1.3.x -->
<div ng-messages="model.$error" ng-messages-include="remote.html">
<div ng-message="required">Your message is required</div>
</div>
<!-- AngularJS 1.4.x -->
<div ng-messages="model.$error">
<div ng-message="required">Your message is required</div>
<div ng-messages-include="remote.html"></div>
</div>
```
在1.3版本中,``ng-messages-include`` 跟随在 ``ng-messages`` 元素上,这样并不灵活。
在1.4+中,``ng-messages-include`` 不允许更随在 ``ng-messages`` 元素上,必须放在内部,这样使得使用远程模板非常灵活。
另外,当存在多个form时,我们在使用ng-messages获取指定form的方法也有变化,如下:
```javascript
// < 1.4
<div ng-messages="ctrl.form['field_{{$index}}'].$error">...</div>
// 1.4 +
<div ng-messages="ctrl.getMessages($index)">...</div>
ctrl.getMessages = function($index) {
return ctrl.form['field_' + $index].$error;
}
```
**1.2.2、ngOptions**
ngOptions仅仅只是内部实现变化,在使用上并没有多大差异。其中当遍历Object时,之前的版本是用的 ``for in``,导致输出的key是字符序的。新版本采用 ``Object.keys``,输出的key是定义时候的顺序。
另外当ngOptions表达式执行之后,将不会再触发ngOptions了。
**1.2.3、select**
在 **select** 元素中,这是一个非常大的变更。简单理解,如下:在1.3中,ngModel和<option>的value比较仅仅是 ``==``,所以 ``200 == '200' //true``。在1.4+中,比较方式成了 ``===``,所以 ``200 === '200' //false``。
这个时候我们可以通过如下方式处理:
```javascript
ngModelCtrl.$parsers.push(function(value) {
return parseInt(value, 10); // Convert option value to number
});
ngModelCtrl.$formatters.push(function(value) {
return value.toString(); // Convert scope value to string
});
```
实现指令如下:
```javascript
app.directive('convertNumber', function() {
return {
require: 'ngModel',
link: function(scope, el, attr, ctrl) {
ctrl.$parsers.push(function(value) {
return parseInt(value, 10);
});
ctrl.$formatters.push(function(value) {
return value.toString();
});
}
}
});
```
当然,如果我们保证ngModel的值为string类型,那就没啥问题了(在不使用ng-value的情况下)。
**1.2.4、Form**
表单的变化,主要是name属性,在 < 1.4 的版本中,我们可以设置name为 "my:form1",在1.4+版本中,不在允许这种特殊的用法。
### 1.3、模板相关
**1.3.1、ngRepeat**
< 1.4 版本中,ng-repeat的遍历顺序是字母序。
1.4+版本中,顺序是有浏览器的 ``for in`` 来返回的。
**1.3.2、ngInclude**
```javascript
// < 1.4
<div ng-include="findTemplate('https://example.com/templates/myTemplate.html')"></div>
$scope.findTemplate = function(templateName) {
return $sce.trustAsResourceUrl(templateName);
};
// 1.4+
var templateCache = {};
$scope.findTemplate = function(templateName) {
if (!templateCache[templateName]) {
templateCache[templateName] = $sce.trustAsResourceUrl(templateName);
}
return templateCache[templateName];
};
// Or
angular.module('myApp', []).config(function($sceDelegateProvider) {
$sceDelegateProvider.resourceUrlWhitelist(['self', 'https://example.com/templates/**'])
});
```
### 1.4、Cookie
在1.4+中,cookie增加了新的API:
```javascript
get
put
getObject
putObject
getAll
remove
```
另外,``$cookieStore`` 将不推荐使用。
### 1.5、HTTP
在1.4+中,$http的 ``transformRequest`` 将不允许修改请求header。但是我们可以使用如下方式进行动态的header设置:
```javascript
$http.get(url, {
headers: {
'X-MY_HEADER': function(config) {
return 'abcd'; //you've got access to a request config object to specify header value dynamically
}
}
})
```
### 1.6、Filter
``fliter``如果作用在非数组上,将会抛出一个异常,之前则是返回一个空数组。
``limitTo``作用在不合法的value上,现在将会原样返回,之前是返回空对象,空数组。
## 2、从1.4.x到1.5.x
从 1.4.x 到 1.5.x 是一个为升级ng2做准备的变更版本。
该版本主要是增加了一些功能,所以从1.4.x升级到1.5.x基本不需要太大的改动。
在 1.5.x 版本中,增加了一些接近ng2的新特性,如:
1. angular.component() //一种偏向于angular2风格的组件(特殊的指令)
2. $onInit //生命周期钩子
3. ngAnimateSwap // ngAnimate中一个新的指令
接着,我们来看下它的一些具体变更。
### 2.1、Core
**2.1.1、$parse**
``$parse`` 增加新特性,可以使用locals来覆盖原本的context。用法如下:
```javascript
var context = { user: { name: 'Jay' } };
var locals = { user: { name1: 'Local Jay' } };
var getter = this.$parse('user.name');
console.log(getter(context)); // 'Jay'
console.log(getter(context, locals)); // 'Local Jay'
```
**2.1.2、ngOptions**
如果元素上有 ``ngOptions`` 指令,那么 ``ngModel`` 指令也必须存在,否则就会抛出错误。
另外 ``ngOptions`` 接受假值('', 0, false, null)
**2.1.3、orderBy**
对 ``undefined`` 或者 ``null`` 进行 ``orderBy`` 将会抛出错误。
### 2.2、ngAria
取消了部分元素的可访问性设置。
### 2.3 ngMessages
将 ``ngMessages`` 指令的优先级设置为1,如果有优先级低于1的指令有transclude功能,那么需要设置为更高的优先级。
### 2.4、ngResource
``$resource`` 增加了 ``$cancelRequest()`` 方法。
### 2.5、ngRoute
增加了 ``$resolveAs`` 配置属性,允许对 ``$resolve`` 指定别名。
### 2.6、ngTouch
默认禁用了 ``ngClickOverrideEnabled``,在触摸屏上,可能还有300ms的延迟。
如果要启用,那么可以使用如下方式:
```javascript
angular.module('myApp').config(function($touchProvider) {
$touchProvider.ngClickOverrideEnabled(true);
});
```
另外,建议使用FastClick来实现该功能。
需要注意,某些现代浏览器在某些场景下已经删除了300ms延迟:
Chrome 和 Firefox for Android 发现设置了 ``<meta name="viewport" content="width=device-width">`` 将会删除300ms延迟。
IE当设置 ``touch-action`` 为 ``none`` 或者 ``manipulation`` 移除延迟。
**注意:这些变化并不影响ngSwipe指令**。
## 3、总结
Angular1.x基本已经进入维护期了,很少会有新特性加入了。现在的重心在angular2,所以我们也可以优先对angular2做一些技术储备。
================================================
FILE: AngularJS相关/AngularJS官方FAQ.md
================================================
---
title: AngularJS官方FAQ
date: 2015/3/13 11:24:39
---
## 相关:最佳实践,反模式
### 1、为什么会觉得jQuery插件缺失?
请记住:当你在使用jQuery插件时,请在AngularJS之前加载jQuery库
**分析:** 因为AngularJS自带jqLite(可以理解为jQuery的精简版),如果先引入AngularJS的话,那么AngularJS会采用jqLite,而不是完整的jQuery库。
### 2、如何从一个Controller中访问DOM元素?
不要从Controller中执行DOM的选择/遍历。HTML还没有被渲染。查一查”directive“
### 3、为什么Angular说 controller/directive等缺失?
调用 `angular.module('myApp',[])` 总是会创建一个新的模块(同时干掉已有的重名模块)。相反,使用一个参数的方式调用 angular.module('myApp') 来引用已经存在的模块。
### 4、如何渲染未转义的数据?
```javascript
$sce.trustAsHtml(data)
如何禁用$sce?
app.config(['$sceProvider', function($sceProvider) {
$sceProvider.enabled(false);
}]);
```
### 5、当array/object/$resource-result变化时,应该如何监视?
```javascript
$scope.$watch 有第三个参数设置来监视值变化(非引用变化)
$watch(watchExpression, listener, [objectEquality]);
[objectEquality]设置为true,则使用angular.equals对象相等,而不是使用引用相等比较。
```
### 6、怎样才能序列化表单数据提交?
**不要这么做!** 不要尝试手动收集输入框值。仅仅只需要在每一个表单元素上附加 `ng-model="data.myField`,在需要使用的地方,使用 `$scope.data` 即可
### 7、总是在 ng-models 上使用(.) =>最佳实践
```html
<any ng-model="book.price" />
<any ng-model="book.name" />
```
### 8、应该如何从 service 中访问 scope ?
`$rootScope` 相当于 `ng-app` 标记,它能够被引导或者是服务注入,可以用于在所有的 scopes 上添加新功能和值
**注意:避免这样做--这相当于定义全局变量**
### 9、`module().factory()` 和 `module().service()` 不同点是什么?
[查看讨论信息](https://groups.google.com/forum/?fromgroups#!topic/angular/56sdORWEoqg)
### 10、如何防止无样式的内容闪现(页面显示双大括号绑定表达式)?
在一些地方使用 `ng-bind` 来替换双括号表达式
### 11、为什么 `<a ng-click="go({{myVal}})">` 不工作?
仅有的 `ng-*` 属性中,需要 `{{xxx}}` 的只有 `ng-src` 和 `ng-href`,因为最终的结果必须是一个字符串,不是一个表达式。所以其他的不能工作。
### 12、嵌套 routes/views?
或许吧
### 13、可以在行内指定模板或者是分部视图吗?
可以。可以采用 `<script id="some/partial.html" type="text/ng-template"></script>` ,Angular会使用它来替换。
### 14、 如何在 ngResource 地址中使用端口?
如下:$resource('example.com\\:8080')
### 15、为什么插件触发的change事件似乎不工作?
Angular监视 [input](https://developer.mozilla.org/en-US/docs/Web/Events/input) 事件,不是'change' 事件。
### 16、不要使用jQuery来切换crap(待定:无效元素),在行内使用一些变量标记。
```html
<a ng-click="flags.open=!flags.open">...<div ng-class="{active:flags.open}">
```
### 17、如何从DOM检查上查看 scope ?
Google Chrome:安装 Batarang extension,检查一个DOM元素,然后在console中键入$scope
Firefox/Firebug:检查一个DOM元素,然后在console中键入 `angular.element($0).scope()`
或者 `$($0).scope()`
IE10+: 使用F12工具,检查一个元素。然后在console中键入 `angular.element($0).scope()`
或者 `$($0).scope()`
### 18、你有一些好的指令示例/库吗?
[AngularUI](http://angular-ui.github.com/) 是非常棒的AngularJS工具集合(甚至是更好的示例代码)
### 19、IE?
针对IE8.0或者更早,你需要[阅读这个](https://docs.angularjs.org/guide/ie)和[使用这个](http://angular-ui.github.io/#ieshiv)
### 20、必须对路由使用#?
参考 [$locationProvider](https://docs.angularjs.org/api/ng/provider/$locationProvider)
### 21、你应该在尝试用指令包装jQuery插件前,优先尝试使用[AngularUI Passthru Directive (uiJq) ](http://angular-ui.github.io/#directives-jq)
### 22、为什么我的 $scope.$watch() 递归触发?
如果你在 $scope.$watch(newVal,oldVal)中改变 newVal ,它会重复触发。在 $watch 运行后,$scope 会重新评估,被观察对象将被重新触发。
### 23、何时我需要使用 $scope.$apply() ?
仅仅需要在没有angular 事件/回调时 使用 $scope.$apply()。它通常不属于任何地方。
### 24、启用了 html5Mode ,如何获取<a href />的后退行为?
如果你想一个链接能够全页面刷新,那么只需要在a标记上添加 target="_self"
### 25、如何 .preventDefualt() 或 .stopPropagation() ?
所有的 ng-click 和相关的绑定都注入了 $event 事件对象,你可以用它来调用 .preventDefualt(),甚至是对象传递给你的方法。
### 26、AngularJS在我的Chrome扩展中不工作!
你需要使用 [ng-csp](http://docs.angularjs.org/api/ng.directive:ngCsp)
### 27、如何缓存 $http 和html 分部视图
```javascript
//使用装饰器,添加缓存功能
myAppModule.config(function($routeProvider, $provide) {
$provide.decorator('$http', function($delegate){
var get = $delegate.get;
$delegate.get = function(url, config){
url += (url.indexOf('?') !== -1) ? '?' : '&';
url += 'v=' + cacheBustVersion;
return get(url, config);
};
return $delegate;
});
});
```
## 测试
### 1、拒绝/解决一个 $q.defer() 不通过
你必须在处理它们的时候添加 `$scope.$apply()`
### 2、Jasmine spyOn() 不执行 spy'd 功能
不一定是AngularJS的问题,但是你需要追加 `.addCallThrough()`
================================================
FILE: AngularJS相关/AngularJS教程:1W字综合指南.md
================================================
---
title: AngularJS教程:1W字综合指南
date: 2015/3/13 11:24:39
---
# AngularJSj教程:1W字指南(译)
**原文地址:**[http://www.airpair.com/angularjs](http://www.airpair.com/angularjs)
## 1、AngularJS简介
Angular 是用于编写引人注目的Web应用程序的是客户端 MVW JavaScript框架。它由Google创建好维护,(offers a futuristic spin on the web and its upcoming features and standards.
Read more at http://www.airpair.com/angularjs#tY7q00WpGrTLB71Z.99)
MVW 即 Model-View-Whatever,它是能在开发应用程序时,为我们提供灵活性的一种设计模式。我们可以选择MVC(Model-View-Controller)或者是MVVM(Model-View-ViewModel)方式。
本教程可以作为一个最终的资源来开始学习AngularJS,它的概念和它背后的API,同时能帮助您学习如何实现现代的Web应用程序。
AngularJS自身作为增强HTML的一个框架。它从多种语言包括JavaScript和服务端语言中获得灵感,使得HTML也成为了动态语言。这意味着我们获得了一个完全的数据数据方式来开发应用程序,不再需要刷新实体,更新DOM和其他费时任务如浏览器bug和不一致。我们可以只关注数据,让数据关心HTML的方式来编写我们的应用程序。
## 2、JavaScript框架中的工程概念
AngularJS在处理提供数据绑定和其他工程概念上,和其他框架如Backbone.js和Ember.js采取了不同了做法。我们坚持使用熟悉的、令人喜欢的HTML,使Angular拦截它,并增强它。Angular将纯粹的JavaScript对象用于数据绑定,保证任何模型变化都会更新DOM。当模型值更新了一次,Angular会更新来自应用程序的状态来源对象。
### 2.1、MVC 和 MVVM
如果你已经习惯了构建静态网站,你可能更熟悉手动一块一块的构建HTML,通过数据一遍一遍的打印相同的HTML。这可能是grid中的列,一个导航结构,一个链接列表或者是图片等等。在这个实例中,你需要习惯一点小东西的变化都需要手动更新HTML的痛苦,你必须更新模板来保持其他用途的一致性。你还要为每个导航项目杜绝相同的HTML。
深呼吸一下,通过Angular我们能实现恰当的关注点分离以及动态HTML。这意味着数据在模Model中,HTML是作为一个微小的模板被渲染为View,我们能使用Controller来连接它们两个,并驱动Model和View值的变化。
================================================
FILE: AngularJS相关/AngularJS:Looking under the hood.md
================================================
---
title: AngularJS:Looking under the hood
date: 2015/3/13 11:24:39
---
原文地址:[https://www.binpress.com/tutorial/angular-js-looking-under-the-hood/153](https://www.binpress.com/tutorial/angular-js-looking-under-the-hood/153)
**用AngularJS写得越多,你就越惊叹于它的神奇。我对Angular能做的一些奇妙的事情非常好奇,然后我决定分析它的源代码,看看我能否揭示它的一些秘密。我记录了我在23000多行Angular源码中发现的真正有用的,能够解释Angular先进(和隐藏)的方面的一些内容。**
## 1、Dependency Injection annotation process
依赖注入(DI)是除开用代码获取或创建依赖之外的一条不同的请求依赖的方式。简单的说,依赖是作为一个注入对象传递给我们的。Angular允许我们在我们的应用程序中通过像Controllers和Directives的方法来使用DI。我们能创建自己的依赖,同时允许Angular在请求它们的时候被注入。
在Angular中,一个最常用的被请求的依赖是 *$scope*。例如:
function MainCtrl ($scope){
//access to $scope
}
angular.module('app').controller('MainCtrl', MainCtrl);
对于没有使用过Angular提供的依赖注入的JavaScript开发者来说,这看起来像一个局部变量名。实际上,它仅仅是我们所请求的依赖名称的一个占位符。Angular查找这些占位符,然后通过DI将它们转换为真正的依赖对象,让我们来仔细看看。
### 方法参数 ###
直到我们压缩我们的应用前,方法参数都运行正常。当你压缩你的代码,你的方法定义将会用字符表示参数而不是单词-这意味着Angular不能找到你想要的!Angular使用了一个方式来解决,调用function的 *toString()* 方法。这将返回函数的字符串形式!接下来我们就能访问正在被请求的参数。Angular
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
Angular做的第一件事就是将函数转换为字符串,这是JavaScript中非常有用的一个特性。这将给我们一个字符串类型的函数,如:
'function MainCtrl ($scope) {...}'
接下来,Angular使用如下方法,移除所有的注释:
fnText = fn.toString().replace(STRIP_COMMENTS, '');
紧接着,Angular从处理好的function中分割参数来创建真正有用的部分,
argDecl = fnText.match(FN_ARGS);
Angular接下来使用 *.split()* 来移除空白字符,同时返回我们请求的参数数组。为了更完美,Angular使用了一个内部的forEach方法来迭代这个数组,并匹配参数名称然后将它们添加到 *$inject* 数组中。
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
arg.replace(FN_ARG, function(all, underscore, name) {
$inject.push(name);
});
});
这是你能想象的一个昂贵的处理流程。对每个函数的4个正则查找和一很多转换会造成性能损耗。当我们得到了Angular抽象的 *$inject* 数组,我们可以直接切入且田填充 *$inject* 数组来保存Angular困难和开销时间长的操作。
### $inject 对象 ###
我们可以通过在函数上添加 *$inject*属性来指定依赖自身,其中,如果存在的话,Angular使用DI注解。这是很容易的最可读的语法。例子如下:
function SomeCtrl ($scope) {
}
SomeCtrl.$inject = ['$scope'];
angular.module('app', [])
.controller('SomeCtrl', ['$scope', SomeCtrl]);
这样节省了Angular的许多工作-替代了检查方法参数,或者是操纵数组(详情请查看下一章节:Array Arguments),它仅仅返回和运行指定的 *$inject* 数组。简单,高性能。
理想情况下,由于依赖注入在我们自己的时间和Angular的转换时间上开销很大,我们可以使用任务运行工具如Grunt.js或者是Gulp.js 来自动化注入任务或者是数组语法。
**Note:这个并没有实例化被依赖的所有服务,Angular所做的只是标注相关的名字-框架的其他部分关心对象注入。**
### Array Arguments
最后一个例子使用了我们通常看见的数组索引对应函数参数序号的语法,例如:
['$scope', function($scope){}]
数组的顺序是非常重要的,因为函数的参数将会按照同样的顺序,以此来避免依赖被错误的实例化和可能引发的错误。
function SomeCtrl ($scope, $rootScope) {
}
angular.module('app', [])
.controller('SomeCtrl', ['$scope', ‘$rootScope’, SomeCtrl]);
我们需要做的是传递函数作为数组的最后一个项,Angular会删除这个函数,并遍历数组所注明的依赖名称,就好像我们创建的 *$inject* 属性。当Angular解析一个方法的时候,它会检查参数是不是一个数组,如果是,那么最后一项是函数,其他的则是依赖。
else if (isArray(fn)) {
last = fn.length - 1;
assertArgFn(fn[last], 'fn');
$inject = fn.slice(0, last);
}
## 2、Factory vs Service ##
Factory 和 Service 非常类似,但往往开发人员都难以理解它们。
当 *.service()* 已经实例化,那么 *new Service()* 将被引擎调用,返回一个新实例给我们。本质上,*。.service()* 是作为构造函数被使用的。
service 基本上是一个 factory,然而它是创建时被实例化,因为,你需要在 service 中使用this来注册变量和函数,来替代在factory中返回一个对象的方式。
factory 是非常接近面向对象中的“工厂模式”,当你注入了这个 factory ,你就获得了完整的方法,允许你创建你需要的新的实例-本质上是通过一个对象创建多个新对象。
你可以看下在Angular源码中的内部的工作:
function factory(name, factoryFn) {
return provider(name, { $get: factoryFn });
}
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
}
## 3、New $scope creation from $rootScope
Angular中所有的scope都是 *$rootScope* 的下级。 *$rootScope* 是通过 *new Scope()*创建的,进一步的子 scope 是通过 *$scope.$new()* 创建的。
var $rootScope = new Scope();
在 *$new* 方法里面,Angular设置了一个原型链来允许允许 scope 引用它们的父亲,它们的自己跟踪(作为生命周期),和以前的兄弟 scope 。
从下面的代码,如果你请求了一个隔离的 scope ,它会创建一个 *new Scope()* ,否则,它会创建一个从父级继承的子 scope 。
我省略了一些不必要的代码,但这里的是重点:
$new: function(isolate) {
var child;
if (isolate) {
child = new Scope();
child.$root = this.$root;
} else {
// Only create a child scope class if somebody asks for one,
// but cache it to allow the VM to optimize lookups.
if (!this.$$ChildScope) {
this.$$ChildScope = function ChildScope() {
this.$$watchers = null;
};
this.$$ChildScope.prototype = this;
}
child = new this.$$ChildScope();
}
child['this'] = child;
child.$parent = this;
return child;
}
当你使用*$scope.$new()*来测试Controller的时候,这也是非常好的能了解测试目的。这有助于明确对我来说Angular是如何创建新的scope的,为什么用Angular mocks 模块来嘲笑测试驱动开发(TDD)。
## 4、Digest Cycle
Digest Cycle 经常作为 *$digest* 被我们看到,这是Angular双向绑定的能力。当一个模型值更新的时候,它会运行,检查它最后已知的值,如果值有变化,呼叫适当的监听器。这是基本的脏检查 - 它针对所有有可能的值来检查,如果是脏值,那么呼叫相关的监听器,直到他没有脏值。我们快速看一下它是如何工作的:
$scope.name = 'Todd';
$scope.$watch(function() {
return $scope.name;
}, function (newValue, oldValue) {
console.log('$scope.name was updated!');
} );
当你调用 *$scope.$watch*,你注册了两件事。参数一是一个函数,返回你想要监视的值(当你提供一个字符串的时候,Angualr会将他转换为函数)。当 $digest 运行时,监视的参数将被调用,返回任何你想要的值。参数二是当你的参数一变化时,想要执行的函数。看一下Angular是怎样注册watch的。
$watch: function(watchExp, listener, objectEquality) {
var get = $parse(watchExp);
if (get.$$watchDelegate) {
return get.$$watchDelegate(this, listener, objectEquality, get);
}
var scope = this,
array = scope.$$watchers,
watcher = {
fn: listener,
last: initWatchVal,
get: get,
exp: watchExp,
eq: !!objectEquality
};
lastDirtyWatch = null;
if (!isFunction(listener)) {
watcher.fn = noop;
}
if (!array) {
array = scope.$$watchers = [];
}
// we use unshift since we use a while loop in $digest for speed.
// the while loop reads in reverse order.
array.unshift(watcher);
return function deregisterWatch() {
arrayRemove(array, watcher);
lastDirtyWatch = null;
};
}
这个函数推送你提供的参数到 scope 的 *$$watchers* 数组中,同时,返回一个方法允许你停止watch。
然后,每当*$scope.$apply*或者*$scope.$digest* 运行时,digest cycle将被运行。
### 未完...待续...
================================================
FILE: AngularJS相关/Angular从0到1:function(上).md
================================================
---
title: Angular从0到1:function(上)
date: 2017/02/21 14:47:10
---
## 1、前言
Angular作为最流行的前端MV*框架,在WEB开发中占据了重要的地位。接下来,我们就一步一步从官方api结合实践过程,来学习一下这个强大的框架吧。
Note:每个function描述标题之后的★标明了该function的重要程度(1~5星)。
## 2、function(上)
Angular封装了一系列公共方法,帮助我们更简单的使用JS。这些就是Angular的function。
### 2.1、angular.bind(★)
angular.bind类似于Function.prototype.bind,实现函数柯里化,返回一个函数代理。eg:
函数原型
angular.bind(/*this对象*/self, /*要封装的function*/fn, /*参数列表*/args);
//原始函数
function fun(arg1, arg2, arg3) {
console.log(this);
console.log(arg1);
console.log(arg2);
console.log(arg3);
}
fun('arg1', 'arg2', 'arg3');
//如果几个常用参数都是固定的,而且该函数被调用频繁,那么就可以包装一下
var fun2 = angular.bind(window, fun, 'arg1', 'arg2');
//新的调用方式
fun2('arg3');
### 2.2、angular.bootstrap(★★★★)
用于使用angular执行渲染元素。也是angular的启动方法,如果没有在页面上指定ng-app,必须要手动调用该函数进行启动。
angular.bootstrap(/*Dom元素*/element, /*Array/String/Function*/[modules], /*Object*/[config]);
//常规用法
angular.bootstrap(document, ['app'])
//带配置项
angular.bootstrap(document, ['app'], {strictDi: true/*Defaults: false*/})
### 2.3、angular.copy(★★★★★)
Angular.copy用于复制对象,由于angular的双向绑定特点,那么如果直接操作$scope对象,那么很容易就会改变ui的显示,这个时候就需要借助angular.copy来创建一个对象副本,并进行操作。
//原型
angular.copy(source, [destination]);
var obj = {a: 1};
var obj2 = angular.copy(obj);
var obj3;
angular.copy(obj, obj3);
console.log(obj2 === obj) //false
console.log(obj3 === obj) //false
var obj4;
//第二个和参数和返回值是相等的,而且第二个参数不管以前是什么,都会被重新赋值
var obj5 = angular.copy(obj, obj4);
console.log(obj4 === obj5); //true
### 2.4、angular.element(★★★★)
等价与jQuery的选择器,如果在angular之前没有引入jQuery,那么就使用jqLite包装.
angular.element('body');
//等价于
$('body');
//用法
var $body = angular.element('body');
### 2.5、angular.equals(★★)
用于比较两个对象是否相等,如下示例的规则和JS有区别,注意识别。
var obj1 = {a: 1};
var obj2 = obj1;
//引用一致,则相等
console.log(angular.equals(obj1, obj2)); // true
obj2 = {a: 1};
//引用不一致,对象表现一致,则相等
console.log(angular.equals(obj1, obj2)); // true
obj2 = {a: 1,$a: 2};
//对象比较时,忽略以$开头的属性
console.log(angular.equals(obj1, obj2)); // true
obj1 = /aa/;
obj2 = /aa/;
//正则表达式表现相等,则相等
console.log(angular.equals(obj1, obj2)); // true
//NaN与NaN比较,则相等
console.log(angular.equals(NaN, NaN)); // true
### 2.6、angular.extend(★★)
功能上和jQuery.extend没多大差异
//原型-第一个参数为目标,之后的参数为源,同时返回dst
angular.extend(dst, src);
//示例
var obj1 = {a: 1};
var obj2 = angular.extend(obj1, {a: 2}, {b: 3});
console.log(obj1)
console.log(obj1 === obj2); //true
### 2.7、angular.forEach(★★★★★)
angular.forEach用于遍历对象或者数组,类似于ES5的Array.prototype.forEach
//原型
angular.forEach(obj, iterator, [context]);
var values = {name: 'misko', gender: 'male'};
var arr = ['misko', 'male'];
angular.forEach(values, function (value, key) {
console.log(key + ' = ' + value);
});
angular.forEach(arr, function (value, i) {
console.log(i + ' = ' + value);
});
//还可以传递this
var obj = {};
angular.forEach(values, function (value, key) {
obj[key] = value;
}, obj);
console.log(obj);
### 2.8、angular.fromJson(★★★)
angular.fromJson将JSON字符串转换为JSON对象,注意,必须严格满足JSON格式,比如属性必须双引号,该函数内部实现是利用JSON.parse()。
//原型
angular.fromJson(/*string*/ jsonString)
var jsonString = '{"p1": "xx", "p2": 1, "p3": true}';
var jsonObj = angular.fromJson(jsonString);
console.log(jsonObj);
### 2.9、angular.toJson(★★★)
angular.toJson可以将对象,数组,日期,字符串,数字转换为json字符串
//原型
angular.toJson(obj, pretty);
var obj = {p1: 1, p2: true, p3: '2'};
var jsonString = angular.toJson(obj);
console.log(jsonString);
//美化输出格式(设置为true,默认采用2个字符缩进)
jsonString = angular.toJson(obj, true);
console.log(jsonString);
//还可以设置缩进字符数
jsonString = angular.toJson(obj, 10);
console.log(jsonString);
### 2.10、angular.identity(★)
angular.identity返回该函数参数的第一个值。编写函数式代码时,非常有用(待使用)。
//官方示例
function transformer(transformationFn, value) {
return (transformationFn || angular.identity)(value);
};
//简单演示
var value = angular.identity('a', 1, true);
console.log(value); // 'a'
### 2.11、angular.injector(★★)
angular.injector能够创建一个injector对象,可以用于检索services以及用于依赖注入。
//原型,[strictDi]默认false,如果true,表示执行严格依赖模式,
//angular则不会根据参数名称自动推断类型,必须使用['xx', function(xx){}]这种形式。
angular.injector(modules, [strictDi]);
//定义模块A
var moduleA = angular.module('moduleA', [])
.factory('F1', [function () {
return {
print: function () {
console.log('I\'m F1 factory');
}
}
}]);
var $injector = angular.injector(['moduleA'])
$injector.invoke(function (F1) {
console.log(F1.print());
});
//此种写法会报错,因为是严格模式
var $injector2 = angular.injector(['moduleA'], true)
$injector2.invoke(function (F1) {
console.log(F1.print());
});
//可以采用如下写法
$injector2.invoke(['F1', function (F1) {
console.log(F1.print());
}]);
### 2.12、angular.module(★★★★★)
angular.module可以说是最常用的function了。通过它,可以实现模块的定义,模块的获取。
//定义模块A
var moduleA = angular.module('moduleA', []);
//定义模块B,模块B依赖moduleA
var moduleB = angular.module('moduleB', ['moduleB']);
//可以在第三个参数上设置配置函数
var moduleB = angular.module('moduleB', ['moduleB'], ['$locationProvider', function ($locationProvider) {
console.log($locationProvider);
}]);
//等价于
var moduleB = angular.module('moduleB', ['moduleB'])
.config(['$locationProvider', function ($locationProvider) {
console.log($locationProvider);
}]);
//获取模块
angular.bootstrap(document, ['moduleB']);
================================================
FILE: AngularJS相关/Angular从0到1:function(下).md
================================================
---
title: Angular从0到1:function(下)
date: 2017/02/21 14:47:10
---
## 1、前言
## 2、function(下)
### 2.13、angular.isArray(★★)
``angular.isArray``用于判断对象是不是数组,等价于``Array.isArray``
console.log(angular.isArray([])); // true
console.log(angular.isArray({0: '1', 1: '2', length: 2})); // false
### 2.14、angular.isDate(★★)
通过判断toString.call(value)是不是等于'[object Date]' 来判断对象是个是一个Date对象.
console.log(angular.isDate(new Date())); // true
console.log(angular.isDate(223)); // false
### 2.15、angular.isDefined(★★)
判断对象或者属性是否定义
var obj = {a: 1, b: null, c: undefined};
console.log(angular.isDefined(obj.a)); // true
console.log(angular.isDefined(obj.b)); // true
console.log(angular.isDefined(obj.c)); // false
console.log(angular.isDefined(obj.d)); // false
### 2.16、angular.isElement(★★)
此方法判断元素是不是一个元素(包含dom元素,或者jquery元素)
console.log(angular.isElement(document.getElementsByTagName('body')[0])); // true
console.log(angular.isElement($('body'))); // true
### 2.17、angular.isFunction(★★)
此方法判断对象是不是一个function ,等价于 typeof fn === 'function'
console.log(angular.isFunction(new Function('a', 'return a'))); // true
console.log(angular.isFunction(function(){})); // true
console.log(angular.isFunction({})); // false
### 2.18、angular.isNumber(★★)
判断数字是否为number
function isNumber(value) {
return typeof value === 'number';
}
### 2.19、angular.isObject(★★)
function isObject(value) {
return value !== null && typeof value === 'object';
}
### 2.20、angular.isString(★★)
function isString(value) {
return typeof value === 'string';
}
### 2.21、angular.isUndefined(★★)
function isUndefined(value) {
return typeof value === 'undefined';
}
### 2.22、angular.lowercase(★★)
转换字符串为小写模式,如果参数不是字符串,那么原样返回
var lowercase = function(string) {
return isString(string) ? string.toLowerCase() : string;
};
console.log(angular.lowercase(1)); // 1
console.log(angular.lowercase('ABCdef')); // 'abcdef'
### 2.23、angular.uppercase(★★)
转换字符串为大写模式,如果参数不是字符串,那么原样返回
var uppercase = function(string) {
return isString(string) ? string.toUpperCase() : string;
};
console.log(angular.uppercase(1)); // 1
console.log(angular.uppercase('ABCdef')); // 'ABCDEF'
### 2.24、angular.merge(★★)
将多个对象进行深度复制,与extend()不同,merge将会递归进行深度拷贝。该拷贝是完全深拷贝,就连对象引用也不一样。
var o = {};
var obj1 = {a1: 1, a2: 2, a3: [1]};
var obj2 = {b1: [2], b2: /abc/};
var obj3 = [o];
var obj4 = {d: o};
var result = angular.merge({}, obj1, obj2, obj3);
console.log(result);
console.log(result[0] === o); // false
console.log(result.d === o); // false
### 2.25、angular.noop(★★)
一个空函数,调试时非常有用。可以避免callback未定义引发的error。
function foo(callback) {
var result = calculateResult();
(callback || angular.noop)(result);
}
### 2.26、angular.reloadWithDebugInfo(★★)
启用DebugInfo,该设置优先级高于``$compileProvider.debugInfoEnabled(false)``
================================================
FILE: AngularJS相关/Angular再回首(1)-Component组件.md
================================================
---
title: Angular再回首(1)-Component组件
date: 2017/02/21 14:47:10
---
## 0、再谈组件
``Component(组件)`` 在 ``Angular1`` 就已经有雏形了,那就是指令。在 ``Angular2`` 中,组件的概念被大大的强化,甚至是Angular2的核心概念。
在前端这么多年的演变中,组件也反哺到 ``Angular1``,成为 ``Angular1`` 的一种重要特性,在此之前,我们仅仅可以用 ``Directive`` 来实现类似组件的效果。
## 1、Angular组件与指令
在 ``Angular 1.5.x`` 中,新增加了 ``angular.component`` 方法,用于实现组件的构造。
在此之前,我们可能用 ``angular.directive`` 来实现类似的效果。
这个时候我们可能就会疑惑,它们有什么区别呢?
| Feature | Directive | Component |
|---|---|---|
| bindings | No | Yes (binds to controller) |
| bindToController | Yes | (default: false) No (use bindings instead) |
| compile function |Yes | No |
| controller | Yes | Yes (default function() {}) |
| controllerAs | Yes | (default: false) Yes (default: $ctrl)|
| link functions | Yes | No|
| multiElement | Yes| No|
| priority| Yes| No|
| require | Yes| Yes|
| restrict |Yes| No (restricted to elements only)|
| scope |Yes (default: false) |No (scope is always isolate)|
| template |Yes |Yes, injectable|
| templateNamespace| Yes| No|
| templateUrl |Yes |Yes, injectable|
| terminal| Yes| No|
| transclude |Yes (default: false) |Yes (default: false)|
更多信息,请参考 [Angular 官方说明](https://docs.angularjs.org/guide/component)
从上表我们可以看出,对于 ``Directive``,``Component`` 从设计思路上更加完善,也更加纯粹。总得来说,组件显得更易理解,更简单易用。
## 2、组件生命周期
在 ``angular.directive()`` 中,是没有生命周期这个概念的,我们无法在指令的特定阶段插入自己的逻辑。
但是在 ``angular.component()`` 中,则是具有特定的生命周期,以方便我们进行控制。
生命周期如下:
1. $onInit -- 指令初始化时执行(放置初始化代码)
2. $onChanges(changesObj) -- 组件数据变化时执行,并可获取变更对象
3. $doCheck() -- 执行变更检测时执行
4. $onDestroy() -- 组件释放时执行(放置清理代码)
5. $postLink() -- 类似后连接函数 (一般放置dom操作,因为此时组件已经渲染好)
实例:
```javascript
((angular, window) => {
class AlertComponent {
constructor() {
}
$onInit() {
console.log('init');
}
$onChanges(changesObj)
console.log('change', changesObj);
}
$doCheck() {
console.log('check');
}
$onDestroy() {
console.log('destroy');
}
$postLink() {
console.log('post link');
}
}
AlertComponent.$inject = []; // 配置依赖项
angular.module('components').component('jAlert', {
templateUrl: 'components/alert/alert.html',
// scope绑定语法,< 单向绑定(变量),@ 单向绑定(纯字符串), = 双向绑定,& 事件绑定
bindings: {
menuData: '<'
},
controller: AlertComponent,
controllerAs: '$ctrl',
require: '',
transclude: false
});
})(window.angular, window);
```
在页面使用该指令后,可以在控制台看出如下输出:
```
init
check
post link
N个check(脏检查)
```
在切换路由,或者其他会删掉该组件的操作时,会看出控制台输出 ``destroy``。
如果中途有数据变化,控制台还会输出 ``change``。
这就是整个组件的生命周期。
## 3、属性绑定
在 ``directive`` 中,我们要获取数据,一般会采用 ``$scope`` 传参,或者通过link函数来捕获参数。
在新的组件申明中,我们只需要通过 ``bindings`` 就可以实现复杂的参数绑定。
简单思考下,我们可能需要哪些绑定呢?
1. 双向绑定 (双向)
2. 单向绑定变量 (从外到内)
3. 单向绑定属性(字符串)(从外到内)
4. 输出绑定 (从内到外)
在组件的 ``bindings`` 属性中,我们也刚好有四种语法,来一一对应这四种绑定。
具体写法如下:
```javascript
bindings: {
model: '=', // 双向绑定
title: '@', // 单项绑定字符串(直接用组件上的属性值)
key: '<', // 单项绑定变量,取到属性值,然后返回$scope[属性值]
onClick: '&' // 输出绑定,执行外部函数
}
```
假设组件标签为 ``<j-test>``,那么用法如下:
```javascript
$scope = {
model: '1',
key: 'abc',
onClick: () => {
}
};
```
```html
<j-test model="model" key="key" title="Title" on-click="onClick()"></j-test>
```
此时,我们在组件中,就能获取到对应的值:
```javascript
{
model: 1, // 从scope中取
key: 'abc', // 从scope中取
title: 'Title', // 直接用string
onClick: fn // 执行该onClick会触发外部函数$scope.onClick
}
```
**注意:关于输出函数传递参数,需要有特定的写法(一定要注意!!!)**
*在组件中的写法*
在组件中,要给该函数传参,必须使用:
```javascript
this.onClick({
param1: 'xxx',
param2: 'BBB'
});
```
的写法,并建议参数名使用 ``$`` 开头,如:``$event``。
*在组件绑定中的写法*
```html
<j-test model="model" key="key" title="Title" on-click="onClick(param1, param2)"></j-test>
```
注意onClick的写法,里面的参数名称,必须和组件中参数对象中的key匹配。
## 4、给组件设定外部HTML
在使用组件过程中,我相信很容易遇到需要使用外部html的组件,如 ``Tabs, Panel`` 等,那我们给组件内部传入自定义的HTML呢?
这个时候,我们可以使用 ``ng-transclude``
### 4.1、传递单个HTML片段
首先,主要在注册组件时,开启 ``transclude``(设置transclude为true),然后我们就可以在组件html中,设定占位符,有如下两种方式:
```html
<!-- 占位符1 -->
<div ng-transclude></div>
<!-- 占位符2 -->
<ng-transclude></ng-transclude>
```
然后在使用组件的地方,就可以直接把要使用的HTML放在组件标记中,如:
```html
<j-test>
<span>我会被传递到主键内部</span>
</j-test>
```
### 4.2、传递多个HTML片段
以上,我们知道了如何传递单个HTML片段,但传递多个HTML片段也是非常有必要的,如 ``Dialog``组件,
我们很可能会传递 ``dialog-header``, ``dialog-body`` 等等,那此时又应如何呢?
这个场景,我们可以借助 ``ng-transclude`` 的 ``slot`` 功能实现,
首先,是占位符的变化,如下:
```html
<!-- 占位符1 -->
<div ng-transclude="header"></div>
<div ng-transclude="body"></div>
<!-- 占位符2 -->
<ng-transclude ng-transclude-slot="header"></ng-transclude>
<ng-transclude ng-transclude-slot="body"></ng-transclude>
```
其次是组件配置的变化,因为有多个 ``transclude``,那么仅仅设置为 ``true``,就不太能满足需求了。
需要修改如下:
```javascript
transclude: {
header: '?panelHeader', // panelHeader表示内部标签,?表示是可选的
body: 'panelBody' // 没有问号,表示该节点必选
}
```
接下来,就应该是调用时的改变,调用如下:
```html
<j-panel>
<panel-header>
我是Panel Header(可选)
</panel-header>
<panel-body>
我是Panel Body(必须)
</panel-body>
</j-panel>
```
## 5、组件 ``require``
同 ``Directive`` 一样,组件也可以相互依赖,只需要在注册组件时,设置require属性即可,写法如下:
```javascript
require: {
componentCtrl: '^parentComponent'
}
```
## 6、小结
新增的 ``angular.component`` 就是这么一个东西,比起 ``directive`` 更加纯粹,更加强大,更加易用。
建议在后续使用中,多多尝试该方式。
================================================
FILE: AngularJS相关/Angular再回首(2)-那些容易忽略的Component细节.md
================================================
---
title: Angular再回首(2)-那些容易忽略的Component细节
date: 2017/02/21 14:47:10
---
## 0、前言
在 ``Angular 1.5.x`` 中,增加的组件方法,相当实用和易用。但也有许多小细节问题值得注意,
以下为本人在组件实践过程中遇到的问题,或者是需要注意的小细节。
## 1、问题/小细节(需要注意的点)
### 1.1、如何判断是否添加了可选的 ``transclude`` 元素?
在很多时候,我们会给一个组件设定多个 ``transclude``,可能其中有一部分是可选的,那如何判断可选的 ``transclude`` 被用户设置了值呢?
此时,我们可以依靠 ``$transclude`` 来进行判断:
```javascript
class XXXComponent{
constructor($transclude){
this.$transclude = $transclude;
}
$onInit(){
// 判断transclude是否存在
let transcludeName = 'xxx';
let hasXXX = this.$transclude.isSlotFilled(transcludeName);
}
}
XXXComponent.$inject = ['$transclude'];
```
### 1.2、如何监控绑定属性的变更?
属性绑定,分为一次性绑定(@)(也算是单向绑定),单向绑定(<),双向绑定(=)。
**# 监控单向绑定属性**
对于单向绑定的属性,可以通过生命周期钩子 ``$onChanges(changesObj)`` 来进行监控。
```javascript
class XXXController{
$onChanges(changesObj){
console.log(changesObj);
}
}
```
其中参数 ``changesObj`` 是所有变更属性的一个汇总,数据结构如下:
```json
changesObj = {
key1: { // 有变更的绑定属性
currentValue: any // 当前值 (变化后的值)
previousValue: any // 上一次的值 (变化前的值)
isFirstChange(): fn // 方法,用于判断是否是第一次变更。
}
}
```
**注意:``$onChanges`` 无法监控双向绑定属性,切记!**
**# 监控双向绑定**
由于 ``$onChanges`` 无法监控双向绑定属性,那么我们就必须另外想办法来进行监控,可以有以下几种方案:
*方案一:利用 ``$interval``*
既然是双向绑定,那么肯定变化是直接生效的,关键就在于我们无法监视到,这个时候我们可以利用 ``$interval`` 来实现定时监控。
```javascript
class XXXController{
constructor($interval){
this.$interval = $interval;
this.init();
}
init(){
let previousValue = null;
this.$interval(() => {
if(previousValue !== this.value){
previousValue = this.value;
console.log('value changed');
}
}, 200);
}
}
XXXController.$inject = ['$interval'];
angular.module('xxx').component('xxx', {
bindings: {
value: '='
},
controller: XXXController,
controllerAs: 'vm'
});
```
优点:
1. 易于理解
缺点:
1. 浪费资源
2. 需要自己书写逻辑
推荐指数: ☆
*方案二:利用 ``$scope.$watch(keyString)``*
组件也有独立的 ``$scope``,那么借助 ``$scope.$watch`` 也可以实现监听属性变化,代码如下:
```javascript
class XXXController{
constructor($scope){
this.$scope = $scope;
this.init();
}
init(){
this.$scope.$watch('vm.value', (newVal, oldVal) => {
console.log('value changed);
});
}
}
XXXController.$inject = ['$scope'];
angular.module('xxx').component('xxx', {
bindings: {
value: '=' // 双向绑定属性
},
controller: XXXController,
controllerAs: 'vm'
});
```
优点:
1. 使用简单
缺点:
1. 字符串形式的 ``$watch``,依赖 ``controllerAs``,不易理解
2. 实质仍然是定时器,只不过是使用的 ``angular`` 自身的 ``$diget`` 循环
推荐指数: ☆☆
*方案三:利用 ``$scope.$watch(fn)``*
``$scope.$watch`` 也接受函数类型的参数,相对于字符串形式,没有 ``controllerAs`` 的相关性,而且更灵活,代码如下:
```javascript
class XXXController{
constructor($scope){
this.$scope = $scope;
this.init();
}
init(){
this.$scope.$watch(() => this.value, (newVal, oldVal) => {
console.log('value changed);
});
}
}
XXXController.$inject = ['$scope'];
angular.module('xxx').component('xxx', {
bindings: {
value: '=' // 双向绑定属性
},
controller: XXXController,
controllerAs: 'vm'
});
```
优点:
1. 使用简单
缺点:
1. 实质仍然是定时器,只不过是使用的 ``angular`` 自身的 ``$diget`` 循环
推荐指数: ☆☆☆☆
*方案四:利用 ``getter & setter``*
因为我们使用了 ``ES6 Class``,那么 ``ES6`` 的 ``getter setter`` 特性,我们也是能够使用的,方式如下:
```javascript
class XXXController{
set value(val){
this._value = val;
console.log('value changed');
}
get value(){
return this._value;
}
}
XXXController.$inject = [];
angular.module('xxx').component('xxx', {
bindings: {
value: '=' // 双向绑定属性
},
controller: XXXController,
controllerAs: 'vm'
});
```
优点:
1. 没有额外的开销,性能高
缺点:
1. 使用相对较为复杂
推荐指数: ☆☆☆☆
================================================
FILE: AngularJS相关/Angular再回首(3)-我们来实现一个组件.md
================================================
---
title: Angular再回首(3)-我们来实现一个组件
date: 2017/02/21 14:47:10
---
## 0、前言
前两文写了 ``Component`` 的一些方面,但没有一个比较线性的串联关系,本文,就来从一个实例出发,来尝试概括一个组件的方方面面。
## 1、
## 2、组件实现
### 2.1、先整一个组件
```javascript
angular.module('app', [])
.component('finalComponent', {});
```
这个组件啥都不干,就提供了一个新的标签,显得毫无意义,但是我们可以从这里看到如何定义一个组件。
**注意:组件名称,请使用小驼峰命名法,在HTML中,请使用连字符+小写字母,这种实现是为了处理js和html大小写敏感的差异(js区分大小写,html不区分)**
**注意2:如果在组件标签中,嵌入有效的标签,是会显示出来的,如下:**
```html
<final-component>
<h1>Hello</h1>
</final-component>
```
会显示出大号的 “Hello”。
### 2.2、带模板的组件
接着,来实现一个有意义的组件,比如我要渲染一个特定的字符串,代码如下:
```javascript
angular.module('app', [])
.component('finalComponent', {
template: '<h1>Hello World.</h1>'
});
```
现在我们再使用:`<final-component>ABC</final-component>`,则会显示 "Hello World" 内容了。
**注意:当组件指定了模板属性后,其内部的标签,将不会生效(transclude除外,)**
### 2.3、复杂模板的组件
以上,我们已经实现了带模板的组件,可是我们的模板可能会比较复杂,这个时候直接写 `template` 就不太好用了,此时,我们会考虑把模板拆分到一个独立的 `.html` 文件中,代码如下:
```html
<!-- 组件模板内容(文件名为:template.html) -->
<h1>Hello World.</h1>
```
然后,使用 `templateUrl` 属性进行关联
```javascript
angular.module('app', [])
.component('finalComponent', {
templateUrl: '/app/template.html'
});
```
该代码可以达到 2.2 同样的效果,只是把模板内容拆分到独立文件中了。
**注意:模板路径可以是相对路径,也可以是绝对路径,需要注意路径的写法,否则会出现找不到模板**
**注意2:如果使用 `gulp` 构建,可以考虑使用 `gulp-angular-embed-templates` 将独立的模板文件,打包到组件中。**
### 2.4、组件属性绑定
之前实现的组件,感觉太死板了,我想改下文字,都不好实现(你非要用js强制操作dom,我拿你也没办法,不过后果自负),这个时候,我们迫切的需要能给组件传递参数。
`Angular` 组件中,有多个参数传递方式,如下:
* @ 单向绑定字符串(原值绑定) - 传什么就是什么,不做任何处理
* < 单向绑定变量(取scope的值绑定) - 传的值会先用 `$scope` 转换,把结果传递给组件
* = 双向绑定 - 组件内外变化都会通知另一方
#### 2.4.1 直接传递字符串参数
使用 `@` 进行单向字符串绑定
```javascript
angular.module('app', [])
.component('finalComponent', {
templateUrl: '/app/template.html'
bindings: {
name: '@'
}
});
```
```html
<!-- 模板内容 -->
<h1>Hello {{$ctrl.name}}.</h1>
<!-- 使用组件 -->
<final-component name="Jay"></final-component>
```
此时,将会显示“Hello Jay”,可以看到,设定的参数值会原样显示了。
**注意:在模板中,要使用变量,需要加$ctrl前缀,先这样用着,后面会提到**
#### 2.4.2 使用单向绑定变量
```javascript
class TestController{
constructor(){
this.name = 'Jay'
}
}
TestController.$inject = []; // 依赖
angular.module('app', [])
.component('finalComponent', {
templateUrl: '/app/template.html'
bindings: {
name: '<'
}
})
.controller('TestController', TestController);
```
```html
<!-- 模板内容 -->
<h1>Hello {{$ctrl.name}}.</h1>
<!-- 使用组件 -->
<div ng-controller="TestController as t">
<final-component name="t.name"></final-component>
</div>
```
此时,也将会显示“Hello Jay”,可以看到,此时 `t.name` 会拿到 `$scope` 中进行解析。
**注意:推荐使用 `controller as` 写法**
#### 2.4.3 双向绑定
```javascript
class TestController{
constructor(){
this.name = 'Jay'
}
}
TestController.$inject = []; // 依赖
angular.module('app', [])
.component('finalComponent', {
templateUrl: '/app/template.html'
bindings: {
name: '='
}
})
.controller('TestController', TestController);
```
```html
<!-- 模板内容 -->
<h1>
<h1>Hello {{$ctrl.name}}.</h1>
<input ng-model="$ctrl.name"></input>
</h1>
<!-- 使用组件 -->
<div ng-controller="TestController as t">
<final-component name="t.name"></final-component>
<h3>{{t.name}}</h3>
</div>
```
此时,在文本框输入值之后,可以看到组件内外都会及时变更。
================================================
FILE: AngularJS相关/Angular开发Tips.md
================================================
---
title: Angular开发Tips
date: 2017/02/21 14:47:10
---
1、在使用$routeProvider的时候,需要让模块依赖ngRoute,否则会提示找不到服务,示例:
angular.module('module1', ['ngRoute'])
.config(['$routeProvider', function($routeProvider){
//do something...
}]);
2、在页面中需要绑定有风险的html的时候,可以使用 `ng-bind-html="html"(version>=1.3)`,如果遇到错误,控制器中可以使用`html = $sce.trustHtml(unsafeHtml)`。
3、 如何动态的向页面添加带指令的HTML?通入如下代码:
$compile(html)($scope);
4、如果阻止事件冒泡?示例如下:
//方式一,利用一个自定义指令实现
.directive('stopEventPropagation', function(){
return {
restrict: 'A',
link: function(scope, iElement, iAttrs){
//通过获取事件对象,来阻止调用
iElement.bind('click', function(e){
e.stopPropagation();
});
}
}
});
<a stop-event-propagation ng-click="doSomething();">Click me</a>
//方式二,直接引用$event对象
<a ng-click="doSomething(); $event.stopPropagation();">Click me</a>
5、关于$route和$location的事件顺序,如下:
$routeChangeStart -> $locationChangeStart -> $locationChangeSuccess -> $routeChangeSuccess
6、有关select标签的使用,当options的来源是ajax时,那么如果指定选中项呢?如下:
<select ng-options="sysOptions" ng-model="selectSystem"></select>
//如上HTML代码,如果sysOptions来自ajax请求,而selectSystem又不是的话,往往会选中一个空值。
//可以使用如下方式避免:
```javascript
.controller('TestCtrl', ['$scope', '$http', function($scope, $http){
$http.get(url).success(function(data){
$scope.sysOptions = data;
//在异步回调函数中,对ng-model赋值。
$scope.selectSystem = 'Test';
});
}]);
```
7、在编写指令时,属性的匹配大小写需要注意:如果在html中使用 `showName="xx"`,那么在指令的iAttrs中,应该使用 `showname` 获取。如果要在指令中使用showName获取的话,那么必须在html中使用 `show-name="xx"`。
8、要生成安全链接时,需要修改配置,代码如下:
```javascript
需要将如下代码: ng-href="{{true: 'javascript:void(0);' : 'url'}}"
生成为: href="javascript:void(0);"
```
```javascript
.config(['$compileProvider', function($compileProvider){
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|javascript):/)
}]);
```
9、在ng-click等ng事件中,如果拿到事件源对象?如下:
```javascript
<a ng-click="click($event);" />
$scope.click = function($event){
var target = $event.target;
};
//注意,如果使用ng-click="click($event.target)",将会导致angular解析错误。
```
10、判断angular的模块是否存在,可以使用如下代码:
var isAngularModuleExists = function(moduleName){
try{
angular.module(moduleName)
}catch{
return false
}
return true;
};
11、在使用coffee编写使用provider方式编写服务时,当心写在最后的this.$get,coffee会将最后一句编译为return this.$get,而这刚好不符合provider的要求,所以应该在末尾手动加上return或者放置一个undefined在最后,放置编译出return this.$get这样的代码。
12、如果要动态控制是否启用非空验证,可以使用ng-required="true|false"指令。
13、当心ng-if指令,在使用ng-if指令时,会创建独立的作用域,如果要在$scope监视ng-if包含的变量,那么是无法成功的。如果一定要监视,可以考虑使用ng-show。
14、注意.value()与.constant的区别,前者只能注入和用于服务或者控制器中,后则可以被注入到配置(.config(['xx']))中。
================================================
FILE: AngularJS相关/Angular:指令、Controller数据共享.md
================================================
---
title: Angular:指令、Controller数据共享
date: 2015/3/13 11:24:39
---
## 1、Directive与Controller数据共享
在指令中,不仅仅需要指令配置信息,很多时候也需要获取$scope的相关数据。那么,如何在指令中拿到$scope的数据呢?
### 1.1、Directive和Controller使用同一个scope
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Angular Demo</title>
</head>
<body>
<div ng-controller="DemoCtrl">
<d1></d1>
</div>
<!-- 脚本区域 -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script>
angular.module('app', [])
.directive('d1', [function(){
return {
restrict: 'E',
scope: false, //defualt value is false
template: '<h1>Hi,{{name}}</h1>',
link: function(scope, iElement, iAttrs){
console.log('directive scope id = ' + scope.$id);
}
}
}])
.controller('DemoCtrl', ['$scope', function($scope){
console.log('controller scope id = ' + $scope.$id);
$scope.name = 'Jay';
}]);
//可以采用如此方式启动angular扫描,或者直接使用ng-app="app"
angular.bootstrap(document.body, ['app']);
</script>
<!-- 脚本区域 End -->
</body>
</html>
执行以上代码,页面显示Hi Jay,并在控制台打印
controller scope id = 2
directive scope id = 2
在指令中,默认会直接使用上级的scope,从控制台来看,先执行controller的scope,再执行directive的scope。因为id一致,所以是同一个scope。既然是同一个scope,那么共享数据自然就不是问题了。该方式,适合业务性质的directive,如果是公共的directive,不建议使用此方式,可能会导致scope杂乱。
### 1.2、在指令作用域中使用@,将当前属性作为字符串传递
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Angular Demo</title>
</head>
<body>
<div ng-controller="DemoCtrl">
<d1 name="{{key}}"></d1>
</div>
<!-- 脚本区域 -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script>
angular.module('app', [])
.directive('d1', [function(){
return {
restrict: 'E',
scope: {
name: '@'
},
template: '<h1>Hi,{{name}}</h1>',
link: function(scope, iElement, iAttrs){
console.log('directive scope id = ' + scope.$id);
}
}
}])
.controller('DemoCtrl', ['$scope', function($scope){
console.log('controller scope id = ' + $scope.$id);
$scope.key = 'Jay';
}]);
//可以采用如此方式启动angular扫描,或者直接使用ng-app="app"
angular.bootstrap(document.body, ['app']);
</script>
<!-- 脚本区域 End -->
</body>
</html>
以上代码,主要修改了指令的scope,从输出来看,指令和controller各自是自己独有的作用域。
``scope = {name: '@'}``,等价于
link:function(scope, iElement, iAttrs){
scope.name = iAttrs.name;
}
Controller中的key的变化,会即时影响到Directive的变化,但是Directive的变化并不会反向影响到Controller,结果近似于单向绑定。
### 1.3、在指令的作用域中使用=,进行数据的双向绑定
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Angular Demo</title>
</head>
<body>
<div ng-controller="DemoCtrl">
key = {{key}}
<d1 name="key"></d1>
</div>
<!-- 脚本区域 -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script>
angular.module('app', [])
.directive('d1', [function(){
return {
restrict: 'E',
scope: {
name: '='
},
template: '<h1>Hi,{{name}}</h1><input type="text" ng-model="name" />',
link: function(scope, iElement, iAttrs){
console.log('directive scope id = ' + scope.$id);
}
}
}])
.controller('DemoCtrl', ['$scope', function($scope){
console.log('controller scope id = ' + $scope.$id);
$scope.key = 'Jay';
}]);
//可以采用如此方式启动angular扫描,或者直接使用ng-app="app"
angular.bootstrap(document.body, ['app']);
</script>
<!-- 脚本区域 End -->
</body>
</html>
以上代码的变化在于,使用了scope: {name: '='},该代码将父作用域的属性和指令的属性进行双向绑定。所以指令中文本框的值的变化,将会同步影响controller中key的变化。
**注意:在使用指令的时候,html代码,并不是和示例1.1一致了,如果是双向绑定,那么应该使用<d1 name="key" />,而不是<d1 name="{{key}}">。**
### 1.4、在Directive中调用Controller的方法
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Angular Demo</title>
</head>
<body ng-app="app">
<div ng-controller="DemoCtrl">
key = {{key}}
<d1 name="key" show-name="show(key)"></d1>
</div>
<!-- 脚本区域 -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script>
angular.module('app', [])
.directive('d1', [function(){
return {
restrict: 'E',
scope: {
name: '=',
showName: '&'
},
template: '<h1>Hi,{{name}}</h1><input type="text" ng-model="name" />'
+ '<button ng-click="showName(name)">Show</button>',
link: function(scope, iElement, iAttrs){
console.log('directive scope id = ' + scope.$id);
}
}
}])
.controller('DemoCtrl', ['$scope', function($scope){
console.log('controller scope id = ' + $scope.$id);
$scope.key = 'Jay';
$scope.show = function(name){
alert(name);
};
}]);
</script>
<!-- 脚本区域 End -->
</body>
</html>
点击指令生成的按钮,会执行controller的show方法,利用在scope: {showName: '&'},可以将父级作用域的方法绑定到指令中。
**注意,一定要注意属性命令,在html中书写showName,那么在iAttrs中对应showname,只有在html中书写show-name,在会在iAttrs中对应showName。**
## 2、在controller中,拿到directive的作用域
### 2.1、拿到scope的元素,调用isolateScope获取scope
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Angular Demo</title>
</head>
<body ng-app="app">
<div ng-controller="DemoCtrl">
key = {{key}}
<button ng-click="click()">Click</button>
<hr />
<d1 id="d1" name="key" show-name="show(key)"></d1>
</div>
<!-- 脚本区域 -->
<script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script>
angular.module('app', [])
.directive('d1', [function(){
return {
restrict: 'E',
scope: {}, //等价于 scope: true
template: '<h1>Hi,{{name}}',
link: function(scope, iElement, iAttrs){
scope.name = 'directive name';
console.log('directive scope id = ' + scope.$id);
}
}
}])
.controller('DemoCtrl', ['$scope', function($scope){
console.log('controller scope id = ' + $scope.$id);
$scope.click = function(){
var dirScope = $('#d1').isolateScope();
alert(dirScope.name);
}
}]);
</script>
<!-- 脚本区域 End -->
</body>
</html>
此代码中,利用$('#d1').isolateScope,拿到了该指令的scope,所以可以随时方式,该方式在多种指令中也有效。
**如果判断应该用isolateScope()还是scope()获取作用域?一个最简单的方式,用F12查看源码,找到该元素,然后查看class是ng-isolate-scope还是ng-scope**
## 3、 指令之间相互获取数据
### 3.1、通过directive依赖来共享数据
<script>
angular.module('app', [])
.directive('d1', [function(){
return {
restrict: 'E',
require: '^ngModel',
scope: {}, //等价于 scope: true
template: false,
link: function(scope, iElement, iAttrs, ngModelCtrl){
}
}
}])
.controller('DemoCtrl', ['$scope', function($scope){
}]);
</script>
### 3.2、通过如2.1的方式获取数据
## 4、 其他Hacky的方式
1. 通过``$parent``访问父级作用域
2. 通过``$$prevSibling``访问该作用域的上一个兄弟作用域
3. 通过``$$nextSibling``访问该作用域的下一个兄弟作用域
4. 通过``$$childHead``访问儿子作用域的第一个
5. 通过``$$childTail``访问儿子作用域的最后一个
## 5、参考资料
1. [SHARING DATA BETWEEN CHILD AND PARENT DIRECTIVES AND SCOPES (IN ANGULARJS)](http://tech.blinemedical.com/sharing-data-between-child-and-parent-directives-and-scopes-in-angularjs/)
2. [directive和controller如何通信](http://www.cnblogs.com/bigdataZJ/p/AngularJS1.html)
================================================
FILE: AngularJS相关/[20140917]Angular:如何编写一个指令.md
================================================
---
title: Angular:如何编写一个指令
date: 2015/3/13 11:24:39
---
## Angular是什么?
AngularJS是一个用JavaScript编写的客户端MVC框架,它运行于Web浏览器,能够极大的帮助我们(开发者)编写模块化,单页面,Ajax风格的Web Applications。
PS:**AngularJS适合开发CRUD的SPA**
## Angular Directive是什么?
Angular Directive是构建在DOM元素(属性、标签名、注释和CSS类)上的标记,告诉AngularJS的HTML编译器($compile) 附加指定的行为到元素或者甚至变换这个元素和它的子集。
PS:**通过扩展HTML标签的方式提供可复用的web组件**
PS2:**指令的作用:提供语义化标签**
## 完整的Directive参数
var directiveModule=angular.module('Newkit.negHotkeys');
directiveModule.directive('negHotkeys',function(injectables){
var directiveDefineObject={
restrict:(string),
priority:(number),
template:(string),
templateUrl:(string),
replace:(bool),
transclude:(bool),
scope:(bool or object),
controller:(function),
require:(string),
link:(function)
compile:(function)
};
return directiveDefineObject;
});
##### 参数说明
- restrict:(string)指令的使用方式,可选值:元素[E]、属性[A]、样式类[C]、注释[M],并且可以采用组合的方式使用,示例:'AE'
- priority:(number)优先级,描述了多个指令时,指令的执行顺序。数字越大,优先级越高,默认值0。
- template:(string)文本模板
- templateUrl:(string)模板文件地址,如果设置了该属性,那么将会忽略template的配置。
- replace:(bool)指示是否替换元素,如果设置为true,则替换,否则(设置为false或不设置)追加到元素内部
- transclude:(bool)是否将指令的子节点移动到一个新模板内部,如果在模板中指定了ng-transclude,那么会将元素原本的内容移动到新的模板内部,具体看示例二
- scope:(bool or object)设置作用域,如果设置为false[默认值],则使用现有的作用域;如果设置为true,则创建一个新的作用域。设置为object时,设定作用域绑定策略
- controller:创建一个控制器,它会暴露一个API,实现在多个指令之间进行通信
- require:设置依赖的指令。不设置,则无依赖,示例:'?\^testDirective',其中,?表示该指令可选,^表示需要遍历DOM树查找指令
- link:链接函数,function(scope,iElement,iAttrs){},其中的i表示实例,所以在link中接收的是实例元素和实例元素属性
- compile:编译函数,function(tElement,tAttrs,transclude){},其中t表示模板,所以在compile中使用的是模板元素。在编译过程中,可以返回preLink(链接前),postLink(链接后)函数,compile函数只会调用一次,而link函数的调用次数等于things中的元素个数,所以多余共同的东西,那么最好放在compile函数中实现(出于效率考虑) **注:设置了compile属性之后,指令将忽略link属性,同时compile函数的返回值将作为link函数使用**
## Angular Directive 示例
### 示例一(简单指令)
```javascript
<!--demo1指令定义-->
angular.module('app').directive('demo1',function(){
return {
restrict:'AE',/*标签或者属性*/
template:'<div>Hello</div>',
replace:true
}
});
<!--使用-->
<html ng-app="app">
<head>...</head>
<body>
<demo1></demo1>
<div data-demo1></div>
</body>
</html>
<!--结果(指令将满足条件的元素替换为了新的内容)-->
<body>
<div>Hello World!</div>
<div demo1="">Hello World!</div>
</body>
```
### 操作步骤分析
1. 定义一个模块app,并创建了一个指令demo1。
2. 设定该指令可采用元素的标签和属性申明,并设置了一个文本模板,同时设置了replace=true。
3. 在html中,采用标签如*<demo1></demo1>*和属性*<div demo1></div>*来实现调用
### 示例二(变换)
```javascript
<!--demo2指令定义-->
angular.module('app.directive.demo2',[]).directive('demo2',function(){
return {
restrict:'E',
template:'<div>This is Demo2<div ng-transclude></div></div>',
transclude:true
}
});
<!--使用-->
<demo2>
<span>原始的内容,</span><br/>
<span>还会在这里。</span>
</demo2>
<demo2></demo2>
<!--页面生成的HTML-->
<demo2>
<div>This is Demo2
<div ng-transclude="">
<span class="ng-scope">原始的内容,</span><br class="ng-scope">
<span class="ng-scope">还会在这里。</span>
</div>
</div>
</demo2>
<demo2>
<div>This is Demo2
<div ng-transclude=""></div>
</div>
</demo2>
```
#### 分析
1. 通过在指令中设置transclude=true,同时在template中包含*<div ng-transclude>*,实现了将元素内部元素移动到了ng-transclude元素内部,并创建了新的作用域
### 示例三(link与compile)
```javascript
/*指令*/
angular.module('app.directive.demo3',[]).directive('demo3Link',function(){
return {
restrict:'E',
template:'<div>This is Demo3Link</div>',
link:function(scope,iElement,iAttrs){
iElement.html('<div>good link</div>');
}
}
}).directive('demo3Compile',function(){
return {
restrict:'E',
template:'<div>This is Demo3Compile</div>',
compile:function(tElement,tAttrs,transclude){
tElement.html('<div>test demo3 compile</div>');
return function(scope,iElement,iAttrs){
//iElement.html('<div>good compile</div>');
};
}
}
});
/*使用*/
<demo3-link></demo3-link>
<demo3-link></demo3-link>
<demo3-compile></demo3-compile>
/*页面生成的HTML*/
<demo3-link><div>good link</div></demo3-link>
<demo3-link><div>good link</div></demo3-link>
<demo3-compile><div>test demo3 compile</div></demo3-compile>
```
#### 分析
compile用于在编译期处理模板内容,并能设置preLink和postLink函数,此时将不能设置link函数,代码如下:
```
compile:function(tElement,tAttrs,transclude){
tElement.html('<div>test demo3 compile</div>');
return {
pre:function preLink(scope,iElement,iAttrs){
console.log('preLink');
},
post:function postLink(scope,iElement,iAttrs){
console.log('postLink');
}
};
}
```
link用于对替换后的元素进行操作,如果参数是iElement。
### 示例四(简单加法计算器)
```
/*代码在这里*/
angular.module('app.directive.demo4',[]).directive('demo4',function(){
return {
restrict:'E',
template:'<fieldset><legend>计算两个数之和</legend>' +
'<div><input type="text" ng-model="num1">+<input type="text" ng-model="num2">=<span>{{total}}</span></div>' +
'</fieldset>',
replace:true,
link:function(scope,iElement,iAttrs){
scope.num1=0;
scope.num2=0;
scope.total=0;
scope.$watch('num1+num2',function(to,from){
scope.total=+scope.num1+(+scope.num2)
})
}
}
});
/*HTML在这里*/
<demo4></demo4>、
/*效果请自行测试*/
```
#### 分析
可以利用指令完成特定的功能了。
### 示例五(negHotkeys指令代码)
[代码在这里](http://trgit/backend_framework/web_platform/blob/master/src/framework/js/directives/custom/negHotKeys.coffee)
## 总结
1. 指令依附于模块
2. 一个模块可以有多个指令,但是需要采用示例三的写法
3. 指令可以语义化标签,实现html组件化
4. 其他...
================================================
FILE: AngularJS相关/用AngularJS开发Web应用程序.md
================================================
---
title: 用AngularJS开发Web应用程序
date: 2015/3/13 11:24:39
---
##章节一:Angular 禅道##
###本章生词###
serve = 提供
take a brief = 先简要的
introduction = 介绍
concept = 概念
a lot of = 许多
material = 材料
cover = 概括
painless = 无痛的
plenty = 丰富、大量
unique = 独特的
doubt = 疑问
shape = 塑造
explain = 解释
expect = 预计
get familiar with = 熟悉
become aware = 察觉
sophisticated = 复杂
dependency injection = 依赖注入nuance
nuance = 细微之处
general = 一般
purpose = 目的
shines = 耀眼
recent = 最近
addition = 此外
mostly = 主要的
due = 由于
innovative = 创新
yet = 但
attract = 吸引
ease = 缓解
solid = 扎实
engineering = 工程
practice = 实践
indeed = 的确
respects = 方面
explicit = 明确的
capable = 能
figure out = 弄清楚
interesting = 有趣的
interpret = 解析
mistaken = 错误,谬
several = 几个,数个
typically = 通常
treasure = 宝藏
testability = 可测试性
built-in support 内置支持
thoroughly = 彻底的
relatively = 比较的
actor = 演员
personal = 个人的
turned out = 横空出世
---
这个章节介绍了AngularJS,包括框架和它背后的项目。首先,我们先简要的了解项目本身:谁become aware驱动了它,在哪儿可以找到源代码和文档,如何寻求帮助等等。
这个章节的大部分是介绍AngularJS框架,它的核心概念和编码模式。包含有许多概括(总结)性的材料,使得学习进程快速无障碍,同时也有丰富的代码示例。
AngularJS是一个独特的框架,毫无疑问的引领一个Web开发潮流。这也是为什么章节的最后部分解释了是什么让AngularJS如此特别,它和其它外部框架之间的差异和我们能在未来如何设想它。
这个章节包含了以下几个主题:
1. 如何用AngularJS书写一个简单的Hello World 程序。在做这个的过程中,你将了解到在哪儿找到框架源代码、文档以及社区。
2. 熟悉AngularJS应用程序的基本构造块:Templates、Directives、Scopes和Controllers。
3. 察觉AngularJS复杂的依赖注入系统以及它所有的细微之处。
4. 理解AngularJS与其他框架或库(特别是jQuery)之间的差异,是什么使得它如此特别。
###AngularJS简介###
AngularJS是一个用JavaScript编写的客户端MVC框架,它运行于Web浏览器,能够极大的帮助我们(开发者)编写模块化,单页面,Ajax风格的Web Applications。它是一个平常的框架,不过如果用于编写CRUD类型的web app,那么它将非常耀眼。
###熟悉框架###
AngularJS是最近的客户端mvc框架的例外,但是它吸引了许多注意力,主要是由于它创新的模板系统,减轻了开发,同时有很扎实的工程实践。的确,它的模板系统独特于许多方面:
1. 使用HTML作为模板语言
2. 不要求明确的DOM刷新,AngularJS 能跟踪用户操作、浏览器事件和模型变化,来选择何时和那个模板将被刷新
3. 它有非常有趣的和可扩展的组件子系统,它能教会浏览器如何解析新的HTML标签和属性
模板子系统可能是AngularJS中最常见的部分,但是不要错误的认为AngularJS是单页Web程序所需要的包含数个工具和常用服务的完整框架包。
AngularJS同样有一些隐藏的宝藏,依赖注入(DI=dependency injection)和可测试特性的强烈关注。DI的内置支持能够非常容易的访问从一个极小的、彻底的可测试服务创建的web app。
###项目发展路线###
AngularJS是客户端MVC框架中比较新的成员;它的1.0版本发布于2012年6月。实际上,这个框架作为谷歌雇员Misko Hevery的个人项目开始于2009年。最初的idea是如此的好,在写作本文的同时,这个项目已经被Google正式支持,并且有Google的完整团队全职维护这个框架。
AngularJS是托管在[GitHub](https://github.com/angular/angular.js)上的,基于MIT协议的开源项目
###社区###
================================================
FILE: AngularJS相关/详解angular之$q.md
================================================
---
title: 详解angular之$q
date: 2017/02/21 14:47:10
---
## 0、什么的Promise
Promise(承诺)是用于改善异步编程体验的一种编程模型,它提供了一些列的API的方法论,让你能更优雅的解决异步编程中出现的一些问题。
## 1、Promise的核心竞争力
在处理有依赖性的回调的时候,我们的代码是这样写的:
step1(function (value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
// Do something with value4
});
});
});
});
这就是我们所谓的回调地狱。
如果用Promise的方式来实现,是怎么样呢?
step1().then(step2).then(step3).then(step4)
代码更简单逻辑也清晰,异步的回调嵌套变成了同步写法,孰优孰劣相信大家都一目了然。
## 2、Angular服务$q
在angular中,基于nodejs中流行的Q提供了一个简化版本的Q,对外的话提供一个service $q。
以下列举出angular中的$q提供的API
#### 1、Promise.then() 将回调变成链式调用,then可以接两个参数,successCallback, errorCallback,示例如下:
var deferred = $q.defer();
var promise = deferred.promise;
promise.then(successCallback, errorCallback);
#### 2、Promise.catch 捕获Promise异常,Promise.catch(errorCallback)等价于Promise.then(null, errorCallback)
#### 3、Promise.finally(callback, notifyCallback) promise结束后要做的事情和接收通知信息
#### 4、Deferred.resolve(val) 通知promise请求处理完毕,并将处理结果传给回调函数(successCallback),示例如下:
var deferred = $q.defer();
setTimeout(function(){
deferred.resolve('abc'); //会将abc传递给successCallback
}, 1000);
var promise = deferred.promise;
promise.then(successCallback, errorCallback);
#### 5、Deferred.reject(msg) 通知promise请求出现异常,将异常信息传给回调函数(errorCallback),示例如下:
var deferred = $q.defer();
setTimeout(function(){
deferred.reject('abc'); //会将abc传递给errorCallback
}, 1000);
var promise = deferred.promise;
promise.then(successCallback, errorCallback);
#### 6、Deferred.notify(value) 内部执行有变化时,对外发起通知。将会在Promise.finally中捕获到
var deferred = $q.defer();
setTimeout(function(){
deferred.reject('abc'); //会将abc传递给errorCallback
}, 1000);
var promise = deferred.promise;
promise.then(successCallback, errorCallback);
#### 7、$q.when(val/fn) 将任意对象/函数包装成promise,返回包装好的promise。
#### 8、$q.all(promises).then() 当所有的promise都成功解析后,流程才继续往下走。示例如下:
$q.all($http.get('xxx'), $http.post('xxx',{}))
.then(successCallback, errorCallback);
## 3、$q的使用
常规使用
//定义开关变量
var canSuccess = false;
//定义一个Promise
var buildPromise = ()=>{
var deferred = $q.defer();
setTimeout(()=>{
if(canSuccess){
deferred.resolve('promise执行成功!')
}else{
deferred.reject('promise执行失败!')
}
},5000);
return deferred.promise;
};
//使用它
var promise = buildPromise();
promise.then(()=>{
console.log('执行成功啦!');
}, ()=>{
console.log('执行失败了!');
})
使用$q.all
var p1 = $http.get('xxxx');
var p2 = $http.get('xxxx2');
$q.all(p1, p2).then(() =>{
console.log('两次请求都成功了!');
});
## 4、$q源码分解
//Deferred定义
function Deferred() {
this.promise = new Promise();
//Necessary to support unbound execution :/
this.resolve = simpleBind(this, this.resolve);
this.reject = simpleBind(this, this.reject);
this.notify = simpleBind(this, this.notify);
}
//函数柯里化
function simpleBind(context, fn) {
return function(value) {
fn.call(context, value);
};
}
通过这种方式,就能将resolve,reject和promise关联起来了。既然我们最终要返回promise,那我们来看已看Promise的实现:
function Promise() {
this.$$state = { status: 0 };
}
extend(Promise.prototype, {
then: function(onFulfilled, onRejected, progressBack) {
if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
return this;
}
var result = new Deferred();
this.$$state.pending = this.$$state.pending || [];
this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
return result.promise;
},
"catch": function(callback) {
return this.then(null, callback);
},
"finally": function(callback, progressBack) {
return this.then(function(value) {
return handleCallback(value, true, callback);
}, function(error) {
return handleCallback(error, false, callback);
}, progressBack);
}
});
从这里很明显可以看出,catch就是一个语法糖,调用的还是then。finally也是一个语法糖,就是不关成功,还是失败,都会调用callback。那这个时候,我们主要关注的方法就放到then这个方法的实现上。
为了实现链式调用,在then方法内部,又实例化了Deferred对象,并返回Defferrd.promise。
接下来就来看处理过程:
this.$$state.pending = this.$$state.pending || [];
this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
extend(Deferred.prototype, {
resolve: function(val) {
if (this.promise.$$state.status) return;
if (val === this.promise) {
this.$$reject($qMinErr(
'qcycle',
"Expected promise to be resolved with value other than itself '{0}'",
val));
} else {
this.$$resolve(val);
}
},
$$resolve: function(val) {
var then, fns;
fns = callOnce(this, this.$$resolve, this.$$reject);
try {
if ((isObject(val) || isFunction(val))) then = val && val.then;
if (isFunction(then)) {
this.promise.$$state.status = -1;
then.call(val, fns[0], fns[1], this.notify);
} else {
this.promise.$$state.value = val;
this.promise.$$state.status = 1;
scheduleProcessQueue(this.promise.$$state);
}
} catch (e) {
fns[1](e);
exceptionHandler(e);
}
},
reject: function(reason) {
if (this.promise.$$state.status) return;
this.$$reject(reason);
},
$$reject: function(reason) {
this.promise.$$state.value = reason;
this.promise.$$state.status = 2;
scheduleProcessQueue(this.promise.$$state);
},
notify: function(progress) {
var callbacks = this.promise.$$state.pending;
if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
nextTick(function() {
var callback, result;
for (var i = 0, ii = callbacks.length; i < ii; i++) {
result = callbacks[i][0];
callback = callbacks[i][3];
try {
result.notify(isFunction(callback) ? callback(progress) : progress);
} catch (e) {
exceptionHandler(e);
}
}
});
}
}
});
在调用then的时候,就将锅中回调写到$$state的pending数组中,让defferred.resolve的时候就会调用Deferred的内部方法,调用我们传递的回调函数。
**源码分解实在是说不明白,后期再发一篇如何实现一个简易的Promise,希望能更简洁易懂**
## 5、 了解更多
[JavaScript Promise迷你书(中文版)](http://liubin.github.io/promises-book/)
[Angular $q api](https://docs.angularjs.org/api/ng/service/$q)
================================================
FILE: Angular系列/01_Angular2初体验.md
================================================
---
title: 01_Angular2初体验
date: 2017/02/21 14:47:10
---
## 0、关于Angular2
Angualr2是前端最流行的MV*框架AngularJS的革命性更新版本,官网:[https://angular.io/](https://angular.io/),号称一个框架统一移动版和桌面。
## 1、背景
将AngularJS升级为Angular2,是大势所趋。在之前,我们就必须要对Angular2有足够的了解。所以这一系列文章,希望从各个点将angular2分而破之。
另外,由于Angular2当前处于Beta阶段,所以代码的时效性不高。所以每篇都会注明相关版本。
Angular2推荐的开发语言是TypeScript [http://www.typescriptlang.org/](http://www.typescriptlang.org/),所以我们这一系列文章也使用TypeScript开发(实际是使用JavaScript,半天没弄成功,丧气ing...)。
不要害怕TypeScript,因为TypeScript是ES6的超集,我们完全可以使用ES6的方式来编写TypeScript代码。对我们来说,仅仅是文件名后缀变化了。
## 2、Angular2 Hello-World
Angular2并不仅仅只有一个JS文件,要想成功运行Angular2,需要包含如下内容:
1. systemjs --模块加载器
2. Rxjs --对Js的扩展,至今不知道它是做什么的
3. angular2
如果要支持IE,那么还需要
1. es6-shim
2. systemjs 中的system-polyfills.js文件
3. angular2中的shims_for_IE.js文件
接着就直接创建项目吧,结构如下:
```
<root folder>
components/
hello_world.html
hello_world.ts
bootstrap.ts
index.html
package.json
```
首先第一步,我们要通过npm安装我们的依赖项:
``npm install angular2 rxjs systemjs es6-shim typescript --save``
接着,实现我们的``index.html``内容:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Angular2 Hello World</title>
</head>
<body>
<!-- Angular2组件标记 -->
<hello_world></hello_world>
<!-- IE required polyfills -->
<script src="node_modules/es6-shim/es6-shim.js"></script>
<script src="node_modules/systemjs/dist/system-polyfills.js"></script>
<script src="node_modules/angular2/es6/dev/src/testing/shims_for_IE.js"></script>
<!-- Compile TypeScript -->
<script src="node_modules/typescript/lib/typescript.js"></script>
<!-- Angular2 required -->
<script src="node_modules/systemjs/dist/system.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.js"></script>
<!-- startup app -->
<script>
System.config({
transpiler: 'typescript',
typescriptOptions: { emitDecoratorMetadata: true },
packages: {'components': {defaultExtension: 'ts'}} //配置components目录下的请求,默认格式为ts
});
//注意:此处import的时候,必须要指明后缀,因为我们是把bootstrap.ts放在index平级的,在System的config中没有配置默认扩展名
System.import('bootstrap.ts').then(null, console.error.bind(console));
</script>
</body>
</html>
```
然后是我们的``hello_world.html``和``hello_world.ts``,内容如下:
```html
<h1>Hello Angular2</h1>
My name is: <input type="text" [(ngModel)]="username">
<br>
Angular2: <span *ngIf="username">Hello, {{username}}</span>
```
```javascript
import {Component} from 'angular2/core';
@Component({
selector: 'hello_world', //此处指明了组件的标记,我们就可以使用<hello_world></hello_world>来使用这个组件了。
templateUrl: 'components/hello_world.html'
})
//export的意思是导出这个组件,在使用的地方,就可以使用import {xx} from 'xxx'来获取到了。
export class HelloWorldComponent{
constructor(){
}
}
```
最后,是我们的``bootstrap.ts``入口JS:
```javascript
import {bootstrap} from 'angular2/platform/browser';
import {HelloWorldComponent} from 'components/hello_world';
bootstrap(HelloWorldComponent);
```
通过``anywhere``启动静态服务器,就可以看到我们的页面了。
**But,理想很丰满,现实很骨感,为嘛不兼容IE11???**
错误提示如下:
```
"'Symbol' is undefined"
```
**坑你没商量*!最终发现是Rx的版本必须用angular2提供的那个版本**
地址是:[https://code.angularjs.org/2.0.0-beta.12/Rx.js](https://code.angularjs.org/2.0.0-beta.12/Rx.js)
所以把Rx.js文件替换下,就可以在IE11中跑起来了。
**另外,经测试,Angular2可兼容IE9及以上版本。**
## 3、结尾
[Demo源码](https://github.com/hstarorg/HstarDemoProject/tree/master/angular2_demo/04)
================================================
FILE: Angular系列/02_Angular2组件生命周期.md
================================================
---
title: 02_Angular2组件生命周期
date: 2017/02/21 14:47:10
---
## 0、Angular2 组件
Angular1并不是围绕组件的概念来实现的。所以,我们需要controller、$scope,同时也需要封装自定义指令。
在Angular2中,把之前的这些东西都丢弃了,使用了一种更面向对象的组件模型。
一个组件控制着我们称之为View的显示部分。组件同时也是自描述的。
**在Angular2中,指令也是存在的,组件只是指令的一种。**
## 1、定义一个组件
最基本的组件只需要提供一个selector和template就足够了。代码如下:
```javascript
import {Component} from 'angular2/core';
@Component({
selector: 'basic-info',
template: '<h1>Basic Info</h1>'
})
export class AboutComponent{
constructor() {
}
}
```
要实现输入和输出呢?
```javascript
import {Component, Input, Output, EventEmitter} from 'angular2/core';
@Component({
selector: 'basic-info',
template: `
<h1>Basic Info, {{abc}}</h1>
<input type="text" [(ngModel)]="abc">
`
})
export class BasicInfo{
@Input('test') set value(val){
this.abc = val;
this.callback.next([val]);
}
@Output('callback') callback = new EventEmitter();
constructor(){
this.abc = 'aaaa';
}
}
```
如何使用?
```html
<input type="text" [(ngModel)] = "value">
<basic-info [test]="value" (callback)="innerCallback($event)"></basic-info>
```
## 2、组件生命周期
Angular2会管理组件的整个生命周期,包括组件的创建、渲染、子组件的创建和渲染、数据绑定属性变化时的校验、从DOM移除之前的销毁等等。
那如果我们想在某个状态时,进行一些操作应该怎么办呢?Angular2提供了组件生命周期的钩子,供我们在这些时间点添加自定义的操作。
在``angular2/core``中提供了多个Lifecycle Hook接口,我们可以实现一个或多个接口,来设置自定义操作。每一个接口,都会有一个钩子方法,钩子方法的名称是接口的名称加上前缀ng。如OnInit的钩子方法如下:
```javascript
import {Component, Input, Output, EventEmitter} from 'angular2/core';
@Component({
selector: 'basic-info',
template: `
<h1>Basic Info, {{abc}}</h1>
<input type="text" [(ngModel)]="abc">
`
})
export class BasicInfo{
@Input('test') set value(val){
this.abc = val;
this.callback.next([val]);
}
@Output('callback') callback = new EventEmitter();
constructor(){
this.abc = 'aaaa';
}
// 组件Init时创建
ngOnInit(){
console.log('basic info init.');
}
}
```
**生命周期钩子(组件和指令都有的)**
1. ngOnInit //组件初始化,在Angular初始化数据绑定输入属性之后
2. ngOnChanges //
3. ngDoCheck
4. ngOnDestroy
**生命周期钩子(组件特有的)**
1. ngAfterContentInit // Angular将外部内容放入视图后
2. ngAfterContentChecked // 在Angular检测放到视图内的外部内容的绑定后
3. ngAfterViewInit // Angular创建视图之后
4. ngAfterViewChecked //Angular检测了组件视图的绑定之后
**执行顺序**
1. ngOnChanges //绑定属性变化时
2. ngOnInit //在第一次ngOnChanges之后,初始化时
3. ngDoCheck //每次Angular变化检测时
4. ngAfterContentInit //组件内容初始化之后
5. ngAfterContentChecked //组件内容变化后
6. ngAfterViewInit //初始化组件视图和子视图之后
7. ngAfterViewChecked //在数组视图和子视图检查之后
8. ngOnDestroy
我们将组件设定上钩子函数如下:
```javascript
import {Component, Input, Output, EventEmitter} from 'angular2/core';
@Component({
selector: 'basic-info',
template: `
<h1>Basic Info, {{abc}}</h1>
<input type="text" [(ngModel)]="abc">
`
})
export class BasicInfo{
@Input('test') set value(val){
this.abc = val;
this.callback.next([val]);
}
@Output('callback') callback = new EventEmitter();
constructor(){
this.abc = 'aaaa';
}
ngOnInit(){
console.log('basic info init.');
}
ngDoCheck(){
console.log('basic info do check.');
}
ngOnChanges(){
console.log('basic info changes.');
}
ngOnDestroy(){
console.log('basic info destroy');
}
ngAfterContentInit(){
console.log('basic info after content init');
}
ngAfterContentChecked(){
console.log('basic info after content checked');
}
ngAfterViewInit(){
console.log('basic info after view init');
}
ngAfterViewChecked(){
console.log('basic info after view checked');
}
}
```
控制台打印的结果是:
```html
basic info changes.
test.component.js:23 basic info init.
test.component.js:26 basic info do check.
test.component.js:35 basic info after content init
test.component.js:38 basic info after content checked
test.component.js:41 basic info after view init
test.component.js:44 basic info after view checked
test.component.js:26 basic info do check.
test.component.js:38 basic info after content checked
test.component.js:44 basic info after view checked
test.component.js:26 basic info do check.
test.component.js:38 basic info after content checked
test.component.js:44 basic info after view checked
test.component.js:26 basic info do check.
test.component.js:38 basic info after content checked
test.component.js:44 basic info after view checked
```
================================================
FILE: Angular系列/03_Angular2的那些Decorator.md
================================================
---
title: 03_Angular2的那些Decorator
date: 2017/02/21 14:47:10
---
## 0、Decorator
``Decorator`` 是ECMAScript中建议的标准,使得我们可以在设计时对类和属性进行注解和修改。
## 1、Angular2的Decorator
在Angular2的早期版本(使用AtScript)中,我们是使用Annotation(注解),它以一个声明的方式将元数据添加到代码中。
在后来迁移到TypeScript的时候,我们可以使用 Decorator 和 Annotation 。作为使用者来说,使用 Decorator 和 Annotation 几乎是一样的,唯一的区别是我们没有去控制 Annotation 如何将元数据添加到我们的代码中,而 Decorator 是对 这些 Annotation 的最终实现。
从长远看,我们更应该多关注 Decorator ,因为它才是真正的标准建议。
## 2、Angular2的那些Decorator
### 2.1、In angular2/core
#### 2.1.1、Component
``Component`` 用于声明可重用的UI构建模块(组件),每个 Angular component都要求有一个 ``@Component`` 注解,它指定了组件何时被实例化,哪些属性和 hostListeners 被绑定。
当组件实现(implements)了一些生命周期钩子(lifecycle-hooks),那么将在特定的时间点访问这些钩子的回调函数。
**如何使用**
```javascript
@Component({
selector: 'demo', // 配置选择器
inputs: [],
outputs: [],
properties: [],
events: [],
host: {},
providers: [], // 设定所依赖的Providers(ng1中的service,provider,factory)
exportAs: '',
moduleId: '', //设定模块ID
viewProviders: [],
queries: {},
//changeDetection
templateUrl : '', // 指定模板文件URL,和template冲突
template: 'Hello {{name}}!', //指定模板内容,和templateUrl冲突
styleUrls: [], // 设定组件依赖的样式表文件
styles : [], //设定组件依赖的样式
directives: [], //设定所依赖的Directives(ng1中的directives)
pipes: [] //设定所依赖的Pipes(ng1中的filter)
//encapsulation
})
export class Demo {
private name: string = 'World';
}
```
**注:从继承关系来看,Component extends Directive。**
#### 2.1.2、Directive
``Directive`` 允许你在DOM元素上附加行为。如果指令带有内嵌视图,那么就成为了组件。
指令同样也有生命周期钩子。使用方式和 Component 雷同。
指令允许多种注入方式来实例化:
1、无注入 -- 该指令没有外部依赖
```javascript
// 空构造,无注入
@Directive({ selector: '[my-directive]' })
class MyDirective {
constructor() {
}
}
```
2、组件级别的注入 -- 该指令依赖一些外部服务
```javascript
import {User} from 'xxx';
@Directive({ selector: '[my-directive]' })
class MyDirective {
constructor(user: User) { //依赖外部服务
}
}
```
3、注入当前元素的其它指令 --该指令依赖当前元素上的其他指令,搭配其他指令一起使用
```javascript
import {User} from 'xxx';
@Directive({ selector: '[my-directive]' })
class MyDirective {
constructor(depDirective: DepDirective) { //依赖当前元素上的其他指令
}
}
```
```html
<div my-directive dep-directive>
</div>
```
4、注入当前元素、父元素、更上层的父元素上的指令 --该指令依赖上层元素的指令
```javascript
import {User} from 'xxx';
@Directive({ selector: '[my-directive]' })
class MyDirective {
//要使用 @Host()
constructor(@Host() depDirective: DepDirective) { //可以依赖父辈元素上的指令
}
}
```
```html
<div dep-directive>
<div my-directive></div>
</div>
```
5、注入直接子集集合元素的的指令 --该指令依赖直接子元素的指令
```javascript
import {User} from 'xxx';
@Directive({ selector: '[my-directive]' })
class MyDirective {
//使用 @Query<Type> ,依赖直接子元素上的指令
constructor(@Query(DepDirective) depDirective: QueryList<DepDirective>) {
}
}
```
```html
<div my-directive>
<p dep-directive></p>
<p dep-directive></p>
</div>
```
6、注入后代集合元素的指令 --该指令依赖后代元素的指令
```javascript
import {User} from 'xxx';
@Directive({ selector: '[my-directive]' })
class MyDirective {
//使用 @Query<Type> ,依赖直接子元素上的指令
constructor(@Query(DepDirective, {descendants: true}) depDirective: QueryList<DepDirective>) {
}
}
```
```html
<div my-directive>
<div>
<p dep-directive></p>
<p dep-directive></p>
</div>
</div>
```
7、可选注入 --该指令的依赖是可选的。
```
@Directive({ selector: '[my-directive]' })
class MyDirective {
// 使用 @Optional 标记,依赖是可选的。
constructor(@Optional() depDirective:DepDirective) {
}
}
```
**注:以上多种注入方式也适用于 Component 。**
#### 2.1.3、Injectable
``Injectable`` 允许使用注入。在编写组件/指令时,如果有注入,那么就需要将指令/组件标记为可注入的。
#### 2.1.4、Pipe
``Pipe`` 允许我们定义管道方法,实现ng1中filter类似的功能。
如何编写一个Pipe?
```javascript
@Pipe({name: 'lowercase'})
class Lowercase {
transform(v: string, args: any[]) { return v.toLowerCase(); }
}
```
### 2.2、In angular2/router
#### 2.2.1、CanActivate
``CanActivate`` 允许我们在使用路由时,检查组件的权限,来确定是否可以使用。
```javascript
@Component({selector: 'control-panel-cmp', template: `<div>Settings: ...</div>`})
@CanActivate(checkIfWeHavePermission)
class ControlPanelCmp {
}
```
#### 2.2.2、RouteConfig
``RouteConfig`` 用于我们配置路由。
使用如下:
```
@Component({
selector: 'dojo-app',
moduleId: module.id,
templateUrl: 'app.html',
styleUrls: ['app.css'],
directives: [ROUTER_DIRECTIVES, HeaderComponent]
})
@RouteConfig([
{path: '/', name: 'Home', component: HomeComponent},
{path: '/about', name: 'About', component: AboutComponent}
])
export class AppComponent{
constructor() {
}
}
```
## 3、参考
1. [https://angular.io/docs/ts/latest/api/index.html#!?apiType=Decorator](https://angular.io/docs/ts/latest/api/index.html#!?apiType=Decorator)
2. [https://angular.io/docs/ts/latest/api/index.html#!?apiFilter=metadata](https://angular.io/docs/ts/latest/api/index.html#!?apiFilter=metadata)
================================================
FILE: Angular系列/04_Angular2指令简析.md
================================================
---
title: 04_Angular2指令简析
date: 2017/02/21 14:47:10
---
## 0、Angular2指令
在Angular1中,就已经有了指令的概念。Angular1中的指令用于实现可复用UI部件,也用于操作dom元素。
那么在Angular2中的指令是一样的东西么?
Angular2中有组件的概念,指令这个东西就变得更加纯粹。
Angular2的指令有三种:
- 组件
- 属性指令
- 结构指令
组件是有模板的指令,是指令的中一个另类,因为它使用@Component来装饰,而不是@Directive。
属性指令用于改变现有元素的展现和行为,使用的时候它们看起来像是正常的HTML属性,所以称之为属性指令。如ngModel指令。
结构指令通过添加、删除和替换DOM树中的元素来改变布局,由于可以更改DOM结构,所以称之为结构指令。如ngIf,ngSwitch。
由于Angular2的API好不够稳定,书写该文时,采用的是Angular2 rc1(@angular rc.1)版本,其他版本请自行测试。
## 1、属性指令
接着,我们就一步步来实现一个属性指令 dynamicColor 。
首先,我们需要创建一个ts文件,然后把指令的骨架搭建起来。
```typescript
import {Directive} from '@angular/core';
@Directive({
selector: '[dynamicColor]'
})
export class DynamicColorDirective{
constructor(){
}
}
```
以上代码中,我们创建了一个dynamicColor指令。
接下来,我们来实现具体的功能,可以设置元素的背景色和前景色,并能实现事件通知。
要实现动态背景色和前景色,那我们需要额外附加两个属性bgColor和color。
要想在指令中获取这两个属性值,那么我们可以通过@Input方式或者是inputs属性,代码如下:
```typescript
import {Directive, Input} from '@angular/core';
@Directive({
selector: '[dynamicColor]'
})
export class DynamicColorDirective{
@Input()
private bgColor: string;
@Input()
private color: string;
constructor(){
}
}
```
或者是:
```typescript
import {Directive, Input} from '@angular/core';
@Directive({
selector: '[dynamicColor]',
//注意,在之前的版本中,使用properties属性,而且,当前还可以使用。
inputs: [
'bgColor: bgColor', //字符串以冒号隔开,前者是DynamicColorDirective的属性,后者的html元素的属性
'color: color'
]
})
export class DynamicColorDirective{
// @Input()
private bgColor: string;
// @Input()
private color: string;
constructor(){
}
}
```
那么html中又应该如何传递值给指令呢?
```html
<div class="test" dynamicColor [bgColor]="testBgColor" [color]="testColor">
Hi!
</div>
```
**注意:在html元素的属性上,我们可以有两种写法。一种是直接书写属性,此时会把属性值原样传递给指令。第二种是使用[属性],此时属性值应该是表达式(可以使用变量,判断等语句),传递给指令的是表达式的结果。**
我们又如何在后端查看这两个值呢?
直接在constructor中console?明确的说是不行的,因为constructor的代码会先于绑定执行。
这个时候,我们就需要借助指令的生命周期钩子。
指令的生命周期钩子有如下几个:
1. ngOnInit --初始化时
2. ngOnChanges -- 属性绑定之时(会有一次inputs属性绑定先于初始化)
3. ngDoCheck -- 执行属性检查时
4. ngOnDestroy -- 指令释放时
了解了生命周期钩子,我们就可以通过ngOnInit来查看绑定好的属性值了。
```typescript
import {Directive, Input, OnInit} from '@angular/core';
@Directive({
selector: '[dynamicColor]',
inputs: [
'bgColor: bgColor', //字符串以冒号隔开,前者是DynamicColorDirective的属性,后者的html元素的属性
'color: color'
]
})
export class DynamicColorDirective implements OnInit{
// @Input()
private bgColor: string;
// @Input()
private color: string;
constructor(){
}
ngOnInit(){
console.log('bgColor', this.bgColor);
console.log('color', this.color);
}
}
```
接下来,我们需要设置元素的background color和color样式,那么我们必须要拿到这个而元素的引用, 并在初始化之后进行绑定。
```typescript
import {Directive, Input, OnInit, ElementRef} from '@angular/core';
@Directive({
selector: '[dynamicColor]',
inputs: [
'bgColor: bgColor', //字符串以冒号隔开,前者是DynamicColorDirective的属性,后者的html元素的属性
'color: color'
]
})
export class DynamicColorDirective implements OnInit{
private nativeElement: any;
// @Input()
private bgColor: string;
// @Input()
private color: string;
constructor(el: ElementRef){
this.nativeElement = el.nativeElement;
}
private _setElementStyle(): void{
this.nativeElement.style.backgroundColor = this.bgColor;
this.nativeElement.style.color = this.color;
}
ngOnInit(){
console.log('bgColor', this.bgColor);
console.log('color', this.color);
this._setElementStyle();
}
}
```
当从元素上绑定的属性变化时,又应该从哪里获取到变更呢?这就需要借助生命周期里面的OnChanges函数,代码如下:
```typescript
import {Directive, Input, ElementRef, OnInit, OnChanges} from '@angular/core';
@Directive({
selector: '[dynamicColor]',
inputs: [
'bgColor: bgColor', //字符串以冒号隔开,前者是DynamicColorDirective的属性,后者的html元素的属性
'color: color'
]
})
export class DynamicColorDirective implements OnInit, OnChanges{
private nativeElement: any;
// @Input()
private bgColor: string;
// @Input()
private color: string;
constructor(el: ElementRef){
this.nativeElement = el.nativeElement;
}
private _setElementStyle(): void{
this.nativeElement.style.backgroundColor = this.bgColor;
this.nativeElement.style.color = this.color;
}
ngOnInit(){
console.log('bgColor', this.bgColor);
console.log('color', this.color);
this._setElementStyle();
}
ngOnChanges(){
console.log('bgColor-change', this.bgColor);
console.log('color-change', this.color);
this._setElementStyle();
}
}
```
由于每次变化都会触发OnChanges,那么为了提高性能,我们可以在这里加入一个节流函数。
```typescript
private _setElementStyle(): void {
clearTimeout(this.timeoutId);
this.timeoutId = setTimeout(() => {
this.nativeElement.style.backgroundColor = this.bgColor;
this.nativeElement.style.color = this.color;
}, 500);
}
```
有了节流函数,我们就不太确定到底执行了几次更新操作了。这个时候,我们可以加入事件通知。这就涉及到指令的@Output了。
```typescript
@Output()
private updated: EventEmitter<any> = new EventEmitter();
```
也等同于:
```typescript
outputs: [
'updated: updated'
]
private updated: EventEmitter<any> = new EventEmitter();
```
**注意:在之前的版本中,也可以用events属性来替代outputs,现在也还可以使用**
要对外发出通知,只需要使用如下代码:
```typescript
this.updated.emit('updated');
this.updated.next('updated2');
```
HTML标签使用时,代码如下:
```html
<div class="test" dynamicColor [bgColor]="testBgColor" [color]="testColor" (updated)="notify($event)">
Hi!
</div>
```
至此,我们这个指令就已经完成了,所有代码如下:
```typescript
//dynamicColor.directive.ts
import {Directive, Input, Output, ElementRef, EventEmitter, OnInit, OnChanges} from '@angular/core';
@Directive({
selector: '[dynamicColor]',
inputs: [
'bgColor: bgColor', //字符串以冒号隔开,前者是DynamicColorDirective的属性,后者的html元素的属性
'color: color'
],
outputs: [
'updated: updated'
]
})
export class DynamicColorDirective implements OnInit, OnChanges {
private nativeElement: any;
private timeoutId: any;
// @Input()
private bgColor: string;
// @Input()
private color: string;
// @Output()
private updated: EventEmitter<any> = new EventEmitter();
constructor(el: ElementRef) {
this.nativeElement = el.nativeElement;
}
private _setElementStyle(): void {
clearTimeout(this.timeoutId); //先清除已有的timeout
//保证只执行最后一次。
this.timeoutId = setTimeout(() => {
this.nativeElement.style.backgroundColor = this.bgColor;
this.nativeElement.style.color = this.color;
this.updated.emit('updated');
this.updated.next('updated2');
}, 500);
}
ngOnInit() {
console.log('bgColor', this.bgColor);
console.log('color', this.color);
this._setElementStyle();
}
ngOnChanges() {
console.log('bgColor-change', this.bgColor);
console.log('color-change', this.color);
this._setElementStyle();
}
}
```
```html
//test.html
<h1>Dynamic Color Directive</h1>
<input type="text" [(ngModel)]="testBgColor">
<div class="test" dynamicColor [bgColor]="testBgColor" [color]="testColor" (updated)="notify($event)">
Hi!
</div>
```
```typescript
//test.component.ts
export class TestComponent{
private testBgColor: string = 'blue';
private testColor: string = 'red';
constructor(){
}
private test(data){
console.log('data', 'my', data);
}
private notify(data){
console.log('notify = ', data);
}
}
```
*思考一下?我们还有没有更简单的方式实现以上的效果呢?*
```typescript
import {Directive, EventEmitter} from '@angular/core';
@Directive({
selector: '[dynamicColor]',
inputs: [
'bgColor: bgColor', //字符串以冒号隔开,前者是DynamicColorDirective的属性,后者的html元素的属性
'color: color'
],
outputs: [
'updated: updated'
],
host: {
'[style.backgroundColor]': 'bgColor',
'[style.color]': 'color'
}
})
export class DynamicColorDirective {
private bgColor: string;
private color: string;
private updated: EventEmitter<any> = new EventEmitter();
constructor() {
}
}
```
通过host直接在元素上添加绑定。
## 2、结构指令
结构指令帮助我们修改dom结构,我们就简单实现一个templateInclude指令。
```typescript
import {Directive, Input, Output, ElementRef, EventEmitter, OnChanges} from '@angular/core';
import {Http} from '@angular/http';
@Directive({
selector: '[templateInclude]'
})
export class TemplateIncludeDirective implements OnChanges {
private nativeElement: any;
@Input('templateInclude')
private templateUrl: string;
@Output()
private loaded: EventEmitter<any> = new EventEmitter();
constructor(el: ElementRef, private http: Http) {
this.nativeElement = el.nativeElement;
}
private _setTemplate() {
this.http.get(this.templateUrl)
.subscribe(res => {
this.nativeElement.innerHTML = res.text();
this.loaded.next(`${this.templateUrl} loaded`);
});
}
ngOnChanges() {
console.log(this.templateUrl);
this._setTemplate();
}
}
```
## 3、总结
**1、指令的元数据有很多属性可以使用**
```typescript
class DirectiveMetadata {
selector : string //指令使用的标记(选择器)
inputs : string[] //输入参数绑定
properties : string[] //属性绑定(过期,请使用inputs)
outputs : string[] //输出参数绑定
events : string[] //事件绑定(过期,请使用outputs)
host : {[key: string]: string} //宿主元素属性设置
providers : any[] //服务绑定
bindings : any[] //服务绑定(过期,请使用providers)
exportAs : string //导出名称
queries : {[key: string]: any} //用于指令依赖关系
}
```
**2、在使用指令(不仅仅是指令)进行绑定的时候,[]表示输入属性,()表示输出属性和事件**
**3、尽量使用统一的做法,用装饰器优于在属性上做绑定**
**4、在编写指令(不仅限于指令)时,将class中内容按照特定顺序进行排列,推荐顺序如下(个人建议,仅供参考):**
1. 私有变量
2. 共有变量
3. @Input变量
4. @Output变量
5. 构造函数
6. 私有方法(建议下划线开头)
7. 公有方法
8. 生命周期钩子方法
================================================
FILE: Angular系列/05_Angular2组件简析.md
================================================
---
title: 05_Angular2组件简析
date: 2017/02/21 14:47:10
---
## 0、Angular2组件
**注:由于Angular2的API好不够稳定,书写该文时,采用的是Angular2 rc1(@angular rc.1)版本,其他版本请自行测试。**
在上篇中,我们已经讲到了指令,这篇呢,我们一起来看看Angular2组件是怎么一回事。
首先,组件也是指令,组件是一种有模板(内嵌视图)的特殊指令。
从元数据[指令源代码](https://github.com/angular/angular/blob/2.0.0-rc.1/modules/%40angular/core/src/metadata/directives.ts)中也可以看出组件与指令的关系:
```typescript
export class ComponentMetadata extends DirectiveMetadata {
}
```
相比 ``Directive``, ``Component`` 新增了一些属性,如下:
```typescript
{
changeDetection : ChangeDetectionStrategy; 定义变化检测类型
viewProviders: any[]; 用于在组件中注入特定的class。一般是实体类
moduleId: string; 定义主键的ID
templateUrl: string; 如ng1,外部模板地址
template: string; 如ng1,内嵌模板内容
styleUrls: string[]; 外部样式表文件
styles: string[]; 内嵌样式
directives: Array<Type | any[]>; 使用到的指令
pipes: Array<Type | any[]>; 使用到的管道
encapsulation: ViewEncapsulation 封装视图的类型
}
```
## 1、组件生命周期
既然组件也是指令,那么指令所拥有的四大阶段组件也同样拥有。
而且,由于组件带有视图,还多了几个和视图相关的生命周期阶段。如下:
1. ngAfterContentInit 组件内容渲染到页面之后触发
2. ngAfterContentChecked 检查组件内容绑定数据后触发
3. ngAfterViewInit 创建组件视图之后触发
4. ngAfterViewChecked 检查组件视图绑定数据后触发
它们的执行顺序也和以上顺序一致。
## 2、整一个组件试试?
接下来,我们就简单实现一个组件 ``TodoList`` 来实验一下以上的知识点。
首先,搭建好一个简单的架子,如下:
```typescript
import {Component} from '@angular/core';
@Component({
selector: 'todo-list'
})
export class TodoListComponent{
constructor(){
}
}
```
使用组件装饰器 ``Component`` 来定义一个组件,注意其中 ``selector`` 属性和 ``Directive`` 中的写法不一样了。
组件必须以标签的方式存在,所以 selector 属性值仅仅只需要写标签名就可以了,不再需要其他特别的符号了。
组件和指令最大的差别就在于模板,所以我们接下来添加上模板代码:
```typescript
import {Component} from '@angular/core';
@Component({
selector: 'todo-list',
template: `
<div class="todo-list">
<h1>Todo List</li>
<ul>
<li></li>
</ul>
</div>
`
})
export class TodoListComponent{
constructor(){
}
}
```
如上,一个简单的模板就搞好了。这里需要注意 ``templateUrl`` 和 ``template`` 是互斥的两个属性。一般来说只选择一个赋值,如果两者都存在,那么会采用 ``tempalte`` 的值。
模板有了,我们就来点业务逻辑:
先假设Todo有三个状态:
```typescript
enum TodoStatus {
Open,
Processing,
Closed
}
```
在来定义Todo的实体类:
```typescript
class Todo {
private name: string;
private description: string;
private status: TodoStatus;
constructor(name: string, status: TodoStatus, description?: string) {
this.name = name;
this.status = status;
this.description = description || '';
}
}
```
接着来实现一个组件:
```typescript
@Component({
selector: 'todo-list',
template: require('./todo-list.component.html')
})
export class TodoList {
private todos: Array<Todo>;
private todo: { name: string, desc: string } = { name: '', desc: '' };
constructor() {
this.todos = [];
}
addTodo() {
this.todos.push(new Todo(this.todo.name, TodoStatus.Open, this.todo.desc));
}
}
```
这个时候,HTML页面内容如下:
```html
<div *ngIf="todos.length === 0">No todos.</div>
<ul class="todo-list">
<li *ngFor="let todo of todos">
{{todo.name}} - {{todo.description}}
</li>
</ul>
<hr>
<div class="todo-edit">
Name: <input type="text" [(ngModel)]="todo.name">
<br>
Description: <br>
<textarea name="" id="" cols="30" rows="3" [(ngModel)]="todo.desc"></textarea>
<br>
<button (click)="addTodo()">Add Todo</button>
</div>
```
功能做好了,我们得给它来点样式美化。
此时我们仅仅需要实现一点样式:
```css
.todo-list{
margin: 0;
padding: 0;
}
.todo-list li{
list-style: none;
border: 1px solid red;
}
```
然后在组件装饰器中申明就可以了:
```typescript
@Component({
selector: 'todo-list',
template: require('./todo-list.component.html'),
styles : [require('./todo-list.component.css')]
})
```
至此,一个简单的可以添加todo的todo-list就已经完成了。
**注意:注入@Input,@Output之类的和Directive都是一样的,此处就不再演示了。**
================================================
FILE: Angular系列/06_Angular2管道(Pipe)简析.md
================================================
---
title: 06_Angular2管道(Pipe)简析
date: 2017/02/21 14:47:10
---
## 0、Angular2 Pipe
**注:由于Angular2的API好不够稳定,书写该文时,采用的是Angular2 rc1(@angular rc.1)版本,其他版本请自行测试。**
对于 ``Pipe``,其实我们并不陌生。在angular1中,它被称之为 ``filter``。
``Pipe``用于对数据进行格式化处理,就好比管道,一个进一头出,中间过程就是管道的处理逻辑。
Angular2中的 ``Pipe`` 本质上是包含特定方法的类。
## 1、编写一个简单Pipe
``Pipe`` 相对于指令和组件来说,非常简单,我们仅仅需要编写一个类:
```typescript
class EmptyToZero{
}
```
实现特定的方法:
```typescript
exprt class EmptyToZero{
transform(v: any, args: any[]){
if(v === undefined || v === null || v === ''){
return 0;
}
return v;
}
}
```
使用 ``Pipe`` 装饰,请设定名称:
```typescript
import {Pipe} from '@angular/core';
@Pipe({ name: 'empty2zero' })
export class EmptyToZero {
transform(v: any, args: any[]) {
if (v === undefined || v === null || v === '') {
return 0;
}
return v;
}
}
```
如何使用?
```html
<span>{{ value | empty2zero}}</span>
```
当然前提是要在Component中申明要使用的Pipe:
```typescript
@Component({
selector: 'todo-list',
template: require('./todo-list.component.html'),
styles : [require('./todo-list.component.css')],
pipes:[EmptyToZero]
})
```
## 2、有状态的Pipe
在使用 ``Pipe`` 装饰器的时候,我们可以提供两个参数:
```typescript
@Pipe({
name: 'empty2zero', //string类型,必填项,指定pipe的名称
pure: true //boolean类型,可选项,默认为true,设定为true时,表示无状态管道。无论是输入或者是什么参数的改变都会触发重新计算结果。
})
```
有状态的 ``Pipe`` 可以收到一个Promise对象或者检测输入和自动订阅输入,最终返回一个可触发的值。
要使用有状态的管道,必须将pure属性设置为false。
比较典型的有状态异步管道如下:
```typescript
import {Pipe} from 'angular2/core';
@Pipe({
name: 'fetch',
pure: false
})
export class FetchJsonPipe {
private fetchedValue:any;
private fetchPromise:Promise<any>;
transform(value:string, args:string[]):any {
if (!this.fetchPromise) {
this.fetchPromise = window.fetch(value)
.then((result:any) => result.json())
.then((json:any) => this.fetchedValue = json);
}
return this.fetchedValue;
}
}
```
================================================
FILE: Angular系列/07_Angular2使用路由.md
================================================
---
title: 07_Angular2使用路由
date: 2017/02/21 14:47:10
---
## 0、关于路由
此处所说的路由是指URL路由(也许叫URL Rewrite)。其实是把网址(URL)映射到相关Controller、Component的这个功能。
Angular2的路由其实也就是URL路由,在Angular2中,有两个模块提供了路由功能,``@angular/router-deprecated`` 和 ``@angular/router``。
``@angular/router-deprecated`` 从名称也可以看出,它是一个过时的模块(beta版本中它的名字是 ``angular2/router``,在rc版本被更名)。但疑惑的是,它一直存在于 ``@angular`` 包中。在这里,我主要使用 ``@angular/router`` 来实现路由功能。
## 1、使用Angular2路由
建议在根组件中配置路由。要使用路由,必须先依赖 ``ROUTER_PROVIDERS``;如果要使用路由指令,必须先依赖 ``ROUTER_DIRECTIVES``。
大概结构如下:
```typescript
//bootstrap.ts
import {provide} from '@angular/core';
import {bootstrap} from '@angular/platform-browser-dynamic';
import {LocationStrategy, HashLocationStrategy} from '@angular/common';
import {AppComponent} from './app/app.component';
bootstrap(AppComponent, [
provide(LocationStrategy, { useClass: HashLocationStrategy })
]);
```
```typescript
//app.component.ts
import {Component, provide} from '@angular/core';
import {Routes, ROUTER_DIRECTIVES, ROUTER_PROVIDERS} from '@angular/router';
import {HomeComponent} from './../home/home.component';
import {AboutComponent} from './../about/about.component';
@Component({
selector: 'demo-app',
template: `
<h3>Angular2 Router Test</h3>
<a [routerLink]="['/home']">Home</a>
<a [routerLink]="['/about']">About</a>
<router-outlet></router-outlet>
`,
directives: [ROUTER_DIRECTIVES],
providers: [ROUTER_PROVIDERS]
})
@Routes([
{ path: '/home', component: HomeComponent},
{ path: '/about', component: AboutComponent }
])
export class AppComponent {
constructor() {
console.log('app init');
}
}
```
通过 ``@Routes``,我们可以配置路由对应的组件。当然,要求这些组件必须已经存在。在配置路由节点的时候,我们仅仅需要提供 ``path`` 和 ``component`` 参数。
在模板中使用 ``[routerLink]`` 可以配置连接,它的值是一个数组,第一个元素是要导向的url,第二个参数是路由参数。
除了使用 ``[routerLink]`` 实现路由跳转,还可以使用特定服务来跳转,代码如下:
```typescript
this.router.navigate(['/about']);
this.router.navigateByUrl('/about');
```
当URL比较复杂,如/home/test/index时,使用navigate方式如下:
```typescript
this.router.navigate(['home', 'test' ,'index']);
```
## 2、路由参数
有时候,我们需要给路由传递一些参数,这个时候就需要在配置路由的时候,指定参数。使用 ``[routerLink]`` 的方式如下:
```html
<a [routerLink]="['/about', {id: 1}]">About</a>
```
那么如何获取这个参数呢?就需要在 ``AboutComponent`` 组件中通过 ``RouteSegment`` 来获取,代码如下:
```typescript
export class AboutComponent {
constructor(private routeSegment: RouteSegment) {
console.log('about init', 'params:', routeSegment.parameters);
}
}
```
## 3、嵌套路由(子路由)
一般来说,我们写一个中大型的应用程序,一个一级路由根本就不够使用。这个时候,就可以使用嵌套路由来把应用程序拆分成很多小的模块。
在这种情况下,就需要我们的子组件也需要使用 ``@Routes`` 来申明自己的路由体系。
```typescript
//about.component.ts
import {Component} from '@angular/core';
import {ROUTER_DIRECTIVES, Routes, RouteSegment} from '@angular/router';
import {AboutUserComponent} from './about-user.component';
import {AboutMeComponent} from './about-me.component';
@Component({
selector: 'demo-about',
template: `
<h1>About</h1>
<a [routerLink]="['/home']">Go to Home</a>
<a [routerLink]="['./user', id]">Go to About User</a>
<a [routerLink]="['./me']">Go to About Me</a>
<router-outlet></router-outlet>
`,
directives: [ROUTER_DIRECTIVES]
})
@Routes([
{ path: '/', component: AboutUserComponent },
{ path: '/user/:id', component: AboutUserComponent },
{ path: '/me', component: AboutMeComponent }
])
export class AboutComponent {
private id: number = 1;
constructor(private routeSegment: RouteSegment) {
console.log('about init', 'params:', routeSegment.parameters);
}
}
```
在这段代码里,我们需要注意,``[routerLink]``的值,有些是 ``['/home']``, 也有 ``['./me]`` 这种,它们有什么区别呢?
其实直接 '/home' 是指从根路径开始计算,也就是跳转到父路由,真实路径就是 /home。如果是使用的 './me' 这种形式,那么是相对路径,所以点击这个链接,跳转到的实际上是 /about/me 这个地址。
## 4、生命周期钩子(拦截器)
在路由中,我们可以通过实现 ``CanDeactivate`` 来控制路由是否可以被解除;还可以通过实现 ``OnActivate`` 来控制路由激活后的操作。
当我们在 ``HomeComponent`` 中编写如下语句时:
```typescript
routerCanDeactivate(curTree: RouteTree, futureTree: RouteTree) {
console.log('abc');
return new Promise((resolve, reject) => {
resolve(false);
});
}
```
当进入Home页之后,我们就已经无法跳出了。
当我们在 ``HomeComponent`` 中编写如下语句时:
```typescript
routerOnActivate(currSegment: RouteSegment, prev: RouteSegment, currTree: RouteTree, prevTree: RouteTree){
console.log('succeed');
}
```
每一次跳转到Home组件,我们都能在控制台看到输出 succeed 。
**很遗憾的是,暂时没有发现有全局的路由钩子,这也就意味着,我们没法在一个地方控制所有的路由是否允许被解除。**
## 5、后记
以上就是Angular2路由的简单使用了。相关Demo,请点击 [这里](https://github.com/hstarorg/HstarDemoProject/tree/master/angular2_demo)
在Angular2中,还有一个路由是 ``@angular/router-deprecated``,它是之前的路由方式,由于已经被标注为过期,这里就不在说明,如果想了解一下,可以查看 [Demo](https://github.com/hstarorg/HstarDemoProject/tree/master/angular2_demo/angular2-router-deprecated-test)
至于更复杂的动态路由,动态加载组件等等,未完待续...
================================================
FILE: Angular系列/08_Angular2动态加载组件.md
================================================
---
title: 08_Angular2动态加载组件
date: 2017/02/21 14:47:10
---
## 0、为什么需要动态加载
在一个比较大的应用程序中,我们不可能将所有的业务逻辑一次性加载出来,比较浪费资源,因为单个用户一般用不到所有的功能,这个时候,就需要部分组件动态加载了。
## 1、Angular2如何动态加载组件
在Angular2,有一个服务是 ``DynamicComponentLoader``,我们就可以通过它来进行动态加载组件。
首先要使用它的话,我们必须要在providers中指定它。
另外,它还有一些必须的依赖,是 ``injector``,当引入了这些元素之后,我们就可以实现一个加载组件的方法:
```javascript
class AppComponent {
constructor(dynamicComponentLoader, viewContainerRef, injector) {
this.dynamicComponentLoader = dynamicComponentLoader;
this.viewContainerRef = viewContainerRef;
this.injector = injector;
}
//动态加载组件的方法,需要传入一个组件
loadComponent(component) {
this.viewContainerRef.clear();
this.dynamicComponentLoader.loadAsRoot(component, '#component-container', this.injector)
.then((componentRef) => {
//必须要这样来编写,否则会导致双向绑定表达式获取不到值。
componentRef.changeDetectorRef.detectChanges();
componentRef.onDestroy(() => {
componentRef.changeDetectorRef.detach();
});
return componentRef;
});
}
}
```
在以上代码中,核心就是 ``dynamicComponentLoader`` 的 ``loadAsRoot`` 方法,这个方法里面有个参数是 ``'#component-container'``,实际上指定将动态加载的组件放置的容器,
```html
<div id="content">
<child id="component-container"></child>
</div>
```
通过这样的方式,我们就能够动态的把组件加载到页面上了。
**注:``dynamicComponentLoader`` 还有一个加载方法是 ``loadNextToLocation(component, viewContainerRef)``,只需要提供要动态加载的组件和一个容器引用,就可以将组件加载到容器中了。**
================================================
FILE: Angular系列/09_Angular2使用ui-router-ng2.md
================================================
---
title: 09_Angular2使用ui-router-ng2
date: 2017/02/21 14:47:10
---
## 0、导言
Angular2的路由组件从beta到rc经历了多次变更,知道rc.4都没有完全稳定下来。其次它的功能也并不强大,全局钩子,动态加载,状态控制都不支持。
如果是使用Angular1,那这个时候我们一般会选择 ``ui-router`` 这一个强大的基于状态的路由。
其实,``ui-router`` 也提供了一个Angular2的版本,那就是 ``ui-router-ng2``。
我们就来简单的尝试下它的使用,和利用它来实现动态加载一批组件(结合webpack)。
## 1、引入 ``ui-router-ng2``
要使用 ``ui-router-ng2``,我们必须要先通过 ``npm install ui-router-ng2`` 来安装该包。
安装成功之后,我们需要实现一个 ``UIRouterConfig`` 的实例,代码如下:
```typescript
import { Injectable } from '@angular/core';
import { UIRouter, UIRouterConfig } from 'ui-router-ng2';
import {AboutComponent} from './about.component';
@Injectable()
export class AppRouterConfig implements UIRouterConfig {
constructor() {
}
configure(uiRouter: UIRouter) {
uiRouter.stateRegistry.register({
name: 'about',
component: AboutComponent,
url: '/about'
});
}
}
```
一般做法,我们需要在 ``configure`` 方法中,注册路由状态对象。
当实现了 ``UIRouterConfig`` 之后,我们就可以在应用启动时来使用它了,具体代码如下:
```typescript
import { enableProdMode, provide, PLATFORM_DIRECTIVES } from '@angular/core';
import { APP_BASE_HREF, LocationStrategy, HashLocationStrategy, PlatformLocation } from '@angular/common';
import { BrowserPlatformLocation } from '@angular/platform-browser';
import { bootstrap } from '@angular/platform-browser-dynamic';
import { UIROUTER_PROVIDERS, UIRouterConfig, UIROUTER_DIRECTIVES } from 'ui-router-ng2';
import { RootComponent } from './../shell';
import { AppRouterConfig } from './routes';
enableProdMode();
bootstrap(RootComponent, [
provide(APP_BASE_HREF, { useValue: '/' }),
provide(LocationStrategy, { useClass: HashLocationStrategy }),
provide(PlatformLocation, { useClass: BrowserPlatformLocation }),
...UIROUTER_PROVIDERS,
provide(UIRouterConfig, { useClass: AppRouterConfig }),
provide(PLATFORM_DIRECTIVES, { useValue: UIROUTER_DIRECTIVES, multi: true })
])
.then(x => {
console.log('app started...');
})
.catch(error => console.log(error));
```
其中最关键是和路由相关的代码是:
```typescript
...UIROUTER_PROVIDERS, //依赖路由提供者
provide(UIRouterConfig, { useClass: AppRouterConfig }), //指定RouterConfig
provide(PLATFORM_DIRECTIVES, { useValue: UIROUTER_DIRECTIVES, multi: true }) //依赖路由指令
```
在经过这些步骤之后,我们的项目就已经可以使用 ``ui-router-ng2`` 来进行路由管理了。
## 2、路由钩子
``ui-router-ng2`` 提供了很强大的路由钩子函数,可以让我们很方便的对路由的各个阶段进行控制。
使用方式如下:
```typescript
import {Injectable, Inject} from '@angular/core';
import {UIRouter, UIRouterConfig} from 'ui-router-ng2';
import {AboutComponent} from './about.component';
@Injectable()
export class AppRouterConfig implements UIRouterConfig {
constructor() {
}
configure(uiRouter: UIRouter) {
uiRouter.stateRegistry.register({
name: 'about',
component: AboutComponent,
url: '/about'
});
// 以下t均为Transition实例
uiRouter.transitionService.onBefore({}, t => {
console.log('onBefore', t); //路由跳转之前
});
uiRouter.transitionService.onStart({}, t => {
console.log('onStart', t); //路由跳转开始
});
uiRouter.transitionService.onExit({}, t => {
console.log('onExit', t); //路由跳出时
});
uiRouter.transitionService.onRetain({}, t => {
console.log('onRetain', t); //...
});
uiRouter.transitionService.onEnter({}, t => {
console.log('onEnter', t); //路由进入时
});
uiRouter.transitionService.onFinish({}, t => {
console.log('onFinish', t); //路由跳转完成
});
uiRouter.transitionService.onSuccess({}, t => {
console.log('onSuccess', t); //路由跳转成功
});
uiRouter.transitionService.onError({}, t => {
console.log('onError', t); //路由跳转出错
});
}
}
```
通过以上的各个阶段,我们可以灵活控制跳转是否继续,每个钩子函数都接受 ``boolean`` 和 ``Promise<boolean>``来让我们确定是否跳转。
## 3、自定义回调处理invalidState
通过以上的方式,我们实现了状态路由,也实现了路由跳转的控制。接下来,我们另辟蹊径来实现动态加载。
由于当我们请求不合法的state时,uiRouter都会执行到 ``invalidCallbacks`` 这个函数,我这里就通过它加载动态模块。
实现代码如下:
```typescript
configure(uiRouter: UIRouter) {
... //省略不相关代码
uiRouter.stateProvider.invalidCallbacks = [($from$, $to$) => {
return new Promise((resolve, reject) => {
let toStateName = $to$.name();
let moduleName = this._getModuleName(toStateName);
if (!moduleName) {
return;
}
this.moduleLoader.load(moduleName).then(_ => {
let state = uiRouter.stateService.target(toStateName);
resolve(state);
});
});
}];
}
```
接着,再来看看moduleLoader的代码:
```typescript
import { Injectable, Inject } from '@angular/core';
import {Http} from '@angular/http';
import {UIRouter} from 'ui-router-ng2';
@Injectable()
export class ModuleLoader {
private uiRouter: UIRouter;
private loadedModules: Set<string>;
constructor(private http: Http) {
this.loadedModules = new Set<string>();
}
setRouter(uiRouter) {
this.uiRouter = uiRouter;
}
load(moduleName): Promise<any> {
if (this.loadedModules.has(moduleName)) {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
this.http.get(`../dist/assets/js/${moduleName}.js`)
.toPromise()
.then(res => {
let mod = eval(res.text());
mod.MODULE_STATES.forEach(state => {
this.uiRouter.stateRegistry.register(state);
});
this.loadedModules.add(moduleName);
resolve();
}).catch(err => reject(err));
});
}
}
```
通过请求指定的文件,然后利用eval执行出具体的state数组,通过register方法,动态注入到我们的ui-router中。
**注意invalidCallbacks回调中的参数($from$, $to$),必须使用这两个名字,通过分析源代码发现它是用参数名做了匹配的,如果换成其他名称,会提示注入错误。**
**invalidCallbacks回调中的逻辑非常关键,演示了如何获取原始要跳转的state,也演示了如何恢复继续跳转。**
## 4、更多待探索
当前对 ``ui-router-ng2`` 的探索还不够多,另外它本身也还在beta版本,该处理方式应该还有优化空间。
================================================
FILE: Angular系列/Angular2踩坑大全.md
================================================
---
title: Angular2踩坑大全
date: 2017/02/21 14:47:10
---
## Angular2的那些坑
1、同样的代码,引用Rxjs库的版本不对,就会导致在IE11下无法运行。(特定版本下重现)
正确的版本:[https://code.angularjs.org/2.0.0-beta.12/Rx.js](https://code.angularjs.org/2.0.0-beta.12/Rx.js)
2、在使用TypeScript编写Angular2代码时,一定要将注意 ``tsconfig.json``,其中 ``experimentalDecorators`` 和 ``emitDecoratorMetadata`` 必须要设置为true,否则无法使用依赖注入。
3、<router-outlet> 不能放在带有 *ngIf的容器内,否则会出现初始化时无法找到。
================================================
FILE: Angular系列/利用Angular实现多团队模块化SPA开发框架.md
================================================
---
title: 利用Angular实现多团队模块化SPA开发框架
date: 2017-11-23 11:11:11
---
# 0、前言
当一个公司有多个开发团队时,我们可能会遇到这样一些问题:
1. 技术选项杂乱,大家各玩各
2. 业务重复度高,各种通用api,登录注销,权限管理都需要重复实现(甚至一个团队都需要重复实现)
3. 业务壁垒,业务之间的互通变得比较麻烦
4. 部署方式复杂,多个域名(或IP地址)访问,给用户造成较大的记忆难度
5. 多套系统,风格难以统一
6. 等等...
当然,解决方式有不少。以下就来讲解下我们这边的一种解决方案。
# 1、思路
**Angualr**
`Angular`(注:非AngularJS) 是流行的前端 `MVVM` 框架之一,配合 `TypeScript`,非常适合用来做后台管理系统。由于我们曾今的一套 `Angularjs` 开发框架,我们继续选择 `Angular` 来进行实现,并尽可能的兼容 `AngularJS` 的模块。
**SPA**
选 `SPA` 还是多页?多余 `Mvvm` 来说,多页并不是标配。而且多页开发中,我们势必会关注更多的内容,包括通用header,footer,而不仅仅是页面的核心内容。
**模块化**
为什么要模块化呢?当有多个团队开发时(或者项目较大时),我们希望各个团队开发出来的东西都是 `模块`(不仅限于JS模块),这样可以让我们独立发布、更新、删除模块,也能让我们的关注点集中在特定模块下,提高开发效率和可维护性。
**平台化**
我们需要有一个运行平台(Website站点),允许在里面运行指定的模块。这样就可以实现单一入口,也容易实现通用逻辑,模块共享机制等等。
**兼容 AngularJS 模块**
在考虑将框架切换到 `Angular` 时,我们无可避免的会遇到如何兼容当前已有模块的问题。大致可选的方案如下:
1. 参考 `AngualrJS -> Angular` 官方升级指南,一步步将模块切换为 `Angular` 的实现。(工作量大,需要开发团队调整很多东西)
2. `iframe嵌入`,会有一定的体验差异,但对开发团队来说,基本无缝升级,也不需要做什么改动。(无疑,我们选择了这套方案)
**模块打包**
我们需要将单个的模块打包为资源包,进行更新。这样才能做到模块独立发布,及时生效。
**CSS冲突**
在大型 `SPA` 中,CSS冲突是很大的一个问题。我们期望通过技术手段,能够根据当前使用的模块,加载和卸载CSS。
**跨页面共享数据**
由于涉及到iframe兼容旧有模块,我们无可避免,需要考虑跨窗口的页面共享。
**公共模块**
当一个团队的模块较多时,就会有一些公共的东西被抽取出来,这个过程,框架是无法知道的,所以这个时候,我们就需要考虑支持公共模块。(模块之间也有依赖关系)
# 3、实现
基于以上的一些思考,我们首先需要实现一个基础的平台网站,这个没什么难度,直接用 `Angular` 实现即可。有了这一套东西,我们的登录注销,基本的菜单权限管理,也就实现了。
在这个基础之上,我们也能实现公共服务、公共组件了(封装一系列常用的玩意)。
## 如何模块化?如何打包?
**注意:此模块并非Angular本身的模块。** 我们通过约定,在 `modules/` 下的每一个目录都是一个业务模块。一个业务模块一般会包含,静态资源、CSS以及JS。根据这个思路,我们的打包策略就是:遍历 `modules/` 的所有目录,对每一个目录进行单独打包(webpack多entry打包+CSS抽取),另外使用 `gulp` 来处理相关的静态资源(在我看来,gulp才是构建工具,webpack是打包工具,所以混合使用,物尽其用)。
一般来说,`webpack` 会把所有相关依赖打包在一起,A、B 模块都依赖了 `@angular/core` 识别会重复打包,而且框架中,也已经打包了 `@angular` 相关组件。这个时候,常规的打包配置就不太合适了。那该如何做呢?
考虑到 `Angular` 也提供了 `CDN` 版本,所以我们将 `Angular` 的组件通过文件合并,作为全局全量访问,如 `ng.core`、`ng.common` 等。
既然这样,那我们打包的时候,就可以利用 `webpack` 的 `externals` 功能,把相关依赖替换为全局变量。
```js
externals: [{
'rxjs': 'Rx',
'@angular/common': 'ng.common',
'@angular/compiler': 'ng.compiler',
'@angular/core': 'ng.core',
'@angular/http': 'ng.http',
'@angular/platform-browser': 'ng.platformBrowser',
'@angular/platform-browser-dynamic': 'ng.platformBrowserDynamic',
'@angular/router': 'ng.router',
'@angular/forms': 'ng.forms',
'@angular/animations': 'ng.animations'
}
```
这样处理之后,我们打包后的文件,也就不会有 `Angular` 框架代码了。
**注:这个对引入资源的方式也有一定要求,就不能直接引入内层资源了。**
## 如何动态加载模块
打包完成之后,这个时候就要考虑平台如何加载这些模块了(发布过程就不说了,放到指定位置即可)。
什么时候决定加载模块呢?其实是访问特定路由的时候,所以我们的顶级路由,会使用Promise方法来实现,如下:
```js
const loadModule = (moduleName) => {
return () => {
return ModuleLoaderService.load(moduleName);
};
};
const dynamicRoutes = [];
modules.forEach(item => {
dynamicRoutes.push({
path: item.path,
canActivate: [AuthGuard],
canActivateChild: [AuthGuard],
loadChildren: loadModule(item.module)
});
});
const appRoutes: Routes = [{
path: 'login', component: LoginComponent
}, {
path: 'logout', component: LogoutComponent
}, {
path: '', component: LayoutComponent, canActivate: [AuthGuard],
children: [
{ path: '', component: HomeComponent },
...dynamicRoutes,
{ path: '**', component: NotFoundComponent },
]
}];
```
我们把每个模块,按照 `umd` 的格式进行打包。然后再需要使用该模块的时候,使用动态构建 `script` 来运行脚本。
```js
load(moduleName, isDepModule = false): Promise<any> {
let module = window['xxx'][moduleName];
if (module) {
return Promise.resolve(module);
}
return new Promise((resolve, reject) => {
let path = `${root}${moduleName}/app.js?rnd=${Math.random()}`;
this._loadCss(moduleName);
this.http.get(path)
.toPromise()
.then(res => {
let code = res.text();
this._DomEval(code);
return window['xxx'][moduleName];
})
.then(mod => {
window['xxx'][moduleName] = mod;
let AppModule = mod.AppModule;
// route change will call useModuleStyles function.
// this.useModuleStyles(moduleName, isDepModule);
resolve(AppModule);
})
.catch(err => {
console.error('Load module failed: ', err);
resolve(EmptyModule);
});
});
}
// 取自jQuery
_DomEval(code, doc?) {
doc = doc || document;
let script = doc.createElement('script');
script.text = code;
doc.head.appendChild(script).parentNode.removeChild(script);
}
```
CSS的动态加载相对比较简单,代码如下:
```js
_loadCss(moduleName: string): void {
let cssPath = `${root}${moduleName}/app.css?rnd=${Math.random()}`;
let link = document.createElement('link');
link.setAttribute('rel', 'stylesheet');
link.setAttribute('href', cssPath);
link.setAttribute('class', `xxx-module-style ${moduleName}`);
document.querySelector('head').appendChild(link);
}
```
为了能够在模块切换时卸载,还需要提供一个方法,供路由切换时使用:
```js
useModuleStyles(moduleName: string): void {
let xxxModuleStyles = [].slice.apply(document.querySelectorAll('.xxx-module-style'));
let moduleDeps = this._getModuleAndDeps(moduleName);
moduleDeps.push(moduleName);
xxxModuleStyles.forEach(link => {
let disabled = true;
for (let i = moduleDeps.length - 1; i >= 0; i--) {
if (link.className.indexOf(moduleDeps[i]) >= 0) {
disabled = false;
moduleDeps.splice(i, 1);
break;
}
}
link.disabled = disabled;
});
}
```
## 公共模块依赖
为了处理模块依赖,我们可以借鉴 AMD规范 以及使用 `requirejs` 作为加载器。当前在我的实现里,是自定义了一套加载器,后期应该会切换到 AMD 规范上去。
## 如何兼容 `AngularJS` 模块?
为了兼容 `AngularJS` 的模块,我们引入了 iframe, iframe会先加载一套曾今的 `AngularJS` 宿主,然后再这个宿主中,运行 `AngularJS` 模块。为了实现通信,我们需要两套平台程序中,都引入一个基于 `postMessage` 实现的跨窗口通信库(因为默认跨域,所以用postMessage实现),有了它之后,我们就可以很方便的两边通信了。
## AOT编译
按照 `Angular` 官方的 `Aot` 编译流程即可。
## 多Tab页
在后台系统中,多Tab页是比较常用了。但是多Tab页,在单页中使用,会有一定的性能风险,这个依据实际的情况,进行使用。实现多Tab页的核心就是如何动态加载组件以及如何获取到要加载的组件。
多Tab页面,实际就是一个 `Tabset` 组件,只是在 `tab-item` 的实现稍显特别一些,相关动态加载的源码:
```js
@ViewChild('dynamicComponentContainer', { read: ViewContainerRef }) dynamicComponentContainer: ViewContainerRef;
constructor(
private elementRef: ElementRef,
private renderer: Renderer2,
private tabset: TabsetComponent,
private resolver: ComponentFactoryResolver,
private parentContexts: ChildrenOutletContexts
) {
}
public destroy() {
let el = this.elementRef.nativeElement as HTMLElement;
// tslint:disable-next-line:no-unused-expression
el.parentNode && (el.parentNode.removeChild(el));
}
private loadComponent(component: any) {
let context = this.parentContexts.getContext(PRIMARY_OUTLET);
let injector = ReflectiveInjector.fromResolvedProviders([], this.dynamicComponentContainer.injector);
const resolver = context.resolver || this.resolver;
let factory = resolver.resolveComponentFactory(component);
// let componentIns = factory.create(injector);
// this.dynamicComponentContainer.insert(componentIns.hostView);
this.dynamicComponentContainer.createComponent(factory);
}
```
**注意:要考虑组件卸载方法,如 destroy()**
为了获取到当前要渲染的组件,我们可以借用路由来抓取:
```js
this.router.events.subscribe(evt => {
if (evt instanceof NavigationEnd) {
let pageComponent;
let pageName;
try {
let nextRoute = this.route.children[0].children[0];
pageName = this.location.path();
pageComponent = nextRoute.component;
} catch (e) {
pageName = '$$notfound';
pageComponent = NotFoundComponent;
}
let idx = this.pageList.length + 1;
if (!this.pageList.find(x => x.name === pageName)) {
this.pageList.push({
header: `页面${idx}`,
comp: pageComponent,
name: pageName,
closable: true
});
}
setTimeout(() => {
this.selectedPage = pageName;
});
}
});
```
# 3、总结
以上就是大概的实现思路以及部分相关的细节。其他细节就需要根据实际的情况,进行酌情处理。
该思路并不仅限于 `Angular` 框架,使用 `Vue、React` 也可以做到类似的效果。同时,这套东西也比较适合中小企业的后台平台(不一定非要多团队,一个团队按模块开发也是不错的)。
如需要了解更多细节,可以参考:[ngx-modular-platform](https://github.com/hstarorg/ngx-modular-platform),能给个 `star` 就更好了。
在此抛砖引玉,希望能集思广益,提炼出更好的方案。欢迎讨论和 `提Issue`, `发PR`。
================================================
FILE: Angular系列/跟我学Angular2(1-初体验).md
================================================
---
title: 跟我学Angular2(1-初体验)
date: 2017/02/21 14:47:10
---
## 0、导言
Angular1作为最流行的前端MV*框架,给前端开发带来了极大的便利性。但是,仍然有许多不好的地方已经很难再改变了。Angular团队根据WEB发展的趋势和Angular1中积累的经验来开发了一个全新的Angular,也就是Angular2。
## 1、优势
Angular2做了很激进的变化,带来的成果也是显而易见的。
1. 极大的提高了性能
2. 更强大的模块化
3. 改进的依赖注入
4. 对Web Component友好
5. 原生移动支持 - iOS 和 Android
6. 服务端渲染,搜索引擎优化
## 2、工具链
由于Angular2面向未来,使用了太多还不被当前主流浏览器支持的技术,跑起来还真不是一个容易的事情,所以我们需要一个工具链:

systemjs - 通用模块加载器,支持AMD、CommonJS、ES6等各种格式的JS模块加载
es6-module-loader - ES6模块加载器,systemjs会自动加载这个模块
traceur - ES6转码器,将ES6代码转换为当前浏览器支持的ES5代码。systemjs会自动加载 这个模块。
## 3、Angular2 Hello world
### Step1、下载angular2
[https://angular.io/](https://angular.io/)是angular2的官网,我们需要通过npm进行下载angular2: npm install angular2 [https://www.npmjs.com/package/angular2](https://www.npmjs.com/package/angular2)。
### Step2、引入angular2
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script src="../node_modules/angular2/bundles/angular2.sfx.dev.js"></script>
</head>
<body>
</body>
</html>
### Step3、 Hello angular
<body>
<app></app>
<script>
var App = ng.Component({
selector: 'app',
template: '<h1>Hello {{name}}.</h1>'
}).Class({
constructor: function() {
this.name = 'Angular2';
}
});
ng.bootstrap(App);
</script>
</body>
================================================
FILE: CSS3学习之路/CSS3入门之文本与字体.md
================================================
---
title: CSS3入门之文本与字体
date: 2017/02/21 14:47:10
---
## 1、CSS3文本效果
### 1.1、text-shadow文本阴影
语法:``text-shadow: h-shadow v-shadow blur color;``(<水平阴影>,<垂直阴影>,[模糊距离],[阴影颜色])
示例:
<h1 style="text-shadow: 5px 5px 2px green;">我是文本阴影</h1>
<h1 style="text-shadow: 0 0 5px blue;">我是文本阴影</h1>
<h1 style="text-shadow: 2px 2px 4px #000000;color: white;">我是文本阴影</h1>
<h1 style="text-shadow: 5px 5px 2px green;">我是文本阴影</h1>
<h1 style="text-shadow: 0 0 5px blue;">我是文本阴影</h1>
<h1 style="text-shadow: 2px 2px 4px #000000;color: white;">我是文本阴影</h1>
**该属性兼容IE10+以及所有现代浏览器**
### 1.2、word-break文本换行
语法: ``word-break: normal|break-all|keep-all;``
normal:默认换行;break-all:允许在单词内换行;keep-all:只能在半角空格或连字符处换行
示例:
<div style="width:100px;word-break:break-all;">Nice to meet you. good mor-ning.</div>
<div style="width:100px;word-break:keep-all;">Nice to meet you. good mor-ning.</div>
<div style="width:100px;word-break:break-all;">Nice to meet you. good mor-ning.</div>
<div style="width:100px;word-break:keep-all;">Nice to meet you. good mor-ning.</div>
### 1.3、text-overflow修剪文本
语法:``text-overflow: clip|ellipsis|string;``
示例:
<div style="width: 100px; overflow:hidden; white-space:nowrap;text-overflow: clip;">Nice to meet you. good mor-ning.</div>
<div style="width: 100px; overflow:hidden; white-space:nowrap;text-overflow: ellipsis;">Nice to meet you. good mor-ning.</div>
<div style="width: 100px; overflow:hidden; white-space:nowrap;text-overflow: clip;">Nice to meet you. good mor-ning.</div>
<div style="width: 100px; overflow:hidden; white-space:nowrap;text-overflow: ellipsis;">Nice to meet you. good mor-ning.</div>
**注意:使用text-overflow的时候,需要与overflow:hidden;white-space:nowrap;协同使用**
## 2、CSS3字体
在CSS3之前,必须使用已经在用户计算机上安装好的字体,给Web设计带来很大的局限性。现在,通过CSS3,Web设计师可以使用他们喜欢的任意字体。
### 2.1、@font-face引入网络字体
Firefox、Chrome、Safari 以及 Opera 支持 .ttf (True Type Fonts) 和 .otf (OpenType Fonts) 类型的字体。
Internet Explorer 9+ 支持新的 @font-face 规则,但是仅支持 .eot 类型的字体 (Embedded OpenType)。
不兼容IE8,IE8-。
示例:
<style>
@font-face {
font-family: SentyPaperCut;
src:url(http://hstarcdn.github.io/fonts/SentyPaperCut.ttf);
}
@font-face {
font-family:SentyCreamPuff;
src:url(http://hstarcdn.github.io/fonts/SentyCreamPuff.otf);
}
.font1,.font2{
font-size: 50px;
}
.font1{
color: red;
font-family: SentyTEA-Platinum;
}
.font2{
color: blue;
font-family: SentyCreamPuff;
}
</style>
<span class="font1">
自定义字体演示
</span>
<span class="font2">
自定义字体演示
</span>
<style>
@font-face {
font-family: SentyTEA-Platinum;
src:url(http://hstarcdn.github.io/fonts/SentyTEA-Platinum.ttf);
}
@font-face {
font-family:SentyCreamPuff;
src:url(http://hstarcdn.github.io/fonts/SentyCreamPuff.otf);
}
.font1,.font2{
font-size: 50px;
}
.font1{
color: red;
font-family: SentyTEA-Platinum;
}
.font2{
color: blue;
font-family: SentyCreamPuff;
}
</style>
<span class="font1">
自定义字体演示
</span>
<span class="font2">
自定义字体演示
</span>
除此之外,在@font-face中,还可以设置多种字体描述符,如:
<table class="dataintable">
<tbody><tr>
<th style="width:20%;">描述符</th>
<th style="width:25%;">值</th>
<th>描述</th>
</tr>
<tr>
<td>font-family</td>
<td><i>name</i></td>
<td>必需。规定字体的名称。</td>
</tr>
<tr>
<td>src</td>
<td><i>URL</i></td>
<td>必需。定义字体文件的 URL。</td>
</tr>
<tr>
<td>font-stretch</td>
<td>
<ul>
<li>normal</li>
<li>condensed</li>
<li>ultra-condensed</li>
<li>extra-condensed</li>
<li>semi-condensed</li>
<li>expanded</li>
<li>semi-expanded</li>
<li>extra-expanded</li>
<li>ultra-expanded</li>
</ul>
</td>
<td>可选。定义如何拉伸字体。默认是 "normal"。</td>
</tr>
<tr>
<td>font-style</td>
<td>
<ul>
<li>ormal</li>
<li>italic</li>
<li>oblique</li>
</ul>
</td>
<td>可选。定义字体的样式。默认是 "normal"。</td>
</tr>
<tr>
<td>font-weight</td>
<td>
<ul>
<li>normal</li>
<li>bold</li>
<li>100</li>
<li>200</li>
<li>300</li>
<li>400</li>
<li>500</li>
<li>600</li>
<li>700</li>
<li>800</li>
<li>900</li>
</ul>
</td>
<td>可选。定义字体的粗细。默认是 "normal"。</td>
</tr>
<tr>
<td>unicode-range</td>
<td><i>unicode-range</i></td>
<td>可选。定义字体支持的 UNICODE 字符范围。默认是 "U+0-10FFFF"。</td>
</tr>
</tbody></table>
================================================
FILE: CSS3学习之路/CSS3入门之转换.md
================================================
---
title: CSS3入门之转换
date: 2017/02/21 14:47:10
---
## 1、CSS3 转换
### 1.1、转换是什么,能实现哪些效果?
转换是使元素改变形状、尺寸和位置的一种效果,主要能实现的效果如下:
1. 移动
2. 缩放
3. 转动
4. 拉长
5. 拉伸
### 1.2、浏览器兼容
CSS3的转换属性为 ``transform`` ,IE10+,Firefox,Chrome,Opera,Safari等现代浏览器支持transform属性,IE9需要-ms-前缀。
## 2、 2D 转换
准备工作:
<style>
.container{
position:relative;border:1px solid red; width: 100px; height: 100px;
}
.container>div{
width: 50px; height: 50px; background: gray;
}
</style>
<style>
.container{
position:relative;border:1px solid red; width: 100px; height: 100px;
}
.container div{
width: 50px; height: 50px; background: gray;
}
</style>
### 2.1、translate() -- 移动
translate(/\*x坐标移动位移\*/ left, /\*y坐标移动位移\*/ top)
<h3>右移20px</h3>
<div class="container">
<div style="transform: translate(20px);"></div>
</div>
<h3>下移20px</h3>
<div class="container">
<div style="transform: translate(0px,20px);"></div>
</div>
<h3>左移20px,下移20px</h3>
<div class="container">
<div style="transform: translate(-20px,20px);"></div>
</div>
<h3>右移20px</h3>
<div class="container">
<div style="transform: translate(20px);"></div>
</div>
<h3>下移20px</h3>
<div class="container">
<div style="transform: translate(0px,20px);"></div>
</div>
<h3>左移20px,下移20px</h3>
<div class="container">
<div style="transform: translate(-20px,20px);"></div>
</div>
### 2.2、rotate() -- 旋转
rotate(/\*旋转角度\*/ deg)
<h3>旋转135度</h3>
<div class="container">
<div style="transform: rotate(135deg);"></div>
</div>
<h3>旋转135度</h3>
<div class="container">
<div style="transform: rotate(135deg);"></div>
</div>
### 2.3、scale() -- 缩放
scale(/\*宽度缩放比例\*/ widthScale, /\*高度缩放比例\*/ heightScale)
<h3>缩放到0.5倍</h3>
<div class="container">
<div style="transform: scale(0.5, 0.5);"></div>
</div>
<h3>宽度缩放到1.5倍,高度缩放到0.25倍</h3>
<div class="container">
<div style="transform: scale(1.5, 0.25);"></div>
</div>
<h3>缩放到0.5倍</h3>
<div class="container">
<div style="transform: scale(0.5, 0.5);"></div>
</div>
<h3>宽度缩放到1.5倍,高度缩放到0.25倍</h3>
<div class="container">
<div style="transform: scale(1.5, 0.25);"></div>
</div>
### 2.4、skew() -- 倾斜
skew(/\*X轴倾斜角度\*/ xDeg, /\*Y轴倾斜角度\*/ yDeg)
<h3>X轴翻转30度</h3>
<div class="container">
<div style="transform: skew(30deg);"></div>
</div>
<h3>X轴翻转30度,Y轴翻转10度</h3>
<div class="container">
<div style="transform: skew(30deg, 10deg)"></div>
</div>
<h3>X轴翻转30度</h3>
<div class="container">
<div style="transform: skew(30deg);"></div>
</div>
<h3>X轴翻转30度,Y轴翻转10度</h3>
<div class="container">
<div style="transform: skew(30deg, 10deg)"></div>
</div>
### 2.5、matrix() --矩阵
<h3>旋转30度</h3>
<div class="container">
<div style="transform: matrix(0.866,0.5,-0.5,0.866,0,0)"></div>
</div>
<h3>旋转30度</h3>
<div class="container">
<div style="transform: matrix(0.866,0.5,-0.5,0.866,0,0)"></div>
</div>
### 2.6 Transform方法
<table class="dataintable">
<tbody><tr>
<th style="width:25%;">函数</th>
<th>描述</th>
</tr>
<tr>
<td>matrix(<i>n</i>,<i>n</i>,<i>n</i>,<i>n</i>,<i>n</i>,<i>n</i>)</td>
<td>定义 2D 转换,使用六个值的矩阵。</td>
</tr>
<tr>
<td>translate(<i>x</i>,<i>y</i>)</td>
<td>定义 2D 转换,沿着 X 和 Y 轴移动元素。</td>
</tr>
<tr>
<td>translateX(<i>n</i>)</td>
<td>定义 2D 转换,沿着 X 轴移动元素。</td>
</tr>
<tr>
<td>translateY(<i>n</i>)</td>
<td>定义 2D 转换,沿着 Y 轴移动元素。</td>
</tr>
<tr>
<td>scale(<i>x</i>,<i>y</i>)</td>
<td>定义 2D 缩放转换,改变元素的宽度和高度。</td>
</tr>
<tr>
<td>scaleX(<i>n</i>)</td>
<td>定义 2D 缩放转换,改变元素的宽度。</td>
</tr>
<tr>
<td>scaleY(<i>n</i>)</td>
<td>定义 2D 缩放转换,改变元素的高度。</td>
</tr>
<tr>
<td>rotate(<i>angle</i>)</td>
<td>定义 2D 旋转,在参数中规定角度。</td>
</tr>
<tr>
<td>skew(<i>x-angle</i>,<i>y-angle</i>)</td>
<td>定义 2D 倾斜转换,沿着 X 和 Y 轴。</td>
</tr>
<tr>
<td>skewX(<i>angle</i>)</td>
<td>定义 2D 倾斜转换,沿着 X 轴。</td>
</tr>
<tr>
<td>skewY(<i>angle</i>)</td>
<td>定义 2D 倾斜转换,沿着 Y 轴。</td>
</tr>
</tbody></table>
## 3、3D 转换
### 3.1、rotateX、rotateY
<div class="container">
<div style="transform: rotateY(0deg)" id="fun2"></div>
</div>
<script>
function fun2 (element) {
var i = 0;
var interval = setInterval(function(){
element.style.transform = 'rotateY(' + i + 'deg)';
i++;
}, 5);
}
fun2(document.getElementById('fun2'));
</script>
<div class="container">
<div style="transform: rotateY(0deg)" id="fun2"></div>
</div>
<script>
function fun2 (element) {
var i = 0;
var interval = setInterval(function(){
element.style.transform = 'rotateY(' + i + 'deg)';
i++;
}, 5);
}
fun2(document.getElementById('fun2'));
</script>
### 3.2、Transform方法
<table class="dataintable">
<tbody><tr>
<th style="width:25%;">函数</th>
<th>描述</th>
</tr>
<tr>
<td>matrix3d(<i>n</i>,<i>n</i>,<i>n</i>,<i>n</i>,<i>n</i>,<i>n</i>,<br><i>n</i>,<i>n</i>,<i>n</i>,<i>n</i>,<i>n</i>,<i>n</i>,<i>n</i>,<i>n</i>,<i>n</i>,<i>n</i>)</td>
<td>定义 3D 转换,使用 16 个值的 4x4 矩阵。</td>
</tr>
<tr>
<td>translate3d(<i>x</i>,<i>y</i>,<i>z</i>)</td>
<td>定义 3D 转化。</td>
</tr>
<tr>
<td>translateX(<i>x</i>)</td>
<td>定义 3D 转化,仅使用用于 X 轴的值。</td>
</tr>
<tr>
<td>translateY(<i>y</i>)</td>
<td>定义 3D 转化,仅使用用于 Y 轴的值。</td>
</tr>
<tr>
<td>translateZ(<i>z</i>)</td>
<td>定义 3D 转化,仅使用用于 Z 轴的值。</td>
</tr>
<tr>
<td>scale3d(<i>x</i>,<i>y</i>,<i>z</i>)</td>
<td>定义 3D 缩放转换。</td>
</tr>
<tr>
<td>scaleX(<i>x</i>)</td>
<td>定义 3D 缩放转换,通过给定一个 X 轴的值。</td>
</tr>
<tr>
<td>scaleY(<i>y</i>)</td>
<td>定义 3D 缩放转换,通过给定一个 Y 轴的值。</td>
</tr>
<tr>
<td>scaleZ(<i>z</i>)</td>
<td>定义 3D 缩放转换,通过给定一个 Z 轴的值。</td>
</tr>
<tr>
<td>rotate3d(<i>x</i>,<i>y</i>,<i>z</i>,<i>angle</i>)</td>
<td>定义 3D 旋转。</td>
</tr>
<tr>
<td>rotateX(<i>angle</i>)</td>
<td>定义沿 X 轴的 3D 旋转。</td>
</tr>
<tr>
<td>rotateY(<i>angle</i>)</td>
<td>定义沿 Y 轴的 3D 旋转。</td>
</tr>
<tr>
<td>rotateZ(<i>angle</i>)</td>
<td>定义沿 Z 轴的 3D 旋转。</td>
</tr>
<tr>
<td>perspective(<i>n</i>)</td>
<td>定义 3D 转换元素的透视视图。</td>
</tr>
</tbody></table>
================================================
FILE: CSS3学习之路/CSS3入门之边框与背景.md
================================================
---
title: CSS3入门之边框与背景
date: 2017/02/21 14:47:10
---
## 1、前言
CSS3作为CSS的最新版本,在展示效果上有非常大的提升,接下来,我们就一起领略一下CSS3的风采吧。
## 2、CSS3边框
```
<style>
.td{
width: 200px;
height: 100px;
border: 1px solid black;
margin: 10px 0;
}
</style>
```
### 2.1、border-radius(用于设置圆角边框)
在CSS2时代,要想实现圆角边框,是一件非常麻烦的事情。一种实现方式是使用一个背景图片,为了实现伸缩效果,还需要至少3张图片拼凑,相当麻烦。另外一种实现方式是使用多个div重叠来实现圆角。
在CSS3中,有一个非常简单的属性,那就是border-radius。
语法: ``border-radius: 1-4 length|% / 1-4 length|%;``
```css
border-radius: 10px;
//等价于
border-top-left-radius:10px;
border-top-right-radius:10px;
border-bottom-right-radius:10px;
border-bottom-left-radius:10px;
```
<div style="border-radius:10px;">
演示圆角边框
</div>
<div style="border-radius:10px;">
演示圆角边框
</div>
**兼容性说明:** IE9+,Chrome,FF,Safari,Oprea
```
div
{
border:2px solid;
border-radius:25px;
-moz-border-radius:25px; /* Old Firefox */
}
```
### 2.2、box-shadow(用于添加边框阴影)
语法: ``box-shadow: h-shadow v-shadow blur spread color inset;``,其中h-shadow和v-shadow是必须设置,允许负值。【参数说明:水平阴影的位置,垂直阴影的位置,模糊距离,阴影的尺寸,阴影的颜色,外部引用(outset)改为内部阴影】
```html
<div class="td" style="border-radius:10px; border: 1px solid red;">
演示圆角边框
</div>
<div class="td" style="box-shadow: 2px 2px red;">
简单阴影
</div>
<div class="td" style="box-shadow: -2px -2px red;">
简单阴影
</div>
<div class="td" style="box-shadow: -2px -2px red;">
简单阴影
</div>
<div class="td" style="box-shadow: -2px -2px 10px red;">
带模糊效果的阴影
</div>
<div class="td" style="box-shadow: -2px -2px 10px red;">
带模糊效果的阴影
</div>
<div class="td" style="box-shadow: 2px 2px 10px 10px red;">
带模糊效果指定尺寸的阴影
</div>
<div class="td" style="box-shadow: 2px 2px 10px 10px red;">
带模糊效果指定尺寸的阴影
</div>
<div class="td" style="box-shadow: 2px 2px 10px 10px red inset;">
内部阴影
</div>
<div class="td" style="box-shadow: 2px 2px 10px 10px red inset;">
内部阴影
</div>
```
**兼容性说明:** IE9+,Chrome,FF,Safari,Oprea
### 2.3、border-image(CSS3边框图片)
border-image是简写属性,全部是:
```css
border-image-source //背景图片源
border-image-slice //图片边框内偏移
border-image-width //图片边框的宽度
border-image-outset //边框图像区域超出边框的量
border-image-repeat //边框是否适应平铺(repeated)、铺满(rounded)、拉伸(stretched)
```
```html
<div style="border-width:10px;border-image: url(http://www.w3school.com.cn/i/border.png) 10 10 round;">
简单图片边框
</div>
<div style="border-width:10px;border-image: url(http://www.w3school.com.cn/i/border.png) 10 10 round">
简单图片边框
</div>
<div style="border-width:10px;border-image: url(http://www.w3school.com.cn/i/border.png) 10 10 50 round">
完全设置的图片边框
</div>
<div style="border-width:10px;border-image: url(http://www.w3school.com.cn/i/border.png) 10 10 50 round">
完全设置的图片边框
</div>
```
**兼容性说明:** Chrome,FF,Safari,Oprea
```
div
{
border-image:url(border.png) 30 30 round;
-moz-border-image:url(border.png) 30 30 round; /* 老的 Firefox */
-webkit-border-image:url(border.png) 30 30 round; /* Safari 和 Chrome */
-o-border-image:url(border.png) 30 30 round; /* Opera */
}
```
## 3、CSS3背景
### 整体兼容性
以下CSS背景的特性,全部支持IE9+,FF,Chrome,Safari,Oprea
### 3.1、background-size(用于规定背景图片的尺寸)
在以前的CSS中,背景图片的大小,是由图片本身的大小决定的。在CSS3中,有一个简单的CSS样式可以设置背景图片的大小,允许我们在不同的环境中重复使用背景图片。可以以像素或百分比规定尺寸。
```html
<div style="
background-image:url(http://www.w3school.com.cn/i/bg_flower_small.gif);
background-size: 50% 70%;
background-repeat:no-repeat;">
</div>
<div style="background-image:url(http://www.w3school.com.cn/i/bg_flower_small.gif);background-size: 50% 70%;background-repeat:no-repeat;">
简单设置背景图大小
</div>
```
### 3.2、background-origin(规定背景图片的定位区域)
盒子模型示意图:
<img src="http://www.w3school.com.cn/i/background-origin.gif" alt="box" />
background-origin属性则可以设置背景图片放置于哪个区域上(content-box,padding-box,border-box)
```html
<div style="width:66px;height:125px;
background-image:url(http://www.w3school.com.cn/i/bg_flower_small.gif);
background-origin:content-box;
padding: 20px;border:20px solid red;"></div>
<div style="width:66px;height:125px;
background-image:url(http://www.w3school.com.cn/i/bg_flower_small.gif);
background-origin:border-box;
padding: 20px;border:20px solid red;"></div>
<div style="width:66px;height:125px;
background-image:url(http://www.w3school.com.cn/i/bg_flower_small.gif);
background-origin:padding-box;
padding: 20px;border:20px solid red;"></div>
```
### 3.3、多重背景
可以针对标签设置多个背景,用法如下:
```css
body
{
background-image:url(bg_flower.gif),url(bg_flower_2.gif);
}
```
================================================
FILE: Canvas学习札记/01_初识Canvas,绘制简单图形.md
================================================
---
title: 01_初识Canvas,绘制简单图形
date: 2017/02/21 14:47:10
---
## 0、关于Canvas
``<canvas>`` 是HTML5新增的一个标签,用于定义图形,比如图表和其他图像。
``<canvas>`` 标签只是图形容器,必须要使用脚本来绘制图形。
一句话概括就是:``<canvas>`` 是浏览器上的画图,允许你通过js自由作画。
### Canvas和SVG与VML的不同
``<canvas>`` 有一个基于JS的绘图API,它本身并不会绘制图形。SVG和VML都是用一个XML文档来描述图形。
虽然它们在功能上基本相同,但是从表面上来看,它们非常不同。SVG和VML绘图易于编辑,只需要从描述中修改元素属性。而Canvas想移除元素,往往需要擦掉绘图重新绘制它。
### Canvas兼容HTML5标准属性和事件
``<canvas>`` 作为一个HTML的新标签,标准的HTML属性和事件它都支持。比如可以设置 ``title、style、class`` 等属性,也可以使用诸如 ``onclick`` 等事件。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Canvas Test</title>
<style>
body{
text-align: center;
}
.c1{
border: 1px solid red;
height: 600px;
width: 800px;
}
</style>
</head>
<body>
<canvas id="c1" onclick="alert('abc')" class="c1"></canvas>
</body>
</html>
```
## 1、使用Canvas
要使用``canvas``,首先,我们先得html中加入canvas标签。最好,再加上一个id属性(也可以不加,只是查找该元素要稍微麻烦点)。
```html
...
<body>
<canvas id="c1" onclick="alert('abc')" class="c1"></canvas>
<script>
var c1 =document.getElementById('c1');
// 如果不用id属性,我们可以用如下方式来获取canvas对象
//var c1 = document.getElementsByTagName('canvas')[0];
</script>
</body>
...
```
在获取到 ``canvas`` 元素之后,我们需要通过 ``getContext(contextID)`` 方法获取到画布。
当前 ``contextID``的值仅仅可以用'2d',在未来,可能会允许传递'3d',来进行三维绘图。
```html
...
<body>
<canvas id="c1" onclick="alert('abc')" class="c1"></canvas>
<script>
var c1 =document.getElementById('c1');
// 如果不用id属性,我们可以用如下方式来获取canvas对象
//var c1 = document.getElementsByTagName('canvas')[0];
var context = c1.getContext('2d');
console.log(context); //可以看到context是一个CanvasRenderingContext2D对象
</script>
</body>
...
```
``CanvasRenderingContext2D`` 对象实现了一个画布所使用的大多数方法。现在我们就需要对它来进行使用,将图像绘制到浏览器上。
### 1.1、绘制矩形
关于矩形的绘制,主要有三个方法:
* fillRect(x, y, width, height) 用于填充矩形
* strokeRect(x, y, width, height) 用于绘制矩形边框
* clearRect(x, y, width, height) 用于清空矩形区域(设置矩形区域为空白)
其中 ``x,y``表示从那个点开始绘制。``width,height`` 表示矩形的宽度和高度。
要设置矩形的填充颜色,需要通过 ``fillStyle`` 来控制,支持 ``'red', '#fff', 'rgb(10,10,10)', 'rgba(10,10,10,10,0.5)'``等多种颜色属性。
要设置矩形的边框颜色,需要通过 ``strokeStyle`` 来控制,属性值和 ``fillStyle`` 一致。
```javascript
...
//绘制红色矩形
context.fillStyle = 'red';
context.fillRect(10,10,100,100);
//绘制蓝色矩形框
context.strokeStyle = 'blue';
context.strokeRect(150,150,100,100);
//清空矩形区域(设置矩形区域为空白)
context.clearRect(100,100,100,100);
...
```
### 1.2、绘制线条
我们可以通过 ``lineTo(x, y)`` 绘制直线。两点成直线,绘制直线需要两个点,所以我们需要先设置一个起点,一般来说,我们使用 ``moveTo(x, y)`` 设置笔触的位置。当然,你也可以用 ``lineTo(x, y)`` 来设置一个笔触点。
在没有设置笔触的场景下,以下两段代码的效果完全一致:
```javascript
//画线,设置起点。
context.moveTo(200, 200);
//设置轨迹
context.lineTo(500,500);
//画线
context.stroke();
```
```javascript
//画线,设置起点。
context.lineTo(200, 200);
//设置轨迹
context.lineTo(500,500);
//画线
context.stroke();
```
**一般来说,我们会在 ``canvas`` 初始化或者 ``beginPath()`` 调用后,通过 ``moveTo(x, y)`` 来设置一个初始笔触点。**
要同时绘制多个线条,我们应该通过 ``beginPath()`` 来建立路径。
```javascript
//用线条绘制了一个矩形
context.beginPath();
context.moveTo(400, 400);
context.lineTo(450, 400);
context.lineTo(450, 450);
context.lineTo(400, 450);
context.closePath();
//真实的绘图
context.stroke();
```
看了以上的代码,可能会有一个疑惑,为什么仅仅三个线条就构成了一个矩形呢?
原因在于当调用 ``closePath()`` 的时候,会把最后的笔触点和最开始的笔触点连接在一起,这个时候也就构成了第四条直线。
**注意:当前路径为空,即调用beginPath()之后,或者canvas刚建的时候,第一条路径构造命令通常被视为是moveTo(),无论最后的是什么。出于这个原因,你几乎总是要在设置路径之后专门指定你的起始位置。**
**闭合路径 ``closePath()``,不是必需的。这个方法会通过绘制一条从当前点到开始点的直线来闭合图形。如果图形是已经闭合了的,即当前点为开始点,该函数什么也不做。**
**当你调用 ``fill()`` 函数时,所有没有闭合的形状都会自动闭合,所以你不需要调用 ``closePath()`` 函数。但是调用stroke()时不会自动闭合。**
再来填充一个梯形玩玩:
```javascript
context.beginPath();
context.moveTo(100, 400);
context.lineTo(200, 400);
context.lineTo(250, 500);
context.lineTo(50, 500);
context.fill();
```
### 1.3、绘制矩形线条
矩形线条是一个比较常用的图形,所以提供了一个简单的方法来直接绘制:
```javascript
//绘制矩形线条
context.beginPath();
context.rect(700, 10, 50, 50);
context.stroke();
context.fillStyle = 'red';
context.fill();
```
### 1.4、绘制圆弧
绘制圆弧或者圆的时候,我们可以使用如下方法:
* arc(x, y, radius, startAngle, endAngle, anticlockwise) 画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成。
* arcTo(x1, y1, x2, y2, radius) 根据给定的控制点和半径画一段圆弧,再以直线连接两个控制点。
anticlockwise为true则表示逆时针绘制。
```javascript
//绘制圆弧
context.beginPath();
context.arc(550, 150, 100, getRadian(90) , getRadian(360), false);
context.stroke();
context.beginPath();
context.arc(550, 150, 100, 0, getRadian(90), false);
context.fill();
```
以上代码,绘制了两个弧形,一个空心,一个实心。一般再绘制圆弧的时候就不要执行 ``moveTo(x, y)``,否则绘制终点会被连接到这个触点上。
**注意:在arc函数中,``startAngle`` 和 ``endAngle`` 属性值都是弧度,而不是我们所熟知的角度。所以我们一个一个角度转换为弧度的函数,如下:**
```javascript
function getRadian(degrees/*角度值*/){
return (Math.PI / 180) * degrees;
}
```
**arcTo没吃透,暂时描述不出来,先简单看看示例:**
```javascript
//绘制圆弧(必须要设定起始点)
context.beginPath();
context.fillRect(600, 400, 10, 10);
context.fillRect(700, 500, 10, 10);
context.fillStyle = 'blue';
context.fillRect(700, 400, 10, 10);
context.beginPath();
context.moveTo(700, 400);
context.arcTo(600, 600, 700, 700, 500);
context.stroke();
```
## 2、其他
### 2.1、canvas检查支持性
如果仅仅需要在UI上体现,那么我们可以在 ``<canvas>`` 标签内部放置元素,如果浏览器不支持 ``<canvas>`` 标签,那么内部的元素就会被浏览器解析,而显示出来。
```html
<canvas id="stockGraph" width="150" height="150">
<p>Canvas not be support.</p>
</canvas>
```
除此之外,我们也可以用js的方式来检查。
```javascript
var c1 = document.getElementById('c1');
//如果canvas元素没有getContext方法,那么就证明浏览器不支持canvas。
if(!c1.getContext){
console.log('Canvas not be support.')
}
```
### 2.2、canvas的width和height属性
``<canvas>`` 对象有两个比较特别的属性,``width、height``,这两者用于控制画布的大小,width的默认值是300,height的默认值为150。**当这两个属性值有变化时,在该画布上已经完成的任何绘图都会擦除掉。**
```javascript
var c1 = document.getElementById('c1');
console.log('default width:', c1.width, '; default height:', c1.height);
c1.width = 500;
c1.height = 600;
```
``<canvas>`` 的的height和width属性如果和用css设置的height和width样式不一致,那么就可能会产生扭曲。
### 2.3、来个好玩的,画个桃心
```javascript
function drawHeart() {
context.fillStyle = 'purple';
//三次曲线
context.beginPath();
context.moveTo(75, 40);
context.bezierCurveTo(75, 37, 70, 25, 50, 25);
context.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5);
context.bezierCurveTo(20, 80, 40, 102, 75, 120);
context.bezierCurveTo(110, 102, 130, 80, 130, 62.5);
context.bezierCurveTo(130, 62.5, 130, 25, 100, 25);
context.bezierCurveTo(85, 25, 75, 37, 75, 40);
context.fill();
}
drawHeart();
```
### 2.4 附上测试代码
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Canvas Test</title>
<style>
body {
text-align: center;
}
.c1 {
border: 1px solid red;
height: 600px;
width: 800px;
}
</style>
</head>
<body>
<canvas id="c1" onclick="alert('abc')" class="c1"></canvas>
<script src="main.js"></script>
</body>
</html>
```
```javascript
var c1 = document.getElementById('c1');
// 如果不用id属性,我们可以用如下方式来获取canvas对象
//var c1 = document.getElementsByTagName('canvas')[0];
c1.width = 800;
c1.height = 600;
var context = c1.getContext('2d');
console.log(context); //可以看到context是一个CanvasRenderingContext2D对象
function getRadian(degrees/*角度值*/) {
return (Math.PI / 180) * degrees;
}
//绘制红色矩形
context.fillStyle = 'red';
context.fillRect(10, 10, 100, 100);
//绘制蓝色矩形框
context.strokeStyle = 'blue';
context.strokeRect(150, 150, 100, 100);
//清空矩形区域(设置矩形区域为空白)
context.clearRect(100, 100, 100, 100);
//画线,设置起点。
context.moveTo(200, 200);
//设置轨迹
context.lineTo(500, 500);
//画线
context.stroke();
//绘制空心矩形
context.beginPath();
context.moveTo(400, 400);
context.lineTo(450, 400);
context.lineTo(450, 450);
context.lineTo(400, 450);
context.closePath();
context.stroke();
//绘制实心梯形
context.beginPath();
context.moveTo(100, 400);
context.lineTo(200, 400);
context.lineTo(250, 500);
context.lineTo(50, 500);
context.fill();
//绘制圆弧
context.beginPath();
context.arc(550, 150, 100, getRadian(90), getRadian(360), true);
context.stroke();
context.beginPath();
context.arc(550, 150, 100, 0, getRadian(90), true);
context.stroke();
//绘制圆弧(必须要设定起始点)
context.beginPath();
context.fillRect(600, 400, 10, 10);
context.fillRect(700, 500, 10, 10);
context.fillStyle = 'blue';
context.fillRect(700, 400, 10, 10);
context.beginPath();
context.moveTo(700, 400);
context.arcTo(600, 600, 700, 700, 500);
context.stroke();
//绘制矩形线条
context.beginPath();
context.rect(700, 10, 50, 50);
context.stroke();
context.fillStyle = 'red';
context.fill();
function drawHeart() {
context.fillStyle = 'purple';
//三次曲线
context.beginPath();
context.moveTo(75, 40);
context.bezierCurveTo(75, 37, 70, 25, 50, 25);
context.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5);
context.bezierCurveTo(20, 80, 40, 102, 75, 120);
context.bezierCurveTo(110, 102, 130, 80, 130, 62.5);
context.bezierCurveTo(130, 62.5, 130, 25, 100, 25);
context.bezierCurveTo(85, 25, 75, 37, 75, 40);
context.fill();
}
drawHeart();
```
================================================
FILE: ES6入门/ES6入门系列一(基础).md
================================================
---
title: ES6入门系列一(基础)
date: 2017/02/21 14:47:10
---
##1、let命令
**Tips:**
1. 块级作用域(只在当前块中有效)
2. 不会变量提升(必须先申明在使用)
3. 让变量独占该块,不再受外部影响
4. 不允许重复声明
**总之:let更像我们熟知的静态语言的的变量声明指令**
ES6新增了let命令,用来声明变量。用法类似于var,但所声明的变量,只能在let命令所在的代码块内有效。
let声明的变量只有块级作用域
'use strict'
{
let a = 1;
}
console.log(a); //结果是什么?
看一段熟悉的代码:
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
console.log(a[6]()); //结果是什么?
如果改用let的话,那么看以下代码输出什么?
'use strict'
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
console.log(a[6]()); // ?
同时,在使用let的时候,必须先申明再使用,不像var会变量提升:
'use strict'
console.log(a);
let a = 1;
ES6中明确规定,如果区块存在let和const,那么该区块就形成封闭作用域,凡是在声明致歉就使用这些变量,就会报错。简称“暂时性死区”(temporal dead zone,简称TDZ)。
看一个不太容易发现的死区:(注:该代码未测试)
function bar(x=y, y=2) {
return [x, y];
}
bar(); // 报错
调用bar之所以报错,是因为参数x默认值等于另一个参数y,而此时y还没有声明,属于“死区”。
需要注意,函数参数作用域和函数体的作用域是分离的:
let foo = 'outer';
function bar(func) {
let foo = 'inner';
console.log(func()); // outer
}
bar(function(){
console.log(foo);
});
同时,let还不允许重复声明
{
let a = 1;
var a = 1;
}
{
let a = 1;
let a = 2;
}
##2、const命令
**Tips:**
1. const用于声明常量,一旦声明,值就不能改变
2. const具有块级作用域
3. const不能变量提升(先声明后使用)
4. 不可重复声明
**const看起来很像我们熟知的静态语言的只读对象**
const声明常量,一旦声明,值将是不可变的。
'use strict'
const PI = 3.1415;
PI // 3.1415
PI = 3; //Error
const指令指向变量所在的地址,所以对该变量进行属性设置是可行的(未改变变量地址),如果想完全不可变化(包括属性),那么可以使用冻结。
'use strict'
const C1 = {};
C1.a = 1;
console.log(C1.a); // 1
//冻结对象,此时前面用不用const都是一个效果
const C2 = Object.freeze({});
C2.a = 1; //Error,对象不可扩展
console.log(C2.a);
##3、全局对象属性
JavaScript中,全局对象是最顶层的对象,浏览器中是window对象,Node中是global对象,ES5规定,所有全局变量都是全局对象的属性。
在ES6中,var和function申明的变量,属于全局对象的属性,let和const则不是全局对象的属性。
'use strict'
let b = 2;
console.log(global.b); // undefined
================================================
FILE: ES6入门/ES6入门系列三(特性总览下).md
================================================
---
title: ES6入门系列三(特性总览下)
date: 2017/02/21 14:47:10
---
# 0、导言
最近从coffee切换到js,代码量一下子变大了不少,也多了些许陌生感。
为了在JS代码中,更合理的使用ES6的新特性,特在此对ES6的特性做一个简单的总览。
# 1、模块(Module - Chrome测试不可用)
>在ES6中,有class的概念,不过这只是语法糖,并没有解决模块化问题。Module功能则是为了解决模块化问题而提出的。
我们可以使用如下方式定义模块:
11_lib.js文件内容
// 导出属性和方法
export var PI = 3.1415926;
export function calcCircularArea(r){
return PI * r * r;
}
app.js文件内容
//导出所有,使用别名调用
import * as lib from '11_lib';
console.log(lib.calcCircularArea(2));
console.log(lib.PI);
//导出属性和方法
import {calcCircularArea, PI} from '11_lib';
console.log(calcCircularArea(2));
console.log(PI);
# 2、模块加载器(Module Loaders - Chrome测试不可用)
既然用了定义module的规范,那么也就需要一个模块加载器,需要支持如下内容:
1. 动态加载
2. 状态隔离
3. 全局命名空间隔离
4. 编译钩子
5. 嵌套虚拟化
```javascript
System.import('11_lib').then(function(m) {
console.log(m.calcCircularArea(2));
console.log(m.PI);
});
```
# 3、图 + 集合 + 弱引用图 + 若引用集合(Map + Set + WeakMap + WeakSet)
在ES6中,新增了几种数据结构。
**Map**是一种类似于Object的结构,Object本质上是键值对的集合,但是只能是字符串当做键,所以有一定的使用限制。Map的话,则可以拿任意类型来作为key。具体使用如下:
var map = new Map();
var key = {key: 'hah'};
map.set(key, '1'); //设置key-value
map.set(key, 2); //对已有key进行设置,表示覆盖
console.log(map.get(key)); //获取key的值
console.log(map.size);//获取map的元素个数
map.has(key); //判断map中有指定的key
map.delete(key); //删除map中指定的key
map.clear(); //清空map
**WeakMap**和Map是比较类似的,唯一的区别是只接受对象作为键名(null除外),而且键名所指向的对象,不计入垃圾回收机制。
**Set**是一种类似于数组,但成员的值都是唯一(引用唯一)的一种数据结构。具体使用如下:
var set = new Set();//定义set
set.add(1).add(2).add(1).add('2'); //添加数据
console.log(set.size);//查看set中元素的数量,结果应该是3,因为重复添加不计算,2和'2'不等。
set.delete(1); //删除set的值(通过value删除)。
set.has(1); //set是否包含某个value
set.keys(); //返回set的所有key
set.values(); //返回set的所有value
set.clear();//清空set
**WeakSet**和Set也是比较类型的,和Set有两个区别,一个是成员只能是对象;二个是WeakSet是不可遍历的。
# 4、代理(Proxies) (Chrome测试不可用)
代理允许用宿主的行为来创建对象,能够实现拦截,对象的虚拟化,日志和分析等功能。
# 5、数据类型Symbols
在ES5中,JS只有6中原始类型,在ES6中,新增了Symbols类型,成为了JS中的第7种原始类型。
该类型表示独一无二的值。使用如下:
var key = Symbol(); //定义Symbol对象
console.log(typeof key); //symbol ,表示为类型,而且不是string类型的。
key = Symbol('这是一个说明'); //可以在定义Symbol的时候,添加一个说明
Symbol不能与其他类型值进行运算,但是可以显式转换为字符串,和转换为布尔值
console.log(key.toString());
console.log(String(key));
if(key){
console.log('key is true');
}
在对象的内部,要使用Symbol值定义属性时,必须放在方括号中。
var obj = {
[key]: 'abc'
};
# 6、可以子类化的内置对象
在ES6中,我们可以自定义类型来继承内置对象,这个时候,如果要自定义构造函数,必须要在构造函数中调用super(),来呼叫父类的构造。
'use strict';
class MyArray extends Array {
// 如果要定义constuctor,那么就必须要使用super来执行父类的构造
constructor(){
super();
}
}
var arr = new MyArray();
arr[1] = 12;
console.log(arr.length === 2);
# 7、新增的API(Math + Number + String + Array + Object APIs)
如下代码,一目了然:
//数字类api
Number.EPSILON; //增加常量e
Number.isInteger(Infinity) // false
Number.isNaN("NaN") // false
//数学类api
Math.acosh(3) // 1.762747174039086
Math.hypot(3, 4) // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2
//字符串类api
"abcde".includes("cd") // true
"abc".repeat(3) // "abcabcabc"
//数组api
Array.from(document.querySelectorAll('*')) // Returns a real Array
Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior
[0, 0, 0].fill(7, 1) // [0,7,7]
[1, 2, 3].find(x => x == 3) // 3
[1, 2, 3].findIndex(x => x == 2) // 1
[1, 2, 3, 4, 5].copyWithin(3, 0) // [1, 2, 3, 1, 2]
["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
["a", "b", "c"].keys() // iterator 0, 1, 2
["a", "b", "c"].values() // iterator "a", "b", "c"
//对象api
Object.assign(Point, { origin: new Point(0,0) })
# 8、二进制和八进制字面量(Binary and Octal Literals)
直接上示例:
var n1 = 0b111110101; //0b前缀,表示二进制字面量
console.log(n1); //输出的时候,直接用10进制展示
var n2 = 0o12345; //0o前缀,表示八进制字面量
console.log(n2);
# 9、承诺(Promises)
**Promise**是ES6中新增的异步编程库。
//使用承诺定义一个异步任务
var p = new Promise((resolve, reject)=>{
return setTimeout(function(){
reject('ok');
}, 2000);
});
p.then((data)=>{
console.log(data);
}, (data)=>{
console.log('error' + data);
}).then(()=>{
console.log('throw err');
throw 'Error';
}).catch(err => {
console.log(err);
});
# 10、参考资料
1、ECMAScript 6 features [https://github.com/lukehoban/es6features](https://github.com/lukehoban/es6features)
2、ECMAScript 6 入门 [http://es6.ruanyifeng.com/](http://es6.ruanyifeng.com/)
================================================
FILE: ES6入门/ES6入门系列二(特性总览上).md
================================================
---
title: ES6入门系列二(特性总览上)
date: 2017/02/21 14:47:10
---
## 0、导言
最近从coffee切换到js,代码量一下子变大了不少,也多了些许陌生感。为了在JS代码中,更合理的使用ES6的新特性,特在此对ES6的特性做一个简单的总览。
## 1、箭头函数(Arrows)
使用 => 简写的函数称之为箭头函数,和C#的lambda,CoffeeScript的语法比较类似。
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
//简单使用
var arr2 = arr.map(x => x + 1);
console.log(arr2);
//等价于
arr2 = arr.map(function (x) {
return x + 1;
});
console.log(arr2);
//此处必须用()包裹对象,否则语法错误
var arr3 = arr.map((x, i) => ({
idx: i,
value: x
}));
console.log(arr3);
//等价于
arr3 = arr.map(function (x, i) {
return {
idx: i,
value: x
};
});
console.log(arr3);
//如果函数是一个语句块
var arr4 = [];
arr.forEach(x => {
if (x % 3 === 0) {
arr4.push(x);
}
});
console.log(arr4);
//等价于
arr4.length = 0;
arr.forEach(function (x) {
if (x % 3 === 0) {
arr4.push(x);
}
});
console.log(arr4);
//此处this一直指向obj对象。和一般function不同,箭头函数共享词法作用域。
var obj = {
name: 'test',
foods: ['fish', 'milk'],
eat() {
this.foods.forEach(x =>
console.log(this.name + 'like eat ' + x) //注意。此地不能有分号,因为属于表达式,不是语句块。
);
}
};
obj.eat();
## 2、类定义(classes)
在ES6中,可以直接使用class关键字定义类,并可以定义构造函数,静态方法,get/set 方法,实例方法等。
'use strict';
class Animal {
constructor(name) {
this.name = name;
}
eat() {
console.log(this.name + ' should eat food.');
}
};
class Dog extends Animal {
//构造函数
constructor(age) {
super('Dog');
this.age = age;
//实例方法
this.instanceFun = function () {
console.log('Instance Function.');
}
}
//静态方法
static go() {
console.log('Dog will go.');
}
//原型方法
prototypeFunc() {
console.log('Prototype Function.');
}
//get、set
get dogName() {
return 'Name: ' + this.name;
}
set dogName(value) {
this.name = value;
}
//get
get dogAge() {
return 'Age: ' + this.age;
}
}
var dog = new Dog(4);
Dog.go();
dog.eat();
dog.instanceFun();
dog.prototypeFunc();
console.log(dog.dogName);
dog.dogName = 'x';
console.log(dog.dogName);
console.log(dog.dogAge);
dog.dogAge = 5; //会失败,属性没有getter。
## 3、增强的对象常量(Enhanced Object Literals)
var obj = {
name: 'obj',
__proto__: {
name: 'parent'
},
toString() {
// 可以通过super直接取到原型对象的属性
return super.name + ':' + this.name;
},
['prop_' + (() => 1)()]: 1 //动态属性
};
console.log(obj.toString());
console.log(obj.prop_1);
## 4、模板字符串(Template Strings)
简化了字符串的构造,拼接等。
//基本字符串,\n有效。
var str = `Basic string '\n' in Javascript.`;
console.log(str);
// Basic string '
// ' in Javascript.
//多行字符串
str = `Multiline
strings`;
console.log(str);
// Multiline
// strings
//字符串插值
var name = 'Jay';
str = `Hello, ${name}`;
console.log(str); // 'Hello, Jay.'
## 5、解构(Destructuring)-- Node和Chrome中执行不成功,忽略
允许使用模式匹配,来匹配数组和对象。
## 6、Default + Rest + Spread -- Node和Chrome中执行不成功,忽略
## 7、局部变量+常量(Let + Const)
'use strict'; //必须启用严格模式
{
let x = 1;
}
console.log(x); //Error:x is not defined.
const PI = 3.14;
PI = 3.15; //Error: 无法对常量赋值
console.log(PI); //3.14
## 8、迭代器 + For..Of(Iterators + For..Of)
比较类似于C#中的IEnumerable,使用for..of来访问迭代器。它不要求实现一个数组,而是使用和LINQ类似的懒加载。
(function () {
'use strict';
let test = {
[Symbol.iterator]() {
let pre = 0,
cur = 1;
return {
next() {//此处方法名不能变
pre = cur;
cur = pre + cur;
console.log('pre = ' + pre);
console.log('cur = ' + cur);
//返回值的属性名也不能改变
return {
done: false,
value: cur
};
}
}
}
};
for (var n of test) {
if (n > 1000) {
break;
}
console.log(n);
}
})();
// 用于获取数组的键值
for (var item of[1, 3, 5, 7, 9]) {
console.log(item);
}
## 9、生成器(Generators)
允许在function*()函数中使用yield关键字。
function* foo(x) {
var y = 2 * (yield(x + 1));
var z = yield(y / 3);
return (x + y + z);
}
var it = foo(5);
console.log(it.next()); // { value:6, done:false }
console.log(it.next(12)); // { value:8, done:false }
console.log(it.next(13)); // { value:42, done:true }
## 10、unicode
增加了对unicode字符的支持。比如“𠮷”(这个和吉不一样哦!)
console.log('𠮷'.length); //2
// 正则表达式增加了u这个参数,匹配unicode字符。
console.log("𠮷".match(/./u)[0].length) // 2
// new form
"\u{20BB7}"=="𠮷"=="\uD842\uDFB7"
// new String ops
"𠮷".codePointAt(0) == 0x20BB7
// for-of iterates code points
for(var c of "𠮷") {
console.log(c);
}
## 11、参考资料
1、ECMAScript 6 features [https://github.com/lukehoban/es6features](https://github.com/lukehoban/es6features)
2、ECMAScript 6 入门 [http://es6.ruanyifeng.com/](http://es6.ruanyifeng.com/)
================================================
FILE: ES6入门/ES6入门系列四(测试题分析).md
================================================
---
title: ES6入门系列四(测试题分析)
date: 2017/02/21 14:47:10
---
## 0、导言
ES6中新增了不少的新特性,来点测试题热热身。具体题目来源请看:[http://perfectionkills.com/javascript-quiz-es6/](http://perfectionkills.com/javascript-quiz-es6/)。
以下将一题一题来解析what和why。
## 1、题目一
(function(x, f = () => x) {
var x;
var y = x;
x = 2;
return [x, y, f()];
})(1)
A、 [2, 1, 1]
B、 [2, undefined, 1]
C、 [2, 1, 2]
D、 [2, undefined, 2]
**解析:**本题主要考察的知识点是1、参数值与函数体内定义的重名变量的优先级;2、ES6的默认参数;3、箭头函数。
在本题中,先执行x的定义,然后函数参数x=1,接着是y = x = 1,接着再x = 2,第三个是执行f函数,箭头函数如果只是表达式,那么等价于return 表达式,由于箭头函数的作用域等于定义时的作用域,那么函数定义时x=1,所以最后的return x 等价于 return 1
## 2、题目二
(function() {
return [
(() => this.x).bind({ x: 'inner' })(),
(() => this.x)()
]
}).call({ x: 'outer' });
A、 ['inner', 'outer']
B、 ['outer', 'outer']
C、 [undefined, undefined]
D、 Error
**解析:**本题主要考察的是箭头函数的作用域问题,箭头函数的作用域等于定义时的作用域,所以通过bind设置的this是无效的。那么结果就显而易见了。
## 3、题目三
let x, { x: y = 1 } = { x }; y;
A、 undefined
B、 1
C、 { x: 1 }
D、 Error
**解析:**本题主要考察的是对象赋值,先定义x,然后在赋值的时候会执行一次y=1,最后返回y的值。
## 4、题目四
(function() {
let f = this ? class g { } : class h { };
return [
typeof f,
typeof h
];
})();
A、 ["function", "undefined"]
B、 ["function", "function"]
C、 ["undefined", "undefined"]
D、 Error
**解析:**本题主要考察定义函数变量时,命名函数的名称作用域问题。在定义函数变量时,函数名称只能在函数体中生效。
## 5、题目五
(typeof (new (class { class () {} })))
A、 "function"
B、 "object"
C、 "undefined"
D、 Error
**解析:**本题主要考察对象的类型,和原型方法。该提可以分解如下:
// 定义包含class原型方法的类。
var Test = class{
class(){}
};
var test = new Test(); //定义类的实例
typeof test; //出结果
## 6、题目六
typeof (new (class F extends (String, Array) { })).substring
A、 "function"
B、 "object"
C、 "undefined"
D、 Error
**解析:**本题主要考察ES6中class的继承,以及表达式的返回值和undefined的类型。题目其实可以按照如下方式分解:
//由于JS的class没有多继承的概念,所以括号被当做表达式来看
(String, Array) //Array,返回最后一个值
(class F extends Array); //class F继承成Array
(new (class F extends Array)); //创建一个F的实例
(new (class F extends (String, Array) { })).substring; //取实例的substring方法,由于没有继承String,Array没有substring方法,那么返回值为undefined
typeof (new (class F extends (String, Array) { })).substring; //对undefined取typeof
## 7、题目七
[...[...'...']].length
A、 1
B、 3
C、 6
D、 Error
**解析:**本题主要考察的是扩展运算符...的作用。扩展运算符是将后面的对象转换为数组,具体用法是:
[...<数据>] 比如 [...'abc']等价于["a", "b", "c"]
## 8、题目八
typeof (function* f() { yield f })().next().next()
A、 "function"
B、 "generator"
C、 "object"
D、 Error
**解析:**本题主要考察ES6的生成器。题目可以如下分解:
function* f() { yield f }; //定义一个生成器
var g = f(); //执行生成器
var temp = g.next(); //返回第一次yield的值
console.log(temp); //测试,查看temp,其实是一个object
temp.next();//对对象调用next方法,无效
## 9、题目九
typeof (new class f() { [f]() { }, f: { } })[`${f}`]
A、 "function"
B、 "undefined"
C、 "object"
D、 Error
**解析:**本题主要考察ES6的class,以及动态属性和模板字符串等。 实际上这个题动态属性和模板字符串都是烟雾弹,在执行new class f()的时候,就已经有语法错误了。
## 10、题目十
typeof `${{Object}}`.prototype
A、 "function"
B、 "undefined"
C、 "object"
D、 Error
**解析:**本题考察的知识点相对单一,就是模板字符串。分解如下:
var o = {Object},
str = `${o}`;
typeof str.prototype;
## 11、题目十一
((...x, xs)=>x)(1,2,3)
A、 1
B、 3
C、 [1,2,3]
D、 Error
**解析:**本题主要考察的是Rest参数的用法,在ES6中,Rest参数只能放在末尾,所以该用法的错误的。
## 12、题目十二
let arr = [ ];
for (let { x = 2, y } of [{ x: 1 }, 2, { y }]) {
arr.push(x, y);
}
arr;
A、 [2, { x: 1 }, 2, 2, 2, { y }]
B、 [{ x: 1 }, 2, { y }]
C、 [1, undefined, 2, undefined, 2, undefined]
D、 Error
**解析:**本题看起来是考察let的作用域和of迭代的用法。实则是考察let的语法,let之后是一个参数名称。所以,语法错误
## 13、题目十三
(function() {
if (false) {
let f = { g() => 1 };
}
return typeof f;
})()
A、 "function"
B、 "undefined"
C、 "object"
D、 Error
**解析:**本题非常有迷惑性,看似考察的let的作用域问题,实则考察了箭头函数的语法问题。
## 14、题目答案
相信大家看过题目的解析,对题目答案已经了然。为了完善本文,还是在最后贴出所有题目的答案:
ABBAB CBDDB DDD
================================================
FILE: GoLang学习笔记/01_开始GO.md
================================================
---
title: 01_开始GO
date: 2017/02/21 14:47:10
---
# 0、前言
Go是啥?看[这里](https://golang.org)。简而言之,就是一门系统级的编程语言。
为嘛要学/用它?
1. 追个潮流,体验下不同风格的语言设计思想
2. 跨平台部署,单文件部署
3. 强大的功能(系统级)
4. 对分布式的友好支持
上手有啥问题?
1. 不友好的语法(也有一部分人觉得语法很优雅,对我来说,这语法可算不上友好)
2. 和大多数语言不一样的设计风格(需要慢慢消化)
3. 没有趁手的IDE(LiteIDE, Sublime Text, VsCode这几个可以自己优化得比较好用)
# 1、开干
虽然比较恶心语法,但是其他优秀的点,也是比较吸引人的,再加上项目需要,那就开干。
因为有其他前/后端语言的经验,就跳过常规学习过程,直接上手做项目。
**注意:虽然跳过了不少步骤,但是语法还是要学习的,书还是要看的,先来一本《Go语言编程》**
### 1.1、创建项目基架
``Go`` 是一个有很多规范的语法,就连创建目录结构也有一定的约束。之前的版本略过,我本地安装的是 ``go1.7.3`` 版本,通过 ``go version`` 可查看。
接下来就为该版本,创建一个相对较为标准的项目结构。
================================================
FILE: JS札记/ES6 Class如何管理私有数据.md
================================================
---
title: ES6 Class如何管理私有数据
date: 2017/02/21 14:47:10
---
## 0、前言
在ES5时代,要模拟对象的私有变量,是比较容易的,代码如下:
```javascript
function Person(){
var _age = 20; //定义一个私有变量,外部无法访问。
this.setAge = function(value){
_age = value;
}
this.getAge = function(){
return _age;
}
}
```
在ES6中,虽然可以在 ``Class`` 的 ``constructor`` 中实现类似function的私有方法,但是实际上,ES6中并不推荐这种做法。这样极大的加重了对象的实例。
那我们就来看看在ES6中有多少方法可以实现私有数据管理。
## 1、在构造函数中存储私有数据
该方式,和在ES5中,没有什么区别。
```javascript
class Person{
constructor(){
var _age = 20;
this.setAge = value => _age = value;
this.getAge = _ => _age;
}
}
```
该方式有一个变种,就是利用构造参数来存储,减少重新定义变量。
```javascript
class Person{
constructor(age){
this.setAge = value => age = value;
this.getAge = _ => age;
}
}
```
**优点:**
1. 数据绝对安全,外部无法直接通过属性访问到。
2. 不会与其他私有属性有任何冲突。如
```javascript
let p = new Person();
p.age = 10;
p.getAge(); //20
```
**缺点:**
1. 代码不怎么优雅,需要把方法设置为实例方法,才能访问到私有数据。
2. 实例方法,比较浪费内存(每个实例都会拷贝一份)。
## 2、通过命名约定来使用私有数据
该方式是在ES6 Class 中,我个人比较推荐的一个方式,实现代码如下:
```javascript
class Person{
constructor(){
this._age = 0;
}
setAge(value){
this._age = value;
}
getAge(){
return this._age;
}
}
```
**优点:**
1. 代码看起来非常不错,简单易懂。
2. 能否在原型方法中访问。
**缺点:**
1. 并不安全,如果不遵守约定,直接操作_age,也是可行的,如:
```javascript
let p = new Person();
p._age = 555;
p.getAge(); // 555
```
2. 如果在对象上设置同名属性,会覆盖掉原本是私有属性。
## 3、利用WeakMap来存储私有数据
该方式是利用WeakMap可以用Object来做key的特点,把this当做key来存储具体的私有属性。具体实现如下:
```javascript
let dataStore = new WeakMap();
class Person{
constructor(){
dataStore.set(this, {age: 20});
}
setAge(value){
let oldObj = dataStore.get(this);
let newObj = Object.assign(oldObj, {age: value});
dataStore.set(this, newObj);
}
getAge(){
return dataStore.get(this).age;
}
}
```
如何使用?
```javascript
let p1 = new Person();
p1.getAge(); // 20
p1.setAge(25);
p1.getAge(); // 25
```
**优点:**
1. 能够使用原型方法,内存占用小;
2. 比命名约定属性名称安全性更高;
2. 不会有命名冲突(允许同名实例属性);
**缺点:**
1. 代码没有命名约定方式(方式2)优雅;
2. 依赖外部对象;
## 4、利用Symbol来生成私有属性key。
该方式和命名约定方式没有本质区别,只是用 ``Symbol`` 来生成key,提高了key的安全性。具体实现代码如下:
```javascript
const keyForAge = Symbol('age');
class Person{
constructor(){
this[keyForAge] = 20;
}
setAge(value){
this[keyForAge] = value;
}
getAge(){
return this[keyForAge];
}
}
```
**优点:**
1. 能够使用原型方法,内存占用小;
2. 比命名约定属性名称安全性更高,但也并不安全;
```javascript
let p1 = new Person();
Object.keys(p1); // [],无法直接访问到属性名
p1[keyForAge] = 30;
p1.getAge(); // 30
Reflect.ownKeys(p1); // [Symbol(age)],通过能方式能遍历Key
```
2. 不会有命名冲突(允许同名实例属性);
**缺点:**
1. 代码没有命名约定方式(方式2)优雅;
2. 依赖外部对象;
3. 不是绝对安全;
## 5、Other
能够达到的目的的方式有很多,也没有那个有绝对优势,根据实际的需求,来选择合适的方式才是最佳的方式。
**参考资料**
1. [http://www.2ality.com/2016/01/private-data-classes.html](http://www.2ality.com/2016/01/private-data-classes.html)
================================================
FILE: JS札记/JS实现继承的几种方式.md
================================================
---
title: JS实现继承的几种方式
date: 2017/02/21 14:47:10
---
## 前言
JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一。那么如何在JS中实现继承呢?让我们拭目以待。
## JS继承的实现方式
既然要实现继承,那么首先我们得有一个父类,代码如下:
// 定义一个动物类
function Animal (name) {
// 属性
this.name = name || 'Animal';
// 实例方法
this.sleep = function(){
console.log(this.name + '正在睡觉!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
### 1、原型链继承
**核心:** 将父类的实例作为子类的原型
function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.eat('fish'));
console.log(cat.sleep());
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
特点:
1. 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
2. 父类新增原型方法/原型属性,子类都能访问到
3. 简单,易于实现
缺点:
1. 要想为子类新增属性和方法,必须要在``new Animal()``这样的语句之后执行,不能放到构造器中
2. 无法实现多继承
3. 来自原型对象的引用属性是所有实例共享的(详细请看附录代码: [示例1](javascript:void(0);))
4. 创建子类实例时,无法向父类构造函数传参
推荐指数:★★(3、4两大致命缺陷)
### 2、构造继承
**核心:**使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
特点:
1. 解决了1中,子类实例共享父类引用属性的问题
2. 创建子类实例时,可以向父类传递参数
3. 可以实现多继承(call多个父类对象)
缺点:
1. 实例并不是父类的实例,只是子类的实例
2. 只能继承父类的实例属性和方法,不能继承原型属性/方法
3. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
推荐指数:★★(缺点3)
### 3、实例继承
**核心:**为父类实例添加新特性,作为子类实例返回
function Cat(name){
var instance = new Animal();
instance.name = name || 'Tom';
return instance;
}
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // false
特点:
1. 不限制调用方式,不管是``new 子类()``还是``子类()``,返回的对象具有相同的效果
缺点:
1. 实例是父类的实例,不是子类的实例
2. 不支持多继承
推荐指数:★★
### 4、拷贝继承
function Cat(name){
var animal = new Animal();
for(var p in animal){
Cat.prototype[p] = animal[p];
}
Cat.prototype.name = name || 'Tom';
}
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
特点:
1. 支持多继承
缺点:
1. 效率较低,内存占用高(因为要拷贝父类的属性)
2. 无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)
推荐指数:★(缺点1)
### 5、组合继承
**核心:**通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
Cat.prototype = new Animal();
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true
特点:
1. 弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
2. 既是子类的实例,也是父类的实例
3. 不存在引用属性共享问题
4. 可传参
5. 函数可复用
缺点:
1. 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
推荐指数:★★★★(仅仅多消耗了一点内存)
### 6、寄生组合继承
**核心:**通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
(function(){
// 创建一个没有实例方法的类
var Super = function(){};
Super.prototype = Animal.prototype;
//将实例作为子类的原型
Cat.prototype = new Super();
})();
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true
特点:
1. 堪称完美
缺点:
1. 实现较为复杂
推荐指数:★★★★(实现复杂,扣掉一颗星)
## 附录代码:
示例一:
function Animal (name) {
// 属性
this.name = name || 'Animal';
// 实例方法
this.sleep = function(){
console.log(this.name + '正在睡觉!');
}
//实例引用属性
this.features = [];
}
function Cat(name){
}
Cat.prototype = new Animal();
var tom = new Cat('Tom');
var kissy = new Cat('Kissy');
console.log(tom.name); // "Animal"
console.log(kissy.name); // "Animal"
console.log(tom.features); // []
console.log(kissy.features); // []
tom.name = 'Tom-New Name';
tom.features.push('eat');
//针对父类实例值类型成员的更改,不影响
console.log(tom.name); // "Tom-New Name"
console.log(kissy.name); // "Animal"
//针对父类实例引用类型成员的更改,会通过影响其他子类实例
console.log(tom.features); // ['eat']
console.log(kissy.features); // ['eat']
原因分析:
关键点:属性查找过程
执行tom.features.push,首先找tom对象的实例属性(找不到),
那么去原型对象中找,也就是Animal的实例。发现有,那么就直接在这个对象的
features属性中插入值。
在console.log(kissy.features); 的时候。同上,kissy实例上没有,那么去原型上找。
刚好原型上有,就直接返回,但是注意,这个原型对象中features属性值已经变化了。
================================================
FILE: JS札记/JavaScript之毒瘤.md
================================================
---
title: JavaScript之毒瘤
date: 2017/02/21 14:47:10
---
## 0、导言
JavaScript中有许多难以避免的问题特性。接下来就一一揭示。
## 1、全局变量
在所有JavaScript的糟糕特性中,最为糟糕的就是全局变量的依赖。全局变量使得在同一个程序中运行独立的子程序变得更难。
## 2、作用域
JavaScript是类C的语法,但是却没有提供类C的块级作用域。
## 3、自动插入分号
JavaScript有一个自动修复机制,会试图通过自动插入分号来修正有缺损的程序,但是它有可能掩盖更为严重的错误。
## 4、保留字
有太多的单词在JavaScript中被保留,它们不能用来命名变量或者函数(在大部分执行环境下,部分关键字是可用的)。
## 5、Unicode字符
JavaScript设计之初,Unicode预计只会有65536个字符。实际上,到现在Unicode有多大百万个字符。这也就导致了JavaScript会认为一对字符是两个不同的字符(Unicode把一对字符视为一个单一的字符)
## 6、typeof
不要指望typeof返回的类型。比如null或者是检测对象,另外检测正则可能会返回function或者是object。
## 7、parseInt
parseInt把一个字符串转换为整数,会在遇到非数字时停止解析。另外如果第一个字符是0,还会按照8进制来取值。
## 8、运算符(+)
+运算符可以用于加法运算或者是字符串连接,究竟如何执行会取决于其参数类型。
## 9、 浮点数
二进制浮点数不能正确的处理十进制小数,因此0.1+0.2 不等于0.3。
## 10、NaN
NaN是一个特殊的数量值,它表示不是一个数字。也是唯一一个不等于自身的JavaScript数值。
## 11、伪数组
JavaScript没有真正的数组,就连Array也是通过object来模拟的,如果完全达不到真正的数组的地步。同时typeof运算符也不能辨别数组和对象。
## 12、假值
JavaScript中包含诸多的假值,如: 0, NaN, '', false, null, undefined
## 13、hasOwnProperty
hasOwnProperty方法被用做一个过滤器来避开for..in语句的隐患,但hasOwnProperty是一个普通的方法,所以是可以被重写的。
## 14、对象
JavaScript的对象,永远不会是真的空对象,因为可以从原形链取得成员属性。
================================================
FILE: JS札记/JavaScript之糟粕.md
================================================
---
title: JavaScript之糟粕
date: 2017/02/21 14:47:10
---
## 0、导言
在上篇《JavaScript之毒瘤》中,列举了一些在JavaScript中难以避免的问题特性。本篇将会展示JavaScript中有问题的特性,但我们很容易就能便面它们。通过这些简单的做法,让JavaScript称为一门更好的语言。
## 1、==
JavaScript有两组相等运算符。 === 和 !==,以及 == 和 !==。 === 和 !== 不会进行类型转换,一般会按照你期望的方式工作。由于JavaScript的类型转换系统相当复杂,如果要确保==和 != 不出错,就必须要牢记转换规则。另外==运算符缺乏传递性。
//关于传递性
if a === b, b === c then a === c;
if a == b, b == c then a 不一定等于 c,破坏了传递性
**小测验**:
'' == '0'
0 == ''
0 == '0'
false == 'false'
false == '0'
false == undefined
false == null
null == undefined
'\t\t\n' == 0
**总结**:推荐使用===和!==,尽量避免使用==和!=。
## 2、with语句
JavaScript提供了一个with语句,本意是用它来快速访问对象,不幸的是,它的结果可能有时不可预料。
**小测验**:
window.a = 1;
var obj={};
with(obj){
console.log(a);
}
obj.a = 2;
with(obj){
console.log(a);
}
**结论**:避免使用with。
## 3、eval
eval函数传递一个字符串给JavaScript*编译器*,并且执行结果。有问题问题呢?首先是代码难以阅读,另外需要运行编译器,导致性能低下;同时,还减弱了程序的安全性。**注:**Function构造函数,setTimeout、setInterval的字符串参数形式和eval是执行方式一致。
**结论**:避免使用eval,setTimeout、setInterval的字符串参数和Function构造函数。
## 4、continue
continue语句跳到循环顶部,性能比较低下。
**小测验**:
var counter = 10;
console.time('t1');
var sum = 0;
for(var i = 0; i < counter; i++){
if(i % 3 !== 0){
continue;
}
sum = sum + i;
}
console.log(sum);
console.timeEnd('t1');
console.time('t2');
var sum = 0;
for(var i = 0; i < counter; i++){
if(i % 3 === 0){
sum = sum + i;
}
}
console.log(sum);
console.timeEnd('t2');
**结论**:尽量优化代码,减少continue的使用。
## 5、switch
switch语句中,除非明确的中断流程,否则每次条件判断后,都可以穿越到下一个case条件。这很容易造成bug。
**小测验**:
var a = 15;
switch(a){
case a * 1 :
console.log('a*1');
case a / 1:
console.log('a/1');
default:
console.log('a');
}
**结论**:不要刻意的使用case条件穿越。
## 6、缺少块的语句
if、while、do或for可以接受代码块,也可以接受单行语句。单行语句的形式是一种带刺的玫瑰。虽然它可以节约2个字节,但它模糊了程序的结构。
**小测验**:
if(1 == '0')
console.log('1 == 0');
console.log('my god');
// VS
if(1 == '0'){
console.log('1 == 0');
}
console.log('my god');
**结论**:避免使用模糊程序结构的单行语句。
## 7、++ --
递增和递减使得可以用非常简洁的风格去编码。但是它可能造成缓冲区溢出、同时往往让代码变得拥挤也不易于理解。
**小测验**:
var a = 1;
a = a++ + ++a;
console.log(a);
a = 1;
a = a++ + a++;
console.log(a);
**结论**:避免使用++ --。
## 8、位运算符
JavaScript有着和Java相同的一套位运算符。Java中位运算符处理整数,非常快。在JavaScript中,只有双精度浮点数,所以位运算非常慢。另外,&非常容易误写为&&,使得bug容易被隐藏起来。
**小测验**:
var counter = 10000;
var a = 5;
var sum = 0;
console.time('t1');
for(var i = 0; i < counter; i++){
sum += a << 1;
}
console.log(sum);
console.timeEnd('t1');
sum = 0;
console.time('t2');
for(var i = 0; i < counter; i++){
sum += a * 2;
}
console.log(sum);
console.timeEnd('t2');
**结论**:避免使用位运算符。
## 9、function语句 与 function表达式
JavaScript中既有function语句,也有function表达式,这令人困惑,似乎看起来也差不多。function语句在解析时会产生变量提升,放宽了函数必须先申明后使用的的要求。同时,JS在if语句中使用function语句也是被禁止的,但实际上大多数浏览器允许在if中使用function语句,这有可能会导致兼容性问题。
由于一个语句不能以一个函数表达式开头,如下如下写法,可以改写为另外一种形式。
function (){}(); //Error
(function(){}()); // Right
**小测验**:
// function语句
function fun(){}
// function表达式
var fun = function(){};
**结论**:合理使用function语句和function表达式。
## 10、类型的包装对象
JavaScript有一种类型的包装对象,如 new Number(1);这很容易令人困惑。
**小测验**:
var num1 = new Number(1);
var num2 = 1;
console.log(typeof num1);
console.log(num1 === num2);
**结论**:避免使用包装对象,如new Boolean(),new String(),new Number()等
## 11、new
new运算符创建一个继承于其原型的新对象,并将新创建的对象绑定给this。但是,如果忘记使用new,那么就得到一个普通的函数调用,对象属性也会被绑定到全局对象上。这不会导致什么编译警告,也没有运行警告。
根据惯例,需要用new的函数,以首字母大写命名。这能部分程度上便于我们发现错误。
**小测验**:
function Person(){
this.name = 'Default';
this.sex = undefiend;
}
Person();
new Person();
//更好的实现
function Person(){
if(this === window){
return new Person();
}
this.name = 'Default';
this.sex = undefiend;
}
**结论**:合理的避免使用new。另外可以先判断this,再做对应处理。
## 12、void
大部分语言中,void是一种类型,在Js中,void是一种运算符,接收一个运算数,并返回undefined
**小测验**:
void 0
void true
**结论**:有限的使用void
================================================
FILE: JS札记/JavaScript的深拷贝的实现.md
================================================
---
title: JavaScript的深拷贝的实现
date: 2017/02/21 14:47:10
---
## JavaScript的数据类型
### 简单数据类型
1. string
2. number
3. boolean
4. function
5. null
6. undefined
### 复杂数据类型
1. String
2. Number
3. Boolean
3. Function
4. Date
5. Array
6. RegExp
7. Object
## 各种类型的深复制方式:
先来看看简单类型的复制方式:
//string
var s1 = 'abc';
var s2 = s1;
s2 = 'ccc';
console.log(s1);
//number
var n1 = 12.1;
var n2 = n1;
n2 = 7410;
console.log(n1);
//boolean
var b1 = true;
var b2 = b1;
b2 = false;
console.log(b1);
//null
var nu1 = null;
var nu2 = nu1;
nu2 = 'abc';
console.log(nu1);
//undefined
var u1 = undefined;
var u2 = u1;
u2 = 'abc';
console.log(u1);
从以上的代码可以看出,简单类型,只需要直接赋值就是深复制了。但是也有一个例外,那就是function。
接着来看看String、Number、Boolean、Date的深复制:
//String
var s1 = new String('s1');
var s2 = new String(s1);
console.log(s2);
//Number
var n1 = new Number('1');
var n2 = new Number(n1);
console.log(n2);
//Boolean
var b1 = new Boolean(1);
var b2 = new Boolean(b1);
console.log(b2);
//Date
var d1 = new Date();
var d2 = new Date(d1);
console.log(d2);
除以上的做法之外,还需要对实例属性进行拷贝。那么剩下的Function、function、RegExp和Array还有Object又该怎么拷贝呢?这几个比较特殊,我们一个一个来:
对于Function和function的深拷贝,我们可以按照如下的方式来做:
var f1 = new Function('a', 'console.log("f1" + a);');
var f2 = function(b){console.log('f2' + b);};
//通过toString获取源代码(有浏览器兼容问题)
var code = f1.toString();
//利用eval进行复制
var f1_copy = (function(functionCode){
eval('var f = ' + functionCode);
return f;
})(code);
f1_copy('abc');
//当然f2也可以用同样的方式来复制。
接着,我们来看下RegExp,可以同样同时eval来执行拷贝,也可以使用如下方式:
var reg1 = /abc/g;
var reg2 = new RegExp('abc', 'gmi');
var reg1_copy = (function(reg){
var pattern = reg.valueOf();
var flags = (pattern.global ? 'g' : '') +
(pattern.ignorecase ? 'i' : '') + (pattern.multiline ? 'm' : '');
return new RegExp(pattern.source, flags);
})(reg1);
最后,我们来说一说Array的复制,有的人可以说,直接用slice复制一份出来就是了,那我们来看看,是否真的达到效果的呢?
var o = {name: 'Jay'};
var arr1 = [o, '22', 1];
var arr2 = arr1.slice(0);
arr2[0].name = 'Arr2';
console.log(arr1[0].name);
很简短的代码,直接就把slice抛弃了,slice只能保证Array是新的,并不意味着内部的元素是深拷贝的,那么如何做呢?就是遍历元素,对每个元素进行深拷贝了。代码如下:
var o = {name: 'Jay'};
var arr1 = [o, '22', 1];
var arr2 = [];
for(var i = 0, len = arr1.length; i < len; i++){
//注意,deepClone还未实现
arr2.push(deepClone(arr1[i]));
}
以上对针对不同的类型,特殊的代码,那么如何来拷贝实例属性呢?代码如下:
var o = {p1: '1', p2: 2, p3: function(){}};
var copy = {};
for(var p in o){
//注意deepClone还未实现
copy[p] = deepClone(o[p]);
}
**注意:针对复杂类型,还需要同时copy.constructor = source.constructor来保证构造函数一致。**
## 最终的深复制代码
通过以上的分析与代码示例,那么我们最终的代码又是怎样的呢?详细代码如下:
//自调用函数,防御性编程
;
(function (window) {
'use strict';
function getCustomType(obj) {
var type = typeof obj,
resultType = 'object';
//简单类型
if (type !== 'object' || obj === null) {
resultType = 'simple';
} else if (obj instanceof String || obj instanceof Number || obj instanceof Boolean || obj instanceof Date) {
resultType = 'complex';
} else if (obj instanceof Function) {
resultType = 'function';
} else if (obj instanceof RegExp) {
resultType = 'regexp';
} else if (obj instanceof Array) {
resultType = 'array';
}
return resultType;
}
function cloneProperties(dest, source) {
dest.constructor = source.constructor;
for (var p in source) {
dest[p] = deepClone(source[p]);
}
return dest;
}
function cloneSimple(obj) {
return obj;
}
function cloneComplex(obj) {
var result = new obj.constructor(obj);
return cloneProperties(result);
}
function cloneFunction(obj) {
var funCopy = (function (f) {
eval('var abcdefg_$$$$ = ' + obj.toString());
return abcdefg_$$$$;
})(obj);
return cloneProperties(funCopy);
}
function cloneRegExp(obj) {
var pattern = obj.valueOf();
var flags = (pattern.global ? 'g' : '') +
(pattern.ignorecase ? 'i' : '') + (pattern.multiline ? 'm' : '');
var reg = new RegExp(pattern.source, flags);
return cloneProperties(reg);
}
function cloneArray(obj) {
var resultArr = [];
for (var i = 0, len = obj.length; i < len; i++) {
resultArr.push(deepClone(obj[i]));
}
for (var p in obj) {
if (typeof p === 'number' && p < len) {
continue;
}
resultArr[p] = deepClone(obj[p]);
}
return resultArr;
}
function cloneObject(obj) {
var result = {};
result.constructor = obj.constructor;
for (var p in obj) {
result[p] = deepClone(obj[p]);
}
return result;
}
function deepClone(obj) {
var f = undefined;
switch (getCustomType(obj)) {
case 'simple':
f = cloneSimple;
break;
case 'complex':
f = cloneComplex;
break;
case 'function':
f = cloneFunction;
break;
case 'regexp':
f = cloneRegExp;
break;
case 'array':
f = cloneArray;
break;
case 'object':
f = cloneObject;
break;
}
return f.call(undefined, obj);
}
//挂载到window对象上
window.deepClone = deepClone;
})(window);
================================================
FILE: JS札记/[20141121]JavaScript之Array常用功能汇总.md
================================================
---
title: JavaScript之Array常用功能汇总
date: 2014/11/21 00:00:00
---
**导语:**在JavaScript中,Array是一个使用比较频繁的对象,那么它到底有哪些常用的方法呢?
首先,我们先看一下Array对象的类型:
typeof Array // 'function'
Array instanceof Object // true
从上可以看出,Array本质是一个function,同样派生自Object,定义如下:
function Array(args) {}
###接下来,我们来看Array自身的方法:
#### #1、concat()
定义:原型方法,连接两个或更多的数组,并返回结果(新数组)。
Array.prototype.concat = function(items) {};
示例:
var arr1 = [1, 2];
var arr2 = arr1.concat([3, 4]);
var arr3 = arr2.concat([5, 6], [7, 8] ,10, {});
console.log(arr1); // [1, 2]
console.log(arr2); // [1, 2, 3, 4]
console.log(arr3); // [1, 2, 3, 4, 5, 6, 7, 8, 10, Object]
**注意:**concat不仅可以连接单个对象,也可以连接多个对象,同时如果是参数为数组,那么会将数组元素拆分并连接,如果是对象,则直接将对象连接。<span style="color:red;"><b>该方法不会改变原始数组</b></span>
#### #2、join()
定义:原型方法,把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。
Array.prototype.join = function(separator) {};
示例:
var arr = [1, 2, 3];
console.log(arr.join('|')); // '1|2|3'
console.log(arr.join('')); // '123'
console.log(arr.join('---')); // '1---2---3'
**注意:**太常用了,没什么可注意的~
#### #3、pop()
定义:原型方法,删除并返回数组的最后一个元素。
Array.prototype.pop = function() {};
示例:
var arr1 = [1, 2, 3, 4];
var lastOne = arr1.pop();
console.log(lastOne); // 4
console.log(arr1); // [1, 2, 3]
**注意:**该方法无参数,有返回值,返回数组最后一个元素。<span style="color:red;"><b>该方法会改变原始数组</b></span>
#### #4、push()
定义:原型方法,向数组的末尾添加一个或更多元素,并返回新的长度。
Array.prototype.push = function(items) {};
示例:
var arr1 = [1, 2];
var len = arr1.push(3);
var arr2 = arr1.push(4, 5);
console.log(len);
console.log(arr1);
console.log(arr2);
**注意:**该方法的返回值会返回数组的新长度。<span style="color:red;"><b>该方法会改变原始数组</b></span>
#### #5、reverse()
定义:原型方法,颠倒数组中元素的顺序。
Array.prototype.reverse = function() {};
示例:
var arr1 = [1, 2, 3, 4, 5];
var res = arr1.reverse();
console.l
gitextract_j4qofkey/
├── .Net Platform/
│ ├── 01_Dotnet Core尝鲜.md
│ ├── 02_Dotnet Core V2.md
│ ├── C#中处理耗时任务的几种方式.md
│ ├── [20140913]可替代反射的几种方式.md
│ ├── 实战Asp.Net Core:DI生命周期.md
│ ├── 实战Asp.Net Core:中间件.md
│ ├── 实战Asp.Net Core:构建一个Core Lib.md
│ └── 实战Asp.Net Core:部署应用.md
├── .gitignore
├── AngularJS相关/
│ ├── Angular1.x升级指南.md
│ ├── AngularJS官方FAQ.md
│ ├── AngularJS教程:1W字综合指南.md
│ ├── AngularJS:Looking under the hood.md
│ ├── Angular从0到1:function(上).md
│ ├── Angular从0到1:function(下).md
│ ├── Angular再回首(1)-Component组件.md
│ ├── Angular再回首(2)-那些容易忽略的Component细节.md
│ ├── Angular再回首(3)-我们来实现一个组件.md
│ ├── Angular开发Tips.md
│ ├── Angular:指令、Controller数据共享.md
│ ├── [20140917]Angular:如何编写一个指令.md
│ ├── 用AngularJS开发Web应用程序.md
│ └── 详解angular之$q.md
├── Angular系列/
│ ├── 01_Angular2初体验.md
│ ├── 02_Angular2组件生命周期.md
│ ├── 03_Angular2的那些Decorator.md
│ ├── 04_Angular2指令简析.md
│ ├── 05_Angular2组件简析.md
│ ├── 06_Angular2管道(Pipe)简析.md
│ ├── 07_Angular2使用路由.md
│ ├── 08_Angular2动态加载组件.md
│ ├── 09_Angular2使用ui-router-ng2.md
│ ├── Angular2知识点.xmind
│ ├── Angular2踩坑大全.md
│ ├── 利用Angular实现多团队模块化SPA开发框架.md
│ └── 跟我学Angular2(1-初体验).md
├── CSS3学习之路/
│ ├── CSS3入门之文本与字体.md
│ ├── CSS3入门之转换.md
│ └── CSS3入门之边框与背景.md
├── Canvas学习札记/
│ └── 01_初识Canvas,绘制简单图形.md
├── ES6入门/
│ ├── ES6入门系列一(基础).md
│ ├── ES6入门系列三(特性总览下).md
│ ├── ES6入门系列二(特性总览上).md
│ └── ES6入门系列四(测试题分析).md
├── GoLang学习笔记/
│ └── 01_开始GO.md
├── JS札记/
│ ├── ES6 Class如何管理私有数据.md
│ ├── JS实现继承的几种方式.md
│ ├── JavaScript之毒瘤.md
│ ├── JavaScript之糟粕.md
│ ├── JavaScript的深拷贝的实现.md
│ ├── [20141121]JavaScript之Array常用功能汇总.md
│ ├── 那些不常见的JavaScript题目(上).md
│ └── 那些不常见的JavaScript题目(下).md
├── LICENSE
├── MongoDB入门基础/
│ ├── 01_记一次MongoDB裸奔.md
│ └── 02_Mongo权限探索.md
├── Other/
│ ├── Go Go.md
│ ├── NPM使用详解(上).md
│ ├── NPM使用详解(下).md
│ ├── Thrift简单实践.md
│ ├── TypeScript札记:初体验.md
│ ├── Windows下把Nginx,PM2包装为服务.md
│ ├── 开发者讨厌你API的十个原因.md
│ ├── 浅析12306前端优化点.md
│ └── 程序集强签名.md
├── PHP学习之路/
│ ├── 01_PHP简易安装环境.md
│ └── 02-PHP基础语法(上).md
├── README.md
├── React Native Cookbook/
│ ├── 章节一-new.md
│ └── 章节一.md
├── React Native 开发笔记/
│ ├── RN Aspect-01-环境准备.md
│ ├── RN Aspect-02-Hello React Native.md
│ ├── RN Aspect-03-修改名称与icon.md
│ ├── RN Aspect-04-打包App.md
│ └── React Native开发之多屏适配.md
├── React面面观/
│ ├── JSX中的那些小细节.md
│ ├── sources/
│ │ └── Redux交互流程.vsdx
│ ├── 【译】参考手册-React组件.md
│ ├── 【译】快速起步-JSX简介.md
│ ├── 【译】快速起步-事件处理.md
│ ├── 【译】快速起步-列表与KEY.md
│ ├── 【译】快速起步-条件渲染.md
│ ├── 【译】快速起步-渲染元素.md
│ ├── 【译】快速起步-状态和生命周期.md
│ ├── 【译】快速起步-状态提升.md
│ ├── 【译】快速起步-组件与属性.md
│ ├── 【译】快速起步-组合与继承.md
│ ├── 【译】快速起步-表单.md
│ ├── 【译】高级指南-不受控组件.md
│ ├── 【译】高级指南-深入JSX.md
│ └── 【译】高级指南-高阶组件.md
├── RxJS小记/
│ └── 02_RxJS之Observable.md
├── Sass学习之路/
│ ├── 01_Sass学习之路:Sass、Compass安装与命令行.md
│ └── 02_Sass学习之路:注释、变量以及导入.md
├── Vue实践之路/
│ ├── 01_认识Vue.md
│ └── 02_Vue组件(上).md
├── catalog_builder.js
├── jQuery拆解/
│ ├── 01-目录篇.md
│ ├── 02-模块化加载&防冲突处理.md
│ ├── 03-基础结构.md
│ └── jQuery中那些有趣的代码.md
├── 从0开始Stylus/
│ └── 01_Stylus简介&基本使用.md
├── 从零开始H5/
│ ├── HTML5探索一(那些新增的标签和属性).md
│ ├── 从零开始H5(一):升级你的HTML到HTML5.md
│ └── 从零开始H5(二):HTML5新技术点.md
├── 前端相关/
│ ├── CORS详解.md
│ ├── CSS布局(上).md
│ ├── CSS布局(下).md
│ ├── Google JavaScript Style Guide(上).md
│ ├── Iframe跨域通信的几种方式.md
│ ├── JSONP详解.md
│ ├── JWT详解.md
│ ├── Nginx常规用法解析.md
│ ├── TypeScript配置文件tsconfig简析.md
│ ├── VsCode简易配置手册.md
│ ├── Web API接口之FileReader.md
│ ├── Web API接口之Geolocation.md
│ ├── Webpack In Angular2.md
│ ├── Webpack初体验.md
│ ├── Webpack小抄.md
│ ├── Web前端基础测试题.md
│ ├── Yarn vs. Npm.md
│ ├── [20140311]前端构建之gulp与常用插件.md
│ ├── [20141025]从0开始Grunt.md
│ ├── [20150107]Web离线存储的几种方式.md
│ ├── 一个元素实现3个回图形.md
│ ├── 再说Promise.md
│ ├── 前端模块化:RequireJS.md
│ ├── 如何用Node编写命令行工具.md
│ ├── 探索Decorator.md
│ ├── 浏览器 Pointer Events.md
│ ├── 浏览器关闭事件分析.md
│ ├── 浏览器内容安全策略解析.md
│ ├── 浏览器历史history对象.md
│ ├── 简单学ES6.md
│ ├── 认识AMD、CMD、UMD、CommonJS.md
│ ├── 记一次Bug排查(Spider).md
│ ├── 说说如何部署node程序.md
│ ├── 这些年我们处理过的跨域.md
│ ├── 那些容易出错的Dom操作.md
│ └── 那些年我们认识的iframe.md
├── 微信小程序/
│ └── 微信小程序.xmind
├── 数据库之路/
│ ├── [20141114]这些年你需要注意的SQL.md
│ └── 说说你所熟知的MSSQL中的substring函数.md
├── 最佳实践系列/
│ └── Express异步进化史.md
├── 正则表达式/
│ └── 你真的理解正则修饰符吗.md
├── 测试相关/
│ ├── MOCHA测试代码汇总.md
│ ├── 使用chai-http实现API测试.md
│ ├── 利用Karma、Mocha搭建测试环境.md
│ └── 利用Nightwatch.js实现e2e测试.md
├── 编写高质量JS代码的68个有效方法-读书笔记/
│ ├── [20140926]编写高质量JS代码的68个有效方法(一).md
│ ├── [20141011]编写高质量JS代码的68个有效方法(二).md
│ ├── [20141030]编写高质量JS代码的68个有效方法(三).md
│ ├── [20141129]编写高质量JS代码的68个有效方法(四).md
│ ├── [20141205]编写高质量JS代码的68个有效方法(五).md
│ ├── [20141213]编写高质量JS代码的68个有效方法(六).md
│ ├── [20141220]编写高质量JS代码的68个有效方法(七).md
│ ├── [20141227]编写高质量JS代码的68个有效方法(八).md
│ ├── [20150110]编写高质量JS代码的68个有效方法(九).md
│ ├── [20150123]编写高质量JS代码的68个有效方法(十).md
│ ├── [20150214]编写高质量JS代码的68个有效方法(十一).md
│ ├── [20150304]编写高质量JS代码的68个有效方法(十二).md
│ └── [20150312]编写高质量JS代码的68个有效方法(十三).md
└── 运维&部署/
├── Docker容器管理平台Humpback进阶-私有仓库.md
├── Docker部署:Mysql Master-Slave.md
├── 一个简单易用的容器管理平台-Humpback.md
├── 前端监控系统实现.md
└── 记一次Zookeeper数据找回.md
Condensed preview — 168 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (973K chars).
[
{
"path": ".Net Platform/01_Dotnet Core尝鲜.md",
"chars": 5124,
"preview": "---\ntitle: 01_Dotnet Core尝鲜\ndate: 2017/02/21 14:47:10\n---\n\n# 0、About Dotnet Core\n\n`Dotnet Core` 是新一代的 `.Net Framework`,是"
},
{
"path": ".Net Platform/02_Dotnet Core V2.md",
"chars": 5188,
"preview": "---\ntitle: 02_Dotnet Core V2\ndate: 2017-05-20 09:14:21\n---\n\n# 前言\n\n时隔三个月,再来看 `.Net Core`,已经到 `2.x preview` 了,虽然这个版本有赶工的嫌疑"
},
{
"path": ".Net Platform/C#中处理耗时任务的几种方式.md",
"chars": 3202,
"preview": "---\ntitle: C#中处理耗时任务的几种方式\ndate: 2017/02/21 14:47:10\n---\n\n## 0、准备\n首先,我们先创建几个耗时任务:\n\n\tpublic class TestTasks\n\t{\n\t\t//无参、无返回值"
},
{
"path": ".Net Platform/[20140913]可替代反射的几种方式.md",
"chars": 2059,
"preview": "---\ntitle: C#可替代反射的几种方式\ndate: 2014/09/13\n---\n\n##标准的反射代码##\n\n\tvar type = obj.GetType();\n\tvar fieldInfo = type.GetField(\"ag"
},
{
"path": ".Net Platform/实战Asp.Net Core:DI生命周期.md",
"chars": 4809,
"preview": "---\ntitle: 实战Asp.Net Core:DI生命周期\ndate: 2018-11-30 21:54:52\n---\n\n# 1、前言\n\n`Asp.Net Core` 默认支持 `DI(依赖注入)` 软件设计模式,那使用 `DI` 的"
},
{
"path": ".Net Platform/实战Asp.Net Core:中间件.md",
"chars": 0,
"preview": ""
},
{
"path": ".Net Platform/实战Asp.Net Core:构建一个Core Lib.md",
"chars": 0,
"preview": ""
},
{
"path": ".Net Platform/实战Asp.Net Core:部署应用.md",
"chars": 4197,
"preview": "# 1、前言\n\n某一刻,你已经把 .Net Core 的程序写好了。接下来,还可以做什么呢?那就是部署了。\n\n作为一名开发工程师,如果不会部署自己开发的应用,那么这也是不完整的。接下来,我们就来说说,如何部署我们的 .Net Core 应用"
},
{
"path": ".gitignore",
"chars": 65,
"preview": ".DS_Store\nThumbs.db\ndb.json\n*.log\nnode_modules/\npublic/\n.deploy*/"
},
{
"path": "AngularJS相关/Angular1.x升级指南.md",
"chars": 7088,
"preview": "---\ntitle: Angular1.x升级指南\ndate: 2017/02/21 14:47:10\n---\n\n## 0、导言\n\nAngular从1.2.x到1.3.x是一个大的跳跃。在1.3.x之后,也有1.4.x,1.5.x这么两个大"
},
{
"path": "AngularJS相关/AngularJS官方FAQ.md",
"chars": 3849,
"preview": "---\ntitle: AngularJS官方FAQ\ndate: 2015/3/13 11:24:39\n---\n\n## 相关:最佳实践,反模式\n\n### 1、为什么会觉得jQuery插件缺失?\n\n请记住:当你在使用jQuery插件时,请在An"
},
{
"path": "AngularJS相关/AngularJS教程:1W字综合指南.md",
"chars": 1245,
"preview": "---\ntitle: AngularJS教程:1W字综合指南\ndate: 2015/3/13 11:24:39\n---\n\n# AngularJSj教程:1W字指南(译)\n**原文地址:**[http://www.airpair.com/an"
},
{
"path": "AngularJS相关/AngularJS:Looking under the hood.md",
"chars": 6034,
"preview": "---\ntitle: AngularJS:Looking under the hood\ndate: 2015/3/13 11:24:39\n---\n\n原文地址:[https://www.binpress.com/tutorial/angula"
},
{
"path": "AngularJS相关/Angular从0到1:function(上).md",
"chars": 5286,
"preview": "---\ntitle: Angular从0到1:function(上)\ndate: 2017/02/21 14:47:10\n---\n\n## 1、前言\n\nAngular作为最流行的前端MV*框架,在WEB开发中占据了重要的地位。接下来,我们就一"
},
{
"path": "AngularJS相关/Angular从0到1:function(下).md",
"chars": 2829,
"preview": "---\ntitle: Angular从0到1:function(下)\ndate: 2017/02/21 14:47:10\n---\n\n## 1、前言\n\n## 2、function(下)\n\n### 2.13、angular.isArray(★★"
},
{
"path": "AngularJS相关/Angular再回首(1)-Component组件.md",
"chars": 5133,
"preview": "---\ntitle: Angular再回首(1)-Component组件\ndate: 2017/02/21 14:47:10\n---\n\n## 0、再谈组件\n\n``Component(组件)`` 在 ``Angular1`` 就已经有雏形了,"
},
{
"path": "AngularJS相关/Angular再回首(2)-那些容易忽略的Component细节.md",
"chars": 3548,
"preview": "---\ntitle: Angular再回首(2)-那些容易忽略的Component细节\ndate: 2017/02/21 14:47:10\n---\n\n## 0、前言\n\n在 ``Angular 1.5.x`` 中,增加的组件方法,相当实用和易"
},
{
"path": "AngularJS相关/Angular再回首(3)-我们来实现一个组件.md",
"chars": 3155,
"preview": "---\ntitle: Angular再回首(3)-我们来实现一个组件\ndate: 2017/02/21 14:47:10\n---\n\n## 0、前言\n\n前两文写了 ``Component`` 的一些方面,但没有一个比较线性的串联关系,本文,就"
},
{
"path": "AngularJS相关/Angular开发Tips.md",
"chars": 2630,
"preview": "---\ntitle: Angular开发Tips\ndate: 2017/02/21 14:47:10\n---\n\n1、在使用$routeProvider的时候,需要让模块依赖ngRoute,否则会提示找不到服务,示例:\n\n\tangular.m"
},
{
"path": "AngularJS相关/Angular:指令、Controller数据共享.md",
"chars": 7894,
"preview": "---\ntitle: Angular:指令、Controller数据共享\ndate: 2015/3/13 11:24:39\n---\n\n## 1、Directive与Controller数据共享\n\n在指令中,不仅仅需要指令配置信息,很多时候也"
},
{
"path": "AngularJS相关/[20140917]Angular:如何编写一个指令.md",
"chars": 5601,
"preview": "---\ntitle: Angular:如何编写一个指令\ndate: 2015/3/13 11:24:39\n---\n\n## Angular是什么?\nAngularJS是一个用JavaScript编写的客户端MVC框架,它运行于Web浏览器,能"
},
{
"path": "AngularJS相关/用AngularJS开发Web应用程序.md",
"chars": 2206,
"preview": "---\ntitle: 用AngularJS开发Web应用程序\ndate: 2015/3/13 11:24:39\n---\n\n##章节一:Angular 禅道##\n###本章生词###\n\tserve = 提供\n\ttake a brief = 先"
},
{
"path": "AngularJS相关/详解angular之$q.md",
"chars": 6384,
"preview": "---\ntitle: 详解angular之$q\ndate: 2017/02/21 14:47:10\n---\n\n## 0、什么的Promise\n\nPromise(承诺)是用于改善异步编程体验的一种编程模型,它提供了一些列的API的方法论,让你"
},
{
"path": "Angular系列/01_Angular2初体验.md",
"chars": 3455,
"preview": "---\ntitle: 01_Angular2初体验\ndate: 2017/02/21 14:47:10\n---\n\n## 0、关于Angular2\n\nAngualr2是前端最流行的MV*框架AngularJS的革命性更新版本,官网:[http"
},
{
"path": "Angular系列/02_Angular2组件生命周期.md",
"chars": 4187,
"preview": "---\ntitle: 02_Angular2组件生命周期\ndate: 2017/02/21 14:47:10\n---\n\n## 0、Angular2 组件\n\nAngular1并不是围绕组件的概念来实现的。所以,我们需要controller、$"
},
{
"path": "Angular系列/03_Angular2的那些Decorator.md",
"chars": 4617,
"preview": "---\ntitle: 03_Angular2的那些Decorator\ndate: 2017/02/21 14:47:10\n---\n\n## 0、Decorator\n\n``Decorator`` 是ECMAScript中建议的标准,使得我们可以"
},
{
"path": "Angular系列/04_Angular2指令简析.md",
"chars": 9348,
"preview": "---\ntitle: 04_Angular2指令简析\ndate: 2017/02/21 14:47:10\n---\n\n## 0、Angular2指令\n\n在Angular1中,就已经有了指令的概念。Angular1中的指令用于实现可复用UI部件"
},
{
"path": "Angular系列/05_Angular2组件简析.md",
"chars": 3564,
"preview": "---\ntitle: 05_Angular2组件简析\ndate: 2017/02/21 14:47:10\n---\n\n## 0、Angular2组件\n\n**注:由于Angular2的API好不够稳定,书写该文时,采用的是Angular2 rc"
},
{
"path": "Angular系列/06_Angular2管道(Pipe)简析.md",
"chars": 1894,
"preview": "---\ntitle: 06_Angular2管道(Pipe)简析\ndate: 2017/02/21 14:47:10\n---\n\n## 0、Angular2 Pipe\n\n**注:由于Angular2的API好不够稳定,书写该文时,采用的是An"
},
{
"path": "Angular系列/07_Angular2使用路由.md",
"chars": 4626,
"preview": "---\ntitle: 07_Angular2使用路由\ndate: 2017/02/21 14:47:10\n---\n\n## 0、关于路由\n\n此处所说的路由是指URL路由(也许叫URL Rewrite)。其实是把网址(URL)映射到相关Cont"
},
{
"path": "Angular系列/08_Angular2动态加载组件.md",
"chars": 1395,
"preview": "---\ntitle: 08_Angular2动态加载组件\ndate: 2017/02/21 14:47:10\n---\n\n## 0、为什么需要动态加载\n\n在一个比较大的应用程序中,我们不可能将所有的业务逻辑一次性加载出来,比较浪费资源,因为单"
},
{
"path": "Angular系列/09_Angular2使用ui-router-ng2.md",
"chars": 5594,
"preview": "---\ntitle: 09_Angular2使用ui-router-ng2\ndate: 2017/02/21 14:47:10\n---\n\n## 0、导言\n\nAngular2的路由组件从beta到rc经历了多次变更,知道rc.4都没有完全稳定"
},
{
"path": "Angular系列/Angular2踩坑大全.md",
"chars": 410,
"preview": "---\ntitle: Angular2踩坑大全\ndate: 2017/02/21 14:47:10\n---\n\n## Angular2的那些坑\n\n1、同样的代码,引用Rxjs库的版本不对,就会导致在IE11下无法运行。(特定版本下重现)\n正确"
},
{
"path": "Angular系列/利用Angular实现多团队模块化SPA开发框架.md",
"chars": 7822,
"preview": "---\ntitle: 利用Angular实现多团队模块化SPA开发框架\ndate: 2017-11-23 11:11:11\n---\n\n# 0、前言\n\n当一个公司有多个开发团队时,我们可能会遇到这样一些问题:\n\n1. 技术选项杂乱,大家各玩各"
},
{
"path": "Angular系列/跟我学Angular2(1-初体验).md",
"chars": 1480,
"preview": "---\ntitle: 跟我学Angular2(1-初体验)\ndate: 2017/02/21 14:47:10\n---\n\n## 0、导言\n\nAngular1作为最流行的前端MV*框架,给前端开发带来了极大的便利性。但是,仍然有许多不好的地方"
},
{
"path": "CSS3学习之路/CSS3入门之文本与字体.md",
"chars": 4110,
"preview": "---\ntitle: CSS3入门之文本与字体\ndate: 2017/02/21 14:47:10\n---\n\n## 1、CSS3文本效果\n\n### 1.1、text-shadow文本阴影\n\n语法:``text-shadow: h-shado"
},
{
"path": "CSS3学习之路/CSS3入门之转换.md",
"chars": 5813,
"preview": "---\ntitle: CSS3入门之转换\ndate: 2017/02/21 14:47:10\n---\n\n## 1、CSS3 转换\n\n### 1.1、转换是什么,能实现哪些效果?\n\n转换是使元素改变形状、尺寸和位置的一种效果,主要能实现的效果"
},
{
"path": "CSS3学习之路/CSS3入门之边框与背景.md",
"chars": 4359,
"preview": "---\ntitle: CSS3入门之边框与背景\ndate: 2017/02/21 14:47:10\n---\n\n## 1、前言\n\nCSS3作为CSS的最新版本,在展示效果上有非常大的提升,接下来,我们就一起领略一下CSS3的风采吧。\n\n## "
},
{
"path": "Canvas学习札记/01_初识Canvas,绘制简单图形.md",
"chars": 8717,
"preview": "---\ntitle: 01_初识Canvas,绘制简单图形\ndate: 2017/02/21 14:47:10\n---\n\n## 0、关于Canvas\n\n``<canvas>`` 是HTML5新增的一个标签,用于定义图形,比如图表和其他图像。"
},
{
"path": "ES6入门/ES6入门系列一(基础).md",
"chars": 1864,
"preview": "---\ntitle: ES6入门系列一(基础)\ndate: 2017/02/21 14:47:10\n---\n\n##1、let命令\n**Tips:**\n\n1. 块级作用域(只在当前块中有效)\n2. 不会变量提升(必须先申明在使用)\n3. 让变"
},
{
"path": "ES6入门/ES6入门系列三(特性总览下).md",
"chars": 4257,
"preview": "---\ntitle: ES6入门系列三(特性总览下)\ndate: 2017/02/21 14:47:10\n---\n\n# 0、导言\n\n最近从coffee切换到js,代码量一下子变大了不少,也多了些许陌生感。\n\n为了在JS代码中,更合理的使用E"
},
{
"path": "ES6入门/ES6入门系列二(特性总览上).md",
"chars": 4693,
"preview": "---\ntitle: ES6入门系列二(特性总览上)\ndate: 2017/02/21 14:47:10\n---\n\n## 0、导言\n\n最近从coffee切换到js,代码量一下子变大了不少,也多了些许陌生感。为了在JS代码中,更合理的使用ES"
},
{
"path": "ES6入门/ES6入门系列四(测试题分析).md",
"chars": 3732,
"preview": "---\ntitle: ES6入门系列四(测试题分析)\ndate: 2017/02/21 14:47:10\n---\n\n## 0、导言\n\nES6中新增了不少的新特性,来点测试题热热身。具体题目来源请看:[http://perfectionkil"
},
{
"path": "GoLang学习笔记/01_开始GO.md",
"chars": 585,
"preview": "---\ntitle: 01_开始GO\ndate: 2017/02/21 14:47:10\n---\n\n# 0、前言\n\nGo是啥?看[这里](https://golang.org)。简而言之,就是一门系统级的编程语言。\n\n为嘛要学/用它?\n\n1"
},
{
"path": "JS札记/ES6 Class如何管理私有数据.md",
"chars": 2756,
"preview": "---\ntitle: ES6 Class如何管理私有数据\ndate: 2017/02/21 14:47:10\n---\n\n## 0、前言\n\n在ES5时代,要模拟对象的私有变量,是比较容易的,代码如下:\n\n```javascript\nfunct"
},
{
"path": "JS札记/JS实现继承的几种方式.md",
"chars": 4389,
"preview": "---\ntitle: JS实现继承的几种方式\ndate: 2017/02/21 14:47:10\n---\n\n## 前言\n\nJS作为面向对象的弱类型语言,继承也是其非常强大的特性之一。那么如何在JS中实现继承呢?让我们拭目以待。\n\n## JS"
},
{
"path": "JS札记/JavaScript之毒瘤.md",
"chars": 1110,
"preview": "---\ntitle: JavaScript之毒瘤\ndate: 2017/02/21 14:47:10\n---\n\n## 0、导言\n\nJavaScript中有许多难以避免的问题特性。接下来就一一揭示。\n\n## 1、全局变量\n\n在所有JavaSc"
},
{
"path": "JS札记/JavaScript之糟粕.md",
"chars": 3832,
"preview": "---\ntitle: JavaScript之糟粕\ndate: 2017/02/21 14:47:10\n---\n\n## 0、导言\n\n在上篇《JavaScript之毒瘤》中,列举了一些在JavaScript中难以避免的问题特性。本篇将会展示Ja"
},
{
"path": "JS札记/JavaScript的深拷贝的实现.md",
"chars": 5184,
"preview": "---\ntitle: JavaScript的深拷贝的实现\ndate: 2017/02/21 14:47:10\n---\n\n## JavaScript的数据类型\n\n### 简单数据类型\n\n1. string\n2. number\n3. boole"
},
{
"path": "JS札记/[20141121]JavaScript之Array常用功能汇总.md",
"chars": 4887,
"preview": "---\ntitle: JavaScript之Array常用功能汇总\ndate: 2014/11/21 00:00:00\n---\n\n**导语:**在JavaScript中,Array是一个使用比较频繁的对象,那么它到底有哪些常用的方法呢?\n\n"
},
{
"path": "JS札记/那些不常见的JavaScript题目(上).md",
"chars": 7495,
"preview": "---\ntitle: 那些不常见的JavaScript题目(上)\ndate: 2017/02/21 14:47:10\n---\n\n## 0、导言\n\nJavaScript超乎寻常的灵活性,让JavaScript可以有很多特殊的用法,让我们来领略"
},
{
"path": "JS札记/那些不常见的JavaScript题目(下).md",
"chars": 4980,
"preview": "---\ntitle: 那些不常见的JavaScript题目(下)\ndate: 2017/02/21 14:47:10\n---\n\n## 0、导言\n\nJavaScript超乎寻常的灵活性,让JavaScript可以有很多特殊的用法,让我们来领略"
},
{
"path": "LICENSE",
"chars": 1073,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2015 hstar\n\nPermission is hereby granted, free of charge, to any person obtaining a"
},
{
"path": "MongoDB入门基础/01_记一次MongoDB裸奔.md",
"chars": 1925,
"preview": "---\ntitle: 01_记一次MongoDB裸奔\ndate: 2017/02/21 14:47:10\n---\n\n# 导言\n\n大意失荆州,裸奔的 `MongoDB` 被黑了。虽然并不是什么非常重要的数据,但也给自己敲响的一个警钟。虽然我们"
},
{
"path": "MongoDB入门基础/02_Mongo权限探索.md",
"chars": 3574,
"preview": "---\ntitle: 02_Mongo权限探索\ndate: 2017/02/21 14:47:10\n---\n\n# 0x0、导言\n\n经过一次 `MongoDB` 被黑之后,就想把 `MongoDB` 的权限仔细的了解一遍。安全第一!安全第一!"
},
{
"path": "Other/Go Go.md",
"chars": 1961,
"preview": "---\ntitle: Go Go\ndate: 2017/02/21 14:47:10\n---\n\n# 题记\n\n> 学习是进步的源泉\n\n在这个云计算、多核盛行的时代,学习一门与之相配合的语言也就无可厚非了。那么对多核与并行计算原生支持的Go就是"
},
{
"path": "Other/NPM使用详解(上).md",
"chars": 3420,
"preview": "---\ntitle: NPM使用详解(上)\ndate: 2017/02/21 14:47:10\n---\n\n## 1、NPM是什么?\n\nNPM是JavaScript的包管理工具,在安装NodeJS(什么?你不知道node?来,我们合计合计:["
},
{
"path": "Other/NPM使用详解(下).md",
"chars": 3499,
"preview": "---\ntitle: NPM使用详解(下)\ndate: 2017/02/21 14:47:10\n---\n\n在浏览本文之前,建议您先浏览《NPM使用详解(上)》\n\n在上一文中,罗列出了最常用的NPM命令,那么本文将继续分解剩下的NPM命令\n\n"
},
{
"path": "Other/Thrift简单实践.md",
"chars": 5024,
"preview": "---\ntitle: Thrift简单实践\ndate: 2017/02/21 14:47:10\n---\n\n## 0、什么是RPC\n\n**RPC**(Remote Procedure Call - 远程过程调用),是通过网络从远程计算机上请求"
},
{
"path": "Other/TypeScript札记:初体验.md",
"chars": 1200,
"preview": "---\ntitle: TypeScript札记:初体验\ndate: 2017/02/21 14:47:10\n---\n\n## 1、简介\n\nTypeScript 是一种由微软开发的自由和开源的编程语言。它是JavaScript的一个超集,而且本"
},
{
"path": "Other/Windows下把Nginx,PM2包装为服务.md",
"chars": 2014,
"preview": "---\ntitle: Windows下把Nginx,PM2包装为服务\ndate: 2017-2-25 09:55:26\n---\n\n# 0x0、前言\n\n在 `Windows` 上部署 `Node` 或者前后端分离的静态Web程序时,我们一般会"
},
{
"path": "Other/开发者讨厌你API的十个原因.md",
"chars": 3451,
"preview": "---\ntitle: 开发者讨厌你API的十个原因\ndate: 2017/02/21 14:47:10\n---\n\n##1、文档的吸引力太弱##\n###解决之道###\n1. 采用大图片:[示例站点](https://www.twilio.co"
},
{
"path": "Other/浅析12306前端优化点.md",
"chars": 1615,
"preview": "---\ntitle: 浅析12306前端优化点\ndate: 2017/02/21 14:47:10\n---\n\n## 关于12306\n\n中国铁路客户服务中心([12306.cn](https://kyfw.12306.cn/)),相信大家都不"
},
{
"path": "Other/程序集强签名.md",
"chars": 922,
"preview": "---\ntitle: 程序集强签名\ndate: 2017/02/21 14:47:10\n---\n\n## 1、特点\n\n1.1、强签名的程序集可以注册到GAC(全局应用程序集缓存),不同的应用程序可以共享同一个dll。\n\n1.2、强签名的库(应"
},
{
"path": "PHP学习之路/01_PHP简易安装环境.md",
"chars": 3043,
"preview": "---\ntitle: 01_PHP简易安装环境\ndate: 2017/02/21 14:47:10\n---\n\n## 0、导言\n\n**PHP** 是啥,我想应该不用解释了吧。\n\n最近发布的最新版本 ``PHP7`` ,提供之前版本的2倍速度提"
},
{
"path": "PHP学习之路/02-PHP基础语法(上).md",
"chars": 4640,
"preview": "---\ntitle: 02-PHP基础语法(上)\ndate: 2017/02/21 14:47:10\n---\n\n# 0、导言\n\n学习一门语言,首先要了解它能做什么?其次,就应该去学习应该如何做。那这个的前提就是语法的学习。\n\n语法决定了代码"
},
{
"path": "README.md",
"chars": 26134,
"preview": "# HstarDoc\nmarkdown docs.\nSave my markdown blogs.\n\n# Table of Contents\n<!--TableOfContnets Start-->\n* [AngularJS相关](Angu"
},
{
"path": "React Native Cookbook/章节一-new.md",
"chars": 4279,
"preview": "# 章节一、React Native 工具链\n\nReact Native 身处由数十种软件工具构成的生态系统中。包含转换器(Babel,Metro 以及 Webpack),包管理工具(NPM,Yarn),检查器,单元测试框架等。本章将介绍语"
},
{
"path": "React Native Cookbook/章节一.md",
"chars": 17976,
"preview": "# 章节一、入门\n\n在本章中,将包含以下内容:\n\n* 在文本和容器上添加样式\n* 使用图片来模仿一个视频播放器\n* 创建一个切换按钮\n* 将数据项作为列表展示\n* 在视窗中添加选项卡\n* 使用 Flexbox 布局来创建个人资料页\n* 设置"
},
{
"path": "React Native 开发笔记/RN Aspect-01-环境准备.md",
"chars": 0,
"preview": ""
},
{
"path": "React Native 开发笔记/RN Aspect-02-Hello React Native.md",
"chars": 0,
"preview": ""
},
{
"path": "React Native 开发笔记/RN Aspect-03-修改名称与icon.md",
"chars": 0,
"preview": ""
},
{
"path": "React Native 开发笔记/RN Aspect-04-打包App.md",
"chars": 0,
"preview": ""
},
{
"path": "React Native 开发笔记/React Native开发之多屏适配.md",
"chars": 756,
"preview": "---\ntitle: React Native开发之多屏适配\ndate: 2018-3-24 08:56:47\n---\n\n# 多屏适配\n\n# 字体不随系统字体缩放\n\n**针对 `iOS`**\n\n需要在 `node_modules/react"
},
{
"path": "React面面观/JSX中的那些小细节.md",
"chars": 595,
"preview": "---\ntitle: JSX中的那些小细节\ndate: 2017-4-6 10:53:53\n---\n\n# 导言\n\n在学习 `React` 的过程中,我们无可厚非,需要接触到 `JSX`。在 `JSX` 中,有一些约定是我们需要遵守的,有一些"
},
{
"path": "React面面观/【译】参考手册-React组件.md",
"chars": 7979,
"preview": "---\ntitle: 参考手册-React组件\ndate: 2017-4-7 13:27:19\nversion: 15.4.2\n---\n\n# React.Component\n\n组件能够让你将UI拆分为多个独立自治并可重用的部分。在 `Rea"
},
{
"path": "React面面观/【译】快速起步-JSX简介.md",
"chars": 3186,
"preview": "---\ntitle: 快速起步-JSX简介\ndate: 2017-4-14 14:12:50\nreact version: 15.5.0\n---\n\n# 快速起步-JSX简介\n\n思考这个变量申明:\n\n```js\nconst element ="
},
{
"path": "React面面观/【译】快速起步-事件处理.md",
"chars": 3423,
"preview": "---\ntitle: 快速起步-事件处理\ndate: 2017-4-18 16:58:04\nreact version: 15.5.0\n---\n\n# 事件处理\n\n使用React元素处理事件和DOM元素上的事件非常相似。但还是有一些语法上的不"
},
{
"path": "React面面观/【译】快速起步-列表与KEY.md",
"chars": 5849,
"preview": "---\ntitle: 快速起步-数组与KEY\ndate: 2017-4-20 13:34:37\nreact version: 15.5.0\n---\n\n# 数组与KEY\n\n首先,我们来看看如何在 `JavaScript` 中转换列表。\n\n如下"
},
{
"path": "React面面观/【译】快速起步-条件渲染.md",
"chars": 5149,
"preview": "---\ntitle: 快速起步-条件渲染\ndate: 2017-4-20 15:38:23\nreact version: 15.5.0\n---\n\n# 条件渲染\n\n在 `React` 中,你可以创建不同的组件来封装你需要的行为。然后,你可以通"
},
{
"path": "React面面观/【译】快速起步-渲染元素.md",
"chars": 1926,
"preview": "---\ntitle: 快速起步-渲染元素\ndate: 2017-4-14 14:10:50\nreact version: 15.5.0\n---\n\n# 渲染元素\n\n\n`Elements` 是 `React` 应用中的最小单元。\n\n一个元素定义"
},
{
"path": "React面面观/【译】快速起步-状态和生命周期.md",
"chars": 8806,
"preview": "---\ntitle: 快速起步-状态和生命周期\ndate: 2017-4-13 16:43:28\nversion: 15.4.2\n---\n\n# 状态和生命周期\n\n到目前为止,我们仅学到了一种更新UI的方法。\n\n我们通过调用 `ReactDO"
},
{
"path": "React面面观/【译】快速起步-状态提升.md",
"chars": 9035,
"preview": "---\ntitle: 快速起步-状态提升\ndate: 2017-4-25 17:27:42\nversion: 15.5.0\n---\n\n# 状态提升\n\n通常,多个组件会响应同样的变化数据。我们建议将这种共享的状态提升到最近的共同的祖先。让我们"
},
{
"path": "React面面观/【译】快速起步-组件与属性.md",
"chars": 4935,
"preview": "---\ntitle: 快速起步-组件与属性\ndate: 2017-4-14 16:01:04\nversion: 15.5.0\n---\n\n# 组件与属性\n\n组件允许您将UI拆分为多个独立的,可重用的部分。\n\n概念上,组件就类似于 `JavaS"
},
{
"path": "React面面观/【译】快速起步-组合与继承.md",
"chars": 3460,
"preview": "---\ntitle: 快速起步-组合与继承\ndate: 2017-4-26 13:19:51\nversion: 15.5.0\n---\n\n# 组合 VS. 继承\n\n`React` 具有很强大的组合模型,我们推荐使用组合而不是继承来重用组件之间"
},
{
"path": "React面面观/【译】快速起步-表单.md",
"chars": 6242,
"preview": "---\ntitle: 快速起步-表单\ndate: 2017-4-28 08:35:09\nversion: 15.5.0\n---\n\n# 表单\n\nHTML的表单元素和React中的表单元素有所不同,因为表单元素会保持一些内部状态。例如,以下是一"
},
{
"path": "React面面观/【译】高级指南-不受控组件.md",
"chars": 1644,
"preview": "---\ntitle: 高级指南-不受控组件\ndate: 2017-4-28 10:47:03\nversion: 15.5.0\n---\n\n# 不受控组件\n\n在很多时候,我们推荐使用 [受控组件](【译】快速起步-表单.md) 来实现表单。在受"
},
{
"path": "React面面观/【译】高级指南-深入JSX.md",
"chars": 8024,
"preview": "---\ntitle: 高级指南-深入JSX\ndate: 2017-4-5 17:13:09\nversion: 15.4.2\n---\n\n# 深入JSX\n\n从根本上来讲,`JSX` 仅仅是提供 `React.createElement(comp"
},
{
"path": "React面面观/【译】高级指南-高阶组件.md",
"chars": 10525,
"preview": "---\ntitle: 高级指南-高阶组件\ndate: 2017-4-6 13:11:48\nversion: 15.4.2\n---\n\n# 高阶组件\n\n高阶组件(HOC)是React中重用组件逻辑的高级技术。HOCs本身不是 `React` A"
},
{
"path": "RxJS小记/02_RxJS之Observable.md",
"chars": 5024,
"preview": "---\ntitle: 02_RxJS之Observable\ndate: 2017-3-31 17:25:52\n---\n\n# 0x0、Observable(可观察对象)\n\n`Observable` 含义是可观察对象,那什么是可观察对象呢?这就"
},
{
"path": "Sass学习之路/01_Sass学习之路:Sass、Compass安装与命令行.md",
"chars": 2345,
"preview": "---\ntitle: 01_Sass学习之路:Sass、Compass安装与命令行\ndate: 2017/02/21 14:47:10\n---\n\n## 导言\n\nCSS不是一门真正意义上的编程语言,很多编程语言理所当然的特性(比如变量),都不"
},
{
"path": "Sass学习之路/02_Sass学习之路:注释、变量以及导入.md",
"chars": 2730,
"preview": "---\ntitle: 02_Sass学习之路:注释、变量以及导入\ndate: 2017/02/21 14:47:10\n---\n\n## 前言\n\n由于.sass不兼容CSS代码,所以以下内容完全使用.scss的语法。\n\n## Sass注释\n\nS"
},
{
"path": "Vue实践之路/01_认识Vue.md",
"chars": 1889,
"preview": "---\ntitle: 01_认识Vue\ndate: 2017/02/21 14:47:10\n---\n\n# 0、关于Vue\n\n[Vue](https://vuejs.org.cn/) 是当前非常流行的一款前端 MV* 库(国人开发),结合 v"
},
{
"path": "Vue实践之路/02_Vue组件(上).md",
"chars": 3430,
"preview": "---\ntitle: 02_Vue组件(上)\ndate: 2017/02/21 14:47:10\n---\n\n# 0、关于Vue组件\n\n组件是 ``Vue`` 中最强大的功能之一,Vue组件也和angular的组件比较类似,可以扩展HTML元"
},
{
"path": "catalog_builder.js",
"chars": 1269,
"preview": "const fs = require('fs');\nconst path = require('path');\n\nconst replaceRegExp = /<!--TableOfContnets Start-->(\\r\\n|\\n|.|\\"
},
{
"path": "jQuery拆解/01-目录篇.md",
"chars": 264,
"preview": "---\ntitle: 01-目录篇\ndate: 2017/02/21 14:47:10\n---\n\n## 0、导言\n\n``jQuery`` 作为最流行的DOM操作库,已经基本上成了浏览器的事实标准。抱着学习高效dom操作的目的,我就来整个系列"
},
{
"path": "jQuery拆解/02-模块化加载&防冲突处理.md",
"chars": 2370,
"preview": "---\ntitle: 02-模块化加载&防冲突处理\ndate: 2017/02/21 14:47:10\n---\n\n## 0、模块化加载\n\n``jQuery`` 在之前的版本(具体不记得是哪个版本以前了)中,是不支持模块化加载的。\n\n当前,模"
},
{
"path": "jQuery拆解/03-基础结构.md",
"chars": 7570,
"preview": "---\ntitle: 03-基础结构\ndate: 2017/02/21 14:47:10\n---\n\n## 0、jQuery() 与 new jQuery();\n\n在 ``jQuery`` 中,我们一般都是用 ``$('body')``,实际"
},
{
"path": "jQuery拆解/jQuery中那些有趣的代码.md",
"chars": 282,
"preview": "---\ntitle: jQuery中那些有趣的代码\ndate: 2017/02/21 14:47:10\n---\n\n## 0、动态执行JS代码\n\n```javascript\nfunction DOMEval(code, doc) {\n\t\tdo"
},
{
"path": "从0开始Stylus/01_Stylus简介&基本使用.md",
"chars": 6141,
"preview": "---\ntitle: 01_Stylus简介&基本使用\ndate: 2017/02/21 14:47:10\n---\n\n## 0、导言\n\n**关于Stylus**\n\nStylus是一个CSS预处理器,也就是利用编程的方式编写css代码,然后s"
},
{
"path": "从零开始H5/HTML5探索一(那些新增的标签和属性).md",
"chars": 3859,
"preview": "---\ntitle: HTML5探索一(那些新增的标签和属性)\ndate: 2017/02/21 14:47:10\n---\n\nhtml5相比html4,添加了部分语义化的标签和属性,现在我们就从这些标签和属性开始,学习html5吧。\n\n首先"
},
{
"path": "从零开始H5/从零开始H5(一):升级你的HTML到HTML5.md",
"chars": 1228,
"preview": "---\ntitle: 从零开始H5(一):升级你的HTML到HTML5\ndate: 2017/02/21 14:47:11\n---\n\n## 现有的网页大部分还是基于HTML4开发的,那么如何简单的升级到HTML5呢?\n\n### 1、从doc"
},
{
"path": "从零开始H5/从零开始H5(二):HTML5新技术点.md",
"chars": 730,
"preview": "---\ntitle: 从零开始H5(二):HTML5新技术点\ndate: 2017/02/21 14:47:11\n---\n\n现在,将我们的页面升级到HTML5了。为什么要这样去做呢?通过简单的升级我们没看到任何大的变化。\n\n答案即将揭晓,*"
},
{
"path": "前端相关/CORS详解.md",
"chars": 5659,
"preview": "---\ntitle: CORS详解\ndate: 2017/02/21 14:47:11\n---\n\n## 0、关于CORS\n\n说到CORS,就不得不先了解跨站HTTP请求(Cross-site HTTP request)。\n\n跨域HTTP请求"
},
{
"path": "前端相关/CSS布局(上).md",
"chars": 5620,
"preview": "---\ntitle: CSS布局(上)\ndate: 2017/02/21 14:47:11\n---\n\n## 1、CSS布局之display\n\n### 1.1、dispaly\n\ndispaly是CSS中最重要的用于控制布局的属性,每个元素都有"
},
{
"path": "前端相关/CSS布局(下).md",
"chars": 4416,
"preview": "---\ntitle: CSS布局(下)\ndate: 2017/02/21 14:47:11\n---\n\n## 1、CSS布局之浮动\n\n### 1.1、float之图文混排\n\nfloat的意思就是元素漂浮在上层。\n\n可以直接通过设置float属"
},
{
"path": "前端相关/Google JavaScript Style Guide(上).md",
"chars": 3858,
"preview": "---\ntitle: Google JavaScript Style Guide(上)\ndate: 2017/02/21 14:47:11\n---\n\n## 引言\n\nJavaScript是一套灵活的语言,所以我们可以用各种风格来编写代码。但为"
},
{
"path": "前端相关/Iframe跨域通信的几种方式.md",
"chars": 3128,
"preview": "---\ntitle: Iframe跨域通信的几种方式\ndate: 2017/02/21 14:47:11\n---\n\n## 0、前言\n\n虽然iframe已经越来越不流行了,但是在某些特定的场景下,使用它可以大大减小我们的工作量。\n\n当在页面内"
},
{
"path": "前端相关/JSONP详解.md",
"chars": 2596,
"preview": "---\ntitle: JSONP详解\ndate: 2017/02/21 14:47:11\n---\n\n## 0、关于JSONP\n\n### 什么的JSONP\nJSONP(JSON with Padding)是资料格式 JSON 的一种“使用模式"
},
{
"path": "前端相关/JWT详解.md",
"chars": 3907,
"preview": "---\ntitle: JWT详解\ndate: 2017-9-9 13:12:15\n---\n\n# 0、什么是JWT(JsonWebToken)\n\nJWT,全称 JSON Web Token,本身是一种开放的行业标准 [JSON Web Tok"
},
{
"path": "前端相关/Nginx常规用法解析.md",
"chars": 8154,
"preview": "---\ntitle: Nginx常规用法解析\ndate: 2017/02/21 14:47:11\n---\n\n## 0、Nginx简介\n\n``Nginx`` 是时下最流行的静态Web服务器之一,使用它能快速的托管一个web站点。\n\n当然,它的"
},
{
"path": "前端相关/TypeScript配置文件tsconfig简析.md",
"chars": 5665,
"preview": "---\ntitle: TypeScript配置文件tsconfig简析\ndate: 2017/02/21 14:47:11\n---\n\n## 0、前言\n\n在使用VsCode编写TypeScript时,VsCode提供了一个tsconfig.j"
},
{
"path": "前端相关/VsCode简易配置手册.md",
"chars": 5045,
"preview": "---\ntitle: VsCode简易配置手册\ndate: 2017/02/21 14:47:11\n---\n\n## 0、什么是VsCode(Visual Studio Code - vsc)\n\nVsCode是MS基于Atom Shell开发"
},
{
"path": "前端相关/Web API接口之FileReader.md",
"chars": 2461,
"preview": "---\ntitle: Web API接口之FileReader\ndate: 2017/02/21 14:47:11\n---\n\n## 0、导言\n\n在给网站编写 JavaScript 代码时,也有很多可用的 API。 [WEB API 参考]("
},
{
"path": "前端相关/Web API接口之Geolocation.md",
"chars": 3324,
"preview": "---\ntitle: Web API接口之Geolocation\ndate: 2017/02/21 14:47:11\n---\n\n## 0、关于Geolocation\n\nGeolocation,地理位置API。用于获取用户的位置信息。它不算是"
},
{
"path": "前端相关/Webpack In Angular2.md",
"chars": 4775,
"preview": "---\ntitle: Webpack In Angular2\ndate: 2017/02/21 14:47:11\n---\n\n## 0、前言\n\n当下Angular2是比较值得关注的技术了,想要把Angular2跑起来,还是比较容易的。但\n\n在"
},
{
"path": "前端相关/Webpack初体验.md",
"chars": 4262,
"preview": "---\ntitle: Webpack初体验\ndate: 2017/02/21 14:47:11\n---\n\n## 0、关于webpack\n\nWebpack是灵活的、可扩展的、开源的模块打包工具。[https://webpack.github."
},
{
"path": "前端相关/Webpack小抄.md",
"chars": 2596,
"preview": "---\ntitle: Webpack小抄\ndate: 2017/02/21 14:47:11\n---\n\n## 0、前言\n\n此文用于记载在使用webpack打包过程中的点点滴滴!(仅适用于 `webpack1.x`)\n\n## 1、动态指定lo"
},
{
"path": "前端相关/Web前端基础测试题.md",
"chars": 3163,
"preview": "---\ntitle: Web前端基础测试题\ndate: 2017/02/21 14:47:11\n---\n\n## 1、HTML篇\n\n#### 1、HTML是一种()。\n\nA、标记语言 \n\nB、编程语言 \n\nC、自然语言 \n\nD、描述语言"
},
{
"path": "前端相关/Yarn vs. Npm.md",
"chars": 3961,
"preview": "---\ntitle: Yarn vs. Npm\ndate: 2017/02/21 14:47:11\n---\n\n# 0、什么是 ``Yarn`` ?\n\n官方解释是:**FAST, RELIABLE, AND SECURE DEPENDENCY"
},
{
"path": "前端相关/[20140311]前端构建之gulp与常用插件.md",
"chars": 6601,
"preview": "---\ntitle: 前端构建之gulp与常用插件\ndate: 2015/3/14 13:56:32\n---\n\n##gulp是什么?\n\n[http://gulpjs.com/](http://gulpjs.com/) 相信你会明白的!\n\n与"
},
{
"path": "前端相关/[20141025]从0开始Grunt.md",
"chars": 2359,
"preview": "---\ntitle: 从0开始Grunt\ndate: 2014/10/25 00:00:00\n---\n\n## 首先,Grunt是什么?\n\nGrunt是JavaScript任务运行工具。使用它可以自动化诸如文件(夹)操作、代码压缩、代码编译、"
},
{
"path": "前端相关/[20150107]Web离线存储的几种方式.md",
"chars": 4893,
"preview": "---\ntitle: Web离线存储的几种方式\ndate: 2015/01/07 00:00:00\n---\n\n随着HTML5的正式定稿,我们也可以大量使用HTML离线网络应用程序的特性。\n\n# #1、Application Cache\n[A"
},
{
"path": "前端相关/一个元素实现3个回图形.md",
"chars": 1063,
"preview": "---\ntitle: 一个元素实现3个回图形\ndate: 2017/02/21 14:47:11\n---\n\n## 题目\n\n如何用一个html元素实现三个回子类似的图形?\n\n## 解题方案\n\n### 方案一,利用outline和outline"
},
{
"path": "前端相关/再说Promise.md",
"chars": 2910,
"preview": "---\ntitle: 再说Promise\ndate: 2017/02/21 14:47:11\n---\n\n## 0、导言\n\n在JavaScript,由于天生的回调机制,当业务逻辑嵌套较多的时候,就很容易产生回调地狱。\n\n为了避免回调地狱,在J"
},
{
"path": "前端相关/前端模块化:RequireJS.md",
"chars": 4386,
"preview": "---\ntitle: 前端模块化:RequireJS\ndate: 2017/02/21 14:47:11\n---\n\n## 前言\n\n前端模块化能解决什么问题?\n\n1. 模块的版本管理\n2. 提高可维护性 -- 通过模块化,可以让每个文件职责单"
},
{
"path": "前端相关/如何用Node编写命令行工具.md",
"chars": 3831,
"preview": "---\ntitle: 如何用Node编写命令行工具\ndate: 2017/02/21 14:47:11\n---\n\n# 0、 命令行工具\n当全局安装模块之后,我们可以在控制台下执行指定的命令来运行操作,如果npm一样。我把这样的模块称之为命令"
},
{
"path": "前端相关/探索Decorator.md",
"chars": 87,
"preview": "---\ntitle: 探索Decorator\ndate: 2017/02/21 14:47:11\n---\n\n## 0、前言\n\n## 1、啥是Decorator?\n\n## 2、"
},
{
"path": "前端相关/浏览器 Pointer Events.md",
"chars": 1244,
"preview": "---\ntitle: 浏览器 Pointer Events\ndate: 2017/02/21 14:47:11\n---\n\n## 前言\n\nPointer Events是一套触控输入处理规格,支持Pointer Events的浏览器包括了IE和"
},
{
"path": "前端相关/浏览器关闭事件分析.md",
"chars": 3144,
"preview": "---\ntitle: 浏览器关闭事件分析\ndate: 2017/02/21 14:47:11\n---\n\n# 0、导言\n\n很多时候,我们可能会遇到这样一类需求:\n\n1. 浏览器关闭时,弹出一个新页面\n2. 浏览器关闭时,发送统计信息(如页面浏"
},
{
"path": "前端相关/浏览器内容安全策略解析.md",
"chars": 54,
"preview": "---\ntitle: 浏览器内容安全策略解析\ndate: 2017/02/21 14:47:11\n---\n\n"
},
{
"path": "前端相关/浏览器历史history对象.md",
"chars": 4226,
"preview": "---\ntitle: 浏览器历史history对象\ndate: 2017/02/21 14:47:11\n---\n\n# 0、导言\n\n在单页应用时代,有一个非常重要的概念,那就是前端路由。那它到底是怎么实现的呢?\n\n路由一般有如下两种方式:\n\n"
},
{
"path": "前端相关/简单学ES6.md",
"chars": 5716,
"preview": "---\ntitle: 简单学ES6\ndate: 2017/02/21 14:47:11\n---\n\n##前言##\n随着ES6标准的定稿,众多的特性也趋于稳定,各大浏览器也在逐步实现这些特性,那么对ES6有更多的了解就无可厚非了。\n\n##准备#"
},
{
"path": "前端相关/认识AMD、CMD、UMD、CommonJS.md",
"chars": 1748,
"preview": "---\ntitle: 认识AMD、CMD、UMD、CommonJS\ndate: 2017/02/21 14:47:11\n---\n\n## 0、导言\n\nJavaScript的生态系统一直在稳步增长,当各种组件混合使用时,就可能会发现不是所有的组"
},
{
"path": "前端相关/记一次Bug排查(Spider).md",
"chars": 2503,
"preview": "---\ntitle: 记一次Bug排查(Spider)\ndate: 2017/02/21 14:47:11\n---\n\n## 0、写在之前\n\n### Spider是什么?\n\nSpider是基于Express框架结合socket.io(现已切换"
},
{
"path": "前端相关/说说如何部署node程序.md",
"chars": 1613,
"preview": "---\ntitle: 说说如何部署node程序\ndate: 2017/02/21 14:47:11\n---\n\n## 0、前言\n\nNode作为时下流行的服务端运行时,我们就不得不接触另外一个方面的内容,那就是部署。此文就来说一下node的部署"
},
{
"path": "前端相关/这些年我们处理过的跨域.md",
"chars": 3749,
"preview": "---\ntitle: 这些年我们处理过的跨域\ndate: 2017-7-8 10:35:58\n---\n\n# 同源策略\n\n在说跨域之前,我们需要先了解下 [同源策略](https://developer.mozilla.org/zh-CN/d"
},
{
"path": "前端相关/那些容易出错的Dom操作.md",
"chars": 6144,
"preview": "---\ntitle: 那些容易出错的Dom操作\ndate: 2017/02/21 14:47:11\n---\n\n## 0、导言\n\n在用惯了 ``jQuery`` 这种利器之后,回到原生JS的Dom操作,一时间反而感觉有点陌生。\n\n最近在做的项"
},
{
"path": "前端相关/那些年我们认识的iframe.md",
"chars": 57,
"preview": "---\ntitle: 那些年我们认识的iframe\ndate: 2017/02/21 14:47:11\n---\n\n"
},
{
"path": "数据库之路/[20141114]这些年你需要注意的SQL.md",
"chars": 4345,
"preview": "---\ntitle: 这些年你需要注意的SQL\ndate: 2014/11/14 00:00:00\n---\n\n## #1、使用对象时,请显式的指定对象的架构者(默认为dbo)\n\n**分析:** 在SQL SERVER中,如果用户User1访"
},
{
"path": "数据库之路/说说你所熟知的MSSQL中的substring函数.md",
"chars": 749,
"preview": "---\ntitle: 说说你所熟知的MSSQL中的substring函数\ndate: 2017/02/21 14:47:10\n---\n\n说说你所熟知的MSSQL中的substring函数\n\n### 函数签名:\n\n```sql\nsubstri"
},
{
"path": "最佳实践系列/Express异步进化史.md",
"chars": 5281,
"preview": "---\ntitle: Express异步进化史\ndate: 2017-9-9 12:59:49\n---\n# 1、导言\n\n在 `Javascript` 的世界里,异步(由于JavaScript的单线程运行,所以JavaScript中的异步是可"
},
{
"path": "正则表达式/你真的理解正则修饰符吗.md",
"chars": 3016,
"preview": "---\ntitle: 你真的理解正则修饰符吗\ndate: 2017-05-27 10:25:04\n---\n\n# 0、前言\n\n一段代码引发的思考:\n\n```js\nvar r = /\\d/g;\nconsole.log(r.test('1'));"
},
{
"path": "测试相关/MOCHA测试代码汇总.md",
"chars": 5890,
"preview": "---\ntitle: MOCHA测试代码汇总\ndate: 2017/02/21 14:47:11\n---\n\n# 0x0、导言\n\nMocha是应用最广泛的JS测试框架,但是现在,它的维护者公开说,Mocha快死了,[原文Twitter地址]("
},
{
"path": "测试相关/使用chai-http实现API测试.md",
"chars": 681,
"preview": "---\ntitle: 使用chai-http实现API测试\ndate: 2017/02/21 14:47:11\n---\n\n# 0x1、导言\n\n一个前后端分离的完整的项目中,一般少不了 `API TEST`,那我们如何来做API相关的测试呢?"
},
{
"path": "测试相关/利用Karma、Mocha搭建测试环境.md",
"chars": 62,
"preview": "---\ntitle: 利用Karma、Mocha搭建测试环境\ndate: 2017/02/21 14:47:11\n---\n\n"
},
{
"path": "测试相关/利用Nightwatch.js实现e2e测试.md",
"chars": 65,
"preview": "---\ntitle: 利用Nightwatch.js实现e2e测试\ndate: 2017/02/21 14:47:11\n---\n\n"
},
{
"path": "编写高质量JS代码的68个有效方法-读书笔记/[20140926]编写高质量JS代码的68个有效方法(一).md",
"chars": 5210,
"preview": "---\ntitle: 编写高质量JS代码的68个有效方法(一)\ndate: 2014/09/26 13:36:14\n---\n\n##No.1、了解你使用的JavaScript版本\n**Tips**:\n\n1. 决定你的应用程序支持JavaScr"
},
{
"path": "编写高质量JS代码的68个有效方法-读书笔记/[20141011]编写高质量JS代码的68个有效方法(二).md",
"chars": 1810,
"preview": "---\ntitle: 编写高质量JS代码的68个有效方法(二)\ndate: 2014/10/11\n---\n\n##No.6、了解分号插入的局限性\n**Tips:**\n\n1. 仅在“}”标记之前、一行的结束和程序的结束处推导分号\n2. 仅在紧接"
},
{
"path": "编写高质量JS代码的68个有效方法-读书笔记/[20141030]编写高质量JS代码的68个有效方法(三).md",
"chars": 3116,
"preview": "---\ntitle: 编写高质量JS代码的68个有效方法(三)\ndate: 2014/10/30\n---\n\n##No.11、熟练掌握闭包\n**Tips:**\n\n1. 函数可以引用定义在其外部的作用域变量。\n2. 闭包比创建它们的函数有更长的"
},
{
"path": "编写高质量JS代码的68个有效方法-读书笔记/[20141129]编写高质量JS代码的68个有效方法(四).md",
"chars": 2247,
"preview": "---\ntitle: 编写高质量JS代码的68个有效方法(四)\ndate: 2014/11/29\n---\n\n##No.16、避免使用eval创建局部变量\n**Tips:**\n\n1. 避免使用eval函数创建的变量污染调用者作用域。\n2. 如"
},
{
"path": "编写高质量JS代码的68个有效方法-读书笔记/[20141205]编写高质量JS代码的68个有效方法(五).md",
"chars": 3361,
"preview": "---\ntitle: 编写高质量JS代码的68个有效方法(五)\ndate: 2014/12/05\n---\n\n##No.21、使用apply方法通过不同数量的参数调用函数\n**Tips:**\n\n1. 使用apply方法自定一个可计算的参数数组"
},
{
"path": "编写高质量JS代码的68个有效方法-读书笔记/[20141213]编写高质量JS代码的68个有效方法(六).md",
"chars": 2112,
"preview": "---\ntitle: 编写高质量JS代码的68个有效方法(六)\ndate: 2014/12/13\n---\n\n##No.26、使用bind方法实现函数柯里化\n**Tips:**\n\n1. 使用bind方法实现函数柯里化,即创建一个固定需求参数子"
},
{
"path": "编写高质量JS代码的68个有效方法-读书笔记/[20141220]编写高质量JS代码的68个有效方法(七).md",
"chars": 3635,
"preview": "---\ntitle: 编写高质量JS代码的68个有效方法(七)\ndate: 2014/12/20\n---\n\n##No.30、理解prototype、getPrototypeOf和__proto__之间的不同\n**Tips:**\n\n1. C."
},
{
"path": "编写高质量JS代码的68个有效方法-读书笔记/[20141227]编写高质量JS代码的68个有效方法(八).md",
"chars": 3720,
"preview": "---\ntitle: 编写高质量JS代码的68个有效方法(八)\ndate: 2014/12/27\n---\n\n##NO.36、只将实例状态存储在实例对象中\n**Tips:**\n\n1. 共享可变数据可能会出问题,因为原型是被其所有的实例共享的\n"
},
{
"path": "编写高质量JS代码的68个有效方法-读书笔记/[20150110]编写高质量JS代码的68个有效方法(九).md",
"chars": 3874,
"preview": "---\ntitle: 编写高质量JS代码的68个有效方法(九)\ndate: 2015/01/10\n---\n\n##No.41、将原型视为实现细节\n**Tips:**\n\n1. 对象是接口,原型是实现\n2. 避免检查你无法控制的对象的原型结构\n3"
},
{
"path": "编写高质量JS代码的68个有效方法-读书笔记/[20150123]编写高质量JS代码的68个有效方法(十).md",
"chars": 3476,
"preview": "---\ntitle: 编写高质量JS代码的68个有效方法(十)\ndate: 2015/01/23\n---\n\n##No.46、使用数组而不要使用字典来存储有序集合\n**Tips:**\n\n1. 使用for...in 循环来枚举对象属性应当与顺序"
},
{
"path": "编写高质量JS代码的68个有效方法-读书笔记/[20150214]编写高质量JS代码的68个有效方法(十一).md",
"chars": 4484,
"preview": "---\ntitle: 编写高质量JS代码的68个有效方法(十一)\ndate: 2015/02/14\n---\n\n##No.51、在类数组对象上附庸通用的数组方法\n**Tips:**\n\n1. 对于类数组对象,通过提取方法对象并使用其call方法"
},
{
"path": "编写高质量JS代码的68个有效方法-读书笔记/[20150304]编写高质量JS代码的68个有效方法(十二).md",
"chars": 2297,
"preview": "---\ntitle: 编写高质量JS代码的68个有效方法(十二)\ndate: 2015/03/04\n---\n\n##No.56、避免不必要的状态\n**Tips:**\n\n1. 尽可能地使用无状态的API\n2. 如果API是有状态的,标示出每个操"
},
{
"path": "编写高质量JS代码的68个有效方法-读书笔记/[20150312]编写高质量JS代码的68个有效方法(十三).md",
"chars": 9106,
"preview": "---\ntitle: 编写高质量JS代码的68个有效方法(十三)\ndate: 2015/03/12\n---\n\n##No.61、不要阻塞I/O事件队列\n\n**Tips:**:\n\n1. 异步API使用回调函数来延缓处理代价高昂的操作以避免阻塞主"
},
{
"path": "运维&部署/Docker容器管理平台Humpback进阶-私有仓库.md",
"chars": 4547,
"preview": "---\ntitle: Docker容器管理平台Humpback进阶-私有仓库\ndate: 2017-6-8 19:20:54\n---\n\n# Docker私有仓库\n\n在 `Docker` 中,当我们执行 `docker pull xxx` 的"
},
{
"path": "运维&部署/Docker部署:Mysql Master-Slave.md",
"chars": 5407,
"preview": "---\ntitle: Docker部署:Mysql主从\ndate: 2019-2-18 20:21:32\n---\n\n# 0、前言\n\n博主有个应用,为了提高可用性,打算使用 Mysql 的主从复制。同时,由于 DB 也是部署在容器中的,所以在"
},
{
"path": "运维&部署/一个简单易用的容器管理平台-Humpback.md",
"chars": 4412,
"preview": "---\ntitle: 一个简单易用的容器管理平台-Humpback\ndate: 2017-05-20 09:09:58\n---\n\n\n# 什么是Humpback?\n\n在回答这个问题前,我们得先了解下什么的 `Docker`(哦,现在叫 `Mo"
},
{
"path": "运维&部署/前端监控系统实现.md",
"chars": 3745,
"preview": "# 0、前言\n\n一个强大,好用的统计分析系统,能够非常方便的将用户访问数据,站点性能数据,各种统计信息进行汇总展示。这对于有点体量的公司都是非常重要的。\n\n# 1、客户端信息收集\n\n## 1.1、基本信息\n\n要收集浏览器信息,一般是通过 `"
},
{
"path": "运维&部署/记一次Zookeeper数据找回.md",
"chars": 3069,
"preview": "---\ntitle: 记一次Zookeeper数据找回\ndate: 2017-11-3 09:25:22\n---\n\n# 1、关于Zookeeper\n\n`ZooKeeper` 是可用于维护配置信息,命名,提供分布式同步分组服务的集中式服务。了"
}
]
// ... and 3 more files (download for full content)
About this extraction
This page contains the full source code of the hstarorg/HstarDoc GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 168 files (618.9 KB), approximately 264.7k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.