├── .gitignore ├── .vscode └── launch.json ├── Readme.md └── src ├── angular di └── Readme.md ├── async ├── Readme.md └── src │ ├── .babelrc │ ├── attribute.html │ ├── await.js │ ├── generator.js │ ├── package.json │ └── promise.js ├── char_encode ├── index.html ├── index.js └── package.json ├── global.md ├── h5editor ├── Readme.md ├── appcache.jpg ├── emmet.gif ├── layout.jpg └── run.gif ├── http2 ├── Readme.md ├── http2.pcapng ├── index.js ├── keys │ ├── ca-cert.pem │ ├── ca-cert.srl │ ├── ca-csr.pem │ ├── ca-key.pem │ ├── certificate.pem │ ├── certrequest.csr │ ├── gitkeep │ ├── openssl.cnf │ ├── privatekey.pem │ ├── server-cert.pem │ ├── server-csr.pem │ ├── server-key.pem │ └── server.pfx └── package.json ├── layout └── web布局的前世今生.md ├── redux └── index.js ├── router └── coffeere_implement │ ├── event.coffee │ └── router.coffee ├── security ├── crsf.html └── index.html ├── vscode ├── babel.js ├── index.js └── package.json ├── webpack ├── bundle.js ├── entry.js ├── loader │ └── infoLoader.js ├── package.json ├── plugin │ └── infoPlugin.js ├── src │ └── lib.js ├── webpack.config.js ├── webpack.debug.js └── webpackPrinciple.md └── wechat └── Readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | async/src/node_modules 2 | http2/node_modules 3 | **/node_modules -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "node debug", 11 | "program": "${workspaceRoot}/src/vscode/index.js", 12 | "cwd": "${workspaceRoot}" 13 | }, 14 | { 15 | "type": "node", 16 | "request": "launch", 17 | "name": "babel debug", 18 | "program": "${workspaceRoot}/src/vscode/babel.js", 19 | "runtimeExecutable": "${workspaceRoot}/src/vscode/node_modules/.bin/babel-node", 20 | "cwd": "${workspaceRoot}" 21 | }, 22 | { 23 | "type": "node", 24 | "request": "launch", 25 | "name": "webpack debug", 26 | "program": "${workspaceRoot}/src/webpack/webpack.debug.js", 27 | "cwd": "${workspaceRoot}/src/webpack" 28 | }, 29 | { 30 | "type": "node", 31 | "request": "attach", 32 | "name": "Attach to Process", 33 | "port": 5858 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ## note 2 | 3 | 0. [微信浏览器2016年4月升级基于Chrome37的WebView内核后 HTML5和CSS3 兼容性总结](https://github.com/etoah/note/tree/master/src/wechat) 4 | 1. [浏览器缓存策略详解及实现](https://github.com/etoah/BrowserCachePolicy) 5 | 2. [angular 依赖注入原理](https://github.com/etoah/note/tree/master/src/angular%20di) 6 | 3. [javascript异步编程的前世今生,从onclick到await/async](https://github.com/etoah/note/tree/master/src/async) 7 | 4. [HTTP2特性预览和抓包分析](https://github.com/etoah/note/tree/master/src/http2) 8 | 5. [微信小程序(应用号)开发体验](https://github.com/etoah/wechat-app) 9 | 6. [javascript 如何列出全局对象window的非原生属性.](https://github.com/etoah/note/tree/master/src/global.md) 10 | -------------------------------------------------------------------------------- /src/angular di/Readme.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 依赖注入(Dependency Injection,简称DI)是像C#,java等典型的面向对象语言框架设计原则控制反转的一种典型的一种实现方式,angular把它引入到js中,介绍angular依赖注入的使用方式的文章很多, 4 | [angular官方的文档](https://docs.angularjs.org/guide/di),也有很详细的说明。但介绍原理的较少,angular代码结构较复杂,文章实现了一简化版本的DI,核心代码只有30行左右,[相看实现效果(可能需翻墙)](http://plnkr.co/edit/sJiIbzEXiqLLoQPeXBnR?p=preview)或查看[源码](https://github.com/etoah/Eg/blob/master/Angular/di.html) 5 | 6 | 这篇文章用尽量简单的方式说一说 angular依赖注入的实现。 7 | 8 | 9 | ## 简化的实现原理 10 | 要实现注入,基本有三步: 11 | 1. 得到模块的依赖项 12 | 2. 查找依赖项所对应的对象 13 | 3. 执行时注入 14 | 15 | ### 1. 得到模块的依赖项 16 | javascript 实现DI的核心api是`Function.prototype.toString`,对一个函数执行toString,它会返回函数的源码字符串,这样我们就可以通过正则匹配的方式拿到这个函数的参数列表: 17 | 18 | ```js 19 | function extractArgs(fn) { //angular 这里还加了注释、箭头函数的处理 20 | var args = fn.toString().match(/^[^\(]*\(\s*([^\)]*)\)/m); 21 | return args[1].split(','); 22 | } 23 | ``` 24 | 25 | 26 | ### 2. 查找依赖项所对应的对象 27 | java与.net通过反射来获取依赖对象,js是动态语言,直接一个`object[name]`就可以直接拿到对象。所以只要用一个对象保存对象或函数列表就可以了 28 | 29 | ```js 30 | function createInjector(cache) { 31 | this.cache = cache; 32 | 33 | } 34 | angular.module = function () { 35 | modules = {}; 36 | injector = new createInjector(modules); 37 | return { 38 | injector: injector, 39 | factory: function (name, fn) { 40 | modules[name.trim()] = this.injector.invoke(fn); 41 | return this; 42 | } 43 | } 44 | }; 45 | ``` 46 | 47 | ### 3. 执行时注入 48 | 49 | 最后通过 fn.apply方法把执行上下文,和依赖列表传入函数并执行: 50 | 51 | ```js 52 | 53 | createInjector.prototype = { 54 | invoke: function (fn, self) { 55 | argsString = extractArgs(fn); 56 | args = []; 57 | argsString.forEach(function (val) { 58 | args.push(this.cache[val.trim()]); 59 | }, this); 60 | return fn.apply(self, args); 61 | } 62 | }; 63 | 64 | ``` 65 | 66 | 简化的全部代码和执行效果见(可能需翻墙):[http://plnkr.co/edit/sJiIbzEXiqLLoQPeXBnR?p=preview](http://plnkr.co/edit/sJiIbzEXiqLLoQPeXBnR?p=preview) 67 | 或查看[源码](https://github.com/etoah/Eg/blob/master/Angular/di.html) 68 | 69 | 这里是简化的版本,实际angular的实现考虑了很多问题,如模块管理,延迟执行等 70 | 71 | 72 | ## angular 的实现 73 | 74 | 为了简单,我们也按这三步来介绍angular DI 75 | 76 | 1. 得到模块的依赖项 77 | 2. 查找依赖项所对应的对象 78 | 3. 执行时注入 79 | 80 | 注:以下代码行数有就可能变 81 | ### 1. 得到模块的依赖项 82 | 83 | https://github.com/angular/angular.js/blob/master/src%2Fauto%2Finjector.js#L81 84 | 85 | ```js 86 | var ARROW_ARG = /^([^\(]+?)=>/; 87 | var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m; 88 | var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; 89 | 90 | function extractArgs(fn) { 91 | var fnText = fn.toString().replace(STRIP_COMMENTS, ''), 92 | args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS); 93 | return args; 94 | } 95 | ``` 96 | 97 | ### 2. 查找依赖项所对应的对象 98 | 99 | https://github.com/angular/angular.js/blob/master/src%2Fauto%2Finjector.js#L807 100 | ```js 101 | function getService(serviceName, caller) { 102 | if (cache.hasOwnProperty(serviceName)) { 103 | if (cache[serviceName] === INSTANTIATING) { 104 | throw $injectorMinErr('cdep', 'Circular dependency found: {0}', 105 | serviceName + ' <- ' + path.join(' <- ')); 106 | } 107 | return cache[serviceName]; 108 | } else { 109 | try { 110 | path.unshift(serviceName); 111 | cache[serviceName] = INSTANTIATING; 112 | return cache[serviceName] = factory(serviceName, caller); 113 | } catch (err) { 114 | if (cache[serviceName] === INSTANTIATING) { 115 | delete cache[serviceName]; 116 | } 117 | throw err; 118 | } finally { 119 | path.shift(); 120 | } 121 | } 122 | } 123 | ``` 124 | 125 | ### 3. 执行时注入 126 | https://github.com/angular/angular.js/blob/master/src%2Fauto%2Finjector.js#L831 127 | 128 | ####得到参数: 129 | ```js 130 | function injectionArgs(fn, locals, serviceName) { 131 | var args = [], 132 | $inject = createInjector.$$annotate(fn, strictDi, serviceName); 133 | 134 | for (var i = 0, length = $inject.length; i < length; i++) { 135 | var key = $inject[i]; 136 | if (typeof key !== 'string') { 137 | throw $injectorMinErr('itkn', 138 | 'Incorrect injection token! Expected service name as string, got {0}', key); 139 | } 140 | args.push(locals && locals.hasOwnProperty(key) ? locals[key] : 141 | getService(key, serviceName)); 142 | } 143 | return args; 144 | } 145 | ``` 146 | #### 调用 147 | https://github.com/angular/angular.js/blob/master/src%2Fauto%2Finjector.js#L861 148 | 149 | ```js 150 | function invoke(fn, self, locals, serviceName) { 151 | if (typeof locals === 'string') { 152 | serviceName = locals; 153 | locals = null; 154 | } 155 | 156 | var args = injectionArgs(fn, locals, serviceName); 157 | if (isArray(fn)) { 158 | fn = fn[fn.length - 1]; 159 | } 160 | 161 | if (!isClass(fn)) { 162 | // http://jsperf.com/angularjs-invoke-apply-vs-switch 163 | // #5388 164 | return fn.apply(self, args); 165 | } else { 166 | args.unshift(null); 167 | return new (Function.prototype.bind.apply(fn, args))(); 168 | } 169 | } 170 | ``` 171 | 172 | ### angular模块管理,深坑 173 | 174 | angular在每次应用启动时,初始化一个Injector实例: 175 | 176 | https://github.com/angular/angular.js/blob/master/src/Angular.js#L1685 177 | ```js 178 | var injector = createInjector(modules, config.strictDi); 179 | ``` 180 | 181 | 由此代码可以看出对每一个Angular应用来说,无论是哪个模块,所有的"provider"都是存在相同的providerCache或cache中 182 | 183 | 所以会导致一个被誉为angular模块管理的坑王的问题: 184 | module 并没有什么命名空间的作用,当依赖名相同的时候,后面引用的会覆盖前面引用的模块。 185 | 186 | 具体的示例可以查看: 187 | 188 | http://plnkr.co/edit/TZ7hpMwuxk0surlcWDvU?p=preview 189 | 190 | >注:angular di用本文的调用方式压缩代码会出问题:可以用[g-annotate](https://www.npmjs.com/search?q=ng-annotate)转为安全的调用方式。 191 | 192 | 到此angular di的实现原理已完成简单的介绍,angular用了项目中几乎不会用到的api:Function.prototype.toString 实现依赖注入,思路比较简单,但实际框架中考虑的问题较多,更加详细的实现可以直接看[angular的源码](https://github.com/angular/angular.js)。 193 | 194 | 以后会逐步介绍angular其它原理。 195 | 196 | 197 | -------------------------------------------------------------------------------- /src/async/Readme.md: -------------------------------------------------------------------------------- 1 | #javascript异步编程的前世今生,从onclick到await/async 2 | 3 | 4 | ## javascript与异步编程 5 | 6 | 为了避免资源管理等复杂性的问题, 7 | javascript被设计为单线程的语言,即使有了html5 worker,也不能直接访问dom. 8 | 9 | javascript 设计之初是为浏览器设计的GUI编程语言,GUI编程的特性之一是保证UI线程一定不能阻塞,否则体验不佳,甚至界面卡死。 10 | 11 | 一般安卓开发,会有一个界面线程,一个后台线程,保证界面的流畅。 12 | 13 | 由于javascript是单线程,所以采用异步非阻塞的编程模式,javascript的绝大多数api都是异步api. 14 | 15 | 本文是本人的一个总结:从Brendan Eich刚设计的初版javascript到现在的ES7,一步步总结javascript异步编程历史。 16 | 17 | 说明: 18 | 本文所有源码请访问:https://github.com/etoah/note/tree/master/async 19 | 请安装babel环境,用babel-node 执行对应例子 20 | 21 | ### 什么是异步编程 22 | 23 | 那么什么是异步编程,异步编程简单来说就是:执行一个指令不会马上返回结果而执行下一个任务,而是等到特定的事件触发后,才能得到结果。 24 | 25 | 以下是当有ABC三个任务,同步或异步执行的流程图: 26 | 27 | >示意图来自[stackoverflow](http://stackoverflow.com/questions/748175/asynchronous-vs-synchronous-execution-what-does-it-really-mean) 28 | **同步 ** 29 | ``` 30 | thread ->|----A-----||-----B-----------||-------C------| 31 | ``` 32 | **异步** 33 | 34 | ``` 35 | A-Start ---------------------------------------- A-End 36 | | B-Start ----------------------------------------|--- B-End 37 | | | C-Start -------------------- C-End | | 38 | V V V V V V 39 | thread-> |-A-|---B---|-C-|-A-|-C-|--A--|-B-|--C--|---A-----|--B--| 40 | 41 | ``` 42 | 43 | 显然,在宏观上,同步程序是串行的执行各任务,执行单个任务时会阻塞纯线程,异步可以“并行”的执行任务。 44 | 45 | 异步编程时就需要指定异步任务完成后需要执行的指令,总的来说有以下几种“指定异步指令”的方式: 46 | 47 | 1. 属性 48 | 2. 回调 49 | 3. Promise 50 | 4. Generator 51 | 5. await,async 52 | 53 | 下面会一步一步展现各种方式。 54 | 55 | ### 属性 56 | 57 | 每个编程语言对异步实现的方式不一样,C#可以用委托,java可以用接口或基类传入的方式, 58 | 59 | 早期的javascript的异步的实现也类似于这种类的属性的方式:每个类实例的相关回调事件有相应的handler(onclick,onchange,onload等)。 60 | 61 | 在DOM0级事件处理程序,就是将一个函数赋值给一个元素的属性。 62 | 63 | ```javascript 64 | element.onclick=function(){ 65 | alert("clicked"); 66 | } 67 | window.onload=function(){ 68 | alert("loaded"); 69 | } 70 | ``` 71 | #### 问题 72 | 73 | 这种写法简单明了,同时会有以下几个问题 74 | 75 | 1. 耦合度高 76 | 所有的事件处理都需要写的一个函数中: 77 | 78 | ```javascript 79 | window.onload=function(){ 80 | handlerA(); 81 | handlerB(); 82 | handlerc(); 83 | } 84 | ``` 85 | 如果这三个handler来自三个不同的模块,那这个文件模块耦合度就为3(华为的计算方法)。依赖高,不利于复用和维护。 86 | 87 | 2. 不安全,容易被重写 88 | 89 | ```javascript 90 | window.onload=function(){ 91 | console.log("handler 1"); 92 | } 93 | //... 很多其它框架,库,主题 的代码 94 | var handlerbak=window.onload 95 | window.onload=function(){ 96 | handlerbak(); //这行注释的话上面handler 1就会被覆盖。 97 | console.log("handler 2"); 98 | } 99 | ``` 100 | 101 | 当代码量大时,这种问题没有warning也没有error, 经验不丰富的前端可能花费大量的时间查找问题。 102 | 103 | 事件handler容易被重写,库/框架的安全,寄托于使用者的对框架的熟练程度,极不安全。 104 | 105 | 106 | ### 回调(发布/订阅) 107 | 108 | 由于javascript支持函数式编程,JavaScript语言对异步编程的实现可以用回调函数。 109 | 110 | DOM2级事件解决了这个问题以上两个问题 111 | ```javascript 112 | element.addEventListener("click",function(){ 113 | alert("clicked"); 114 | }) 115 | 116 | ``` 117 | 这里实际上是一个发布订阅模式,addEventListener相当于subscribe, dispatchEvent相当于publish, 118 | 很好的解决了订阅者之前的依赖,jquery,vue,flux,angularjs均实现了类似的模式。 119 | 120 | 发布订阅模式虽解决了上面耦合和不安全的问题,但是在实现大型应用时,还会有以下问题。 121 | #### 问题 122 | 123 | - 回调黑洞 124 | 多层回调嵌套,代码可读性差。 125 | ```js 126 | step1(function (value1) { 127 | step2(value1, function(value2) { 128 | step3(value2, function(value3) { 129 | step4(value3, function(value4) { 130 | // Do something with value4 131 | }); 132 | }); 133 | }); 134 | }); 135 | ``` 136 | - 异常无法捕捉 137 | 138 | ```javascript 139 | try{ 140 | setTimeout(function(){ 141 | JSON.parse("{'a':'1'}") 142 | console.log("aaaa") 143 | },0) 144 | } 145 | catch(ex){ 146 | console.log(ex); //不能catch到这个异常 147 | } 148 | ``` 149 | 150 | - 流程控制(异步代码,同步执行) 151 | 152 | 当C操作依赖于B操作和C操作,而B与A没有依赖关系时,不用第三方库(如async,eventproxy)的话,B与A本可以并行,却串行了,性能有很大的提升空间。 153 | 流程图如下: 154 | 155 | ``` 156 | graph LR 157 | Start-->A 158 | A-->B 159 | B-->C 160 | 161 | ``` 162 | 但用promise后,可以方便的用并行: 163 | **Promise**: 164 | ``` 165 | graph LR 166 | Start-->A 167 | Start-->B 168 | A-->C 169 | B-->C 170 | ``` 171 | 172 | ### Promise(ECMAScript5) 173 | 174 | 如上流程图,Promise很好的解决了“并行”的问题,我们看看用promise库怎么发送get请求: 175 | ```javascript 176 | import fetch from 'node-fetch' 177 | fetch('https://api.github.com/users/etoah') 178 | .then((res)=>res.json()) 179 | .then((json)=>console.log("json:",json)) 180 | ``` 181 | 182 | 可以看到promise把原来嵌套的回调,改为级连的方式了,实际是一种代理(proxy)。 183 | 184 | 新建一个promise实例: 185 | ```javascript 186 | var promise = new Promise(function(resolve, reject) { 187 | // 异步操作的代码 188 | if (/* 异步操作成功 */){ 189 | resolve(value); 190 | } else { 191 | reject(error); 192 | } 193 | }); 194 | ``` 195 | promise把成功和失败分别代理到resolved 和 rejected . 196 | 同时还可以级连catch异常。 197 | 198 | 199 | 到这里异步的问题,有了一个比较优雅的解决方案了,如果要吹毛求疵,还有一些别扭的地方,需要改进。 200 | #### 问题 201 | 202 | 封装,理解相对回调复杂,这是以下我公司项目的一段代码(coffeescript),并发代码,加上resolved,rejected的回调, 203 | 即使是用了coffee,混个业务和参数处理,第一眼看上去还是比较懵,代码可读性并没有想象中的好。 204 | 205 | ```coffeescript 206 | #并发请求companyLevel 207 | companyInfoP = companyinfoServicesP.companyLevel({hid: req.session.hid}) 208 | requestP(userOption).success((userInfo)-> 209 | roleOption = 210 | uri: "#{config.server_host}/services/rights/userroles?userid=#{user.userId}" 211 | method: 'GET' 212 | #保证companyInfo 写入 213 | Q.all([companyInfoP, requestP(roleOption)]).spread( 214 | (companyinfo, roles)-> 215 | Util.session.init req, user, roles.payload 216 | Util.session.set(req, "companyInfo", companyinfo.payload) 217 | Util.session.set(req, "roleids", roles.payload) 218 | u = Util.session.getInfo req 219 | return next {data: Util.message.success(u)} 220 | , (err)-> 221 | return next err 222 | ) 223 | ``` 224 | 225 | 在指明resolved 和 rejected的时,用的还是最原始的回调的方式。 226 | 227 | 能不能用同步的方式写异步代码? 228 | 229 | 在ES5前是这基本不可实现,但是,ES6的语法引入了Generator, yeild的关键字可以用同步的语法写异步的程序。 230 | 231 | ### Generator(ECMAScript6) 232 | 简单来说generators可以理解为一个可遍历的状态机。 233 | 语法上generator,有两个特征: 234 | 1. function 关键字与函数名之前有一个星号。 235 | 2. 函数体内部使用yield关键字,定义不同的内部状态。 236 | 237 | 由于generator是一个状态机,所以需要手动调用next 才能执行,但TJ大神开发了co模块,可以自动执行generator。 238 | 239 | ```javascript 240 | import co from 'co'; 241 | co(function* (){ 242 | var now = Date.now(); 243 | yield sleep(150); //约等待150ms 244 | console.log(Date.now() - now); 245 | }); 246 | 247 | function sleep(ms){ 248 | return function(cb){ 249 | setTimeout(cb, ms); 250 | }; 251 | } 252 | import fetch from 'node-fetch' 253 | 254 | co(function* (){ 255 | let result= yield [ 256 | (yield fetch('https://api.github.com/users/tj')).json(), 257 | (yield fetch('https://api.github.com/users/etoah')).json(), 258 | ]; 259 | console.log("result:",result) 260 | }); 261 | 262 | ``` 263 | 无论是延迟执行,还是并发的从两个接口获取数据,generator都可以用同步的方式编写异步代码。 264 | >注意:co模块约定,yield命令后面只能是Thunk函数或Promise对象 265 | 266 | #### 问题 267 | 268 | - 需要手动执行 269 | 即使用了TJ的CO模块,不是标准的写法,感觉用hack解决问题 270 | - 不够直观,没有语义化。 271 | 272 | 273 | ### await,async(ECMAScript7) 274 | 275 | ES7 引入了像C#语言中的 await,async关键字,而且babel已支持(引入plugins:transform-async-to-generator ) 276 | 277 | async函数完全可以看作多个异步操作,包装成的一个Promise对象,而await命令就是内部then命令的语法糖。 278 | 279 | ```javascript 280 | import fetch from 'node-fetch'; 281 | (async function (){ 282 | let result= await fetch('https://api.github.com/users/etoah'); 283 | let json =await result.json(); 284 | console.log("result:",json); 285 | })(); 286 | 287 | //exception 288 | (async function (){ 289 | try{ 290 | let result= await fetch('https://api.3github.com/users/etoah'); 291 | let json =await result.json(); 292 | console.log("result:",json); 293 | } 294 | catch(ex){ 295 | console.warn("warn:",ex); 296 | } 297 | })() 298 | ``` 299 | 300 | 简单比较会发现,async函数就是将Generator函数的星号(*)替换成async,将yield替换成await,同时不需要co模块,更加语义化。 301 | 302 | 但是与yeild又不完全相同,标准没有接收await*的语法( :( [查看详情](https://github.com/tc39/ecmascript-asyncawait/issues/61)), 303 | 304 | 若需“并行”执行promise数组,推荐用Promise.All,所以需要并行请求时,需要这样写: 305 | 306 | ```javascript 307 | (async function (){ 308 | let result= await Promise.all([ 309 | (await fetch('https://api.github.com/users/tj')).json(), 310 | (await fetch('https://api.github.com/users/etoah')).json() 311 | ]); 312 | console.log("result:",result); 313 | })(); 314 | 315 | ``` 316 | 虽说没有不能用 await* , 总体来说结构还是简单清晰的 317 | 318 | 没有任何callback,流程和异常捕获是完全同步的写法。而且javascript语言级别支持这种写法。可以说这是异步的终极解决方案了。 319 | 320 | 321 | ### 总结 322 | 323 | 到这里,jser结合promise,yield,await的写法,可以和回调嵌套说拜拜了。 324 | 325 | 虽有这么多的不同的异步编程方式,但是异步编程的本质并没有变,只有对coder更友好了而已,但对工程化可读性和可维护性有很大的改进。 326 | 327 | 全文完,如有不严谨的地方,欢迎指正。 -------------------------------------------------------------------------------- /src/async/src/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": ["transform-async-to-generator"] 4 | } -------------------------------------------------------------------------------- /src/async/src/attribute.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 | 20 | 21 | -------------------------------------------------------------------------------- /src/async/src/await.js: -------------------------------------------------------------------------------- 1 | import fetch from 'isomorphic-fetch'; 2 | 3 | (async function (){ 4 | let result= await fetch('https://api.github.com/users/etoah'); 5 | let json =await result.json(); 6 | console.log("result:",json); 7 | })(); 8 | 9 | // parallel 10 | (async function (){ 11 | let result= await Promise.all([ 12 | (await fetch('https://api.github.com/users/tj')).json(), 13 | (await fetch('https://api.github.com/users/etoah')).json() 14 | ]); 15 | console.log("result:",result); 16 | })(); 17 | 18 | 19 | //exception 20 | (async function (){ 21 | try{ 22 | let result= await fetch('https://api.3github.com/users/etoah'); 23 | let json =await result.json(); 24 | console.log("result:",json); 25 | } 26 | catch(ex){ 27 | console.warn("warn:",ex); 28 | } 29 | })() 30 | 31 | -------------------------------------------------------------------------------- /src/async/src/generator.js: -------------------------------------------------------------------------------- 1 | //generator 2 | function* test(p){ 3 | console.log(p); // 1 4 | var a = yield p + 1; 5 | console.log(a); // 3 6 | } 7 | 8 | var g = test(1); 9 | var ret; 10 | ret = g.next(); 11 | console.log(ret); // { value: 2, done: false } 12 | ret = g.next(ret.value + 1); 13 | console.log(ret); // { value: undefined, done: true } 14 | 15 | console.log("---------------delay-------------------") 16 | 17 | import co from 'co'; 18 | co(function* (){ 19 | var now = Date.now(); 20 | yield sleep(150); 21 | console.log(Date.now() - now); 22 | }); 23 | 24 | function sleep(ms){ 25 | return function(cb){ 26 | setTimeout(cb, ms); 27 | }; 28 | } 29 | 30 | console.log("---------------fetch data-------------------") 31 | import fetch from 'node-fetch' 32 | 33 | co(function* (){ 34 | let result= yield [ 35 | (yield fetch('https://api.github.com/users/tj')).json(), 36 | (yield fetch('https://api.github.com/users/etoah')).json(), 37 | ]; 38 | console.log("result:",result) 39 | }); 40 | 41 | //处理并发 42 | 43 | //数组的写法 44 | co(function* () { 45 | var res = yield [ 46 | Promise.resolve(1), 47 | Promise.resolve(2) 48 | ]; 49 | console.log(res); 50 | }).catch(()=>{}); -------------------------------------------------------------------------------- /src/async/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "async", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "generator .js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "babel-preset-es2015": "^6.14.0", 13 | "babel-register": "^6.14.0", 14 | "co": "^4.6.0", 15 | "node-fetch": "^1.6.0" 16 | }, 17 | "devDependencies": { 18 | "babel": "^6.5.2", 19 | "babel-cli": "^6.14.0", 20 | "babel-plugin-transform-async-to-generator": "^6.8.0", 21 | "isomorphic-fetch": "^2.2.1" 22 | } 23 | } -------------------------------------------------------------------------------- /src/async/src/promise.js: -------------------------------------------------------------------------------- 1 | import fetch from 'node-fetch' 2 | fetch('https://api.github.com/users/etoah') 3 | .then((res)=>res.json()) 4 | .then((json)=>console.log("json:",json)) -------------------------------------------------------------------------------- /src/char_encode/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 | alert(1) 9 | 12 | <a href=... > 13 | 14 | -------------------------------------------------------------------------------- /src/char_encode/index.js: -------------------------------------------------------------------------------- 1 | var he = require('he'); 2 | const rawStr="© 1234 < > & etoah 中文" 3 | var str=(he.encode(rawStr)); 4 | console.log('encode:',str); 5 | console.log('decode:',he.decode(str)) 6 | 7 | var encodedStr = rawStr.replace(/[\u00A0-\u9999<>\&]/gim, function(i) { 8 | return '&#'+i.charCodeAt(0)+';'; 9 | }); 10 | 11 | console.log('encodedStr:',encodedStr); 12 | console.log('decodedStr:',he.decode(encodedStr)); -------------------------------------------------------------------------------- /src/char_encode/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "char_encode", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "he": "^1.1.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/global.md: -------------------------------------------------------------------------------- 1 | 2 | # javascript 如何列出全局对象window的非原生属性。 3 | ## Why 4 | 5 | 研究一个网站前端技术的时候,了解它的全局的对象是一个好的入口, 6 | 一般来说,常见的库就会用外观模式,最后暴露一个对象给用户调用, 7 | 比如jQuery,requirejs,angular,react 8 | 均是用这种方式。 9 | 10 | 如果没有用cmd/amd模块化或类似webpack工具打包的话,会给全局对象window添加一个属性,如angular: 11 | ![](http://images2015.cnblogs.com/blog/585323/201611/585323-20161107154438889-1981509001.png) 12 | 13 | 如React 14 | ![](http://images2015.cnblogs.com/blog/585323/201611/585323-20161107165638983-170144089.png) 15 | 16 | 17 | 同时,为了避免全局污染,也要关注全局变量的个数和详情。 18 | 19 | 20 | ## How 21 | 22 | 可以通过ES5的新增api(Object.keys)看浏览器全局变量列表:`Object.keys(window)` 23 | 24 | 发现一般的网站都有两百多个全局变量,人力去看且需要区分是否是用户定义的比较困难,需要一个`script`去列出所有的非原生的全局属性. 25 | 26 | 开始想的是能不能防篡改对象的相关检测api(Object.isExtensible,Object.isSealed,Object.isFrozen)来判断是否原生api? 27 | 28 | 但并不是所有的原生对象都是seaded. 所以此方法行不通。 29 | 30 | 那么能不能有一个纯净的、没有加载第三方库的全局对象? 31 | 32 | 对于浏览器环境,我们有iframe 33 | 34 | 可以添加一个iframe,然后对比当前的window,就可以得到详细列表。 35 | 36 | 37 | ```javascript 38 | var iframe = document.createElement("iframe"); 39 | document.body.appendChild(iframe); 40 | Object.keys(iframe.contentWindow).length 41 | 42 | ``` 43 | 44 | 列出非原来对象 45 | 46 | ```javascript 47 | (function(){ 48 | var iframe = document.createElement("iframe"); 49 | document.body.appendChild(iframe); 50 | var originWindow=iframe.contentWindow, 51 | currentWindow=window 52 | var origin =Object.keys(originWindow), 53 | current =Object.keys(currentWindow), 54 | extendAttr={}; 55 | current.forEach((key)=>{ 56 | if(originWindow[key]===undefined){ 57 | extendAttr[key]=currentWindow[key] 58 | }; 59 | }) 60 | console.log(`origin window:${origin.length},current window:${current.length},extentAttr:${Object.keys(extendAttr).length}`) 61 | console.log("extendAttr:",extendAttr); 62 | document.body.removeChild(iframe); 63 | })(); 64 | 65 | ``` 66 | cnblogs的全局对象: 67 | ![](http://images2015.cnblogs.com/blog/585323/201611/585323-20161108165450702-67997046.png) 68 | 69 | 70 | ### Node怎么处理 71 | 72 | 由于node没有像Chrome Dev Tools Console一样的工具,可以直观简单的执行js代码片段, 73 | 74 | 对于Nodejs,可以在应用运行稳定(所有的全局,单例对象都初始化完成)后,再导出全局对象, 75 | 76 | 再在同一环境,不导入任何库导出全局对象,进行对比即可。 77 | 78 | 79 | 80 | ### 怎么知道一个原生函数有没有覆盖 81 | 82 | 由于`Function.prototype.toString`API,对原生函数返回`[native code]` 83 | 84 | ```javascript 85 | setTimeout.toString() 86 | "function setTimeout() { [native code] }" 87 | ``` 88 | 但对于自定义的函数会返回源码: 89 | ```javascript 90 | jQuery.toString() 91 | "function (e,t){return new x.fn.init(e,t,r)}" 92 | ``` 93 | angular就是利用这一特性实现依赖注入[http://www.cnblogs.com/etoah/p/5460441.html](http://www.cnblogs.com/etoah/p/5460441.html) 94 | 95 | 可以用此特性,来检测是否是原生的api(仅适用于浏览器运行环境,node环境有差异). 96 | 97 | 98 | 一个原生属性(Object,string...)怎么检测有没有被用户重置,除了用`typeof`检测数据类型, 本人暂没有更好的方案,欢迎讨论。 99 | 100 | 101 | -------------------------------------------------------------------------------- /src/h5editor/Readme.md: -------------------------------------------------------------------------------- 1 | 2 | #用H5打造一个离线的Web编辑器 3 | 4 | ##目录 5 | - 一背景 6 | 7 | ## 一. 背景 8 | 9 | 个人看好H5的发展,个人认为h5带给我们最大的好处,是以后web app既可以有c/s模式的原生应用一样的优点:有本地数据库indexDB,离线操作在线同步,又可以免下载易嵌入易排板跨平台推广更方便。 10 | 一个C/S App,不考虑流量资费,从用户点击下载到App成功启动,至少要1分钟,中间可能还要人为操作点数次。而H5做的Web App,可边展示边加载,首屏加载只要2s内. 11 | 首屏加载完成后,可开始动态的加载其它资源:同步indexDB,缓存其它页面的css,js,img,达到每个页面都可以“秒进”的效果。 12 | 本人之前做的是后端项目,看好H5的发展,这是一个H5练手的项目,一个基于CodeMirrir的Web可离线编辑器,与大家分享。 13 | 14 | 首先说一说实现的特性和效果,再从技术的细节分析各个模块的实现和部份性能优化。 15 | 16 | 17 | 18 | Github:https://github.com/etoah/Lucien 19 | 20 | ## 二. 特性及效果 21 | ###效果 22 | 23 | ####运行 24 | ![run](run.gif) 25 | ####emmet 26 | ![emmet](emmet.gif) 27 | ####AppCache 28 | ![appcache](appcache.jpg) 29 | ###功能 30 | * 支持emmet语法 31 | * 支持完全断网离线 32 | * 支持本地下载 33 | * 支持indexDB保存 34 | * 支持代码格式化 35 | * 支持快捷键 36 | * 代码高亮 37 | * 运行展示 38 | 39 | Demo地址:http://fer.ren/Lucien/ 40 | 41 | 42 | ## 布局 43 | 44 | 对于一个项目首先映入眼帘的 45 | ![layout](layout.jpg) 46 | 47 | 布局用的是CSS3的弹性布局。为了实现导航栏和内容页(编辑器)布满屏幕,同时减少repaint和redraw的影响。主要的上下栏布局的方法是弹性布局。 48 | 代码如下: 49 | ```css 50 | .nav { 51 | position: absolute; 52 | height: 30px; 53 | line-height: 30px; 54 | width: 100%; 55 | background: #2c2827;/*TODO:theme*/ 56 | } 57 | .editors 58 | { 59 | position: absolute; 60 | top: 30px; 61 | bottom: 0; 62 | display: -webkit-flex; /* Safari */ 63 | display: flex; 64 | flex-wrap: nowrap; 65 | justify-content:flex-end; 66 | background: #2c2827;/*TODO:theme*/ 67 | } 68 | 69 | ``` 70 | 71 | 72 | 而对于CSS编辑窗口,CSS3的弹性布局可很方便的实现,自动充满 73 | 74 | 75 | ##交互事件绑定和快捷键 76 | 77 | 78 | ##提示和动画 79 | 80 | ## 三. 存储:indexDB 81 | 82 | ###用什么存 83 | 要实现一个编辑器,首先我们面临着一个问题,代码存在哪? 84 | 要离线可用,那么肯定不能post到服务器端,client端只有cookie,storge,WebSQL,indexDB 85 | cookie在部份浏览器有大小限制,而且如果后面要做服务器端的话,数据会写的http头部,不考虑。 86 | WebSQL是废弃的标准,不考虑。 87 | 剩下的只有localstorge和indexDB. 88 | 做过后端马上反应是用DB,那为什么不用localstorge呢。 89 | localstorge也有大小限制(5MB),而且不像indexDB有存储空间,localstorge所有的数据直接用key访问。不适合存数据。 90 | 91 | 所以选择了indexDB 。 92 | 93 | ###异步嵌套的问题 94 | 由于所有的indexDB的操作都是异步的,如果我们希望把取出的id加1然后返回就需要这样: 95 | 96 | ```js 97 | indexedDB.open(dbName, dbVersion).onsuccess=function(e){ 98 | var db=e.target.result; 99 | var tx = db.transaction('info', 'readwrite'); 100 | var store = tx.objectStore('info'); 101 | store.get('id').onsuccess = function (e) { 102 | store.put({key:'id',value:e.target.result.value + 1}).onsuccess = function (e) { 103 | …… 104 | }; 105 | }; 106 | } 107 | 108 | ``` 109 | 110 | 这里还没有onerror的操作,如果加上了更是不忍直视,所以引进了Promise异步编程模式。 111 | 112 | 我们对所有的异步操作都Promise化: 113 | 114 | 比如 打开数据库和添加数据。 115 | ```js 116 | 117 | function DBready(storeName) { 118 | return new Promise(function (resolve, reject) { 119 | new IDBStore({ 120 | storePrefix: '', 121 | dbVersion: '1', 122 | storeName: storeName, 123 | keyPath: 'id', 124 | autoIncrement: true 125 | }, resolve);//'this' is IDBStore 126 | }); 127 | } 128 | 129 | function add(storeName, value) { 130 | 131 | return DBready(storeName).then(function (factory) { 132 | 133 | return new Promise(function (resolve, reject) { 134 | 135 | factory.put(value, resolve, reject) 136 | }) 137 | }); 138 | } 139 | 140 | ``` 141 | 142 | 把所有的CURD操作封装在[Entity.js](https://github.com/etoah/Lucien/blob/master/Source/FE/js/lib/Entity.js) 143 | 144 | 在code.js就可以实现多态了。完整代码请移步:[Code.js](https://github.com/etoah/Lucien/blob/master/Source/FE/js/app/Code.js) 145 | ```js 146 | 147 | Code.prototype.add = function () { 148 | this.entity.synctime = new Date().format("yyyyMMddhhmmss"); 149 | parseInt(local(config.storeKey))&&(this.entity.id=parseInt(local(config.storeKey))); 150 | return Entity.add(STORE_NAME,this.entity).then(function(data){ 151 | return new Promise(function(resolve, reject){ 152 | local(config.storeKey,data); 153 | this.entity&&(this.entity.id=data); 154 | resolve(this.entity); 155 | }); 156 | 157 | }); 158 | }; 159 | 160 | ``` 161 | 162 | 优化后新增一条记录就是这样: 163 | ```js 164 | 165 | Code.save=function(editor){ 166 | return new Code(editor.html.getValue(), editor.css.getValue(), editor.js.getValue()).add(); 167 | 168 | }; 169 | ``` 170 | 171 | ###storage存储 172 | 173 | 174 | ##通迅:work实现多线程 175 | 数据我们存储在client的indexDB,如果我们要实现server与client可以相互同步,就要考虑可服务性和性能的问题了。 176 | 如果需要同步,我们就要对比client和server的数据的时间戳,以最新的为准,如果同步一两个数据,由于indexDB和request都是异步的, 177 | 页面即不会卡住,可步的时间点也可控。 178 | 179 | 但是问题来了,如果我要同步1000条数据呢。 180 | 181 | 182 | 183 | ## 四. 模块化和打包 184 | 185 | 这个WebApp,虽功能较少,但五脏俱全。模块较多,如果不进行模块化的话,可能过了一周,我可能自己都不能快速的找到代码了, 186 | 模块化可以更好的把复杂的问题,分解成更小的问题,同时更好的功能细分,也会带来更佳的复用。 187 | 但同时也带来的问题:模块化给我们带来了29个js文件! 188 | 我们不可能让一个WebAPP每次都加载29个js文件。 189 | 190 | 我们需要打包,当时试用了几个打包的工具都不好用,还是requirejs官方的r.js好用 191 | 192 | 打包的配置文件[build.js](https://github.com/etoah/Lucien/blob/master/Source/FE/js/build.js): 193 | ```js 194 | 195 | ({ 196 | 197 | baseUrl: 'lib', 198 | paths: { 199 | app: '../app' , 200 | main:'../main' 201 | }, 202 | dir: '../public/js', 203 | modules: [ 204 | { 205 | name: 'main' 206 | } 207 | ], 208 | optimize:"uglify2", 209 | fileExclusionRegExp: /^(r|build)\.js$/, 210 | optimizeCss: "none", 211 | removeCombined: true 212 | }) 213 | ``` 214 | 215 | gulp合并压缩js的任务代码: 216 | 217 | ```js 218 | // 脚本 219 | gulp.task('rjs', shell.task(['node js/r.js -o js/build.js'])); 220 | gulp.task('js', function () { 221 | return gulp.src('js/require.js') 222 | .pipe(uglify()) 223 | .pipe(gulp.dest(config.jsPath)) 224 | .pipe(notify({message: 'require task complete'})); 225 | }); 226 | ``` 227 | 全部打包代码请移步:[gulpfile.js](https://github.com/etoah/Lucien/blob/master/Source/FE/gulpfile.js) 228 | 229 | ## 五. 离线:appCache 230 | 231 | 用appCache和indexDB,我们可以轻易的实现离线。但是除了离线这个好处,还有什么好处吗? 232 | 233 | 即使合并压缩了js文件和css文件,由于引用了CodeMirror库的原因,js文件还是有733kb! 234 | 235 | 我们不可能每次都让server返回这么大的文件回来。 236 | 237 | 我们用appCache,除了第一次访问,每次都从cache中加载。实现第二次秒进! 238 | 239 | ###第一次加载怎么提升速度 240 | 如果是商业化的应用,一般会有一个介绍页,我们可以在介绍页内动态的加载一个frame(window.onload后),这加载一个空的页面,页面的html属性设计为 `manifest="editor.appcache"` 241 | 这个页面的appcache与webapp应用的cache设置为同一个文件,就可以“偷偷”提前的加载缓存,这样webapp第一次也可以“秒进” 242 | 243 | 关于appCache可以看持续更新的松土文:[HTML5 Application cache初探和企业应用启示](http://www.cnblogs.com/etoah/p/4931903.html) 244 | 245 | 246 | 247 | ## 六. 更一步 248 | 249 | 1. 云端同步。由于本人原来是做后端,这一步太熟悉了反而没有兴趣去实现。后面可能会实现这个功能。 250 | 251 | 252 | 253 | ## 总结 254 | 255 | 这次总结,比较随意,只有部份的技术要点,没有清晰的思路,此项目持续更新中。 -------------------------------------------------------------------------------- /src/h5editor/appcache.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etoah/note/edd66168c4c574ce815a881a784dffe42fb51bae/src/h5editor/appcache.jpg -------------------------------------------------------------------------------- /src/h5editor/emmet.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etoah/note/edd66168c4c574ce815a881a784dffe42fb51bae/src/h5editor/emmet.gif -------------------------------------------------------------------------------- /src/h5editor/layout.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etoah/note/edd66168c4c574ce815a881a784dffe42fb51bae/src/h5editor/layout.jpg -------------------------------------------------------------------------------- /src/h5editor/run.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etoah/note/edd66168c4c574ce815a881a784dffe42fb51bae/src/h5editor/run.gif -------------------------------------------------------------------------------- /src/http2/Readme.md: -------------------------------------------------------------------------------- 1 | #HTTP2特性预览和抓包分析 2 | 3 | ## 背景 4 | 5 | 近年来,http网络请求量日益添加,以下是[httparchive](http://httparchive.org/trends.php?s=All&minlabel=Nov+1+2012&maxlabel=Sep+1+2016)统计,从2012-11-01到2016-09-01的请求数量和传输大小的趋势图: 6 | 7 | ![chart](https://cloud.githubusercontent.com/assets/7630567/18625558/f029e0cc-7e81-11e6-8131-39d5ae2cb982.png) 8 | 9 | 当前大部份客户端&服务端架构的应用程序,都是用http/1.1连接的,现代浏览器与单个域最大连接数,都在4-6个左右,由上图Total Requests数据,如果不用CDN分流,平均有20个左右的串行请求。 10 | HTTP2 是1999年发布http1.1后的一次重大的改进,在协议层面改善了以上问题,减少资源占用,来,直接感受一下差异: 11 | 12 | [HTTP/2 is the future of the Web, and it is here!](https://http2.akamai.com/demo) 13 | 这是 Akamai 公司建立的一个官方的演示,用以说明 HTTP/2 相比于之前的 HTTP/1.1 在性能上的大幅度提升。 同时请求 379 张图片,从Load time 的对比可以看出 HTTP/2 在速度上的优势。 14 | 15 | ![image](https://cloud.githubusercontent.com/assets/7630567/18613229/7246311c-7da8-11e6-80fb-562729c71069.png) 16 | 17 | >[本文所有源码和抓包文件在github](https://github.com/etoah/note/tree/master/http2) 18 | 19 | ### HTTP/2 源自 SPDY/2 20 | 21 | SPDY 系列协议由谷歌开发,于 2009 年公开。它的设计目标是降低 50% 的页面加载时间。当下很多著名的互联网公司都在自己的网站或 APP 中采用了 SPDY 系列协议(当前最新版本是 SPDY/3.1),因为它对性能的提升是显而易见的。主流的浏览器(谷歌、火狐、Opera)也都早已经支持 SPDY,它已经成为了工业标准,HTTP Working-Group 最终决定以 SPDY/2 为基础,开发 HTTP/2。HTTP/2标准于2015年5月以RFC 7540正式发表。 22 | 23 | 但是,HTTP/2 跟 SPDY 仍有不同的地方,主要是以下两点: 24 | 25 | HTTP/2 支持明文 HTTP 传输,而 SPDY 强制使用 HTTPS 26 | HTTP/2 消息头的压缩算法采用 HPACK ,而非 SPDY 采用的 DEFLATE 27 | 28 | >协议文档请见:[rfc7540:HTTP2](https://tools.ietf.org/html/rfc7540) 29 | 30 | ## HTTP2特性概览 31 | ### 1. 二进制协议 32 | HTTP/2 采用二进制格式传输数据,而非 HTTP/1.x 的文本格式 33 | 34 | ![image](https://cloud.githubusercontent.com/assets/7630567/18660522/7b510934-7f43-11e6-8af2-26d10abe69d0.png) 35 | 36 | 37 | 由上图可以看到HTTP2在原来的应用层和HTTP层添加了一层二进制传输。 38 | 39 | 二进制协议的一个好处是,可以定义额外的帧。 40 | 41 | HTTP/2 定义了近十种帧(详情可分析抓包文件),为将来的高级应用打好了基础。如果使用文本实现这种功能,解析数据将会变得非常麻烦,二进制解析则方便得多。 42 | [RFC7540:Frame Definitions](https://tools.ietf.org/html/rfc7540#page-31) 43 | ![image](https://cloud.githubusercontent.com/assets/7630567/18627461/90325440-7e8d-11e6-90d7-4adf8a412445.png) 44 | **协议中定义的帧** 45 | 46 | ### 2. 多路复用 47 | HTTP/2 复用TCP连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应,这样就避免了"队头堵塞"(见TCP/IP详解卷一)。 48 | 每个 Frame Header 都有一个 Stream ID 就是被用于实现该特性。每次请求/响应使用不同的 Stream ID。就像同一个 TCP 链接上的数据包通过 IP: PORT 来区分出数据包去往哪里一样。 49 | ![image](https://cloud.githubusercontent.com/assets/7630567/18660493/52b354aa-7f43-11e6-8a21-2f6fedf2a042.png) 50 | 51 | [rfc7540: HTTP2 Multiplexing](https://tools.ietf.org/html/rfc7540#page-15)中对Multiplexing的说明 52 | ``` 53 | Streams and Multiplexing 54 | 55 | A "stream" is an independent, bidirectional sequence of frames 56 | exchanged between the client and server within an HTTP/2 connection. 57 | Streams have several important characteristics: 58 | 59 | o A single HTTP/2 connection can contain multiple concurrently open 60 | streams, with either endpoint interleaving frames from multiple 61 | streams. 62 | 63 | o Streams can be established and used unilaterally or shared by 64 | either the client or server. 65 | 66 | o Streams can be closed by either endpoint. 67 | 68 | o The order in which frames are sent on a stream is significant. 69 | Recipients process frames in the order they are received. In 70 | particular, the order of HEADERS and DATA frames is semantically 71 | significant. 72 | 73 | o Streams are identified by an integer. Stream identifiers are 74 | assigned to streams by the endpoint initiating the stream. 75 | ``` 76 | 77 | 78 | ### 3. 数据流 79 | 数据流发送到一半的时候,客户端和服务器都可以发送信号(RST_STREAM帧),取消这个数据流。1.1版取消数据流的唯一方法,就是关闭TCP连接。这就是说,HTTP/2 可以取消某一次请求,同时保证TCP连接还打开着,可以被其他请求使用。 80 | 81 | ### 4. 头信息压缩: 82 | HTTP/2 对消息头采用 [HPACK](http://http2.github.io/http2-spec/compression.html) 进行压缩传输,能够节省消息头占用的网络的流量。而 HTTP/1.x 每次请求,都会携带大量冗余头信息,浪费了很多带宽资源。 83 | HTTP2对http头建立[索引表](http://http2.github.io/http2-spec/compression.html#rfc.section.A),相同的头只发送hash table 的index, 同时还用了霍夫曼编码和传统的gzip压缩。 84 | ### 5. 服务器推送 85 | 服务端能够更快的把资源推送给客户端。例如服务端可以主动把 JS 和 CSS 文件推送给客户端,而不需要客户端解析 HTML 再发送这些请求。当客户端需要的时候,它已经在客户端了。 86 | 87 | 那么存在一个问题,如果客户端设置了缓存怎么办。有三种方式(来自社区) 88 | - 客户端可以通过设置SETTINGS_ENABLE_PUSH为0值通知服务器端禁用推送 89 | - 发现缓存后,客户端和服务器都可以发送信号(RST_STREAM帧),取消这个数据流。 90 | - [cache-digest](https://github.com/h2o/cache-digest.js)(提案) 91 | > [rfc7540: HTTP2 Server Push ](https://tools.ietf.org/html/rfc7540#page-60) 92 | #### 6. 流优先级 93 | HTTP2允许浏览器指定资源的优先级。 94 | >[rfc7540: Stream Priority](https://tools.ietf.org/html/rfc7540#page-24) 95 | 96 | 97 | ### 浏览器支持 98 | ![image](https://cloud.githubusercontent.com/assets/7630567/18593777/904a9aa2-7c6f-11e6-8e50-b1edf6b92a4e.png) 99 | 100 | **主流浏览器都只支持 HTTP/2 Over TLS** 101 | 102 | ### node中启用http2 103 | 104 | node中可以用spdy模块来启动应用,spdy的api,与https是一致的且主流浏览器只支持HTTP/2 Over TLS,需要配置 私钥和证书,本地自签名服务器配置可参考**引用6,7**。 105 | 106 | ```javascript 107 | const express = require('express'); 108 | const fs = require('fs'); 109 | const http2 = require('spdy'); 110 | const path = require('path'); 111 | const options = { 112 | key: fs.readFileSync('./keys/privatekey.pem'), 113 | cert: fs.readFileSync('./keys/certificate.pem') 114 | }; 115 | const app = new express(); 116 | http2 117 | .createServer(options, app) 118 | .listen(8080, ()=>{ 119 | console.log(`Server is listening on https://localhost:8080. 120 | You can open the URL in the browser.`) 121 | } 122 | ) 123 | app.use("/",(req,res)=>{ 124 | 125 | res.send("hello http2!"); 126 | }) 127 | ``` 128 | 如上,对于已存在的项目只要修改几行代码就可以使用http2.0了。 129 | 130 | 131 | 请求头和响应头: 132 | ![image](https://cloud.githubusercontent.com/assets/7630567/18627369/28a74b14-7e8d-11e6-8c85-4c0d8974569f.png) 133 | >说明:新版的Chrome,对不安全的证书(如本地的自签名服务)会降级到http1.1,firefox不会出现此问题。 134 | 135 | **启动server push** 136 | 137 | ``` 138 | 139 | app.get("/",(req,res)=>{ 140 | var stream = res.push('/app.js', { //服务器推送 141 | status: 200, // optional 142 | method: 'GET', // optional 143 | request: { 144 | accept: '*/*' 145 | }, 146 | response: { 147 | 'content-type': 'application/javascript' 148 | } 149 | }) 150 | stream.on('error', function() { 151 | }) 152 | stream.end('console.log("http2 push stream, by Lucien ");') 153 | 154 | res.send(`hello http2! 155 | `);//express 并没有host static ,这个app.js 来自push 156 | }) 157 | ``` 158 | >[源码在github](https://github.com/etoah/note/tree/master/http2) 159 | 160 | 161 | **响应** 162 | 163 | ![image](https://cloud.githubusercontent.com/assets/7630567/18631346/f952546e-7ea3-11e6-8858-07a1519bb7af.png) 164 | 165 | 166 | ![image](https://cloud.githubusercontent.com/assets/7630567/18631363/0e7044e6-7ea4-11e6-872a-6e9176c42c79.png) 167 | 168 | 169 | 170 | 171 | ## 抓包分析 172 | 173 | 可以用chrome 内部自带的工具(chrome://net-internals/)查看http2流量,但这个包信息量比较少,结构不如我们熟悉的Fiddler or Wireshark清晰。 174 | 175 | Fiddler是直接作为中间代理,可以作为客户端直接与服务端通讯,可以像浏览器那样直接解密https,直接看到https报文, 176 | 但是由于[受限于.NET Framework](http://www.telerik.com/forums/http-2-support)暂不支持Http2. 177 | 178 | 用wireshark直接抓包 https:443端口的流量是这样的: 179 | ![image](https://cloud.githubusercontent.com/assets/7630567/18628348/5b523236-7e92-11e6-91e3-08529fc48fe2.png) 180 | 181 | 数据被加密了,协议细节完全看不到。 182 | [这里](https://ismisepaul.wordpress.com/2015/03/04/http2-traffic-in-wireshark/)介绍了一种方法获取私钥解包。 183 | 抓包https包时要把代理关了,不然私钥不是同一个,wireshark不能解包(被这个坑了两小时T T)。 184 | 185 | 186 | ![image](https://cloud.githubusercontent.com/assets/7630567/18659652/876b4d7e-7f3e-11e6-8494-0631ff9ee358.png) 187 | 188 | ![image](https://cloud.githubusercontent.com/assets/7630567/18660343/721f9048-7f42-11e6-81b2-cd0e7400495e.png) 189 | 190 | 一个包内有多个不同的Steam ID 191 | 192 | ![image](https://cloud.githubusercontent.com/assets/7630567/18660190/b9bce3d4-7f41-11e6-8a6d-3c05e0514318.png) 193 | 194 | 追踪解密后TCP流可以看到,由于多路复用,各个不同的请求交替传输不同的帧,所以流数据是乱的。但在同一帧内数据还是正常的。 195 | 196 | 197 | ## 最后 198 | 最后,HTTP2有更高的传输速度,更少的资源占用,可以去除各种性能优化tricks(如css sprite,inline-image.) 199 | 转向WEB开发的美好未来T.T 200 | 201 | 202 | ## 参考资料 203 | 204 | 1. [Turn-on HTTP/2 today!](https://http2.akamai.com/) 205 | 2. [Hypertext Transfer Protocol Version 2 (HTTP/2)](https://tools.ietf.org/html/rfc7540) 206 | 3. [npm spdy](https://github.com/indutny/node-spdy) 207 | 4. [npm spdy push ](https://github.com/jshttp/spdy-push) 208 | 5. [How to create a self-signed SSL Certificate ](http://www.akadia.com/services/ssh_test_certificate.html) 209 | 6. [HPACK: Header Compression for HTTP/2](http://http2.github.io/http2-spec/compression.html) 210 | 7. [用Node.js创建自签名的HTTPS服务器](https://cnodejs.org/topic/54745ac22804a0997d38b32d) 211 | 212 | -------------------------------------------------------------------------------- /src/http2/http2.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etoah/note/edd66168c4c574ce815a881a784dffe42fb51bae/src/http2/http2.pcapng -------------------------------------------------------------------------------- /src/http2/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const fs = require('fs'); 3 | const http2 = require('spdy'); 4 | const path = require('path'); 5 | const options = { 6 | key: fs.readFileSync('./keys/server-key.pem'), 7 | cert: fs.readFileSync('./keys/server-cert.pem') 8 | }; 9 | const app = new express(); 10 | http2 11 | .createServer(options, app) 12 | .listen(8080, ()=>{ 13 | console.log(`Server is listening on https://localhost:8080. 14 | You can open the URL in the browser.`) 15 | } 16 | ) 17 | //app.use('/', express.static(path.join(__dirname, './assets'))); 18 | 19 | app.get("/",(req,res)=>{ 20 | var stream = res.push('/app.js', { //服务器推送 21 | status: 200, // optional 22 | method: 'GET', // optional 23 | request: { 24 | accept: '*/*' 25 | }, 26 | response: { 27 | 'content-type': 'application/javascript' 28 | } 29 | }) 30 | stream.on('error', function() { 31 | }) 32 | stream.end('console.log("http2 push stream, by Lucien ");') 33 | 34 | res.send(`hello http2! 35 | `);//express 并没有host static ,这个app.js 来自push 36 | }) -------------------------------------------------------------------------------- /src/http2/keys/ca-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICATCCAWoCCQDD+AEdCxZtSzANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB 3 | VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 4 | cyBQdHkgTHRkMB4XDTE2MDkxOTIyNTM0NFoXDTE2MTAxOTIyNTM0NFowRTELMAkG 5 | A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 6 | IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAzZZ+ 7 | pbajSbsinmOzjiRozo0spKD0vEv9SzBPiK7ZRHhaNF5k5ympvWMpqQqeYP3sdvT5 8 | jMCRjC5xnuNMVMo25eqhGb6HUH3TNIY8mySELOd9tBtl29s8IXDm+IppnMBQ5qvR 9 | xoSXoCl5eIfrpegvOwicNIFCbCTkgQwhdVsrsdUCAwEAATANBgkqhkiG9w0BAQsF 10 | AAOBgQAz8Ga5SC74oPfhR9jOTYGmY/twUxPMylmVxh/2ULZjshuhM1LNN8yaxdef 11 | EyuCVxOnhTqCYcWdXcj3un19GH+WkU6LaC/nXMCuVWYf369gPvODB7IBtoI80UH0 12 | /Y7mP0YquqPtuO0wEGk+Uo8sOiEEfIw4qBJ5I+w+QxTBAvqTsQ== 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /src/http2/keys/ca-cert.srl: -------------------------------------------------------------------------------- 1 | AB38DAA0479C867D 2 | -------------------------------------------------------------------------------- /src/http2/keys/ca-csr.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIBhDCB7gIBADBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh 3 | MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEB 4 | AQUAA4GNADCBiQKBgQDNln6ltqNJuyKeY7OOJGjOjSykoPS8S/1LME+IrtlEeFo0 5 | XmTnKam9YympCp5g/ex29PmMwJGMLnGe40xUyjbl6qEZvodQfdM0hjybJIQs5320 6 | G2Xb2zwhcOb4immcwFDmq9HGhJegKXl4h+ul6C87CJw0gUJsJOSBDCF1Wyux1QID 7 | AQABoAAwDQYJKoZIhvcNAQELBQADgYEAlR2HzO/Bzrew0Asw9TRLxddFGUQCV9KJ 8 | S0B0WrbRNfB6VNux1fn1FIEAW6UcyOwSo90w+XxCJewrJRNBzix5Q+6ISWp9/2xT 9 | xKEffIUAQvC73BHRBvEFkg8g7/+OQlxs2GnO3g+mNyF/LUV71XDHsPypHXKpz4Kj 10 | lDhN0YZo3eY= 11 | -----END CERTIFICATE REQUEST----- 12 | -------------------------------------------------------------------------------- /src/http2/keys/ca-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: DES-CBC,BA57C2BAD8462AD5 4 | 5 | gYLBVqcOO0CLJB7i4CWeGeoDsw9Q3La92rrEDdCBoIey279rezt1W4oBDdRS8Dtf 6 | gpFDRhdwwwvJy0ejwEmGDOuJufptAdbg1pdHitEphU5siZBUiRDjlrJvZlLP7Xcf 7 | cFMqrwO2e9XMW/2+5fSWLIAqPGOS5HGyd4JO9SXEmGSR3oDlS5CdVvsB4unHUu9D 8 | U6hpUVRK/wgL8oWcU2lxE8+s89SCDrw9rpsTxPhp/nf32sjp8BUlk9U0IkjI3195 9 | UmMlMwEs8yemOFSgBR45HN2nJFfeSS12RCfYmPv/ydHxDJj8xDZT9OYiZf/m9/KW 10 | L4lJzDVGf11HO5yr5mps7FmrD4BF3VMAanQ5K+RNGEsWZ4180REpDYA/teP01y1m 11 | uZrILuvJOg5/ILVb00CJUF9DsmlEvTibU79B5OTxxYDEkrT8BIOAaoYqmyUMOJ1c 12 | dC9uXbbmW/oXEn9j0O9ognSiY0aSN/uAA+yuihURY+/X/bY0tKi+6LfoqKMYJbtx 13 | XOb9CdjuYi7mh+NkTYYkGqwDOF6XO757/ujeHI1ZCl+D/MT/BNniJDvzjfIzr1Wl 14 | tPQzI6ya3W0gg8ZkacKdXMnLsIycj0k8l68cAUk9PVfSmQNbfSqRE9P4RiG5hw+c 15 | zrGvZae1LFtsYE+HPjJHQDeTkc49Y41uiwqcRSHK8IDIq9i19k/rRQMkBU0yMZ4z 16 | 0T80f4AXdUdpn5cKr1WdrPgGfW3fMmG6+OEzQakDGvAqn08OB1vkVco8+P+zNu7t 17 | B1uIv0QcG/20sdp76QZZ43fHecD9VUpjA7HPLrAzCRcwfeTgFiOxSg== 18 | -----END RSA PRIVATE KEY----- 19 | -------------------------------------------------------------------------------- /src/http2/keys/certificate.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICkTCCAfoCCQCY3Cexw5bAVzANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMC 3 | Q04xDzANBgNVBAgMBkJlaWppbjEPMA0GA1UEBwwGQmVpamluMRIwEAYDVQQKDAls 4 | dWNpZW4uY24xDTALBgNVBAsMBGNvbmExGTAXBgNVBAMMEDk5Njc3ODgzMUBxcS5j 5 | b20xHTAbBgkqhkiG9w0BCQEWDmV0b2FobkAxNjMuY29tMB4XDTE2MDkxODAwMDEx 6 | MloXDTE2MTAxODAwMDExMlowgYwxCzAJBgNVBAYTAkNOMQ8wDQYDVQQIDAZCZWlq 7 | aW4xDzANBgNVBAcMBkJlaWppbjESMBAGA1UECgwJbHVjaWVuLmNuMQ0wCwYDVQQL 8 | DARjb25hMRkwFwYDVQQDDBA5OTY3Nzg4MzFAcXEuY29tMR0wGwYJKoZIhvcNAQkB 9 | Fg5ldG9haG5AMTYzLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxjx8 10 | KLFKShUYX+1vrsJadKLH6Oj11HGIT0J+Bqg4L5OMRz8KJNXq2B5Bm0Gja6b5U0mN 11 | fRkPzby5CUFNSaPmB6L9rIE/QBBcp0G8SOKhjJ5GThgyccvyDsNaIYqa2qQieomt 12 | MboYNf8w/XngjMcV1V+YGLkHMSGNMrlY/uCh1D0CAwEAATANBgkqhkiG9w0BAQsF 13 | AAOBgQCpFuQTYJgx9BCaRrBHm+ptZJLR3vzQ1oN8DpniZzOmR+BSzDVWZT4UPslK 14 | I+RRIxDpVF2dsh7zpUTJgKoXi00xa+TUDmQQ0DS0FP96T/HUQI9sLZGnK+k6lPUZ 15 | MA3OwXdjGvRLTTTY3nm0n63muV0AMnfCVu3+VTanHnPOiOO75Q== 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /src/http2/keys/certrequest.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIB5zCCAVACAQAwgYwxCzAJBgNVBAYTAkNOMQ8wDQYDVQQIDAZCZWlqaW4xDzAN 3 | BgNVBAcMBkJlaWppbjESMBAGA1UECgwJbHVjaWVuLmNuMQ0wCwYDVQQLDARjb25h 4 | MRkwFwYDVQQDDBA5OTY3Nzg4MzFAcXEuY29tMR0wGwYJKoZIhvcNAQkBFg5ldG9h 5 | aG5AMTYzLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxjx8KLFKShUY 6 | X+1vrsJadKLH6Oj11HGIT0J+Bqg4L5OMRz8KJNXq2B5Bm0Gja6b5U0mNfRkPzby5 7 | CUFNSaPmB6L9rIE/QBBcp0G8SOKhjJ5GThgyccvyDsNaIYqa2qQieomtMboYNf8w 8 | /XngjMcV1V+YGLkHMSGNMrlY/uCh1D0CAwEAAaAaMBgGCSqGSIb3DQEJBzELDAlE 9 | dWFuMjMyMyMwDQYJKoZIhvcNAQELBQADgYEABntqEt+DwMfhzA0Xr7nL4VUmMk67 10 | 1vtPa4v9lgXmXkctj+069R+NwQNdELc0Bkm0rUXt+0OvCTPdQRgM0CKmqn2lNcNZ 11 | i6132p/ixu3lnE63O5SIWjc1qA2GVNwD8vO7VNkX/kxq0pZsKAlDvfkOO2e5XpMQ 12 | vY2no/Vay1yGKVQ= 13 | -----END CERTIFICATE REQUEST----- 14 | -------------------------------------------------------------------------------- /src/http2/keys/gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etoah/note/edd66168c4c574ce815a881a784dffe42fb51bae/src/http2/keys/gitkeep -------------------------------------------------------------------------------- /src/http2/keys/openssl.cnf: -------------------------------------------------------------------------------- 1 | [req] 2 | distinguished_name = req_distinguished_name 3 | req_extensions = v3_req 4 | 5 | [req_distinguished_name] 6 | countryName = Country Name (2 letter code) 7 | countryName_default = CN 8 | stateOrProvinceName = State or Province Name (full name) 9 | stateOrProvinceName_default = BeiJing 10 | localityName = Locality Name (eg, city) 11 | localityName_default = YaYunCun 12 | organizationalUnitName = Organizational Unit Name (eg, section) 13 | organizationalUnitName_default = Domain Control Validated 14 | commonName = Internet Widgits Ltd 15 | commonName_max = 64 16 | 17 | [ v3_req ] 18 | # Extensions to add to a certificate request 19 | basicConstraints = CA:FALSE 20 | keyUsage = nonRepudiation, digitalSignature, keyEncipherment 21 | subjectAltName = @alt_names 22 | 23 | [alt_names] 24 | #注意这个IP.1的设置,IP地址需要和你的服务器的监听地址一样 25 | IP.1 = 127.0.0.1 -------------------------------------------------------------------------------- /src/http2/keys/privatekey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXgIBAAKBgQDGPHwosUpKFRhf7W+uwlp0osfo6PXUcYhPQn4GqDgvk4xHPwok 3 | 1erYHkGbQaNrpvlTSY19GQ/NvLkJQU1Jo+YHov2sgT9AEFynQbxI4qGMnkZOGDJx 4 | y/IOw1ohiprapCJ6ia0xuhg1/zD9eeCMxxXVX5gYuQcxIY0yuVj+4KHUPQIDAQAB 5 | AoGBAKMfEmvJt3/ObAhDsSr08soXSLj3OZJe+fN+m4WO1u0PVNcroIU/IchE3WeM 6 | U5U6CN/oHm1aOg7pxLlA6wA1xgn9bYKsKToxvYNWXoBQPGxf6rnfQT7wfsZe/ldE 7 | IHWu2xjuUHksI/JctKMvI3mue3ReCF4Grgxv58LejVmGX64BAkEA/QO//X1zsqv2 8 | TynZ+UCSEsRXanLBqBHqMGxk4eGZswdvDjgK+R8wVwWbgia8DNScwvSSD5+H/GfF 9 | M7d4N48mEQJBAMiTRdPeenlz1CIl3VDEOj+/qsEQZZV5a/FHLhVWgYsPFU+9bFfK 10 | lXiW7s4IsFMClZJJY2yMEME/d3EfYUYZr20CQQDJRzpsxmhMM1D3GSw2hY+vLlL8 11 | c95rfhT4AMMgjwBe1AMdJKhWFuG12NglafsfmeQ7k6S+mQLtYQAYbFROnqqBAkAc 12 | b1yH49ODIluhl7Kff6IuZXLEuhlcroESDKBI4CGvYC0KR/VzIMI9/U/Pn8W3jvc7 13 | bPIOcnDxpJi/Tc6RjJSlAkEAuNzjJlo+QNL/QeeHNJr7FIqJPxbRVfx5o0EpRwW6 14 | Hi+TbC5aYhKwHzxdOy/xOm07k6F30067hyPpQZgvs2u2rQ== 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /src/http2/keys/server-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICQzCCAaygAwIBAgIJAKs42qBHnIZ9MA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV 3 | BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX 4 | aWRnaXRzIFB0eSBMdGQwHhcNMTYwOTE5MjI1NDIxWhcNMTYxMDE5MjI1NDIxWjBV 5 | MQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVpSmluZzERMA8GA1UEBwwIWWFZdW5D 6 | dW4xITAfBgNVBAsMGERvbWFpbiBDb250cm9sIFZhbGlkYXRlZDCBnzANBgkqhkiG 7 | 9w0BAQEFAAOBjQAwgYkCgYEA0CNILyGGx6x/EXdqQZYZeMcLJQA2oCF4kQSAC1JE 8 | AMoRt3jz0Il6FGINSuEM85RamKDeHrxL9CGw8loEKOxQK0dfBlnRIi6YdM6Lkod6 9 | zneUq2aIY4sjsmj0z7yNaJWmSMqhRHE/wlDJFm/faZkTZ64TTPs0QqGeOlBUc2j+ 10 | 5zcCAwEAAaMrMCkwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwDwYDVR0RBAgwBocE 11 | fwAAATANBgkqhkiG9w0BAQsFAAOBgQBbR7q+EPI6mkvQrBZkAJvMxvV7bRZsOm0b 12 | LQTSKPT7R28cMyCNjdmY8FSG8+tGNAqSZXWaMYCPZMTM7BRPtUD43sGwl1btkQlz 13 | 4skbPT83cezaGrgYUlS1Fs62FslgpX62b6YZJ2k/+m1nhCzuX8WdQIlywVZ73PJE 14 | 2pkNkPmZ1g== 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /src/http2/keys/server-csr.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIBzzCCATgCAQAwVTELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaUppbmcxETAP 3 | BgNVBAcMCFlhWXVuQ3VuMSEwHwYDVQQLDBhEb21haW4gQ29udHJvbCBWYWxpZGF0 4 | ZWQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANAjSC8hhsesfxF3akGWGXjH 5 | CyUANqAheJEEgAtSRADKEbd489CJehRiDUrhDPOUWpig3h68S/QhsPJaBCjsUCtH 6 | XwZZ0SIumHTOi5KHes53lKtmiGOLI7Jo9M+8jWiVpkjKoURxP8JQyRZv32mZE2eu 7 | E0z7NEKhnjpQVHNo/uc3AgMBAAGgOjA4BgkqhkiG9w0BCQ4xKzApMAkGA1UdEwQC 8 | MAAwCwYDVR0PBAQDAgXgMA8GA1UdEQQIMAaHBH8AAAEwDQYJKoZIhvcNAQELBQAD 9 | gYEAaVKhbc4y5QgDnpkB1bMHH84hik0qlYYHEKlHW0KjOziV3vheSFwo3gIhA8OB 10 | tU/h4JGlD1C2dKzro9y+SpczGzwrGoLe06r3St5yzLNKDVQ6VlzAFJpI30kqOIlA 11 | tMKG4DLAn3NB/8OS7/bBaE29DQg0fNdMGNFFsU+Aswh54+s= 12 | -----END CERTIFICATE REQUEST----- 13 | -------------------------------------------------------------------------------- /src/http2/keys/server-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXgIBAAKBgQDQI0gvIYbHrH8Rd2pBlhl4xwslADagIXiRBIALUkQAyhG3ePPQ 3 | iXoUYg1K4QzzlFqYoN4evEv0IbDyWgQo7FArR18GWdEiLph0zouSh3rOd5SrZohj 4 | iyOyaPTPvI1olaZIyqFEcT/CUMkWb99pmRNnrhNM+zRCoZ46UFRzaP7nNwIDAQAB 5 | AoGBAMIc9ZhHG00jmgpvJWDvnOfgMouajFeBf9E3sjdczOEzk5xaKIGHhZx26dC4 6 | 3Pxakref6CggEnIn7b/IpfKImiZkQY4HtypOz8CV6b+yvNh5vpvFSUzRtWUYFg75 7 | gRU8nAQgYBSpr629EXlK1NMe/22zPYAMusyinS9QGRklt8fhAkEA6JzXGQKguouC 8 | sqQa744HNTFIpfX6lSzx2A9WxPx43Q8p/79Y5ac60FeuYB7IbfzVskXeNf2F8kcF 9 | thhAn1LVUQJBAOUQfW3ClMdcrt6fQaC+UdBSFM1Xky4SKowvC8Kcn4futaDRQRYT 10 | bZoFT3dEwvpZeAZz6Ijyhvd/134s3ROtcgcCQQCKYLtJIpoRP2s58IwhlGFPUIlC 11 | SWmPktQwTze0KemQwDOg8+jjfa9sP6z5e3aKj81kp1HA1pf3gC6ynAzdev5xAkEA 12 | 4+zLkbqrBdCQZA49ZC/cApiaAFTiq1YCJijt38Cu8Lgy3Ak7ZTcBuN8N2lR+WlNK 13 | HcmZmSMR7xWzG+aSOHE+YwJAYMny60VOpT11BQ5DuCrN7YFg40cuLR4FIVhECteJ 14 | tB1lhCn3CQRjfRWBo8u+W++CbeOZ7pLFXFEXCzOMJqUc+w== 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /src/http2/keys/server.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etoah/note/edd66168c4c574ce815a881a784dffe42fb51bae/src/http2/keys/server.pfx -------------------------------------------------------------------------------- /src/http2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "http2", 3 | "version": "1.0.0", 4 | "description": "test http2.o", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "none" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+ssh://git@github.com/etoah/note.git" 12 | }, 13 | "keywords": [ 14 | "http2", 15 | "koa" 16 | ], 17 | "author": "Lucien", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/etoah/note/issues" 21 | }, 22 | "homepage": "https://github.com/etoah/note#readme", 23 | "dependencies": { 24 | "express": "^4.14.0", 25 | "http2": "^3.3.6", 26 | "koa": "^1.2.4", 27 | "spdy": "^3.4.0" 28 | } 29 | } -------------------------------------------------------------------------------- /src/layout/web布局的前世今生.md: -------------------------------------------------------------------------------- 1 | ##web布局的前世今生 2 | 3 | 前端工程师,很多工作是和布局打交道,现在和大家介绍一下布局的前世今生 4 | 5 | 下面,我将会用web不同时代的不同的方式,实现经典的圣杯布局。圣杯布局是web最基础的布局之一,很多布局是由此布局演变而来 6 | 7 | ![F](http://www.alistapart.com/d/holygrail/diagram_05.gif) 8 | **圣杯布局** 9 | ####table布局的上古时代 10 | 11 | 用table布局实现较简单,三行三列,header,footer三列合并,实现代码如下: 12 | 13 | 实现代码 14 | 15 | ```css 16 | table 17 | { 18 | border-collapse:collapse; 19 | width:100%; 20 | height:100%; 21 | } 22 | 23 | #header,#footer 24 | { 25 | height:80px; 26 | background-color:#fdf; 27 | } 28 | 29 | #leftbar,#main,#rightbar 30 | { 31 | height:100%; 32 | } 33 | 34 | #main 35 | { 36 | width:68%; 37 | } 38 | 39 | ``` 40 | ``` html 41 | 42 | 43 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |
leftbarmainrightbar
56 | ``` 57 | 咋一看,实现代码简单清晰,没有任何兼容性问题。 58 | 但是只要布局一复杂,就会带来以下问题。 59 | 1. colspan,rowspan不能写在css中,不能复用。 60 | 2. 一个table中各tr中colspan,必须相同,不同时会出现样式错乱,需求变更,布局变复杂时,在任何一行添加td,其它行都要变更,耦合大。没有可读性和可维护性。 61 | 3. table是用来显示数据,而不是布局,不够规范,没有实现语义化。 62 | 4. 网友反馈有性能问题,表格内所有的加载完成才显示,未证实。 63 | 5. 当前table如果需充满父节点,父节点的高步必须固定,如果以上在body中,必须加以下代码 64 | ``` css 65 | body,html 66 | { 67 | height: 100%; 68 | width: 100%; 69 | } 70 | ``` 71 | 正因为以下缺点,后面出现了div+css. 72 | 73 | 74 | ####规范初现,div+css 75 | 76 | ``` css 77 | .header,.footer 78 | { 79 | height:80px; 80 | background-color:#fdf; 81 | } 82 | .leftside,.main,.rightside 83 | { 84 | float: left; 85 | height: 100%; 86 | } 87 | 88 | .main 89 | { 90 | width:68%; 91 | } 92 | 93 | .leftside,.rightside 94 | { 95 | width: 16%; 96 | } 97 | .main-con 98 | { 99 | height: 800px; 100 | } 101 | ``` 102 | 103 | ```html 104 |
105 |
106 |
107 |
108 |
109 |
110 | 111 | ``` 112 | 113 | 114 | div+css解决了复用的问题,维护性也不错,同时,只要定义好class,可读性也不错。。。 115 | 是当前互联网主要的布局方式。 116 | 117 | 118 | 119 | 但是还是有经下缺陷: 120 | 1. 绝对居中还需要hack来实现。[绝对居中的方法](http://blog.csdn.net/freshlover/article/details/11579669),代码没有自解释。 121 | 2. 如果需要充满父节点,需要用绝对定位等其它技术。 122 | 3. block的块布局需要浮动,为了防止塌缩,需要的父级的后加:after清除浮动,inline-block布局会出现间隙问题,先设置font-size为零。再恢复。两者者需要做与布局无关的操作。 123 | 4. 宽度不能自适应。 124 | 125 | ####走向新时代 html5+css,弹性布局 126 | 127 | ``` css 128 | ``` 129 | 130 | ``` html 131 | ``` 132 | 133 | 134 | 弹性布局通过设置容器和元素的属性,解决了以上所有问题,同时借助html5,实现了语义化。 135 | 136 | 但是会带来一个问题,兼容性, 137 | 138 | 不兼容IE10以下的浏览器, 139 | 140 | web,由于各厂商各持自家标准,不统一,导致标准发展缓慢,,可怜fer. 141 | 142 | -------------------------------------------------------------------------------- /src/redux/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etoah/note/edd66168c4c574ce815a881a784dffe42fb51bae/src/redux/index.js -------------------------------------------------------------------------------- /src/router/coffeere_implement/event.coffee: -------------------------------------------------------------------------------- 1 | #事件分发 2 | class Event 3 | constructor: ()-> 4 | #储存订阅事件 5 | @subscription = {} 6 | 7 | #订阅 8 | #@type 事件类型 9 | #@fn 事件的回调函数 10 | #@cons 执行回调函数的对象 11 | subscribe: (type, fn, cons)-> 12 | if "undefined" is typeof @subscription.eventmap 13 | @subscription.eventmap = {} 14 | if "undefined" is typeof @subscription[type] 15 | @subscription[type] = [] 16 | p = @subscription[type] 17 | r = false 18 | if p.length > 0 19 | f = fn.toString() 20 | r = !@each.call(p, (item)-> 21 | if item and f is item.response.toString() and cons and cons is item.caller 22 | return false 23 | ) 24 | if false is r 25 | id = Math.floor(Math.random() * 1000000000000000).toString(36) 26 | @subscription.eventmap[id] = type 27 | p.push({"id": id, "response": fn, "caller": cons}) 28 | return id 29 | 30 | #触发事件 31 | #@type 事件类型 32 | #如果还有其它参数,则会传入回调函数 33 | publish: (type)-> 34 | p = @subscription[type] 35 | if p and p.length 36 | params = if arguments.length > 1 then Array.prototype.slice.call(arguments,1) else [] 37 | @each.call(p, (item)-> 38 | if item 39 | cons = item.caller || null 40 | item.response.apply(cons, params) 41 | ) 42 | return @ 43 | 44 | #取消订阅,如果没有参数,则取消所有订阅 45 | #@id 订阅时生成的ID或订阅的事件类型 46 | #@handle 如果handle值为"type",则@id表示订阅事件类型,此时将取消所有此类型订阅 47 | unsubscribe: (id, handle)-> 48 | if "string" is typeof id 49 | if "type" is handle 50 | @subscription[type] and (@subscription[type].length = 0) 51 | else 52 | map = @subscription.eventmap 53 | type = map[id] 54 | if type 55 | p = @subscription[type] 56 | if p and p.length > 0 57 | @each.call(p, (item, index)-> 58 | if item and id is item.id 59 | #p.splice(index, 1) 60 | p[index] = null 61 | map[id] = null 62 | delete map[id] 63 | return false 64 | ) 65 | else 66 | @subscription = {} 67 | return @ 68 | 69 | #遍历对象或数组,遍历时如果返回false,则退出循环 70 | each: (fn)-> 71 | len = @length 72 | params = arguments.length > 1 and Array.prototype.slice.call(arguments, 1) or [] 73 | if len 74 | for item, index in @ 75 | result = fn.apply(@, [item, index].concat(params)) 76 | if result is false 77 | return false 78 | else 79 | for key, item of @ 80 | if @hasOwnProperty(key) 81 | result = fn.apply(@, [item, key].concat(params)) 82 | if result is false 83 | return false 84 | return true 85 | 86 | module.exports = Event 87 | -------------------------------------------------------------------------------- /src/router/coffeere_implement/router.coffee: -------------------------------------------------------------------------------- 1 | Event = require './event' 2 | _getParams = (path)-> 3 | params = [] 4 | patten = path.replace /\/:([^/]+)/g, (s0, param)-> 5 | params.push(param) 6 | return '' 7 | return { 8 | patten: patten 9 | params: params 10 | } 11 | 12 | clickEvent = 'click' #document && document.ontouchstart ? 'touchstart': 13 | 14 | _clickHandler = (e)-> 15 | el = e.target 16 | while el and el.nodeName != 'A' 17 | el = el.parentNode 18 | if !el or el.nodeName != 'A' or el.hasAttribute('download') or el.hasAttribute('data-escape') or !el.hasAttribute('href') or (el.target and el.target != '_self') or el.href.indexOf(location.href.match(/^.+?\/\/+[^\/]+/)[0]) == -1 19 | return 20 | e.preventDefault() 21 | if el.href != location.href 22 | path = el.href.replace(@location.origin, '') 23 | @go(path, el.title or document.title) 24 | 25 | _getQuery=()-> 26 | q = {} 27 | location.href.replace /[?&](.+?)=([^&]*)/g, (_, k, v)-> q[k] = decodeURIComponent(v) 28 | return q 29 | 30 | 31 | class Router extends Event 32 | constructor: (root) -> 33 | super() 34 | @root = root or '' 35 | @routes = [] 36 | @currentUrl = @root 37 | @location = window.location 38 | @history = window.history 39 | @started = false 40 | return @ 41 | 42 | use: (path, callback) -> 43 | route = _getParams(path) 44 | that = @ 45 | @routes.push { 46 | path 47 | patten: route.patten 48 | callback: -> 49 | that.publish(Router.stateChange, path) 50 | callback&&callback.apply(@,arguments) 51 | params: route.params 52 | } 53 | return @ 54 | 55 | refresh: (path, hasRoot = false)-> 56 | if hasRoot then @currentUrl = @location.pathname.replace(@root, '') else @currentUrl = path 57 | @routes.forEach (item)=> 58 | if item.params.length is 0 and item.patten is @currentUrl 59 | item.callback.apply(@) 60 | else if item.params.length isnt 0 and 0 is @currentUrl.indexOf(item.patten) 61 | item.callback.apply(@, @currentUrl.replace(item.patten, '').split('/').slice(1)) 62 | return @ 63 | 64 | # Go to the path 65 | # @param {string} path - destination path 66 | # @param {string} title - page title 67 | # @param {boolean} shouldReplace - use replaceState or pushState 68 | # @returns {boolean} - route not found flag 69 | go: (path, title, shouldReplace = false) -> 70 | path = @location.origin + @root + path 71 | title = title || document.title 72 | 73 | if shouldReplace then @history.replaceState(null, title, path) else @history.pushState(null, title, path) 74 | document.title = title 75 | @refresh path, true 76 | return @ 77 | 78 | query: _getQuery 79 | 80 | 81 | start: -> 82 | if @started 83 | return @ 84 | window.addEventListener 'popstate', (-> @refresh(location.pathname, true)).bind(this), false 85 | document.addEventListener clickEvent, _clickHandler.bind(@) 86 | path = @location.pathname.replace(@root, '') || '/' 87 | @refresh path, !@root 88 | @started = true 89 | return @ 90 | 91 | stop: -> 92 | if not @started 93 | return @ 94 | window.removeEventListener 'popstate', @refresh.bind(this), false 95 | document.removeEventListener clickEvent, _clickHandler.bind(@) 96 | @started = false 97 | return @ 98 | 99 | Router.stateChange = "STATE_CHANGE" 100 | Router.query=_getQuery 101 | 102 | module.exports = Router -------------------------------------------------------------------------------- /src/security/crsf.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

crsf test

8 | 33 | 34 | -------------------------------------------------------------------------------- /src/security/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etoah/note/edd66168c4c574ce815a881a784dffe42fb51bae/src/security/index.html -------------------------------------------------------------------------------- /src/vscode/babel.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const a=1, b=1,count=100; 3 | var sum=0; 4 | for(let i=0;i>>>>this.query:",this.query); 6 | source+=";console.log('infoloader');"; 7 | return source; 8 | }; -------------------------------------------------------------------------------- /src/webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "debug": "^2.3.2", 13 | "webpack": "^1.13.3" 14 | } 15 | } -------------------------------------------------------------------------------- /src/webpack/plugin/infoPlugin.js: -------------------------------------------------------------------------------- 1 | var debug = require('debug')('InfoPlugin'); 2 | 3 | function InfoPlugin(options) {} 4 | InfoPlugin.prototype.apply = function (compiler) { 5 | // Setup callback for accessing a compilation: 6 | compiler.plugin("compilation", function (compilation, params) { 7 | // Now setup callbacks for accessing compilation steps: 8 | compilation.plugin("optimize", function () { 9 | debug(">>>hook:optimize"); 10 | // console.log("---------------start------------------"); 11 | // console.log("compiler:",compiler); 12 | // console.log("---------------gap------------------\n\n\n\n"); 13 | // console.log("compilation:",compilation,params); 14 | // console.log("---------------end------------------"); 15 | debug(">>>hook:optimize"); 16 | 17 | }); 18 | compilation.plugin("optimize-chunk-assets", function (chunks, callback) { 19 | console.log("chunks:",chunks); 20 | callback(); 21 | }); 22 | compilation.plugin('before-hash', function () { 23 | debug(">>>hook:before-hash"); 24 | }); 25 | }); 26 | compiler.plugin("context-module-factory", function () { 27 | debug(">>>hook:context-module-factory"); 28 | }); 29 | 30 | compiler.plugin("before-compile", function () { 31 | debug(">>>hook:before-compile"); 32 | }); 33 | 34 | compiler.plugin("compile", function () { 35 | debug(">>>hook:compile"); 36 | }); 37 | 38 | //why 没有调用callback 39 | compiler.plugin("make", function(data,callback){ 40 | debug(">>>hook:make"); 41 | callback(); 42 | }); 43 | 44 | }; 45 | 46 | module.exports = InfoPlugin; -------------------------------------------------------------------------------- /src/webpack/src/lib.js: -------------------------------------------------------------------------------- 1 | 2 | console.log("lib"); 3 | module.exports={ 4 | a:1, 5 | b:2, 6 | c:3 7 | } 8 | console.log("------------lib end----------------"); 9 | -------------------------------------------------------------------------------- /src/webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | var infoPlugin = require("./plugin/infoPlugin"); 2 | var path = require('path'); 3 | module.exports = { 4 | // 入口文件,是模块构建的起点,同时每一个入口文件对应最后生成的一个 chunk。 5 | entry: "./entry.js", 6 | // 生成文件,是模块构建的终点,包括输出文件与输出路径。 7 | output: { 8 | path: __dirname, 9 | filename: "bundle.js" 10 | }, 11 | // 这里配置了处理各模块的 loader ,包括 css 预处理 loader. 12 | module: { 13 | loaders: [{ 14 | test: /\.css$/, 15 | loader: "style!css" 16 | }, { 17 | test: /\.js$/, 18 | loader: "info?a=1" 19 | }] 20 | }, 21 | resolveLoader: { 22 | alias: { 23 | "info": path.join(__dirname, "./loader/infoLoader") 24 | } 25 | }, 26 | 27 | // webpack 各插件对象,在 webpack 的事件hook中执行对应的方法。 28 | plugins: [ 29 | new infoPlugin() 30 | ] 31 | }; -------------------------------------------------------------------------------- /src/webpack/webpack.debug.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | require('child_process').exec("npm config get prefix", function(err, stdout, stderr) { 4 | console.log("stdout:",stdout) 5 | var nixLib = (process.platform.indexOf("win") === 0) ? "" : "lib"; // win/*nix support 6 | 7 | var webpackPath = path.resolve(path.join(stdout.replace("\n", ""), nixLib, 'node_modules', 'webpack', 'bin', 'webpack.js')); 8 | require(webpackPath); 9 | }); -------------------------------------------------------------------------------- /src/webpack/webpackPrinciple.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 准备工作 4 | #### 配置 5 | 6 | 这里只介绍vscode配置, webstorm调试原理差不多,配置应该不异。 7 | 8 | 9 | ![](http://images2015.cnblogs.com/blog/585323/201611/585323-20161119172021638-942180638.gif) 10 | 11 | 12 | 在launch.json里修改入口文件即可。 13 | 14 | ```json 15 | { 16 | "type": "node", 17 | "request": "launch", 18 | "name": "node debug", 19 | "program": "${workspaceRoot}/src/vscode/index.js", 20 | "cwd": "${workspaceRoot}" 21 | } 22 | ``` 23 | 24 | 对于webpack, gulp 这类命令行的应用,还需要手动添加一个入口,以webpack为例: 25 | 1. 添加webpack.debug.js: 26 | 27 | ```javascript 28 | var path = require('path'); 29 | 30 | require('child_process').exec("npm config get prefix", function(err, stdout, stderr) { 31 | console.log("stdout:",stdout) 32 | var nixLib = (process.platform.indexOf("win") === 0) ? "" : "lib"; // win/*nix support 33 | 34 | var webpackPath = path.resolve(path.join(stdout.replace("\n", ""), nixLib, 'node_modules', 'webpack', 'bin', 'webpack.js')); 35 | require(webpackPath); 36 | }); 37 | ``` 38 | 原理也简单, 手动调用webpack命令行文件。 39 | 40 | 2. 在launch.json文件中修改入口文件和执行路径: 41 | ```json 42 | { 43 | "type": "node", 44 | "request": "launch", 45 | "name": "webpack debug", 46 | "program": "${workspaceRoot}/src/webpack/webpack.debug.js", 47 | "cwd": "${workspaceRoot}/src/webpack" 48 | } 49 | ``` 50 | 51 | 然后启动调试即可。 52 | 53 | ## 开发插件 54 | 55 | ### webpack 原理 56 | 57 | compilation 58 | compiler 59 | 60 | ### 编写loader 61 | ```javascript 62 | var debug = require('debug')('InfoLoader'); 63 | // Cacheable identity loader 64 | module.exports = function(source,map) { 65 | this.cacheable(); 66 | debug(">>>>>this.query:",this.query); 67 | source+=";console.log('infoloader');"; 68 | return source; 69 | }; 70 | ``` 71 | 自定义(非npm包)的loader,注意定义解析: 72 | 73 | ```javascript 74 | resolveLoader: { 75 | alias: { 76 | "info": path.join(__dirname, "./loader/infoLoader") 77 | } 78 | }, 79 | ``` 80 | 81 | ### 编写plugin 82 | 83 | ```javascript 84 | var debug = require('debug')('InfoPlugin'); 85 | function InfoPlugin(options) { 86 | } 87 | InfoPlugin.prototype.apply = function (compiler) { 88 | // Setup callback for accessing a compilation: 89 | compiler.plugin("compilation", function (compilation,params) { 90 | // Now setup callbacks for accessing compilation steps: 91 | compilation.plugin("optimize", function () { 92 | debug(">>>hook:optimize"); 93 | debug(">>>hook:optimize"); 94 | 95 | }); 96 | compilation.plugin('before-hash', function(){ 97 | debug(">>>hook:before-hash"); 98 | }); 99 | }); 100 | compiler.plugin("context-module-factory", function(){ 101 | debug(">>>hook:context-module-factory"); 102 | }); 103 | 104 | compiler.plugin("before-compile", function(){ 105 | debug(">>>hook:before-compile"); 106 | }); 107 | 108 | compiler.plugin("compile", function(){ 109 | debug(">>>hook:compile"); 110 | }); 111 | }; 112 | 113 | module.exports = InfoPlugin; 114 | ``` 115 | 怎么快速的编写插件:找到一个类似(作用时间,功能范围)插件来修改。 116 | 117 | 118 | -------------------------------------------------------------------------------- /src/wechat/Readme.md: -------------------------------------------------------------------------------- 1 | 2 | # 微信浏览器是移动端的IE6?微信升级内核后Html5和CSS3兼容性总结 3 | 4 | 今年(2016)4月,自从微信浏览器X5 升级Blink内核之后,各前端社区一片高潮,仿佛看到了前端er,眼含热泪进而抱头痛头的说:终于可以不用兼容这“移动端的IE6 ”了,可以早点回家了!!! 5 | 那真实情况是不是这样呢?正好最近在做一款微信的小游戏,项目结束后,我做了一个小的总结,分享如下,时间宝贵,先上结论。 6 | 7 | 8 | 9 | ## 结论 10 | 11 | 总的来说,自从微信4月升级 X5 Blink内核之后,兼容性大大好转。 12 | 1. 安卓版的微信浏览器,全面升级为TBS2.0 (基于Android 5.0 WebView Blink内核,Chrome 37),所有版本的安卓系统均为同一内核,开发只需考虑适屏问题了,HTML5和CSS3均有较好的支持(基于Chrome 37,详情可以上caniuse查)。 13 | 2. IOS虽说没有升级统一为同一版本的内核,但IOS版本的微信一直是WKWebView内核,WKWebView的版本依赖于IOS的版本。 IOS 8.0(下文有IOS8以下系统的占比,可忽略)以上的系统,对Html5和css3的支持率也很高,基本的H5,CSS3的特性均得到支持,测试中有详细数据。 14 | 3. 兼容性详情请查看html5和css3测试或直接用真机在以下提供的测试地址测试。 15 | 16 | 从我最近调试游戏和微信端的页面和以下测试来看,基本和媒体预期一致,Html5和Css3兼容良好。希望后面X5的Blink能够保持一定节奏的更新,不要那么多坑。 17 | 18 | 19 | ## 真机测试 20 | 21 | ### html5测试 22 | 测试地址:https://html5test.com/ 23 | 24 | 测试结果: 25 | 1. honor 7:475分 安卓6.0 测试报告: https://html5test.com/s/e61f0b2ed3825842.html 26 | ![image](https://gitlab.szzbmy.com/duanlongxian/TuApp/uploads/04023a9b5e8e37be5af20a66c0e8ef78/image.png) 27 | 2. vivo xplay:475分 安卓4.2 测试报告: https://html5test.com/s/9b411b2ed390367b.html 28 | ![image](https://gitlab.szzbmy.com/duanlongxian/TuApp/uploads/cba150b8bb3f61395f33e78788f509a9/image.png) 29 | 3. iphone4:401分 iOS 9.3.1 测试报告: http://html5test.com/s/e0c5562ed81761a7.html 30 | ![image](https://gitlab.szzbmy.com/duanlongxian/TuApp/uploads/a810c798b0c596c4ece62ff786f7ff90/image.png) 31 | 4. iphone6 plus: 387分 IOS 8.4 测试报告: https://html5test.com/s/e5b68e2ed8206f48.html 32 | ![image](https://gitlab.szzbmy.com/duanlongxian/TuApp/uploads/363f91654a779e917de65e016f150645/image.png) 33 | 34 | 35 | ### css3测试 36 | 37 | 38 | 该网站不支持输出报告:( ,只截了一个图。 39 | (说明,chrome 49 测试支持度为:52%,相对来说,下面浏览器的测试对CSS3的支持度还是比较高的 ) 40 | 41 | 测试地址:http://css3test.com/ 42 | 43 | 1. honor 7 安卓6.0 :49% 44 | ![image](https://gitlab.szzbmy.com/duanlongxian/TuApp/uploads/2e5a87ab08ab0d7a5e2cc7acf9c23c41/image.png) 45 | 2. vivo xplay 安卓4.2:49% 46 | 3. iphone4 iOS 9.3.1:56% 47 | 4. iphone6 plus IOS 8.4 :51% 48 | 49 | 注:从Layabox引擎的游戏运营统计数据上看,低于IOS 8.0的游戏用户终端占比仅为3%左右。几可忽略不计。 50 | ## FYI 51 | 52 | ### 官方人员说明 53 | http://bbs.mb.qq.com/thread-202308-1-1.html 54 | > 基于BLINK的新X5内核已经在手机QQ浏览器上上线了,最近在微信、手机QQ、空间上灰度。 4月份应该会全量发布。 55 | 很抱歉给大家的开发带来了不便。 56 | 这里介绍一下微信、手机QQ、空间内嵌X5的背景:最初是因为在微信发现系统WebView的一些安全漏洞,对微信业务影响非常严重,但是这些漏洞单纯在APP侧无法解决,所以微信提出要求内嵌X5内核替换系统WebView。后来手机QQ、空间也提出了内嵌X5的需求。所以为了APP的安全考虑,这里是不可能让前端控制用不用X5的,不然的话,恶意的网站直接跳过X5,利用系统WebView的安全漏洞,就可以获取用户的银行账号等信息了。 57 | 内嵌X5最初是解决APP的问题,主要是APP终端开发的述求,前端同学没有参与,上线后,给前端同学带来了一些挑战,这主要是因为之前的X5内核是基于Android 4.2 WebView定制优化而来的,很多H5, CSS3属性支持是以Android 4.2系统为基础的,虽然后续我们在此基础上做了增强,但是比起Chrome的Blink内核,还是要差很多。 而Android 4.4开始,系统WebView切换到了Blink内核,所以导致在新Android机型上,X5内核的一些CSS3/H5支持弱于系统WebView。 58 | 为了解决这个问题,去年X5内核团队,投入了全部人力,全力将X5内核升级到了Blink。 全新的X5内核基于Android 5.0系统的Blink内核,已经在15年11月份在QQ浏览器6.2版本上线,经过两个版本的迭代,现在基本稳定,近期已经开始在微信、手机QQ和空间上灰度,预期会在4月份全量上线。新内核上线后,会在新Android版本手机上对齐Chrome blink内核在前端的表现能力,同时在低版本的Android手机上也提供相同表现能力,相信会给前端同学带来更多的想象空间。 59 | 60 | 61 | 62 | ### 报道 63 | 1. [Layabox 解读微信全面升级 X5 Blink 内核](https://m.oschina.net/news/72755/layabox-weixin-x5-blink-kernel) 64 | 2. [iOS 8 HTML5性能测试:苹果有多爱HTML5?](http://www.cocoachina.com/ios/20140919/9690.html) 65 | ### 相关信息 66 | 67 | 官网:http://x5.tencent.com/ 68 | 69 | 内核信息:http://x5.tencent.com/guide?id=4000 70 | 1. 内核基准从WebKit升级到Blink版本,更高的性能,更完善的H5/CSS3支持。 71 | 2. 内核版本号升级到362xx版本。 可以根据UserAgent判断当前环境是否已升级到 TBS2.0版本,包含(TBS/03xxxx)字段 72 | 3. 更完善的H5支持,HTML5跑分475 73 | 4. CSS3属性支持增强,完整支持flex 74 | 5. 更完善的filter支持 75 | 6. 支持Spdy 3.1 76 | 7. 动画性能提升 77 | 8. 支持伪元素动画效果 78 | 9. 更好的inspector支持 79 | 80 | caniuse测试: http://res.imtt.qq.com/tbs/incoming20160419/home.html 81 | 82 | 调试方法:http://bbs.mb.qq.com/thread-1143161-1-1.html 83 | 84 | 85 | 86 | ------- 87 | 88 | ## office share 89 | 90 | #### 微信浏览器4月升级基于Chrome37的Blink内核后 Html5和CSS3 兼容性总结 91 | 最近在做金种子的小游戏,其中用了不少的HTML5和CSS3的新特性,实现的过程中做了一些兼容性的小调查,Demo项目基本结束,做了一个小结,分享如下,时间宝贵,先上结论。 92 | --------------------------------------------------------------------------------