├── koa2 ├── context.md ├── compose.js └── application.md ├── test-cross ├── serverProxy │ ├── index.html │ └── server.js ├── cors │ ├── server.js │ └── index.html ├── postMessage │ ├── server │ │ └── data.html │ └── client │ │ └── index.html ├── jsonp │ ├── server.js │ └── index.html └── package.json ├── .gitignore ├── test-webpack ├── src │ ├── b.css │ ├── a.css │ ├── a.less │ ├── b.less │ ├── common.js │ ├── common.css │ ├── a.js │ └── b.js ├── dist │ ├── pageA-35be2c21107ce4016c324daaa1dd5e28.css │ ├── pageA-d7ac82de795ddf50c9df43291d77b4c8.css │ ├── pageB-56185455ea60f01155a65497e9bf6c85.css │ ├── index.html │ ├── pageA-8ddb2f0f51a8139475bb.js │ ├── pageA-b5b4e2893bce99fd5c57.js │ ├── pageB-34be879b3374ac9b2072.js │ ├── bundle.js │ ├── runtime-12168eb5ce29428d6ad5.js │ └── runtime-1b40e842c0decf252041.js ├── package.json └── webpack.config.js ├── vue.md ├── webpack.md ├── README.md ├── test-html&css ├── 3.html ├── 4.html ├── 5.html ├── 1.html ├── 2.html └── 6.html ├── async.md ├── careful.md ├── test-javascript ├── 4.js ├── 2.js ├── 1.js └── 3.js ├── xss&csrf.md ├── brower.md ├── test-drag └── index.html ├── javascript.md ├── http.md ├── prototype.md ├── html&css.md └── observer.md /koa2/context.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-cross/serverProxy/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .history 2 | .vscode 3 | node_modules 4 | yarn-error.log -------------------------------------------------------------------------------- /test-webpack/src/b.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: yellow; 3 | } 4 | -------------------------------------------------------------------------------- /test-webpack/src/a.css: -------------------------------------------------------------------------------- 1 | p { 2 | width: 100px; 3 | height: 200px; 4 | } 5 | -------------------------------------------------------------------------------- /test-webpack/src/a.less: -------------------------------------------------------------------------------- 1 | @import url('./a.css'); 2 | div { 3 | width: 200px; 4 | } 5 | -------------------------------------------------------------------------------- /test-webpack/dist/pageA-35be2c21107ce4016c324daaa1dd5e28.css: -------------------------------------------------------------------------------- 1 | *{padding:0;margin:0}body{width:100%;height:200%} -------------------------------------------------------------------------------- /test-webpack/src/b.less: -------------------------------------------------------------------------------- 1 | @import url('./b.css'); 2 | span { 3 | display: inline-block; 4 | width: 100px; 5 | } 6 | -------------------------------------------------------------------------------- /test-webpack/src/common.js: -------------------------------------------------------------------------------- 1 | require('./common.css') 2 | module.export = function sum(a, b) { 3 | return a + b 4 | } 5 | -------------------------------------------------------------------------------- /test-webpack/src/common.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | } 5 | 6 | body { 7 | width: 100%; 8 | height: 200%; 9 | } 10 | -------------------------------------------------------------------------------- /test-webpack/dist/pageA-d7ac82de795ddf50c9df43291d77b4c8.css: -------------------------------------------------------------------------------- 1 | p{width:100px;height:200px}div{width:200px}*{padding:0;margin:0}body{width:100%;height:200%} -------------------------------------------------------------------------------- /test-webpack/dist/pageB-56185455ea60f01155a65497e9bf6c85.css: -------------------------------------------------------------------------------- 1 | *{padding:0;margin:0}body{width:100%;height:200%}body{background:#ff0}span{display:inline-block;width:100px} -------------------------------------------------------------------------------- /test-webpack/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Getting Started 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test-cross/cors/server.js: -------------------------------------------------------------------------------- 1 | const http = require('http') 2 | http.createServer((req, res) => { 3 | res.writeHead(200, { 4 | 'Access-Control-Allow-Origin': 'http://127.0.0.1:8080', 5 | 'Content-Type': 'text/html;charset=utf-8' 6 | }) 7 | res.end('这是你要的数据:1111') 8 | }) 9 | -------------------------------------------------------------------------------- /test-cross/postMessage/server/data.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-webpack/src/a.js: -------------------------------------------------------------------------------- 1 | const sum = require('./common.js') 2 | const _ = require('lodash') 3 | function component() { 4 | var element = document.createElement('div') 5 | 6 | // Lodash(目前通过一个 script 脚本引入)对于执行这一行是必需的 7 | element.innerHTML = _.join(['Hello', 'webpack'], ' ') 8 | 9 | return element 10 | } 11 | 12 | document.body.appendChild(component()) 13 | -------------------------------------------------------------------------------- /test-webpack/src/b.js: -------------------------------------------------------------------------------- 1 | require('./b.less') 2 | const sum = require('./common.js') 3 | const _ = require('lodash') 4 | 5 | function component() { 6 | var element = document.createElement('div') 7 | 8 | // Lodash(目前通过一个 script 脚本引入)对于执行这一行是必需的 9 | element.innerHTML = _.join(['Hello', 'webpack'], ' ') 10 | 11 | return element 12 | } 13 | 14 | document.body.appendChild(component()) 15 | -------------------------------------------------------------------------------- /test-cross/jsonp/server.js: -------------------------------------------------------------------------------- 1 | const url = require('url') 2 | const http = require('http') 3 | http 4 | .createServer((req, res) => { 5 | const data = { 6 | a: 10 7 | } 8 | 9 | const callback = url.parse(req.url, true).query.callback 10 | res.writeHead(200) 11 | res.end(`${callback}(${JSON.stringify(data)})`) 12 | }) 13 | .listen(3000, '127.0.0.1') 14 | console.log('服务启动,监听127.0.0.1') 15 | -------------------------------------------------------------------------------- /test-cross/jsonp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | test jsonp 9 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test-cross/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-cross", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "jsonp": "node ./jsonp/server.js & http-server ./jsonp", 8 | "cors": "node ./cors/server.js & http-server ./cors", 9 | "proxy": "node ./serverProxy/server.js", 10 | "postMessage": 11 | "http-server ./postMessage/client/ -p 8080 & http-server ./postMessage/server/ -p 8081" 12 | }, 13 | "author": "lip.fan", 14 | "license": "ISC", 15 | "dependencies": { 16 | "http-server": "^0.11.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /vue.md: -------------------------------------------------------------------------------- 1 | #### Vue 相关知识点总结 2 | 3 | vue 在初始化的时候实际上执行的方法 4 | 5 | ```js 6 | initLifecycle(vm) 7 | initEvents(vm) 8 | initRender(vm) 9 | callHook(vm, 'beforeCreate') 10 | initInjection(vm) 11 | initProps(vm) 12 | initMethods(vm) 13 | initData(vm) 14 | initComputed(vm) 15 | initWatch(vm) 16 | initPrivode(vm) 17 | callHook(vm, 'created') 18 | if (vm.$option.el) { 19 | vm.$mount(vm.$option.el) 20 | } 21 | ``` 22 | 23 | mounted 调用逻辑判断是否存在 template 选项有的话就直接获取 dom 字符串。没有的话就是通过 el 选择器选择出 dom。在获得模版后,会将模版编译成为渲染函数。这里生成的渲染函数有两个一个是静态节点的渲染函数,一个是动态的渲染函数。最后会把渲染函数挂在 vue.$options 上面。最后就调用缓存下来的$mount 24 | -------------------------------------------------------------------------------- /webpack.md: -------------------------------------------------------------------------------- 1 | ### webpack 相关知识点总结 2 | 3 | **chunkid 和 moduleid** 4 | 5 | * 每个 chunkid 对应的是一个 js 文件 6 | * 每个 moduleid 对应的是一个个 js 文件的内容的模块(一个 js 文件里面可以 require 多个资源,每 个资源分配一个 moduleid) 7 | 8 | **compiler 和 compilation** 9 | 10 | * compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境。 11 | 12 | * compilation 对象代表了一次资源版本构建。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。 13 | -------------------------------------------------------------------------------- /test-webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-webpack", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "build": "webpack --color --config ./webpack.config.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "css-loader": "^0.28.7", 14 | "extract-text-webpack-plugin": "^3.0.2", 15 | "less": "^2.7.3", 16 | "less-loader": "^4.0.5", 17 | "lodash": "^4.17.4", 18 | "style-loader": "^0.19.0", 19 | "webpack": "^3.8.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Blog 2 | 3 | A blog for learning knowledge 4 | 5 | ## Articles 6 | 7 | * [x] 1、[从观察者模式(发布订阅模式)到 vue 响应系统](https://github.com/Copyes/Articles/blob/master/observer.md) 8 | * [x] 2、[html&css 知识点总结](https://github.com/Copyes/Articles/blob/master/html%26css.md) 9 | * [x] 3、[javascript 知识点总结](https://github.com/Copyes/Articles/blob/master/javascript.md) 10 | * [x] 4、[原型和原型链的简单总结](https://github.com/Copyes/Articles/blob/master/prototype.md) 11 | * [x] 5、[http 知识点总结](https://github.com/Copyes/Articles/blob/master/http.md) 12 | * [x] 6、[XSS 和 CSRF 知识点总结](https://github.com/Copyes/Articles/blob/master/xss%26csrf.md) 13 | -------------------------------------------------------------------------------- /test-cross/cors/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | test cors 9 | 10 | 11 | 12 | 13 | 14 | 24 | 25 | -------------------------------------------------------------------------------- /test-html&css/3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Flex布局 9 | 21 | 22 | 23 | 24 |
25 |
26 |
27 |
28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /test-cross/serverProxy/server.js: -------------------------------------------------------------------------------- 1 | const url = require('url') 2 | const http = require('http') 3 | const https = require('https') 4 | 5 | http 6 | .createServer((req, res) => { 7 | const path = url.parse(req.url).path.slice(1) 8 | if (path === 'topics') { 9 | https.get('https://cnodejs.org/api/v1/topics', resp => { 10 | let data = '' 11 | resp.on('data', chunk => { 12 | data += chunk 13 | }) 14 | resp.on('end', () => { 15 | res.writeHead(200, { 16 | 'Content-Type': 'application/json; charset=utf-8' 17 | }) 18 | res.end(data) 19 | }) 20 | }) 21 | } 22 | }) 23 | .listen(3000, '127.0.0.1') 24 | console.log('The server is listening to 127.0.0.0') -------------------------------------------------------------------------------- /test-cross/postMessage/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | postMessage 9 | 10 | 11 | 12 | 13 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test-html&css/4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 用 Flex 画出一个直径 100px 的圆,并放在屏幕中间 9 | 31 | 32 | 33 | 34 |
35 | 36 | 37 | -------------------------------------------------------------------------------- /test-html&css/5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | css实现垂直居中 9 | 34 | 35 | 36 | 37 |
38 |
39 | 40 |
41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /test-webpack/dist/pageA-8ddb2f0f51a8139475bb.js: -------------------------------------------------------------------------------- 1 | webpackJsonp(["pageA"],{ 2 | 3 | /***/ "645S": 4 | /***/ (function(module, exports) { 5 | 6 | // removed by extract-text-webpack-plugin 7 | 8 | /***/ }), 9 | 10 | /***/ "MvGc": 11 | /***/ (function(module, exports, __webpack_require__) { 12 | 13 | /* WEBPACK VAR INJECTION */(function(module) {__webpack_require__("645S") 14 | module.export = function sum(a, b) { 15 | return a + b 16 | } 17 | 18 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__("3IRH")(module))) 19 | 20 | /***/ }), 21 | 22 | /***/ "aQZI": 23 | /***/ (function(module, exports, __webpack_require__) { 24 | 25 | const sum = __webpack_require__("MvGc") 26 | const _ = __webpack_require__("M4fF") 27 | function component() { 28 | var element = document.createElement('div') 29 | 30 | // Lodash(目前通过一个 script 脚本引入)对于执行这一行是必需的 31 | element.innerHTML = _.join(['Hello', 'webpack'], ' ') 32 | 33 | return element 34 | } 35 | 36 | document.body.appendChild(component()) 37 | 38 | 39 | /***/ }) 40 | 41 | },["aQZI"]); -------------------------------------------------------------------------------- /async.md: -------------------------------------------------------------------------------- 1 | async函数是generator函数的语法糖 2 | 3 | 同样的一个函数用async替换的时候只需要吧 function*(){}替换成 async 把yield替换成await即可 4 | 5 | 但是async函数有4点改进 6 | 7 | 1. 内置执行器 (generator需要用co模块) 8 | 2. 更好的语义 await表示需要等待异步结果 9 | 3. 更广的适用性 10 | 4. 返回值与generator不同返回Promise对象(即使你返回的是一个值也会被包装成Promise.resolve(值)) 11 | 12 | async返回的是Promise对象,可以再被await做为参数 13 | 14 | async函数可以放在对象,类,表达式,函数声明,箭头函数中 15 | 16 | async的错误机制是个难点 17 | 18 | > async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。 19 | 20 | async函数返回的promise对象,必须等return前的所有await的promise执行完成之后才会执行 21 | 22 | unhandledRejection事件可以捕获错误 23 | 24 | await后面是一个promise对象如果不是会立即被转化成一个resolve的promise对象 25 | 26 | await中的任何一个promise被reject之后都会中断后续执行,如果不想因为其中一个reject就影响后面的执行可以使用try...catch,或者还有一种方法是对await的promise直接调用.catch 27 | 28 | 在async函数中如果两个异步可以并行,就不要写成串行,并行不一定非要用promise.all 29 | 30 | 31 | 32 | 注意点: 33 | - [ ] yield 和 await 后面可以是原始类型的值(string,number,boolean)(实际测试能返回任何值)或者promise对象, 但是co模块约定yield后面只能返回Thunk函数或Promise对象 34 | - [ ] async 函数执行过程如果报错可以在最后返回的promise里.catch捕获错误 35 | - [ ] async 函数在执行的时候一旦遇到await就会返回Promise 36 | 37 | 38 | -------------------------------------------------------------------------------- /test-html&css/1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 圣杯布局 9 | 40 | 41 | 42 | 43 |
44 |
45 |
46 |
47 |
48 | 49 | 50 | -------------------------------------------------------------------------------- /test-html&css/2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 双飞翼布局 9 | 38 | 39 | 40 | 41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | 49 | 50 | -------------------------------------------------------------------------------- /careful.md: -------------------------------------------------------------------------------- 1 | ### 小程序审核注意事项 2 | 3 | 以下为育儿宝和育儿宝成长记录以及早教宝小程序开发以及审核相关注意事项。 4 | 5 | **审核** 6 | 7 | > 1、类目不完善或者是类目选择不当 8 | 9 | 提交审核前一定要仔细看区分,可以了解下竞品发的是什么类目。 10 | 11 | > 2、“功能不完善/功能不完整” 12 | 13 | 这个有时候是审核人员不会用误以为你的小程序没开发完整,如果你确定没问题的话可以多提交一次试试。 14 | 15 | > 3、含有声音视频类目 16 | 17 | 这个就要把音视频类目选择上 18 | 19 | > 4、不得展示和推荐第三方小程序 20 | 21 | 不能做小程序导航、小程序排行榜之类的产品或功能。 22 | 23 | > 5、不能含有诱导分享的内容 24 | 25 | 这个已经不用多说了,微信在诱导分享方面的规则有多严大家应该都了解了。 26 | 27 | > 6、必须登录才能体验的小程序,必须提供测试账号 28 | 29 | 测试账号目前是产品 3 君提供了一个自己的账号如有需要请自行想法,账号如下: 30 | 31 | 账号:xiaohao3jun 32 | 密码:ylj9694 33 | 34 | > 7、一些建议 35 | 36 | * 1.尽量早提交,虽然目前小程序的审核期已经比公测时短了不少,但是审核依然严格,早提交早发现问题。 37 | * 2.仔细阅读官方文档!!!这点非常重要!不看文档活该一直被拒。 38 | * 3.可以通过邮件跟微信团队沟通。 39 | 40 | **注意** 41 | 42 | * 1、育儿宝 APP 和育儿成长记录是两个小程序共用一套代码,通过 type 来区分。 43 | 44 | ```js 45 | // utils下面的user.js文件中 46 | method: 'POST', 47 | data: { 48 | method: 'yuerbao.user.applet.auth', 49 | code, 50 | type: 14 // 7 是育儿宝app , 14是育儿成长记录 51 | }, 52 | ``` 53 | 54 | * 2、架构问题 55 | 56 | 目前育儿宝 app 小程序和早教宝的架构是不同的。育儿宝这边是一年半前小程序才出来的时候封装的。育儿宝是前段时间才封装的。育儿宝小程序主要是异步回调的开发方式,早教宝主要是同步的开发方式。(如果有问题育儿宝可以找金金,早教宝找万利) 57 | 58 | * 3、后期可以思考的点 59 | 60 | 1、早教宝和育儿宝中的落地页可以换成小程序即分享出去的是小程序。 61 | 62 | 2、小程序来做拉新的活动 63 | -------------------------------------------------------------------------------- /test-webpack/dist/pageA-b5b4e2893bce99fd5c57.js: -------------------------------------------------------------------------------- 1 | webpackJsonp(["pageA"],{ 2 | 3 | /***/ "645S": 4 | /***/ (function(module, exports) { 5 | 6 | // removed by extract-text-webpack-plugin 7 | 8 | /***/ }), 9 | 10 | /***/ "LJbA": 11 | /***/ (function(module, exports) { 12 | 13 | // removed by extract-text-webpack-plugin 14 | 15 | /***/ }), 16 | 17 | /***/ "MvGc": 18 | /***/ (function(module, exports, __webpack_require__) { 19 | 20 | /* WEBPACK VAR INJECTION */(function(module) {__webpack_require__("645S") 21 | module.export = function sum(a, b) { 22 | return a + b 23 | } 24 | 25 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__("3IRH")(module))) 26 | 27 | /***/ }), 28 | 29 | /***/ "aQZI": 30 | /***/ (function(module, exports, __webpack_require__) { 31 | 32 | __webpack_require__("LJbA") 33 | const sum = __webpack_require__("MvGc") 34 | const _ = __webpack_require__("M4fF") 35 | function component() { 36 | var element = document.createElement('div') 37 | 38 | // Lodash(目前通过一个 script 脚本引入)对于执行这一行是必需的 39 | element.innerHTML = _.join(['Hello', 'webpack'], ' ') 40 | 41 | return element 42 | } 43 | 44 | document.body.appendChild(component()) 45 | 46 | 47 | /***/ }) 48 | 49 | },["aQZI"]); -------------------------------------------------------------------------------- /test-webpack/dist/pageB-34be879b3374ac9b2072.js: -------------------------------------------------------------------------------- 1 | webpackJsonp(["pageB"],{ 2 | 3 | /***/ "645S": 4 | /***/ (function(module, exports) { 5 | 6 | // removed by extract-text-webpack-plugin 7 | 8 | /***/ }), 9 | 10 | /***/ "JIOT": 11 | /***/ (function(module, exports, __webpack_require__) { 12 | 13 | __webpack_require__("lhZZ") 14 | const sum = __webpack_require__("MvGc") 15 | const _ = __webpack_require__("M4fF") 16 | 17 | function component() { 18 | var element = document.createElement('div') 19 | 20 | // Lodash(目前通过一个 script 脚本引入)对于执行这一行是必需的 21 | element.innerHTML = _.join(['Hello', 'webpack'], ' ') 22 | 23 | return element 24 | } 25 | 26 | document.body.appendChild(component()) 27 | 28 | 29 | /***/ }), 30 | 31 | /***/ "MvGc": 32 | /***/ (function(module, exports, __webpack_require__) { 33 | 34 | /* WEBPACK VAR INJECTION */(function(module) {__webpack_require__("645S") 35 | module.export = function sum(a, b) { 36 | return a + b 37 | } 38 | 39 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__("3IRH")(module))) 40 | 41 | /***/ }), 42 | 43 | /***/ "lhZZ": 44 | /***/ (function(module, exports) { 45 | 46 | // removed by extract-text-webpack-plugin 47 | 48 | /***/ }) 49 | 50 | },["JIOT"]); -------------------------------------------------------------------------------- /test-html&css/6.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 |
13 | 14 |
15 | 16 | 47 | 48 | -------------------------------------------------------------------------------- /test-webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 4 | module.exports = { 5 | entry: { 6 | pageA: './src/a.js', 7 | pageB: './src/b.js', 8 | vendor: ['lodash'] 9 | }, 10 | output: { 11 | filename: '[name]-[chunkhash].js', 12 | path: path.resolve(__dirname, 'dist') 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.css$/, 18 | use: ExtractTextPlugin.extract({ 19 | fallback: 'style-loader', 20 | use: ['css-loader?minimize'] 21 | }) 22 | }, 23 | { 24 | test: /\.less$/, // ['css-loader?minimize&-autoprefixer!postcss-loader', 'less-loader'] 25 | use: ExtractTextPlugin.extract({ 26 | fallback: 'style-loader', 27 | use: ['css-loader?minimize', 'less-loader'] 28 | }) 29 | } 30 | ] 31 | }, 32 | plugins: [ 33 | new ExtractTextPlugin('[name]-[contenthash].css'), 34 | new webpack.HashedModuleIdsPlugin(), 35 | new webpack.NamedChunksPlugin(chunk => { 36 | if (chunk.name) { 37 | return chunk.name 38 | } 39 | 40 | return chunk 41 | .mapModules(m => path.relative(m.context, m.request)) 42 | .join('_') 43 | }), 44 | new webpack.optimize.CommonsChunkPlugin({ 45 | name: 'vendor', 46 | minChunks: Infinity 47 | }), 48 | new webpack.optimize.CommonsChunkPlugin({ 49 | name: 'runtime' 50 | }) 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /koa2/compose.js: -------------------------------------------------------------------------------- 1 | function compose(middleware) { 2 | // 传入的 middleware 参数必须是数组 3 | if (!Array.isArray(middleware)) 4 | throw new TypeError('Middleware stack must be an array!') 5 | // middleware 数组的元素必须是函数 6 | for (const fn of middleware) { 7 | if (typeof fn !== 'function') 8 | throw new TypeError('Middleware must be composed of functions!') 9 | } 10 | 11 | // 返回一个函数闭包, 保持对 middleware 的引用 12 | return function(context, next) { 13 | // 这里的 context 参数是作为一个全局的设置, 所有中间件的第一个参数就是传入的 context, 这样可以 14 | // 在 context 中对某个值或者某些值做"洋葱处理" 15 | 16 | // 解释一下传入的 next, 这个传入的 next 函数是在所有中间件执行后的"最后"一个函数, 这里的"最后"并不是真正的最后, 17 | // 而是像上面那个图中的圆心, 执行完圆心之后, 会返回去执行上一个中间件函数(middleware[length - 1])剩下的逻辑 18 | 19 | // index 是用来记录中间件函数运行到了哪一个函数 20 | let index = -1 21 | // 执行第一个中间件函数 22 | return dispatch(0) 23 | 24 | function dispatch(i) { 25 | // i 是洋葱模型的记录已经运行的函数中间件的下标, 如果一个中间件里面运行两次 next, 那么 i 是会比 index 小的. 26 | // 如果对这个地方不清楚可以查看下面的图 27 | if (i <= index) 28 | return Promise.reject(new Error('next() called multiple times')) 29 | index = i 30 | let fn = middleware[i] 31 | if (i === middleware.length) { 32 | // 这里的 next 就是一开始 compose 传入的 next, 意味着当中间件函数数列执行完后, 执行这个 next 函数, 即圆心 33 | fn = next 34 | } 35 | // 如果没有函数, 直接返回空值的 Promise 36 | if (!fn) return Promise.resolve() 37 | try { 38 | // 为什么这里要包一层 Promise? 39 | // 因为 async 需要后面是 Promise, 然后 next 函数返回值就是 dispatch 函数的返回值, 所以运行 async next(); 需要 next 包一层 Promise 40 | // next 函数是固定的, 可以执行下一个函数 41 | return Promise.resolve( 42 | fn(context, function next() { 43 | return dispatch(i + 1) 44 | }) 45 | ) 46 | } catch (err) { 47 | return Promise.reject(err) 48 | } 49 | } 50 | } 51 | } 52 | 53 | async function first(ctx, next) { 54 | console.log('1') 55 | // async 与 co + yield 的模型不同, await 是需要后面是 promise 的函数, 并且自己执行一次, 而 co 是自己拿到 value 然后帮你自动执行. 56 | await next() 57 | await next() // 两次调用 next 58 | console.log(ctx) 59 | } 60 | 61 | async function second(ctx, next) { 62 | console.log('2') 63 | await next() 64 | } 65 | 66 | async function third(ctx, next) { 67 | console.log('3') 68 | await next() 69 | console.log('4') 70 | } 71 | 72 | const middleware = [first, second, third] 73 | 74 | const com = compose(middleware) 75 | 76 | com('ctx', function() { 77 | console.log('hey') 78 | }) 79 | -------------------------------------------------------------------------------- /test-javascript/4.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var _createClass = (function() { 4 | function defineProperties(target, props) { 5 | for (var i = 0; i < props.length; i++) { 6 | var descriptor = props[i] 7 | descriptor.enumerable = descriptor.enumerable || false 8 | descriptor.configurable = true 9 | if ('value' in descriptor) descriptor.writable = true 10 | Object.defineProperty(target, descriptor.key, descriptor) 11 | } 12 | } 13 | return function(Constructor, protoProps, staticProps) { 14 | if (protoProps) defineProperties(Constructor.prototype, protoProps) 15 | if (staticProps) defineProperties(Constructor, staticProps) 16 | return Constructor 17 | } 18 | })() 19 | 20 | function _possibleConstructorReturn(self, call) { 21 | if (!self) { 22 | throw new ReferenceError( 23 | "this hasn't been initialised - super() hasn't been called" 24 | ) 25 | } 26 | return call && (typeof call === 'object' || typeof call === 'function') 27 | ? call 28 | : self 29 | } 30 | 31 | function _inherits(subClass, superClass) { 32 | if (typeof superClass !== 'function' && superClass !== null) { 33 | throw new TypeError( 34 | 'Super expression must either be null or a function, not ' + 35 | typeof superClass 36 | ) 37 | } 38 | subClass.prototype = Object.create(superClass && superClass.prototype, { 39 | constructor: { 40 | value: subClass, 41 | enumerable: false, 42 | writable: true, 43 | configurable: true 44 | } 45 | }) 46 | if (superClass) 47 | Object.setPrototypeOf 48 | ? Object.setPrototypeOf(subClass, superClass) 49 | : (subClass.__proto__ = superClass) 50 | } 51 | 52 | function _classCallCheck(instance, Constructor) { 53 | if (!(instance instanceof Constructor)) { 54 | throw new TypeError('Cannot call a class as a function') 55 | } 56 | } 57 | 58 | var A = (function() { 59 | function A() { 60 | _classCallCheck(this, A) 61 | } 62 | 63 | _createClass(A, [ 64 | { 65 | key: 'a', 66 | value: function a() {} 67 | } 68 | ]) 69 | 70 | return A 71 | })() 72 | 73 | var B = (function(_A) { 74 | _inherits(B, _A) 75 | 76 | function B() { 77 | _classCallCheck(this, B) 78 | 79 | return _possibleConstructorReturn( 80 | this, 81 | (B.__proto__ || Object.getPrototypeOf(B)).call(this) 82 | ) 83 | } 84 | 85 | _createClass(B, [ 86 | { 87 | key: 'b', 88 | value: function b() {} 89 | } 90 | ]) 91 | 92 | return B 93 | })(A) 94 | -------------------------------------------------------------------------------- /test-webpack/dist/bundle.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // The module cache 3 | /******/ var installedModules = {}; 4 | /******/ 5 | /******/ // The require function 6 | /******/ function __webpack_require__(moduleId) { 7 | /******/ 8 | /******/ // Check if module is in cache 9 | /******/ if(installedModules[moduleId]) { 10 | /******/ return installedModules[moduleId].exports; 11 | /******/ } 12 | /******/ // Create a new module (and put it into the cache) 13 | /******/ var module = installedModules[moduleId] = { 14 | /******/ i: moduleId, 15 | /******/ l: false, 16 | /******/ exports: {} 17 | /******/ }; 18 | /******/ 19 | /******/ // Execute the module function 20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 | /******/ 22 | /******/ // Flag the module as loaded 23 | /******/ module.l = true; 24 | /******/ 25 | /******/ // Return the exports of the module 26 | /******/ return module.exports; 27 | /******/ } 28 | /******/ 29 | /******/ 30 | /******/ // expose the modules object (__webpack_modules__) 31 | /******/ __webpack_require__.m = modules; 32 | /******/ 33 | /******/ // expose the module cache 34 | /******/ __webpack_require__.c = installedModules; 35 | /******/ 36 | /******/ // define getter function for harmony exports 37 | /******/ __webpack_require__.d = function(exports, name, getter) { 38 | /******/ if(!__webpack_require__.o(exports, name)) { 39 | /******/ Object.defineProperty(exports, name, { 40 | /******/ configurable: false, 41 | /******/ enumerable: true, 42 | /******/ get: getter 43 | /******/ }); 44 | /******/ } 45 | /******/ }; 46 | /******/ 47 | /******/ // getDefaultExport function for compatibility with non-harmony modules 48 | /******/ __webpack_require__.n = function(module) { 49 | /******/ var getter = module && module.__esModule ? 50 | /******/ function getDefault() { return module['default']; } : 51 | /******/ function getModuleExports() { return module; }; 52 | /******/ __webpack_require__.d(getter, 'a', getter); 53 | /******/ return getter; 54 | /******/ }; 55 | /******/ 56 | /******/ // Object.prototype.hasOwnProperty.call 57 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 58 | /******/ 59 | /******/ // __webpack_public_path__ 60 | /******/ __webpack_require__.p = ""; 61 | /******/ 62 | /******/ // Load entry module and return exports 63 | /******/ return __webpack_require__(__webpack_require__.s = 0); 64 | /******/ }) 65 | /************************************************************************/ 66 | /******/ ([ 67 | /* 0 */ 68 | /***/ (function(module, exports) { 69 | 70 | function component() { 71 | var element = document.createElement('div') 72 | 73 | // Lodash(目前通过一个 script 脚本引入)对于执行这一行是必需的 74 | element.innerHTML = _.join(['Hello', 'webpack'], ' ') 75 | 76 | return element 77 | } 78 | 79 | document.body.appendChild(component()) 80 | 81 | 82 | /***/ }) 83 | /******/ ]); -------------------------------------------------------------------------------- /xss&csrf.md: -------------------------------------------------------------------------------- 1 | ### 关于 XSS 攻击和 CSRF 攻击的总结 2 | 3 | > 对于跨站脚本攻击(XSS 攻击)的理解和总结 4 | 5 | **什么是 XSS 攻击** 6 | 7 | * XSS 是跨站脚本攻击的缩写,是一种网站应用程序的安全漏洞攻击,是代码注入的一种。 8 | * 通常是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。 9 | * 这些恶意网页程序通常是 JavaScript,但实际上也可以包括 Java,VBScript,ActiveX,Flash 或者甚至是普通的 HTML。 10 | * 攻击成功后,攻击者可能得到更高的权限(如执行一些操作)、私密网页内容、会话和 cookie 等各种内容。 11 | 12 | **XSS 攻击基本原理---代码注入** 13 | 14 | 在 web 的世界里有各种各样的语言,于是乎对于语句的解析大家各不相同,有一些语句在一种语言里是合法的,但是在另外一种语言里是非法的。这种二义性使得黑客可以用代码注入的方式进行攻击——将恶意代码注入合法代码里隐藏起来,再诱发恶意代码,从而进行各种各样的非法活动。只要破坏跨层协议的数据/指令的构造,我们就能攻击。历史悠久的 SQL 注入和 XSS 注入都是这种攻击方式的典范。现如今,随着参数化查询的普及,我们已经离 SQL 注入很远了。但是,历史同样悠久的 XSS 却没有远离我们。 15 | XSS 的基本实现思路很简单——比如持久型 XSS 通过一些正常的站内交互途径,例如发布评论,提交含有 JavaScript 的内容文本。这时服务器端如果没有过滤或转义掉这些脚本,作为内容发布到了页面上,其他用户访问这个页面的时候就会运行这些脚本,从而被攻击。 16 | 17 | **攻击分类** 18 | 19 | * 反射型 XSS 20 | 21 | 反射性 XSS,也就是被动的非持久性 XSS。诱骗用户点击 URL 带攻击代码的链接,服务器解析后响应,在返回的响应内容中隐藏和嵌入攻击者的 XSS 代码,被浏览器执行,从而攻击用户。 22 | URL 可能被用户怀疑,但是可以通过短网址服务将之缩短,从而隐藏自己。 23 | 24 | * 持久型 XSS 25 | 26 | 也叫存储型 XSS——主动提交恶意数据到服务器,攻击者在数据中嵌入代码,这样当其他用户请求后,服务器从数据库中查询数据并发给用户,用户浏览此类页面时就可能受到攻击。可以描述为:恶意用户的 HTML 或 JS 输入服务器->进入数据库->服务器响应时查询数据库->用户浏览器。 27 | 28 | **防范** 29 | 30 | * 使用 XSS Filter 31 | 32 | 输入过滤,对用户提交的数据进行有效性验证,仅接受指定长度范围内并符合我们期望格式的的内容提交,阻止或者忽略除此外的其他任何数据。 33 | 34 | 输出转义,当需要将一个字符串输出到 Web 网页时,同时又不确定这个字符串中是否包括 XSS 特殊字符,为了确保输出内容的完整性和正确性,输出 HTML 属性时可以使用 HTML 转义编码(HTMLEncode)进行处理,输出到 136 | 137 | -------------------------------------------------------------------------------- /javascript.md: -------------------------------------------------------------------------------- 1 | ### JavaScript 相关知识总结 2 | 3 | > JavaScript 的基本数据类型和引用数据类型分别有哪些? 4 | 5 | * 基本类型的数据:Undefined, Null, String, Number, Boolean, Symbol(ES6 新增) 6 | * 引用类型的数据:Object, Array, RegExp, Date, Function, 基本包装类型(Boolean, Number, String 这是三种特殊的引用类型,也可以算作基本类型) 7 | * 需要注意的是,typeof 的返回值(number, boolean, string, object, function, undefined, symbol)和 JS 的基本类型不一样。 8 | 9 | > JavaScript 哪些类型存在堆上,哪些存在栈上? 10 | 11 | * 栈:原始数据类型 12 | 13 | 原始数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储; 14 | 15 | * 堆:引用数据类型 16 | 17 | * 引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能 18 | * 引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体 19 | 20 | > JavaScript 继承有的方式总结 21 | 22 | * 原型继承 23 | 24 | ```js 25 | let Base = function() { 26 | this.name = 'base' 27 | this.toString = function() { 28 | return this.name 29 | } 30 | } 31 | 32 | let Sub = function() {} 33 | Sub.prototype = new Base() 34 | Sub.name = 'Sub' 35 | 36 | let a = new Sub() 37 | console.log(a instanceof Sub) // true 38 | console.log(a instanceof Base) // true 39 | ``` 40 | 41 | 原型链继承是一种非常传统的继承方式。如果从 instanceof 关键字来看,实例既是父类的实例,也是子类的实例。但是这样的继承也有一个明显的问题:子类区别于父类的属性和方法需要在完成原型链继承之后再进行,同时,由于使用了 prototype ,无法实现多重继承 42 | 43 | * 构造继承 44 | 45 | ```js 46 | let Base = function() { 47 | this.name = 'Base' 48 | this.toString = function() { 49 | return this.name 50 | } 51 | } 52 | 53 | let Sub = function() { 54 | Base.call(this) 55 | this.name = 'Sub' 56 | } 57 | ``` 58 | 59 | 这种继承解决了原型链继承的问题,但它也有一些问题:使用 instanceof 运算符的时候会发现,它的实例并不是父类的实例(因为父类没有在它的原型链上) 60 | 61 | * 实例继承 62 | 63 | ```js 64 | let Base = function() { 65 | this.name = 'Base' 66 | this.toString = function() { 67 | return this.name 68 | } 69 | } 70 | let Sub = function() { 71 | let instance = new Base() 72 | instance.name = 'Sub' 73 | 74 | return instance 75 | } 76 | ``` 77 | 78 | 这种继承方法只能强行被归类为继承。因为它实际上是返回了一个父类的实例,与子类可以说毫无关系。同样,这种方法也不支持多继承。 79 | 80 | * 拷贝继承 81 | 82 | ```js 83 | let Base = function() { 84 | this.name = 'Base' 85 | this.toString = function() { 86 | return this.name 87 | } 88 | } 89 | 90 | let Sub = function() { 91 | let base = new Base() 92 | for (let i in base) { 93 | Sub.prototype[i] = base[i] 94 | } 95 | Sub.prototype[name] = 'Sub' 96 | } 97 | ``` 98 | 99 | 首先,只有可枚举的对象类型才能使用 foreach 的方式获取到,其次,这种写法真的快绝迹了。它的优点是可以实现多继承,缺点是写起来真的难受,而且效率很低。 100 | 101 | > 原型链的简单描述 102 | 103 | * 判断某个对象是不是另一个对象的原型 104 | 105 | ```js 106 | // ES3 107 | Object.prototype.isPrototypeOf(o) // 返回true值可以确定Object.prototype就是o对象的原型 108 | // ES5 109 | Object.getPrototypeOf(o) === Object.prototype 110 | // ES6 111 | o.__proto__ === Object.prototype 112 | // 设置一个原型 113 | Object.setPrototypeOf(Child.prototype, Father.prototype) // 将父类的原型设置为子类的原型 114 | ``` 115 | 116 | 原型链是 JavaScript 实现继承最重要的一种方式。每一个对象都有自己的原型对象,根原型对象没有原型,所以其 **proto** 属性值为 null 。在调用时,如果访问的对象属性没有找到,JavaScript 会顺着原型链继续往下找,直到触碰到根原型为止。 117 | 118 | > ES5 和 ES6 中的继承的区别 119 | 120 | ![ES5](https://user-images.githubusercontent.com/10307282/37136881-681c1d28-22de-11e8-946e-03983e9e4426.png) 121 | ![ES6](https://user-images.githubusercontent.com/10307282/37136911-818dac0e-22de-11e8-9990-483273493e92.png) 122 | 123 | > JavaScript 垃圾回收机制 124 | 125 | **标记清除** 126 | 127 | 这是 JavaScript 最常见的垃圾回收方式,当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”。至于怎么标记有很多种方式,比如特殊位的反转、维护一个列表等,这些并不重要,重要的是使用什么策略,原则上讲不能够释放进入环境的变量所占的内存,它们随时可能会被调用的到。 128 | 129 | 垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量以及被环境中变量所引用的变量(闭包),在这些完成之后仍存在标记的就是要删除的变量了,因为环境中的变量已经无法访问到这些变量了,然后垃圾回收器相会这些带有标记的变量机器所占空间。 130 | 131 | **引用计数** 132 | 133 | 在低版本 IE 中经常会出现内存泄露,很多时候就是因为其采用引用计数方式进行垃圾回收。引用计数的策略是跟踪记录每个值被使用的次数,当声明了一个变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加 1,如果该变量的值变成了另外一个,则这个值得引用次数减 1,当这个值的引用次数变为 0 的时候,说明没有变量在使用,这个值没法被访问了,因此可以将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为 0 的值占用的空间。 134 | 135 | > JavaScript 内存泄漏 136 | 137 | * 全局变量引起的内存泄漏 138 | 139 | ```js 140 | function leaks() { 141 | leak = 'xxxxxx' //leak 成为一个全局变量,不会被回收 142 | } 143 | ``` 144 | 145 | * 闭包引起的内存泄漏 146 | 147 | ```js 148 | var leaks = (function() { 149 | var leak = 'xxxxxx' // 被闭包所引用,不会被回收 150 | return function() { 151 | console.log(leak) 152 | } 153 | })() 154 | ``` 155 | 156 | * dom 清空或删除时,事件未清除导致的内存泄漏 157 | 158 | ```html 159 |
160 |
161 | ``` 162 | 163 | ```js 164 | $('#container') 165 | .bind('click', function() { 166 | console.log('click') 167 | }) 168 | .remove() // 未把事件移除 169 | ``` 170 | 171 | * 子元素存在引用引起的内存泄漏 172 | 173 | > AMD,CMD,CommonJs, ES6 module 各种模块规范 174 | 175 | * AMD:requirejs 在推广过程中对模块定义的规范化产出,提前执行,推崇依赖前置 176 | * CMD:seajs 在推广过程中对模块定义的规范化产出,延迟执行,推崇依赖就近 177 | * CommonJs:模块输出的是一个值的 copy,运行时加载,加载的是一个对象(module.exports 属性),该对象只有在脚本运行完才会生成 178 | * ES6 Module:模块输出的是一个值的引用,编译时输出接口,ES6 模块不是对象,它对外接口只是一种静态定义,在代码静态解析阶段就会生成。 179 | 180 | > Hybrid 应用知识点总结 181 | -------------------------------------------------------------------------------- /http.md: -------------------------------------------------------------------------------- 1 | ### http 和 https 相关知识点总结 2 | 3 | ##### 网络模型 4 | 5 | * 7 层网络模型 6 | 7 | ![7层网络模型](http://img.blog.csdn.net/20160127132708631?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 8 | 9 | 1》应用层:提供给操作系统或者应用程序,用来进行网络通信的标准接口 10 | 11 | 2》表示层:不同的 pc 机拥有不同的编码方式,需要在这里进行转换,转换成网络通信中采用的标准表现形式 12 | 13 | 3》会话层:负责在不同的 PC 的不同进程之间建立或者拆除连接,另外,还有插入同步点的机制(保证断线重新从这个位置传输) 14 | 15 | 4》传输层:负责两个主机之间的端对端的数据连接&传输 16 | 17 | 5》网络层:选择合适的路由,职责就是正确的找着目的站 18 | 19 | 6》数据链路层:负责在两个相邻的节点之间准确的传输数据(以帧为单位,每一帧:数据+控制信息) 20 | 21 | 7》物理层:让原始的数据比特流能在物理介质上传输 22 | 23 | * 4 层网络模型 24 | 25 | ![4层网络模型](http://img.blog.csdn.net/20160127132929760?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 26 | 27 | 1》应用层:向网络应用提供接口,并且对应用内的数据格式进行统一编码 28 | 29 | 2》传输层:负责两个主机之间的端对端的数据连接&传输&传输控制、错误恢复 30 | 31 | 3》网络层:选择合适的路由,职责就是正确的找着目的站 32 | 33 | 4》网络接口层:负责通过网络发送和接收 IP 数据报 34 | 35 | ##### DNS 查询过程 36 | 37 | * 查找浏览器缓存 38 | * 查找系统缓存 39 | * 查找路由器缓存 40 | * 查找 ISP DNS 缓存 41 | * 递归从根域开始搜索 42 | 43 | **耗时** 44 | 45 | ```js 46 | let pt = window.performance.timeing 47 | let dns = pt.domainLookupEnd - pt.domainLookuoStart 48 | ``` 49 | 50 | **优化建议** 51 | 52 | * 控制域名数量,推荐是两个。 53 | * 使用缓存 Last-Modified,If-Modified-Since,ETag,If-None-Match,Expires,Cache-Control。 54 | * 使用 CDN,提高缓存命中率。 55 | * 服务根据需要设置合理的 TTL。 56 | * DNS 的预解析。 57 | 58 | ##### 缓存协商 59 | 60 | **Last-Modified 与 If-Modified-Since** 61 | 62 | * 浏览器第一次请求资源时,服务器会把资源的最新修改时间 Last-Modified:Thu, 29 Dec 2011 18:23:55 GMT 放在响应头中返回给浏览器 63 | * 第二次请求时,浏览器就会把上一次服务器返回的修改时间放在请求头 If-Modified-Since:Thu, 29 Dec 2011 18:23:55 发送给服务器,服务器就会拿这个时间跟服务器上的资源的最新修改时间进行对比 64 | 65 | 如果两者相等或者大于服务器上的最新修改时间,那么表示浏览器的缓存是有效的,此时缓存会命中,服务器就不再返回内容给浏览器了,同时 Last-Modified 头也不会返回,因为资源没被修改,返回了也没什么意义。如果没命中缓存则最新修改的资源连同 Last-Modified 头一起返回。 66 | 67 | **ETag 与 If-None-Match** 68 | 69 | ETag/If-None-Match 与 Last-Modified/If-Modified-Since 的流程其实是类似的,唯一的区别是它基于资源的内容的摘要信息(比如 MD5 hash)来判断 70 | 71 | 浏览器发送第二次请求时,会把第一次的响应头信息 ETag 的值放在 If-None-Match 的请求头中发送到服务器,与最新的资源的摘要信息对比,如果相等,取浏览器缓存,否则内容有更新,最新的资源连同最新的摘要信息返回。用 ETag 的好处是如果因为某种原因到时资源的修改时间没改变,那么用 ETag 就能区分资源是不是有被更新。 72 | 73 | ##### 加密算法相关 74 | 75 | > 1、对称加密 76 | 77 | 有流式、分组两种,加密和解密都是使用的同一个密钥。 78 | 79 | 例如:DES、AES-GCM、ChaCha20-Poly1305 等,最常用的就是 DES 80 | 81 | > 2、非对称加密 82 | 83 | 加密使用的密钥和解密使用的密钥是不相同的,分别称为:公钥、私钥,公钥和算法都是公开的,私钥是保密的。非对称加密算法性能较低,但是安全性超强,由于其加密特性,非对称加密算法能加密的数据长度也是有限的。 84 | 85 | 例如:RSA、DSA、ECDSA、 DH、ECDHE,最常用的就是 RSA 86 | 87 | > 3、哈希算法 88 | 89 | 将任意长度的信息转换为较短的固定长度的值,通常其长度要比信息小得多,且算法不可逆。 90 | 91 | 例如:MD5、SHA-1、SHA-2、SHA-256 等 92 | 93 | > 4、数字签名 94 | 95 | 签名就是在信息的后面再加上一段内容(信息经过 hash 后的值),可以证明信息没有被修改过。hash 值一般都会加密后(也就是签名)再和信息一起发送,以保证这个 hash 值不被修改。 96 | 97 | ##### http 详情 98 | 99 | > 1、http 访问过程 100 | 101 | 主要就是三次握手的过程。 102 | ![三次握手](http://pic4.zhimg.com/v2-e367a5e3bc28fb7fd083ddc201e7e693_b.png) 103 | 从上图可以看出客户端和服务端之间的数据传输都是“裸奔”的 104 | 105 | ![中间人](http://pic4.zhimg.com/v2-831635f04f3732e866af0ec6ce1040e7_b.png) 106 | 从上图可以看出,http 请求在发送过程中,客户端与服务端之间没有任何身份确认的步骤,数据全部是裸奔在网络上。有人存心想攻击你的话,直接在客户端和服务端之间截获消息的传递。消息截获后,黑客就可以冒充服务端给客户端返回消息了。这一现象也是我们常说的劫持。 107 | 108 | 所以 http 传输所面临的风险就是: 109 | 110 | * 窃听风险:黑客可以获取通信内容 111 | * 篡改风险:黑客可以修改通信内容 112 | * 冒充风险:黑客可以冒充他人的身份参与通信 113 | 114 | ##### http 向 https 演化的过程 115 | 116 | > 1、传输内容对称加密 117 | 118 | 因为信息泄露大概率是因为信息的明文传输,所以想到这点,于是就产生了加密传输内容的方法。(黑客在不知道密钥的情况下是不能解密来知道传输内容的) 119 | ![传输内容对称加密](http://pic1.zhimg.com/v2-8d8138e883455e4d316d644c79a89314_b.png) 120 | 121 | 这种方式还是有很多缺点的: 122 | 123 | * 不同客户端和服务端众多,那么就要维护很多的密钥,导致维护成本会很高 124 | * 因为每个客户端和服务端的安全级别不一样,所以也会导致密钥的泄露 125 | 126 | > 2、传输内容非对称加密 127 | 128 | 因为传输内容对称加密的缺点,那么我们就可以换种思路了。就将传输内容非对称加密后传输。 129 | ![传输内容非对称加密](http://pic2.zhimg.com/v2-660bec42419281a9ec47c029089a77c9_b.png) 130 | 131 | 如上图所示,客户端用公钥对请求内容加密,服务器使用私钥对内容解密,反之亦然,但上述过程也存在缺点: 132 | 133 | * 公钥是公开的(也就是黑客也会有公钥),所以第 ④ 步私钥加密的信息,如果被黑客截获,其可以使用公钥进行解密,获取其中的内容 134 | 135 | > 3、对称加密和非对称加密的结合 136 | 137 | 通过上面的两步,我们发现两种方式都有着自己的优缺点。那么我们就将这两种方式结合起来。取其精华,去其糟粕,发挥各自的优点就好了。 138 | 139 | ![两种方式结合](http://pic3.zhimg.com/v2-22570e3e422de7951ce7c5c3e8435312_b.png) 140 | 141 | * 如上图所示第 ③ 步时,客户端说:(咱们后续回话采用对称加密吧,这是对称加密的算法和对称密钥)这段话用公钥进行加密,然后传给服务器 142 | * 服务器收到信息后,用私钥解密,提取出对称加密算法和对称密钥后,服务器说:(好的)对称密钥加密 143 | * 后续两者之间信息的传输就可以使用对称加密的方式了 144 | 145 | 上面还是会有问题的: 146 | 147 | * 第一步中的公钥怎么获取 148 | * 怎么确信当前送到服务器是真实的服务器而不是黑客呢? 149 | 150 | > 4、获取公钥和身份确认 151 | 152 | ![获取公钥和身份确认](http://pic3.zhimg.com/v2-f2ac6567fa1a3c10e73eba59eab3823a_b.png) 153 | 154 | **获取公钥** 155 | 156 | * 提供一个下载公钥的地址,回话前让客户端去下载。(缺点:下载地址有可能是假的;客户端每次在回话前都先去下载公钥也很麻烦) 157 | * 回话开始时,服务器把公钥发给客户端(缺点:黑客冒充服务器,发送给客户端假的公钥) 158 | 159 | 那有木有一种方式既可以安全的获取公钥,又能防止黑客冒充呢? 那就需要用到终极武器了:SSL 证书 160 | 161 | ![带证书](http://pic1.zhimg.com/v2-5e2241fae8b593ff7f3b3a308ef81c10_b.png) 162 | 163 | **SSL 证书包括:** 164 | 165 | * 证书颁发机构的 CA 166 | * 证书的有效期 167 | * 公钥 168 | * 证书所有者 169 | * 签名 170 | * 等等 171 | 172 | > 5、SSL 证书校验 173 | 174 | * 首先浏览器读取证书中的证书所有者、有效期等信息进行一一校验 175 | * 浏览器开始查找操作系统中已内置的受信任的证书发布机构 CA,与服务器发来的证书中的颁发者 CA 比对,用于校验证书是否为合法机构颁发 176 | * 如果找不到,浏览器就会报错,说明服务器发来的证书是不可信任的。 177 | * 如果找到,那么浏览器就会从操作系统中取出 颁发者 CA 的公钥,然后对服务器发来的证书里面的签名进行解密 178 | * 浏览器使用相同的 hash 算法计算出服务器发来的证书的 hash 值,将这个计算的 hash 值与证书中签名做对比 179 | * 对比结果一致,则证明服务器发来的证书合法,没有被冒充 180 | * 此时浏览器就可以读取证书中的公钥,用于后续加密了 181 | -------------------------------------------------------------------------------- /prototype.md: -------------------------------------------------------------------------------- 1 | ### 开始 2 | 3 | 原型和原型链的理解和学习 4 | 5 | ### 正文 6 | 7 | 从一个常见的例子开始吧: 8 | 9 | ```js 10 | function Person() {} 11 | var person = new Person() 12 | person.name = 'kobe' 13 | console.log(person.name) // kobe 14 | ``` 15 | 16 | 这个简单的例子就是我们定义一个构造函数,然后使用这个构造函数生成一个 person 的实例对象。给这个实例对象添加一个属性 name。接下来我们就来分析一波。 17 | 18 | > 0、对象 19 | 20 | 浓情原型和原型链就要弄清楚对象。 21 | 22 | * 普通对象 23 | * 最普通的对象:有\_\_proto\_\_属性,没有 prototype 属性 24 | * 原型对象 25 | * 函数对象 26 | * 凡是通过 new Function()创建的都是函数对象。(拥有\_\_proto\_\_、prototype 属性(指向原型对象)) 27 | 28 | ```js 29 | //函数对象 30 | function F1(){} 31 | var F2 = function(){} 32 | var F3 = function("n1","n2","return n1+n2") 33 | console.log(typeof F1); //function 34 | console.log(typeof F2); //function 35 | console.log(typeof F3); //function 36 | console.log(typeof Object); //function 37 | console.log(typeof Array); //function 38 | console.log(typeof String); //function 39 | console.log(typeof Date); //function 40 | console.log(typeof Function); //function 41 | ``` 42 | 43 | > 1、prototype 44 | 45 | 学习前端的人或多或少都听说过 prototype, 我们在很多的地方都可以看到对 prototype 的使用,就像下面的例子。 46 | 47 | ```js 48 | function Person() {} 49 | // prototype 本身只有函数才会有的 50 | Person.prototype.name = 'kobe' 51 | var person1 = new Person() 52 | var person2 = new Person() 53 | 54 | console.log(person1.name) // kobe 55 | console.log(person2.name) // kobe 56 | ``` 57 | 58 | 看了上面的例子,你可能会有个疑问。就是这个函数的 prototype 属性到底是指向什么地方呢?是指向这个函数的原型么? 59 | 60 | 我们可以去浏览器里面试着访问下`Person.prototype`我们就可以发现端倪。经过手动测试,我们发现函数的 prototype 是指向的一个对象,这个对象就是传说中的实例的原型,就是上面`person1`和`person2`的原型。这个对象包含两个属性: 61 | 62 | ```js 63 | { 64 | contructor: xxx, 65 | __proto__: Object 66 | } 67 | ``` 68 | 69 | 对于我们来说,我们怎么理解这个原型对象呢?我们可以大致理解为:JavaScript 函数对象在创建的时候,都会为它关联一个对象,这个对象就是我们说的原型。 70 | 71 | ```js 72 | // 注意:系统内置的函数对象有下面 73 | Function, Object, Array, String, Number 74 | Function.prototype 75 | Object.prototype 76 | Array.prototype 77 | String.prototype 78 | Number.prototype 79 | ``` 80 | 81 | 下面这个图能够简单说明构造函数和实例原型对象(其实原型对象就是构造函数的一个实例对象)之间的关系。 82 | ![image](https://raw.githubusercontent.com/mqyqingfeng/Blog/master/Images/prototype1.png) 83 | 84 | 看了上面的关系后,那实例和构造函数还有实例原型的关系是什么呢? 85 | 86 | > 2、\_\_proto\_\_ 87 | 88 | 每一个 JavaScript 对象都有一个属性 \_\_proto\_\_,这个属性是指向的该对象的是原型。 89 | 90 | ```js 91 | function Person() {} 92 | var person = new Person() 93 | console.log(person.__proto__ === Person.prototype) // true 94 | ``` 95 | 96 | 所以根据上面的代码可以得到下面的图: 97 | 98 | ![ssss](https://github.com/mqyqingfeng/Blog/raw/master/Images/prototype2.png) 99 | 100 | `person`就是我们通过 new 方式创建的普通对象。因此它有\_\_proto\_\_属性。根据上面的代码就可以验证上面的图了。 101 | 102 | 既然实例和构造函数都可以通过自己的方式指向原型对象,那我们的原型能够反向指向构造函数和实例么? 103 | 104 | > 4、contructor 105 | 106 | 上面的问题的答案就是:指向实例是没有办法了,指向构造函数倒是有办法。不能指向实例主要是因为一个构造函数能够创建多个实例。 107 | 108 | 最后就有了下面的关系: 109 | 110 | ```js 111 | function Person() {} 112 | console.log(Person === Person.prototype.constructor) // true 113 | ``` 114 | 115 | 更新下关系图: 116 | ![xxx](https://github.com/mqyqingfeng/Blog/raw/master/Images/prototype3.png) 117 | 118 | 经过上面的一系列的 操作,我们可以得出如下结论: 119 | 120 | ```js 121 | function Person() {} 122 | 123 | var person = new Person() 124 | 125 | console.log(person.__proto__ == Person.prototype) // true 126 | console.log(Person.prototype.constructor == Person) // true 127 | console.log(Person.prototype.isPrototypeOf(person)) // true 128 | console.log(Object.getPrototypeOf(person) === Person.prototype) // true 129 | ``` 130 | 131 | 看了上面的一系列操作,大致的理清楚了构造函数,实例,以及原型三者之间的关系。 132 | 133 | > 5、实例与原型 134 | 135 | 当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。 136 | 137 | ```js 138 | function Person() {} 139 | Person.prototype.name = 'Curry' 140 | var person = new Person() 141 | person.name = 'kobe' 142 | console.log(person.name) 143 | delete person.name 144 | console.log(person.name) 145 | ``` 146 | 147 | 看看上面这个例子,我给实例对象 person 添加了一个属性 name,当我们打印 person.name 的时候我们就可以看到输出的是 curry。当我们把这个属性删掉的时候就会打印出 kobe 了。这就验证了上面的话,先找对象自身的属性,找不到就延原型链找,直到最后。 148 | 149 | > 6、原型的原型 150 | 151 | 在控制台里面打印 Person.prototype 的时候我们可以看到打印出来的对象(普通对象)是有\_\_proto\_\_属性的。所以我们可以展开查看下。 152 | ![image](https://user-images.githubusercontent.com/10307282/34213261-a6fd85fa-e5d9-11e7-8f50-20c88df03abd.png) 153 | 因为原型也是一个普通对象,那么就可以通过 Object 的方式构造。下面就是构造一个对象的方式 154 | 155 | ```js 156 | var obj = new Object() 157 | obj.name = 'Kevin' 158 | console.log(obj.name) // Kevin 159 | ``` 160 | 161 | 原型对象是由下面的方式 162 | 163 | ```js 164 | var Person.prototype = new Object() 165 | Person.prototype.xxx = 'xxx' 166 | ``` 167 | 168 | 所以可以理解 Person.prototype.\_\_proto\_\_指向的是 Object.prototype。 169 | ![xxx](https://github.com/mqyqingfeng/Blog/raw/master/Images/prototype4.png) 170 | 171 | > 7、原型链与最上级 172 | 173 | Object.prototype 的原型是啥呢? 174 | 175 | ```js 176 | console.log(Object.prototype.__proto__ === null) // true 177 | ``` 178 | 179 | 最后这个 null 是什么呢?null 值表示一个空对象指针。null 表示“没有对象”,即该处不应该有值。所以 Object.prototype.\_\_proto\_\_ 的值为 null 跟 Object.prototype 没有原型,其实表达了一个意思。 180 | 181 | 所以查找属性的时候查到 Object.prototype 就可以停止查找了。 182 | 183 | 最后产生的原型链的图: 184 | 185 | ![xxxx](https://github.com/mqyqingfeng/Blog/raw/master/Images/prototype5.png) 186 | 187 | ### 感谢 188 | 189 | [深入理解原型与原型链](https://github.com/mqyqingfeng/Blog/issues/2) 190 | [JS 重点整理之 JS 原型链彻底搞清楚](https://zhuanlan.zhihu.com/p/22787302) 191 | 《你不知道的 javascript 上卷》《javascript 高级程序设计》 192 | -------------------------------------------------------------------------------- /koa2/application.md: -------------------------------------------------------------------------------- 1 | #### 关于 koa2 的源码学习总结 2 | 3 | 通过一个简单的例子来看看 koa2 的源码 4 | **示例** 5 | 6 | 原生 http 请求方式 7 | 8 | ```js 9 | const http = require('http') 10 | 11 | const server = http.createServer((req, res) => { 12 | res.statusCode = 200 13 | res.setHeader('Content-Type', 'text/plain') 14 | res.end('Hello World\n') 15 | }) 16 | 17 | server.listen(3000) 18 | ``` 19 | 20 | koa2 使用方式 21 | 22 | ```js 23 | const Koa = require('koa') 24 | const app = new Koa() 25 | 26 | app.use(ctx => { 27 | ctx.body = 'Hello Koa' 28 | }) 29 | 30 | app.listen(3000) 31 | ``` 32 | 33 | > 1、构造函数 `application.js` 34 | 35 | 这个文件里面主要是在做`new` 一个`koa`实例的初始化工作,以及收集中间件等操作。关键操作就是下面这几步。 36 | 37 | ```js 38 | this.middleware = [] // 用于存储中间件的数组 39 | this.context = Object.create(context) // 创建上下文 40 | this.request = Object.create(request) // 创建request 41 | this.response = Object.create(response) // 创建response 42 | ``` 43 | 44 | > 2、注册中间件 45 | 46 | ```js 47 | use(fn) { 48 | if (typeof fn !== 'function') throw new TypeError('middleware must be a function!'); 49 | if (isGeneratorFunction(fn)) { 50 | deprecate('Support for generators will be removed in v3. ' + 51 | 'See the documentation for examples of how to convert old middleware ' + 52 | 'https://github.com/koajs/koa/blob/master/docs/migration.md'); 53 | fn = convert(fn); 54 | } 55 | debug('use %s', fn._name || fn.name || '-'); 56 | this.middleware.push(fn); 57 | return this; 58 | } 59 | ``` 60 | 61 | 初始化构造函数完成后,如果我们有很多中间件的话,那么这个时候就是调用 use 来注册中间件了。上面就是注册中间件的方法。这个方法里面主要就是判断,传进来的是不是一个函数,如果是一个迭代器函数,那么会调用 convert 方法,将迭代器函数转化成为普通的函数,当然还会提示你不要用迭代器了。最后就是将传进来的函数存在构造函数里面声明的 middleware 数组中。 62 | 63 | > 3、调用 listen 方法 64 | 65 | ```js 66 | listen(...args) { 67 | debug('listen'); 68 | const server = http.createServer(this.callback()); 69 | return server.listen(...args); 70 | } 71 | ``` 72 | 73 | 在构造函数里面初始化完成后,按照我们的使用方式就是注册中间件,然后在最后的时候调用 listen 方法。这个方法很简单,就是以向原生 http.createServer 传入一个函数的形式来创建自身的一个实例。listen 方法就做了这么简单的一个事。 74 | 75 | > 4、在看 this.callback() 76 | 77 | 看到这里我们就明白我们实际上最关心的就是这个 this.callback 函数。其实这个也是 koa2 的核心所在。 78 | 79 | ```js 80 | callback() { 81 | const fn = compose(this.middleware); 82 | // 这里是调用Emitter类里面的方法 83 | if (!this.listeners('error').length) this.on('error', this.onerror); 84 | // 包装函数,将ctx和中间件和并函数传给内部 85 | const handleRequest = (req, res) => { 86 | // 基于req和req封装出我们使用的ctx对象。 87 | const ctx = this.createContext(req, res); 88 | return this.handleRequest(ctx, fn); 89 | }; 90 | 91 | return handleRequest; 92 | } 93 | ``` 94 | 95 | this.callback 执行的结果是一个函数,这个函数的主要作用就是根据 req 获取请求信息,然后向 res 中写入返回内容。具体做法就是在一开始的时候合并中间件返回一个函数。然后基于 res 和 req 封装出我们平时使用的 ctx 对象。接着就是调用 koa 自己的 handleRequest 方法,将合并好的中间件函数和刚生成的 ctx 对象传入。 96 | 97 | > 5、创建 ctx---createContext 98 | 99 | 前面已经说过了 ctx 这个对象就是基于 res 和 res 封装来的,接下来就看看是怎么封装的。 100 | 101 | ```js 102 | createContext(req, res) { 103 | const context = Object.create(this.context); 104 | const request = context.request = Object.create(this.request); 105 | const response = context.response = Object.create(this.response); 106 | context.app = request.app = response.app = this; 107 | context.req = request.req = response.req = req; 108 | context.res = request.res = response.res = res; 109 | request.ctx = response.ctx = context; 110 | request.response = response; 111 | response.request = request; 112 | context.originalUrl = request.originalUrl = req.url; 113 | context.cookies = new Cookies(req, res, { 114 | keys: this.keys, 115 | secure: request.secure 116 | }); 117 | request.ip = request.ips[0] || req.socket.remoteAddress || ''; 118 | context.accept = request.accept = accepts(req); 119 | context.state = {}; 120 | return context; 121 | } 122 | ``` 123 | 124 | 上面的主要操作就是创建了三个对象 context,request,response 125 | 126 | > 6、关于 handleRequest 函数 127 | 128 | 上面我们知道传进 handleRequest 方法的参数就是经过封装的 ctx 和合并后的中间件函数。并且将他们的原型指定为我们 app 中对应的对象,然后将原生的 req 和 res 赋值给相应的属性,就完成了。 129 | 130 | ```js 131 | handleRequest(ctx, fnMiddleware) { 132 | const res = ctx.res; 133 | res.statusCode = 404; 134 | const onerror = err => ctx.onerror(err); 135 | // response 辅助函数 136 | const handleResponse = () => respond(ctx); 137 | // onFinished 是确保一个流在关闭、完成和报错时都会执行相应的回调函数 138 | onFinished(res, onerror); 139 | return fnMiddleware(ctx).then(handleResponse).catch(onerror); 140 | } 141 | ``` 142 | 143 | 这里做的主要是将封装的 ctx 传给合并后的中间件函数 fnMiddleware,中间件函数返回的是一个 promise。resolve 的话就调用 handleResponse,reject 的话就调用 onerror。handleResponse 里面主要做的操作就是通过 ctx 中的信息向 res 中写入信息。 144 | 145 | > 7、respond 的分析 146 | 147 | respond 方法就是一个辅助方法,主要作用就是根据 ctx 中的相关信息向 res 中写入信息。 148 | 149 | ```js 150 | function respond(ctx) { 151 | // allow bypassing koa 152 | if (false === ctx.respond) return 153 | 154 | const res = ctx.res 155 | if (!ctx.writable) return 156 | 157 | let body = ctx.body 158 | const code = ctx.status 159 | 160 | // ignore body 161 | if (statuses.empty[code]) { 162 | // strip headers 163 | ctx.body = null 164 | return res.end() 165 | } 166 | 167 | if ('HEAD' == ctx.method) { 168 | if (!res.headersSent && isJSON(body)) { 169 | ctx.length = Buffer.byteLength(JSON.stringify(body)) 170 | } 171 | return res.end() 172 | } 173 | 174 | // status body 175 | if (null == body) { 176 | body = ctx.message || String(code) 177 | if (!res.headersSent) { 178 | ctx.type = 'text' 179 | ctx.length = Buffer.byteLength(body) 180 | } 181 | return res.end(body) 182 | } 183 | 184 | // responses 185 | if (Buffer.isBuffer(body)) return res.end(body) 186 | if ('string' == typeof body) return res.end(body) 187 | if (body instanceof Stream) return body.pipe(res) 188 | 189 | // body: json 190 | body = JSON.stringify(body) 191 | if (!res.headersSent) { 192 | ctx.length = Buffer.byteLength(body) 193 | } 194 | res.end(body) 195 | } 196 | ``` 197 | -------------------------------------------------------------------------------- /test-webpack/dist/runtime-12168eb5ce29428d6ad5.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // install a JSONP callback for chunk loading 3 | /******/ var parentJsonpFunction = window["webpackJsonp"]; 4 | /******/ window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) { 5 | /******/ // add "moreModules" to the modules object, 6 | /******/ // then flag all "chunkIds" as loaded and fire callback 7 | /******/ var moduleId, chunkId, i = 0, resolves = [], result; 8 | /******/ for(;i < chunkIds.length; i++) { 9 | /******/ chunkId = chunkIds[i]; 10 | /******/ if(installedChunks[chunkId]) { 11 | /******/ resolves.push(installedChunks[chunkId][0]); 12 | /******/ } 13 | /******/ installedChunks[chunkId] = 0; 14 | /******/ } 15 | /******/ for(moduleId in moreModules) { 16 | /******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) { 17 | /******/ modules[moduleId] = moreModules[moduleId]; 18 | /******/ } 19 | /******/ } 20 | /******/ if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules); 21 | /******/ while(resolves.length) { 22 | /******/ resolves.shift()(); 23 | /******/ } 24 | /******/ if(executeModules) { 25 | /******/ for(i=0; i < executeModules.length; i++) { 26 | /******/ result = __webpack_require__(__webpack_require__.s = executeModules[i]); 27 | /******/ } 28 | /******/ } 29 | /******/ return result; 30 | /******/ }; 31 | /******/ 32 | /******/ // The module cache 33 | /******/ var installedModules = {}; 34 | /******/ 35 | /******/ // objects to store loaded and loading chunks 36 | /******/ var installedChunks = { 37 | /******/ "runtime": 0 38 | /******/ }; 39 | /******/ 40 | /******/ // The require function 41 | /******/ function __webpack_require__(moduleId) { 42 | /******/ 43 | /******/ // Check if module is in cache 44 | /******/ if(installedModules[moduleId]) { 45 | /******/ return installedModules[moduleId].exports; 46 | /******/ } 47 | /******/ // Create a new module (and put it into the cache) 48 | /******/ var module = installedModules[moduleId] = { 49 | /******/ i: moduleId, 50 | /******/ l: false, 51 | /******/ exports: {} 52 | /******/ }; 53 | /******/ 54 | /******/ // Execute the module function 55 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 56 | /******/ 57 | /******/ // Flag the module as loaded 58 | /******/ module.l = true; 59 | /******/ 60 | /******/ // Return the exports of the module 61 | /******/ return module.exports; 62 | /******/ } 63 | /******/ 64 | /******/ // This file contains only the entry chunk. 65 | /******/ // The chunk loading function for additional chunks 66 | /******/ __webpack_require__.e = function requireEnsure(chunkId) { 67 | /******/ var installedChunkData = installedChunks[chunkId]; 68 | /******/ if(installedChunkData === 0) { 69 | /******/ return new Promise(function(resolve) { resolve(); }); 70 | /******/ } 71 | /******/ 72 | /******/ // a Promise means "currently loading". 73 | /******/ if(installedChunkData) { 74 | /******/ return installedChunkData[2]; 75 | /******/ } 76 | /******/ 77 | /******/ // setup Promise in chunk cache 78 | /******/ var promise = new Promise(function(resolve, reject) { 79 | /******/ installedChunkData = installedChunks[chunkId] = [resolve, reject]; 80 | /******/ }); 81 | /******/ installedChunkData[2] = promise; 82 | /******/ 83 | /******/ // start chunk loading 84 | /******/ var head = document.getElementsByTagName('head')[0]; 85 | /******/ var script = document.createElement('script'); 86 | /******/ script.type = 'text/javascript'; 87 | /******/ script.charset = 'utf-8'; 88 | /******/ script.async = true; 89 | /******/ script.timeout = 120000; 90 | /******/ 91 | /******/ if (__webpack_require__.nc) { 92 | /******/ script.setAttribute("nonce", __webpack_require__.nc); 93 | /******/ } 94 | /******/ script.src = __webpack_require__.p + "" + chunkId + "-" + {"vendor":"ed00d7222262ac99e510","pageA":"b5b4e2893bce99fd5c57","pageB":"34be879b3374ac9b2072"}[chunkId] + ".js"; 95 | /******/ var timeout = setTimeout(onScriptComplete, 120000); 96 | /******/ script.onerror = script.onload = onScriptComplete; 97 | /******/ function onScriptComplete() { 98 | /******/ // avoid mem leaks in IE. 99 | /******/ script.onerror = script.onload = null; 100 | /******/ clearTimeout(timeout); 101 | /******/ var chunk = installedChunks[chunkId]; 102 | /******/ if(chunk !== 0) { 103 | /******/ if(chunk) { 104 | /******/ chunk[1](new Error('Loading chunk ' + chunkId + ' failed.')); 105 | /******/ } 106 | /******/ installedChunks[chunkId] = undefined; 107 | /******/ } 108 | /******/ }; 109 | /******/ head.appendChild(script); 110 | /******/ 111 | /******/ return promise; 112 | /******/ }; 113 | /******/ 114 | /******/ // expose the modules object (__webpack_modules__) 115 | /******/ __webpack_require__.m = modules; 116 | /******/ 117 | /******/ // expose the module cache 118 | /******/ __webpack_require__.c = installedModules; 119 | /******/ 120 | /******/ // define getter function for harmony exports 121 | /******/ __webpack_require__.d = function(exports, name, getter) { 122 | /******/ if(!__webpack_require__.o(exports, name)) { 123 | /******/ Object.defineProperty(exports, name, { 124 | /******/ configurable: false, 125 | /******/ enumerable: true, 126 | /******/ get: getter 127 | /******/ }); 128 | /******/ } 129 | /******/ }; 130 | /******/ 131 | /******/ // getDefaultExport function for compatibility with non-harmony modules 132 | /******/ __webpack_require__.n = function(module) { 133 | /******/ var getter = module && module.__esModule ? 134 | /******/ function getDefault() { return module['default']; } : 135 | /******/ function getModuleExports() { return module; }; 136 | /******/ __webpack_require__.d(getter, 'a', getter); 137 | /******/ return getter; 138 | /******/ }; 139 | /******/ 140 | /******/ // Object.prototype.hasOwnProperty.call 141 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 142 | /******/ 143 | /******/ // __webpack_public_path__ 144 | /******/ __webpack_require__.p = ""; 145 | /******/ 146 | /******/ // on error function for async loading 147 | /******/ __webpack_require__.oe = function(err) { console.error(err); throw err; }; 148 | /******/ }) 149 | /************************************************************************/ 150 | /******/ ([]); -------------------------------------------------------------------------------- /test-webpack/dist/runtime-1b40e842c0decf252041.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // install a JSONP callback for chunk loading 3 | /******/ var parentJsonpFunction = window["webpackJsonp"]; 4 | /******/ window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) { 5 | /******/ // add "moreModules" to the modules object, 6 | /******/ // then flag all "chunkIds" as loaded and fire callback 7 | /******/ var moduleId, chunkId, i = 0, resolves = [], result; 8 | /******/ for(;i < chunkIds.length; i++) { 9 | /******/ chunkId = chunkIds[i]; 10 | /******/ if(installedChunks[chunkId]) { 11 | /******/ resolves.push(installedChunks[chunkId][0]); 12 | /******/ } 13 | /******/ installedChunks[chunkId] = 0; 14 | /******/ } 15 | /******/ for(moduleId in moreModules) { 16 | /******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) { 17 | /******/ modules[moduleId] = moreModules[moduleId]; 18 | /******/ } 19 | /******/ } 20 | /******/ if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules); 21 | /******/ while(resolves.length) { 22 | /******/ resolves.shift()(); 23 | /******/ } 24 | /******/ if(executeModules) { 25 | /******/ for(i=0; i < executeModules.length; i++) { 26 | /******/ result = __webpack_require__(__webpack_require__.s = executeModules[i]); 27 | /******/ } 28 | /******/ } 29 | /******/ return result; 30 | /******/ }; 31 | /******/ 32 | /******/ // The module cache 33 | /******/ var installedModules = {}; 34 | /******/ 35 | /******/ // objects to store loaded and loading chunks 36 | /******/ var installedChunks = { 37 | /******/ "runtime": 0 38 | /******/ }; 39 | /******/ 40 | /******/ // The require function 41 | /******/ function __webpack_require__(moduleId) { 42 | /******/ 43 | /******/ // Check if module is in cache 44 | /******/ if(installedModules[moduleId]) { 45 | /******/ return installedModules[moduleId].exports; 46 | /******/ } 47 | /******/ // Create a new module (and put it into the cache) 48 | /******/ var module = installedModules[moduleId] = { 49 | /******/ i: moduleId, 50 | /******/ l: false, 51 | /******/ exports: {} 52 | /******/ }; 53 | /******/ 54 | /******/ // Execute the module function 55 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 56 | /******/ 57 | /******/ // Flag the module as loaded 58 | /******/ module.l = true; 59 | /******/ 60 | /******/ // Return the exports of the module 61 | /******/ return module.exports; 62 | /******/ } 63 | /******/ 64 | /******/ // This file contains only the entry chunk. 65 | /******/ // The chunk loading function for additional chunks 66 | /******/ __webpack_require__.e = function requireEnsure(chunkId) { 67 | /******/ var installedChunkData = installedChunks[chunkId]; 68 | /******/ if(installedChunkData === 0) { 69 | /******/ return new Promise(function(resolve) { resolve(); }); 70 | /******/ } 71 | /******/ 72 | /******/ // a Promise means "currently loading". 73 | /******/ if(installedChunkData) { 74 | /******/ return installedChunkData[2]; 75 | /******/ } 76 | /******/ 77 | /******/ // setup Promise in chunk cache 78 | /******/ var promise = new Promise(function(resolve, reject) { 79 | /******/ installedChunkData = installedChunks[chunkId] = [resolve, reject]; 80 | /******/ }); 81 | /******/ installedChunkData[2] = promise; 82 | /******/ 83 | /******/ // start chunk loading 84 | /******/ var head = document.getElementsByTagName('head')[0]; 85 | /******/ var script = document.createElement('script'); 86 | /******/ script.type = 'text/javascript'; 87 | /******/ script.charset = 'utf-8'; 88 | /******/ script.async = true; 89 | /******/ script.timeout = 120000; 90 | /******/ 91 | /******/ if (__webpack_require__.nc) { 92 | /******/ script.setAttribute("nonce", __webpack_require__.nc); 93 | /******/ } 94 | /******/ script.src = __webpack_require__.p + "" + chunkId + "-" + {"vendor":"ed00d7222262ac99e510","pageA":"8ddb2f0f51a8139475bb","pageB":"34be879b3374ac9b2072"}[chunkId] + ".js"; 95 | /******/ var timeout = setTimeout(onScriptComplete, 120000); 96 | /******/ script.onerror = script.onload = onScriptComplete; 97 | /******/ function onScriptComplete() { 98 | /******/ // avoid mem leaks in IE. 99 | /******/ script.onerror = script.onload = null; 100 | /******/ clearTimeout(timeout); 101 | /******/ var chunk = installedChunks[chunkId]; 102 | /******/ if(chunk !== 0) { 103 | /******/ if(chunk) { 104 | /******/ chunk[1](new Error('Loading chunk ' + chunkId + ' failed.')); 105 | /******/ } 106 | /******/ installedChunks[chunkId] = undefined; 107 | /******/ } 108 | /******/ }; 109 | /******/ head.appendChild(script); 110 | /******/ 111 | /******/ return promise; 112 | /******/ }; 113 | /******/ 114 | /******/ // expose the modules object (__webpack_modules__) 115 | /******/ __webpack_require__.m = modules; 116 | /******/ 117 | /******/ // expose the module cache 118 | /******/ __webpack_require__.c = installedModules; 119 | /******/ 120 | /******/ // define getter function for harmony exports 121 | /******/ __webpack_require__.d = function(exports, name, getter) { 122 | /******/ if(!__webpack_require__.o(exports, name)) { 123 | /******/ Object.defineProperty(exports, name, { 124 | /******/ configurable: false, 125 | /******/ enumerable: true, 126 | /******/ get: getter 127 | /******/ }); 128 | /******/ } 129 | /******/ }; 130 | /******/ 131 | /******/ // getDefaultExport function for compatibility with non-harmony modules 132 | /******/ __webpack_require__.n = function(module) { 133 | /******/ var getter = module && module.__esModule ? 134 | /******/ function getDefault() { return module['default']; } : 135 | /******/ function getModuleExports() { return module; }; 136 | /******/ __webpack_require__.d(getter, 'a', getter); 137 | /******/ return getter; 138 | /******/ }; 139 | /******/ 140 | /******/ // Object.prototype.hasOwnProperty.call 141 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 142 | /******/ 143 | /******/ // __webpack_public_path__ 144 | /******/ __webpack_require__.p = ""; 145 | /******/ 146 | /******/ // on error function for async loading 147 | /******/ __webpack_require__.oe = function(err) { console.error(err); throw err; }; 148 | /******/ }) 149 | /************************************************************************/ 150 | /******/ ([]); -------------------------------------------------------------------------------- /test-javascript/3.js: -------------------------------------------------------------------------------- 1 | const destructuringArray = (values, keys) => { 2 | try { 3 | const obj = {} 4 | if (typeof keys === 'string') { 5 | keys = JSON.parse(keys.replace(/\w+/g, '"$&"')) 6 | } 7 | const iterate = (values, keys) => { 8 | keys.forEach((key, i) => { 9 | if (Array.isArray(key)) iterate(values[i], key) 10 | else obj[key] = values[i] 11 | }) 12 | } 13 | iterate(values, keys) 14 | return obj 15 | } catch (e) { 16 | console.log(e) 17 | } 18 | } 19 | 20 | // 将 destructuringArray([1, [2, 3], 4], "[a, [b], c]") => {a: 1, b: 2, c: 4} 21 | const targetArray = [1, [2, 3], 4] 22 | const formater = '[a, [b], c]' 23 | //console.log(destructuringArray(targetArray, formater)) 24 | 25 | // 数组展平,主要利用map返回新的数组 26 | const flatten = arr => { 27 | return [].concat( 28 | ...arr.map(item => (Array.isArray(item) ? flatten(item) : item)) 29 | ) 30 | } 31 | 32 | //console.log(flatten([[1, 2], 3, [[[4], 5]]])) 33 | 34 | // 二分查找,非递归手段 35 | function binarySearch(arr, dest) { 36 | let low = 0 37 | let high = arr.length - 1 38 | 39 | while (low <= high) { 40 | let mid = Math.floor((low + high) / 2) 41 | if (dest > arr[mid]) { 42 | low = mid + 1 43 | } else if (dest < arr[mid]) { 44 | high = mid - 1 45 | } else { 46 | return mid 47 | } 48 | } 49 | return -1 50 | } 51 | const arr = [1, 2, 3, 4, 5, 6, 7, 8] 52 | //console.log(binarySearch(arr, 3)) // 2 53 | 54 | // 找出数组中重复出现的元素 55 | // 例如:[1,2,4,4,3,3,1,5,3] 56 | // 输出:[1,3,4] 57 | let arr1 = [1, 2, 4, 4, 3, 3, 1, 5, 3] 58 | 59 | function findRepeat(arr) { 60 | var result = [], 61 | map = {} 62 | arr.forEach(item => { 63 | if (map[item] === 1) result.push(item) 64 | map[item] = (map[item] || 0) + 1 65 | }) 66 | return result 67 | } 68 | //console.log(findRepeat(arr1)) 69 | 70 | function createArray(len, arr = []) { 71 | if (len > 0) { 72 | arr[--len] = len 73 | createArray(len, arr) 74 | } 75 | return arr 76 | } 77 | 78 | function findDocList(docs, keys) { 79 | let result = [] 80 | docs.forEach(doc => { 81 | keys.forEach(key => { 82 | doc.words.indexOf(key) > -1 && result.push(doc.id) 83 | }) 84 | }) 85 | return result 86 | } 87 | var docs = [ 88 | { 89 | id: 1, 90 | words: ['hello', 'world'] 91 | }, 92 | { 93 | id: 2, 94 | words: ['hello', 'hihi'] 95 | }, 96 | { 97 | id: 3, 98 | words: ['haha', 'hello'] 99 | }, 100 | { 101 | id: 4, 102 | words: ['world', 'nihao'] 103 | } 104 | ] 105 | //console.log(findDocList(docs, ['hello'])) // 文档id1,文档id2,文档id3 106 | //findDocList(docs, ['hello', 'world']) // 文档id1 107 | 108 | class EventEmitter { 109 | constructor() { 110 | this.events = {} 111 | this.onceEvents = {} 112 | } 113 | 114 | on(event, fn) { 115 | let callback = this.events[event] || [] 116 | callback.push(fn) 117 | this.events[event] = callback 118 | } 119 | fire(obj) { 120 | let { type, value } = obj 121 | let handle = function(list) { 122 | for (let i in list) { 123 | if (i === type) { 124 | for (let j in list[i]) { 125 | list[i][j](value) 126 | } 127 | } 128 | } 129 | } 130 | handle(this.events) 131 | handle(this.onceEvents) 132 | } 133 | off(event, fn) { 134 | let callback = this.events[event] 135 | let index = callback.indexOf(fn) 136 | index > -1 && callback.splice(index, 1) 137 | } 138 | once(event, fn) { 139 | let callback = this.onceEvents[event] || [] 140 | callback.push(fn) 141 | this.onceEvents[event] = callback 142 | } 143 | } 144 | 145 | const cycleObj = { 146 | foo: { 147 | name: 'foo', 148 | bar: { 149 | name: 'bar', 150 | baz: { 151 | name: 'baz', 152 | aChild: null 153 | } 154 | } 155 | } 156 | } 157 | cycleObj.foo.bar.baz.aChild = cycleObj.foo 158 | function cycleDetector(obj) { 159 | let res = false 160 | let stack = [] 161 | // 判断是不是在栈中 162 | let inCycle = function(item) { 163 | if (stack.indexOf(item) > -1) { 164 | return true 165 | } 166 | return false 167 | } 168 | // panduan 169 | let read = function(_obj) { 170 | for (let prop in _obj) { 171 | if (Object.prototype.toString.call(_obj[prop]) === '[object Object]') { 172 | if (!inCycle(_obj[prop])) { 173 | stack.push(_obj[prop]) 174 | read(_obj[prop]) 175 | } else { 176 | res = true 177 | } 178 | } 179 | } 180 | } 181 | read(obj) 182 | return res 183 | } 184 | // console.log(cycleDetector(cycleObj)) 185 | 186 | function template(str) { 187 | let replace = {} 188 | var result 189 | var reg = new RegExp('\\<\\%\\=\\w+\\%\\>', 'g') 190 | while ((result = reg.exec(str))) { 191 | let _re = new RegExp('\\w+', 'g') 192 | var item = result[0].match(_re)[0] 193 | replace[item] = {} 194 | replace[item].origin = result[0] 195 | } 196 | return function(obj) { 197 | for (let i in obj) { 198 | if (replace[i]) { 199 | str = str.replace(replace[i].origin, replace[i]) 200 | } 201 | } 202 | return str 203 | } 204 | } 205 | 206 | function parseUrl(url) { 207 | let obj = {} 208 | let index = url.indexOf('?') 209 | if (index > -1) { 210 | let param = url.substring(index + 1, url.length).split('&') 211 | for (let i = 0, l = param.length; i < l; ++i) { 212 | let arr = param[i].split('=') 213 | obj[arr[0]] = arr[1] 214 | } 215 | } 216 | return obj 217 | } 218 | 219 | // crash 220 | class Cash { 221 | constructor(num) { 222 | this.num = num 223 | } 224 | init(args) { 225 | if (args.length === 1) { 226 | if (typeof args[0] === 'number') { 227 | const num = (this.num || 0) + args[0] 228 | 229 | return `${parseInt(num / 100)}元${parseInt(num / 10) % 10}角${num % 230 | 10}分` 231 | } 232 | 233 | if (typeof args[0] === 'object' && args[0] instanceof Cash) { 234 | const num = this.num + args[0].num 235 | 236 | return `${parseInt(num / 100)}元${parseInt(num / 10) % 10}角${num % 237 | 10}分` 238 | } 239 | } 240 | 241 | if (args.length > 1) { 242 | let num = 0 243 | 244 | args.forEach(item => (num += item)) 245 | 246 | return `${parseInt(num / 100)}元${parseInt(num / 10) % 10}角${num % 10}分` 247 | } 248 | } 249 | add(...args) { 250 | return this.init(args) 251 | } 252 | 253 | static add(...args) { 254 | if (args.length === 1) { 255 | if (typeof args[0] === 'number') { 256 | const num = (this.num || 0) + args[0] 257 | 258 | return `${parseInt(num / 100)}元${parseInt(num / 10) % 10}角${num % 259 | 10}分` 260 | } 261 | 262 | if (typeof args[0] === 'object' && args[0] instanceof Cash) { 263 | const num = this.num + args[0].num 264 | 265 | return `${parseInt(num / 100)}元${parseInt(num / 10) % 10}角${num % 266 | 10}分` 267 | } 268 | } 269 | 270 | if (args.length > 1) { 271 | let num = 0 272 | 273 | args.forEach(item => (num += item)) 274 | 275 | return `${parseInt(num / 100)}元${parseInt(num / 10) % 10}角${num % 10}分` 276 | } 277 | } 278 | 279 | valueOf() { 280 | return this.num 281 | } 282 | } 283 | 284 | const cash1 = new Cash(105) 285 | const cash2 = new Cash(66) 286 | const cash3 = cash1.add(cash2) 287 | const cash4 = Cash.add(cash1, cash2) 288 | const cash5 = Cash.add(cash1 + cash2) 289 | console.log(`${cash3}`, `${cash3}`, `${cash3}`) 290 | 291 | function isDuplicate() { 292 | let n = arguments.length 293 | for (let i = 0; i < n; ++i) { 294 | for (let j = i + 1; j < n; ++j) { 295 | if (arguments[i] === arguments[j]) { 296 | return true 297 | } 298 | } 299 | } 300 | return false 301 | } 302 | 303 | // console.log(isDuplicate(1, 2, 3, 2)) 304 | -------------------------------------------------------------------------------- /html&css.md: -------------------------------------------------------------------------------- 1 | ### HTML & CSS 相关知识总结 2 | 3 | > 圣杯布局 4 | 5 | 主要是对负边距的利用。使用负边距,将左右两边的盒子移动到中间盒子的两边。 6 | 7 | ```html 8 |
9 |
10 |
11 |
12 |
13 | ``` 14 | 15 | ```css 16 | .container { 17 | padding: 0 200px; 18 | } 19 | 20 | .container > div { 21 | height: 200px; 22 | float: left; 23 | } 24 | 25 | .middle { 26 | width: 100%; 27 | background: blue; 28 | } 29 | 30 | .left { 31 | margin-left: -100%; 32 | position: relative; 33 | left: -200px; 34 | width: 200px; 35 | background: red; 36 | } 37 | 38 | .right { 39 | margin-left: -200px; 40 | position: relative; 41 | right: -200px; 42 | width: 200px; 43 | background: yellow; 44 | } 45 | ``` 46 | 47 | [一篇关于圣杯布局的文章](https://alistapart.com/article/holygrail) 48 | 49 | > 双飞翼布局 50 | 51 | 这个是淘宝出来的布局。也是利用了负边距,相对于圣杯布局,这里的样式写法好理解点。 52 | 53 | ```html 54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | ``` 62 | 63 | ```css 64 | .container > div { 65 | float: left; 66 | height: 200px; 67 | } 68 | 69 | .middle { 70 | width: 100%; 71 | background: red; 72 | } 73 | 74 | .middle-container { 75 | height: 200px; 76 | margin: 0 200px; 77 | background: darkred; 78 | } 79 | 80 | .left { 81 | width: 200px; 82 | margin-left: -100%; 83 | background: blue; 84 | } 85 | 86 | .right { 87 | width: 200px; 88 | margin-left: -200px; 89 | background: yellow; 90 | } 91 | ``` 92 | 93 | > Flex 布局 94 | 95 | Flex 是 CSS3 中的提供的属性。 96 | 97 | ```html 98 |
99 |
100 |
101 |
102 |
103 | ``` 104 | 105 | ```css 106 | .container { 107 | display: flex; 108 | width: 100%; 109 | } 110 | 111 | .container > div { 112 | height: 200px; 113 | flex: 1; 114 | border: 1px solid red; 115 | } 116 | ``` 117 | 118 | > 使用 Flex 居中一个圆 119 | 120 | ```html 121 | 122 |
123 | 124 | ``` 125 | 126 | ```css 127 | html { 128 | width: 100%; 129 | height: 100%; 130 | } 131 | 132 | body { 133 | display: flex; 134 | justify-content: center; 135 | align-items: center; 136 | margin: 0; 137 | padding: 0; 138 | height: 100%; 139 | } 140 | 141 | .circle { 142 | width: 100px; 143 | height: 100px; 144 | border-radius: 50%; 145 | background: red; 146 | } 147 | ``` 148 | 149 | > 使用 css 实现垂直居中 150 | 151 | 简单的居中大概是可以分为两类的: 152 | 153 | * 文本居中,只需要在他的父标签上指定高度(height)与相同的行高(line-height)即可。 154 | * 标签元素的居中方式比较多,也比较复杂 155 | 156 | ```html 157 |
158 |
159 | 160 |
161 |
162 | ``` 163 | 164 | ```css 165 | html, 166 | body { 167 | margin: 0; 168 | padding: 0; 169 | width: 100%; 170 | height: 100%; 171 | } 172 | 173 | .container { 174 | width: 100%; 175 | height: 100%; 176 | } 177 | 178 | .vertical { 179 | width: 100px; 180 | height: 100px; 181 | background: red; 182 | margin: 0 auto; 183 | position: relative; 184 | top: 50%; 185 | transform: translateY(-50%); 186 | /* 将元素沿 Y 轴方向移动自身的 50% */ 187 | } 188 | ``` 189 | 190 | > CSS 的怪异盒模型 191 | 192 | 由于某些历史遗留问题,CSS 的盒子模型有两种标准:W3C 和 IE 。怪异盒模型主要表现在 IE 上,当不对 Doctype 进行定义时,会触发怪异模式。 193 | 194 | 标准盒模型中,一个 `block` 的总宽度 = `width + margin-left + margin-right + padding-left + padding-right + border-left + border-right` 195 | 而怪异盒模型中,一个 `block` 的总宽度 = `width + margin-left + margin-right` ,`width` 包含了 `padding` 和 `border` ;高度也是一样的区别。在 CSS3 中,box-sizing 指定为 border-box 时,会采用怪异盒计算。 196 | 197 | > BFC 的相关理解 198 | 199 | 块级格式化上下文,容器里面的子元素不会在布局上影响到外面的元素,反之也是如此(按照这个理念来想,只要脱离文档流,肯定就能产生 BFC)。 200 | 201 | 触发 BFC 的条件 202 | 203 | * float 值不为 node 204 | * overflow 值不为 visible 205 | * position 值不为 relative 和 static 206 | * display 值为 table-cell,table-caption, inline-block 中的任何一个 207 | 208 | **相关 IFC** 209 | 内联格式化上下文,IFC 的 line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的 padding/margin 影响)。 210 | 211 | 水平居中:当一个块要在环境中水平居中时,设置其为 inline-block 则会在外层产生 IFC,通过 text-align 则可以使其水平居中。垂直居中:创建一个 IFC,用其中一个元素撑开父元素的高度,然后设置其 vertical-align: middle,其他行内元素则可以在此父元素下垂直居中 212 | 213 | > rem , em 和 px 的区别 214 | 215 | 在 CSS 中,em 和 rem 都属于灵活的单位,都会由浏览器根据实际情况转换成 px 值。 216 | 217 | * 任何浏览器的默认字体高度都是 16px 。当然我们可以通过设置 font-size 强行改变这个高度。 218 | * em 是相对长度单位。相对于当前对象内文本的字体尺寸。em 会继承父级元素的字体大小 219 | * rem 是 CSS3 新增的一个相对长度单位,r 是 root 的缩写。使用 rem 为元素设定字体大小时,仍然是相对大小,但相对的只是 HTML 根元素。 220 | 221 | px 根据不同人理解是不同的。比如说设计师理解的 px 是设备像素,而相对于前端工程师来说 px 是逻辑像素。在 CSS 规范中,长度单位可以分为两类,绝对(absolute)单位以及相对(relative)单位。px 是一个相对单位,相对的是设备像素(device pixel)。 222 | 223 | 要做设备像素与 CSS 像素之间的转换,现来看两个概念: 224 | 225 | * 每英寸像素:ppi,表示每英寸所拥有的像素(pixel)数目,数值越高,代表显示屏能够以越高的密度显示图像 226 | * 设备像素比:根据计算出的 ppi 和基本密度分界,获得默认缩放比例,即设备像素比。(先规定密度分界基数为 160,然后设备像素比=ppi 除以密度分界基数 160) 227 | ![密度分界](https://upload-images.jianshu.io/upload_images/8133-ef1f05dc254add2e.jpg) 228 | 229 | > 移动端布局方案 230 | 231 | 由于移动端的屏幕尺寸的比例不固定,所以布局的时候大多采用的是流式布局。移动端布局需要注意下面几点: 232 | 233 | * 指定好相关的 viewport 234 | * `width`: 设置 `viewport` 的宽度(即之前所提及到的,浏览器的宽度详),这里可以为一个整数,又或者是字符串"width-device" 235 | * `initial-scale`: 页面初始的缩放值,为数字,可以是小数 236 | * `minimum-scale`: 允许用户的最小缩放值,为数字,可以是小数 237 | * `maximum-scale`: 允许用户的最大缩放值,为数字,可以是小数 238 | * `height`: 设置`viewport`得高度(一般不设置) 239 | * `user-scalable`: 是否允许用户进行缩放,'no' 为不允许,'yes' 为允许 240 | 241 | ```html 242 |