-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
148 lines (148 loc) · 37.2 KB
/
search.xml
File metadata and controls
148 lines (148 loc) · 37.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[mobx 初尝试]]></title>
<url>%2F2019%2F07%2F01%2Fmobx-%E5%88%9D%E5%B0%9D%E8%AF%95%2F</url>
<content type="text"></content>
</entry>
<entry>
<title><![CDATA[Array.flat原理实现]]></title>
<url>%2F2019%2F06%2F25%2FArray-flat%E5%8E%9F%E7%90%86%E5%AE%9E%E7%8E%B0%2F</url>
<content type="text"><![CDATA[Array.flat实现1// input [1, 2, [3, 4, [5, 6, 7]], 8] -> output [1, 2, 3, 4, 5, 6, 7, 8]; 1234567891011121314151617181920interface Array<T> { flatInfinity(): T[]; flat(n?: number): T[];}Array.prototype.flatInfinity = function() { const stack = [...this]; const value = []; while (stack.length) { const next = stack.pop(); if (Array.isArray(next)) { stack.push(...next); } else { value.push(next); } } return value.reverse();};Array.prototype.flat = function(n = 1) { return baseFlatten(this, n, []);}; baseFlatternlodash.js12345678910111213141516171819202122232425/** * 将数组拉平 * @param {Array} array 需要拉平数组 * @param {number} depth 数组的深度 * @param {Array} result 初始化将要返回的数组 * @returns {Array} 返回扁平后的数组 */function baseFlatten(array, depth, result) { result || (result = []); if (array == null) { return result; } for (const value of array) { if (depth > 0 && Array.isArray(value)) { if (depth > 1) { baseFlatten(value, depth - 1, result); } else { result.push(...value); } } else { result[result.length] = value; } } return result;}]]></content>
<categories>
<category>Coding</category>
</categories>
<tags>
<tag>JS原理实现</tag>
</tags>
</entry>
<entry>
<title><![CDATA[ES6中import的循环依赖的理解]]></title>
<url>%2F2019%2F06%2F20%2FES6%E4%B8%ADimport%E7%9A%84%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96%E7%9A%84%E7%90%86%E8%A7%A3%2F</url>
<content type="text"><![CDATA[ES6 循环依赖的一个例子a.js 123456import {bar} from './b.js';export function foo() { bar(); console.log('执行完毕');}foo(); b.js 123456import {foo} from './a.js';export function bar() { if (Math.random() > 0.5) { foo(); }} babel-node a.js -> 执行完毕 import {bar} from ‘./b.js’ -> 此时对b模块只是一个引用, 不会去执行 foo() -> bar() -> 执行b import {foo} from ‘./a.js’ -> 指向a.js 中的foo对象 Math.random() > 0.5 条件成立执行 foo 跳到2, 否则 不执行, 直接执行5 执行完毕 ES6处理循环依赖ES6根本不关心会不会发生循环依赖, 只是生产一个指向被加载模块的一个引用, 这和CommonJS不同 上述代码webpack编译后如下: 编译后源码完整123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960(function(modules) { var installedModules = {}; function __webpack_require__(moduleId) { if (installedModules[moduleId]) { return installedModules[moduleId]; } var module = installedModules[moduleId] = { i: moduleId, l: false, exports: {} } modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); module.l = true; return module.exports; } __webpack_require_.d = function(exports, name, getter) { if(!__webpack_require__.o(exports, name)) { Object.defineProperty(exports, name, { enumerable: true, get: getter }); } }; __webpack_require__.r = function(exports) { if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); } Object.defineProperty(exports, '__esModule', { value: true }); }; ... return __webpack_require__(__webpack_require__.s = "./test/index.ts");})({ './test/a.ts': (function(module, __webpack_exports__, __webpack_require__) { __webpack_require__.r(__webpack_exports__); __webpack_require__.d(__webpack_exports__, "foo", function() { return foo; }); var _b__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./b */ "./test/b.ts"); function foo() { Object(_b__WEBPACK_IMPORTED_MODULE_0__["bar"])(); console.log('foo 执行完毕'); } foo(); }), './test/b.ts': (function(module, __webpack_exports__, __webpack_require__) { __webpack_require__.r(__webpack_exports__); __webpack_require__.d(__webpack_exports__, "bar", function() { return bar; }); var _a__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./a */ "./test/a.ts"); function bar() { if (Math.random() > 0.5) { Object(_a__WEBPACK_IMPORTED_MODULE_0__["foo"])(); } } }), './test/index.ts': (function(module, __webpack_exports__, __webpack_require__) { __webpack_require__.r(__webpack_exports__); var _a_ts__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./a.ts */ "./test/a.ts"); })}) 12// 说明当前module是使用ES6模块Object.defineProperty(exports, '__esModule', { value: true }); ES6 输出值的引用a.js 12345678import {foo} from './b';console.log(foo);setTimeout(() => { console.log(foo); import('./b').then(({foo}) => { console.log(foo); });}, 1000); b.js 1234export let foo = 1;setTimeout(() => { foo = 2;}, 500); babel-node a.js -> 1 -> 2 -> 2 编译后源码完整12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152(function(modules) { var installedModules = {}; function __webpack_require__(moduleId) { if (installedModules[moduleId]) { return installedModules[moduleId]; } var module = installedModules[moduleId] = { i: moduleId, l: false, exports: {} } modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); module.l = true; return module.exports; } __webpack_require__.r = function(exports) { if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); } Object.defineProperty(exports, '__esModule', { value: true }); }; ... return __webpack_require__(__webpack_require__.s = "./other/index.ts");})({ './other/a.ts': (function(module, __webpack_exports__, __webpack_require__) { __webpack_require__.r(__webpack_exports__); var _b__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./b */ "./other/b.ts"); console.log(_b__WEBPACK_IMPORTED_MODULE_0__["foo"]); setTimeout(() => { console.log(_b__WEBPACK_IMPORTED_MODULE_0__["foo"]); Promise.resolve(/*! import() */).then(__webpack_require__.bind(null, /*! ./b */ "./other/b.ts")).then(({ foo }) => { console.log(foo); }); }, 1000); }), './other/b.ts': (function(module, __webpack_exports__, __webpack_require__) { __webpack_require__.r(__webpack_exports__); __webpack_require__.d(__webpack_exports__, "foo", function() { return foo; }); let foo = 1; setTimeout(() => { foo = 2; }, 500); }), './other/index.ts': (function(module, __webpack_exports__, __webpack_require__) { __webpack_require__.r(__webpack_exports__); var _a_ts__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./a.ts */ "./other/a.ts"); })}) ES6模块的特性 import 命令会被javascript 引擎静态分析, 先于模块内的其它内容执行 export 命令会有变量声明提前的效果 a.js 12console.log('a.js')import { foo } from './b'; b.js 12export let foo = 1;console.log('b.js 先执行'); 执行结果 12b.js 先执行a.js 参考文章]]></content>
<categories>
<category>Coding</category>
</categories>
<tags>
<tag>Module</tag>
</tags>
</entry>
<entry>
<title><![CDATA[CommonJS中require的循环依赖理解]]></title>
<url>%2F2019%2F06%2F20%2FCommonJS%E4%B8%ADrequire%E7%9A%84%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96%E7%90%86%E8%A7%A3%2F</url>
<content type="text"><![CDATA[Nodejs 官网的一个循环依赖的例子a.js 123456console.log('a starting');exports.done = false;const b = require('./b.js');console.log('in a, b.done = %j', b.done);exports.done = true;console.log('a done'); b.js 123456console.log('b starting');exports.done = false;const a = require('./a.js');console.log('in b, a.done = %j', a.done);exports.done = true;console.log('b done'); index.js 1234console.log('main starting');const a = require('./a.js');const b = require('./b.js');console.log('in main, a.done = %j, b.done = %j', a.done, b.done); 运行node index.js后输出顺序如下 12345678main startinga startingb startingin b, a.done = falseb donein a, b.done = truea donein main, a.done = true, b.done = true CommonJS规范的执行逻辑如下 执行 main.js -> main starting require(‘./a.js’) -> 执行a.js -> a starting exports.done = false -> 内存中针对a模块对应对象 123456{ id: '模块名a', exports: {done: false}, loaded: false, ...} require(‘./b.js’) -> 执行b.js -> a.js代码暂停 -> b starting exports.done = false; -> 内存中针对b模块对应对象 123456{ id: '模块名b', exports: {done: false}, loaded: false, ...} reuqire(‘./a.js’) -> 加载a.js 发生循环依赖 -> a 模块对应的模块对象的exports.done: false -> in b, a.done = false 继续执行b.js exports.done = true -> b done 123456{ id: '模块名b', exports: {done: true}, loaded: true, ...} 继续执行a.js -> in a, b.done = true 执行 a.js exports.done = true -> b done 123456{ id: '模块名a', exports: {done: true}, loaded: true, ...} main.js in main, a.done = true, b.done = true CommonJS规范如何处理循环加载CommonJS模块的重要特性就是加载时执行, 当脚本require时, 就会全部执行; 并且在内存中生成一个对象, 遇到某个模块循环加载, 则只输出已经执行的部分, 还未执行的部分不会输出. reuqire()源码解读http://www.ruanyifeng.com/blog/2015/05/require.html 123456{ id: '模块名', exports: {}, // 以后用到该模块就会取在exports的值, 不会重新执行模块. loaded: true, // 表示该模块的脚本是否执行完毕 ...} webpack 对require 的编译如下: 编译后源码完整1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556(function(modules) { var installedModules = {}; function __webpack_require__(moduleId) { if (installedModules[moduleId]) { return installedModules[moduleId]; } var module = installedModules[moduleId] = { i: moduleId, l: false, exports: {} } modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); module.l = true; return module.exports; } __webpack_require_.d = function(exports, name, getter) { if(!__webpack_require__.o(exports, name)) { Object.defineProperty(exports, name, { enumerable: true, get: getter }); } }; __webpack_require__.r = function(exports) { if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); } Object.defineProperty(exports, '__esModule', { value: true }); }; ... return __webpack_require__(__webpack_require__.s = "./server/index.js");})({ "./server/a.js": (function(module, exports, __webpack_require__) { console.log('a starting'); exports.done = false; const b = __webpack_require__("./server/b.js"); console.log('in a, b.done = %j', b.done); exports.done = true; console.log('a done'); }), "./server/b.js": (function(module, exports, __webpack_require__) { console.log('b starting'); exports.done = false; const a = __webpack_require__("./server/a.js"); console.log('in b, a.done = %j', a.done); exports.done = true; console.log('b done'); }), "./server/index.js": (function(module, exports, __webpack_require__) { console.log('main starting'); const a = __webpack_require__("./server/a.js"); const b = __webpack_require__("./server/b.js"); console.log('in main, a.done = %j, b.done = %j', a.done, b.done); })})]]></content>
<categories>
<category>Coding</category>
</categories>
<tags>
<tag>Module</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Promise实现&Promise/A+规范]]></title>
<url>%2F2019%2F06%2F19%2FPromise%E5%AE%9E%E7%8E%B0-Promise-A-%E8%A7%84%E8%8C%83%2F</url>
<content type="text"></content>
<categories>
<category>Coding</category>
</categories>
<tags>
<tag>JS原理实现</tag>
</tags>
</entry>
<entry>
<title><![CDATA[React Hooks]]></title>
<url>%2F2019%2F06%2F18%2FReact-Hooks%2F</url>
<content type="text"><![CDATA[React Hook 暴露的API12345678910111213import { useCallback, useContext, useEffect, useImperativeHandle, useDebugValue, useLayoutEffect, useMemo, useReducer, useRef, useState, useEvent,} from './ReactHooks';]]></content>
<categories>
<category>Coding</category>
</categories>
<tags>
<tag>React</tag>
</tags>
</entry>
<entry>
<title><![CDATA[中间件]]></title>
<url>%2F2019%2F06%2F17%2F%E4%B8%AD%E9%97%B4%E4%BB%B6%2F</url>
<content type="text"><![CDATA[wiki: 中间件Middleware, 是提供系统软件和应用软件之间连接的软件, 以便软件各部分之间的沟通, 特别是应用软件对于系统软件的集中的逻辑在现代信息技术应用框架如Web服务、面向服务的体系结构中应用广泛、如数据库Apache的Tomcat, IBM的websphere, BEA公司的WebLogic… Express中间件中间件的本质就是一个函数, 在收到请求和返回响应的过程中做一些我们想要做的事情, 见如下描述: 执行任何代码, 修改请求和响应对象 -> 终结请求-> 响应循环 -> 调用堆栈中的下一个中间件 1234567const express = require('express');const app = express();app.use('/user', function(req, res, next) { next();})app.listen(8080) Express中有以下5类中间件 应用级中间件 路由级中间件 错误处理中间件 内置中间件 第三方中间件 Koa中间件 koa-router、koa-compress、koa-respond、kors、koa-convert、koa-bodyparser、koa-compose、koa-static Koa2 基于async\await实现中间件 12345678910function logger(format) { format = format || ':methos ":url"'; return async function logger(ctx, next) { const str = format.replace(':method', ctx.method).replace(':url', ctx.url); console.log(str); await next(); };}app.use(logger());app.use(logger(':method :url')); Koa1 给予Generator 1234567const koa = require('koa');const app = koa();app.use(function*(next) { console.log('before middleware'); yield next; console.log('after middleware');}) Redux 中间件123456789101112131415161718192021222324252627282930// applyMiddlewarefunction compose(...funcs) { if (funcs.length === 0) { return arg => arg; } if (funcs.length === 1) { return funcs[0]; } return funcs.reduce((a, b) => (...args) => a(b(...agrs)))}export default function applyMiddleware(...middlewares) { return createStore => (...args) => { const store = createStore(...args); let dispatch = () => { throw new Error('XXXX'); } const middlewareAPI = { getState: store.getState, dispatch: (...agrs) => dispatch(...args) } const chain = middlewares.map(middleware => middleware(middlewareAPI)); dispatch = compose(...chain)(store.dispatch); return { ...store, dispatch } }} 123456789101112131415const logger = ({getState, dispatch}) => next => action => { console.log('before next ', action); const returnValue = next(action); console.log('after next, getState()); return returnValue;}let store = createStore(reducer, initState, applyMiddleware(logger));/** 现在尝试发送一个 action**/store.dispatch({ type: 'CHANGE_SCORE', score: 0.8})/** 打印:**/// 【logger】即将执行: { type: 'CHANGE_SCORE', score: 0.8 }// 【logger】执行完成后 state: { score: 0.8 } 1234567// redux-thunkconst thunk = ({getState, dispatch}) => next => action => { if (typeof action === 'function') { return action(dispatch, getState); } return next(action);}]]></content>
<categories>
<category>Coding</category>
</categories>
<tags>
<tag>中间件</tag>
</tags>
</entry>
<entry>
<title><![CDATA[理解React Fiber]]></title>
<url>%2F2019%2F06%2F15%2F%E7%90%86%E8%A7%A3React-Fiber%2F</url>
<content type="text"><![CDATA[React 渲染时的问题当组件比较大时, js的执行会占用主线程时间, 此时对动画和手势会出现响应变差.针对这个问题React, 对核心算法 reconciliation 进行了重写 -> Fiber. 以下图片形象说明了stack reconciler 和 fiber reconciler在渲染上的差别 Fiber-tree 是一个单链表树结构在stack reconciler 下React 会创建以下几种实例 fiber reconciler 中会创建以下实例 12345678910111213141516171819202122232425262728DOM 真是DOM节点------effect 每个workInProgress tree 节点上都有一个effect list 用于存放diff的结果 当前节点更新完毕后会向上merge effect list, 此时queue会收集diff结果- - - -workInProgress workInProgress tree是reconcile过程中从fiber tree建立的当前进度快照, 用于断点恢复- - - -filber fiber tree 和 vDOM tree类似, 用来描述增量更新所需的上下文信息, fiber结构如下 { stateNode, // 管理Instance 自身的特性 child, // 当前节点的孩子节点 return, // 表示处理完成后返回结果所要合并的目标, 通常指向父节点 sibling, // 当前节点的下一个兄弟节点 ... }------Elements 描述UI长什么样子 { type, props, children } 参考文章 https://juejin.im/post/5ab7b3a2f265da2378403e57https://juejin.im/entry/5b9885496fb9a05cf3711471https://raphamorim.io/understanding-react-fiber-incremental-rendering-feature/]]></content>
<categories>
<category>Coding</category>
</categories>
<tags>
<tag>React Fiber</tag>
</tags>
</entry>
<entry>
<title><![CDATA[script标签defer vs async]]></title>
<url>%2F2019%2F06%2F14%2Fscript%E6%A0%87%E7%AD%BEdefer-vs-async%2F</url>
<content type="text"><![CDATA[如果async为true,那么脚本在下载完成后异步执行。 如果async为false,defer为true,那么脚本会在页面解析完毕之后执行。 如果async和defer都为false,那么脚本会在页面解析中,停止页面解析,立刻下载并且执行 浏览器处理script <script src="a.js"></script> 浏览器会立即加载并执行脚本, 也就是在渲染该script标签之下的文档元素前, 就会加载并之下. <script src="b.js" defer></script> defer 加载后续文档元素的过程和加载b.js 并行进行(异步), 但是b.js的之下会在所有元素解析完成之后, DOMContentLoaded 事件触发之前完成. DOMContentLoaded事件是当初始的HTML文档被完全加载和解析完成之后触发, 不需要等待样式表、图像和子框架的加载完成. <script src="c.js" async></script> async 加载和渲染后续文档元素的过程将和c.js的加载和执行并行进行(异步) 相同点: defer&async 在网络下载的过程时一样的, 都是异步的(和HTML解析). 执行时间不同: 下载到执行的时间不同 执行顺序不同: defer会按照加载顺序执行, async的执行会乱序]]></content>
<categories>
<category>Coding</category>
</categories>
<tags>
<tag>JS基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[CSS元素居中]]></title>
<url>%2F2019%2F06%2F13%2FCSS%E5%85%83%E7%B4%A0%E5%B1%85%E4%B8%AD%2F</url>
<content type="text"><![CDATA[居中 单行文本水平垂直&垂直居中: text-align、line-height, 元素必须有高度 123456789101112<style> .single-text { height: 50px; line-height: 50px; background: #000; color:#ee00ff; text-align: center; }</style><div class="single-text"> 这里是一个单行文本</div> 多行文本垂直居中: display: table-cell 、vertical-align: middle; 12345678910111213<style> .mult-text { width: 400px; height: 90px; background: #ee00ff; color: #000; display: table-cell; vertical-align: middle; }</style><div class="mult-text"> 多行文本垂直居中: display: table-cell 、vertical-align: middle;</div> div 垂直居中: 父元素 display: table-cell; vertical-align: middle; 1234567891011121314<style> .table-parent { background: #000; height: 60px; display: table-cell; color: #fff; vertical-align: middle; }</style><div class="table-parent"> <div class="table"> display: table-cell; </div></div> div水平垂直居中: 父元素relative. 子元素: absolute left right bottom top : 9; margin: auto; 子元素有高度、宽度 1234567891011121314151617181920212223<style> .relative-parent { background: #000; height: 90px; color: #fff; position: relative; } .position-child { position: absolute; height: 50px; width: 300px; left: 0; right: 0; bottom: 0; top: 0; margin: auto; }</style><div class="relative-parent"> <div class="position-child"> 父元素relative. 子元素: absolute; </div></div> 绝对定位&负值, 子元素有宽度、高度 123456789101112131415161718192021<style> .parent { position: relative; width: 200px; height: 200px; background-color: #e0ffee; } .child { position: absolute; width: 100px; height: 100px; top: 50%; margin-top: -50px; left: 50%; margin-left: -50px; background: #2545c4; }</style><div class="parent"> <div class="child"></div></div> 同上.绝对定位&translate 子元素不需要有宽度、高度 123456789101112131415161718192021<style> .parent-translate { position: relative; width: 200px; height: 200px; background-color: #e0ffee; } .child-translate { position: absolute; background: #2545c4; left: 50%; top: 50%; padding: 20px; transform: translateX(-50%) translateY(-50%); }</style><div class="parent-translate"> <div class="child-translate"> 哈哈这里是一个子元素哦哦哦哦 </div></div> div水平垂直居中: 负元素dispaly: flex; juestify-content: center; align-item: center; 12345678910111213141516171819<style> .flex-parent { background: #2545c4; height: 90px; color: #fff; display: flex; justify-content: center; align-items: center; } .flex-child { background-color: #e0ffee; }</style><div class="flex-parent"> <div class="flex-child"> 父元素flex </div></div>]]></content>
<categories>
<category>Coding</category>
</categories>
<tags>
<tag>CSS技巧</tag>
</tags>
</entry>
<entry>
<title><![CDATA[CSS-渲染原理二 渲染层合并 Composite]]></title>
<url>%2F2019%2F06%2F12%2FCSS-%E6%B8%B2%E6%9F%93%E5%8E%9F%E7%90%86%E4%BA%8C-%E6%B8%B2%E6%9F%93%E5%B1%82%E5%90%88%E5%B9%B6%20Composite%2F</url>
<content type="text"><![CDATA[https://juejin.im/entry/59dc9aedf265da43200232f9 上一篇描述了针对transform、opacity动画化的元素, 浏览器会提升一个合成层来优化渲染以下几点也会导致浏览器将元素提升到一个新的渲染层. position: fixed; 直接是一个新的Layer will-change: transform, opacity; 两者设置其一就会形成 一个新的Layer translation: transform || opacity || translate3d || filter || backdrop-filter; 需要结合对transform 值的改变才会形成. video, canvas, iframe; 直接是一个新的Layer; perspective 元素A有一个z-index 比自己小的元素B, B是一个合成层(即该元素在复合层上渲染), 则A会提升位合成层(composite隐式合成); 渲染层合并 Composite http://taobaofed.org/blog/2016/04/25/performance-composite/http://jartto.wang/2017/08/28/how-to-optimize-marker-of-AMap/https://www.cnblogs.com/feng9exe/p/10907959.html DOM树中每个节点都会对应一个LayoutObject, 处于相同坐标空间的LayoutObject会形成一个RenderLayers(渲染层); RenderLayers保证页面元素以正确的顺序合成, 此时就会出现层合成(composite), 以正确的处理透明元素和重叠元素 ReanderLayers & GraphicsLayers 上图 RenderLayers渲染层,是负责对应DOM子树 GraphicsLayers图形层, 是负责对应RenderLayers子树 RenedrObjects保持树结构, RenderObject记录如何绘制一个node的内容, RenderObject 通过向绘图上下文(GraphicsContext)发出绘制来绘制nodes GraphicsLayer有一个GraphicsContext, GraphicsContext负责输出该层的位图, 位图存储在共享内存中, 作为纹理上传到GPU 最后有GPU将多个位图合成, draw到屏幕上 某些特殊的渲染层会被认为是合成层(Compositing Layers), 合成层拥有单独的GraphicsLayers. 其他非合成层的渲染层, 会和其第一个拥有GraphicsLayer父层公用一个. composite 隐式合成一个或多个非合成元素如果出现在堆叠顺序上的合成元素之上, 则会提升到合成层, 即被绘制成分离的图像, 将图像交给GPU处理.例如: 元素A有一个z-index 比自己小的元素B, B是一个合成层(即该元素在复合层上渲染), 则A会提升位合成层(composite隐式合成); 层压缩&层爆炸浏览器为处理大量合成层的情况, 会对层做Layer Squashing(层压缩)的处理, 但是以下情况无法进行层压缩. 当有大量额外的合成层时, 会出现层爆炸的现象. 无法进行会打破渲染顺序的压缩 video元素的渲染层无法被压缩同时也无法将别的渲染层压缩到video所在的合成层上 iframe、plugin的渲染层无法被压缩同时也无法将别的渲染层压缩到iframe所在的合成层上 无法压缩有reflection属性的渲染层 无法压缩有blend mode属性的渲染层 当渲染层和合成层有不同的裁减容器时, 该渲染层无法压缩 相对于合成层滚动的渲染层无法被压缩 当渲染层和合成层有不同的具有opacity的祖先层(一个设置了opacity < 1, 一个没有设置opacity也算不同), 该渲染层无法压缩 当渲染层和合成层有不同的具有transform的祖先层时, 该渲染层无法压缩 当渲染层和合同层有不同的具有filter的祖先层时, 该渲染层无法压缩 当覆盖的合成层正在运行动画时, 该渲染层无法压缩, 当动画未开始或者运行完毕以后, 该渲染层才可以被压缩]]></content>
<categories>
<category>Coding</category>
</categories>
<tags>
<tag>CSS优化</tag>
</tags>
</entry>
<entry>
<title><![CDATA[CSS 渲染原理一渲染过程]]></title>
<url>%2F2019%2F06%2F11%2FCSS-%E6%B8%B2%E6%9F%93%E5%8E%9F%E7%90%86%E4%B8%80-%E6%B8%B2%E6%9F%93%E8%BF%87%E7%A8%8B%2F</url>
<content type="text"><![CDATA[如下代码 浏览器执行了handleResize、handleClick分别干了什么事情123456789101112131415161718<style> ele3 { // will-change: transform; 和 translateZ(0) 效果雷同 translation: transform 0.2s; }<style>window.addEventListener('resize', handleResize)window.addEventListener('click', handleClick)function handleResize() { ele1.style.width = xxx; ele1.style.height = yyy; ele2.style.color = '#fff';}function handleClick() { ele3.style.transform = 'translateX(1000px)';} 浏览器渲染文档的步骤 https://developers.google.com/web/fundamentals/performance/critical-rendering-path/render-tree-construction?hl=zh-cn 处理HTML标记并构建DOM树 处理CSS标记并构建CSSOM树 合并DOM&CSSOM 为一个渲染树RenderTree, display: none 的元素并不会出现在RenderTree 根据RenderTree, 布局(计算节点在页面上的大小和位置) 将节点绘制到屏幕 渲染层合并(见下篇文章) 回到上述问题, resize时浏览器干了啥? 浏览器窗口resize时, 执行上述步骤 1 -> 4 确定哪些元素需要Reflow, 或者Repaint: ele1元素需要Reflow, ele2需要Repaint 浏览器执行Reflow -> Repaint, 这个过程浏览器会使用批处理优化 click的时候浏览器干了啥? https://developers.google.com/web/fundamentals/design-and-ux/animations/animations-and-performance css translation动画, 会将transform || opacity 的元素提升一个复合层. will-change: transform || opacity; 会告诉浏览器在做更新前就使用最合适的优化, 进行高度优化. 也会将元素强行提升至一个复合层. 使用js做动画时, js必须在动画的每一帧计算元素的状态, 发送给GPU, 不会将元素提升至一个复合层, 想让元素提升至复合层, 必须使用translateZ或者will-change: transform, opacity; 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748// 方式一: CSS动画: 设置以下CSS样式, hover时动画时, 查看Chrome Console 的Layers, 会有两个layer层;<div class='bg-change'></div><style> .bg-change { width: 100px; height: 100px; background: red; translation: transform 20s; } .bg-change:hover { transform: translateX(1000px); }</style>// 方式二 和 方式一样都会产生两个layer<div class="bg-change-2"></div><style> .bg-change-2 { width: 100px; height: 100px; background: red; /* transition: transform 20s; */ }</style><script> const dom = document.body.querySelector('.bg-change-2'); dom.onclick = function() { this.style.transform = 'translateX(1000px)'; }</script> // 方式三: 通过js修改样式, requestAnimationFrame 过程中, 如果不加translateZ(0) 只会有一个Layer, translateZ(0) 才能强制的将元素提升一个复合层;<div class="bg-change"></div><style> .bg-change { width: 100px; height: 100px; background: red; }</style><script> var bg = document.querySelector('.bg-change'); var distance = 0; requestAnimationFrame(function animate(){ bg.style.transform = 'translateX(' + ++distance + 'px) translateZ(0)'; requestAnimationFrame(animate); })</script> 先了解下浏览器的回流和重绘 浏览器使用流式布局模型(Flow Based Layout) 由于浏览器使用流式布局, 对Render Tree的计算通常遍历一次即可, 遇到table或者内部元素通常要花3倍等同元素, 因此避免使用table布局 回流一定会导致重绘, 重绘未必会引起回流 回流 Reflow https://csstriggers.com/ Render Tree 中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程成为回流。导致回流的操作: 页面首次渲染 浏览器窗口大小改变 元素尺寸或位置发生改变 元素内容变化(文字数量或者图片大小等等) 元素字体大小变化 添加或删除可见的DOM元素 激活CSS伪类(:hover) 查询某些属性或调用某些方法 导致回流的属性和方法 clientWidth、clientHeight、clientTop、clientLeft offsetWidth、offsetHeight、offsetTop、offsetLeft scrollWidth、scrollHeight、scrollTop、scrollLeft scrollIntoView()、scrollIntoViewIfNeeded() getComputedStyle() getBoundingClientRect() scrollTo() 重绘 Repaint当页面中元素样式的改变并不影响它在文档流中的位置时 (例如: color、background-color、visibility …), 浏览器会将新样式赋予给元素并重新绘制。 浏览器的优化会维护一个队列, 引起回流和重绘的操作放在队列中, 达到一定的阀值会清空队列, 进行一次批处理, 多次合成一次. 如果访问以下属性或方法, 浏览器会立刻清空队列, 确保你拿到的值是最精确的 clientWidth、clientHeight、clientTop、clientLeft offsetWidth、offsetHeight、offsetTop、offsetLeft scrollWidth、scrollHeight、scrollTop、scrollLeft width、height getComputedStyle() getBoundingClientRect() 避免回流或重绘CSS 避免使用table布局 尽量在DOM树的最末端改变class 避免设置多层内联样式 将动画应用在position属性位absolute或fixed的元素 避免使用css表达式: calc() JS 避免频繁操作样式, 一次性重写style, 通过把样式写在class中, 一次性更改class属性 避免频繁操作DOM, 创建一个documentFragment, 在其上应用所有DOM操作, 之后再把dF添加到文档中 先将元素设置display: none, 操作结束后再显示出来: display: none的元素进行DOM操作不会引发回流和重绘 避免频繁读取会引起回流/重绘的属性, 如需多次使用, 使用变量缓存起来 复杂动画的元素使用绝对定位, 使其脱离文档流. 避免引起父元素和后续元素的频繁回流]]></content>
<categories>
<category>Coding</category>
</categories>
<tags>
<tag>CSS优化</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Async/Await 实现原理]]></title>
<url>%2F2019%2F06%2F10%2FAsync-Await-%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86%2F</url>
<content type="text"><![CDATA[Async是什么 Async 实现原理Async 是对Generator的一个升级 12345678910111213141516171819202122232425262728293031323334353637383940414243async function fn(args) { // ...awiat}// 等价于function fn(args) { return spawn(function* () { });}function spawn(genF) { return new Promise(function(resolve, reject) { const gen = genF(); function step(nextF) { let next; try { next = nextF(); } catch (e) { return reject(e); } if (next.done) { return resolve(next.value); } Promise.resolve(next.value) .then(function(v) { step(function() { return gen.next(v); }) }, function(v) { step(function() { return gen.throw(e); }) }); } step(function() { return gen.next(undefined); }); })} Babel 如何对Async 做的编译 参考抽象语法树1: https://juejin.im/post/5c8d3c48f265da2d8763bdaf#heading-12参考文档2: https://segmentfault.com/a/1190000015653342#articleHeader24 Babel 通过Babylon(Babel parser) 将async经过词法分析&语法分析后输出AST(抽象语法树) 结构如下 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687async function fn(args) { const t = await 1; return t;}// 可以通过esprima 分析工具查看: http://esprima.org/demo/parse.html#// 所有节点都会实现以下接口interface Node { type: string; range?: [number, number]; loc? SourceLocation;}interface SourceLocation { start: Position; end: Position; source?: string | null;}interface Position { line: uint32 >= 1; column: uint32 >= 0;}// type: FunctionDeclaration 函数声明的抽象树实现以下接口interface FunctionDeclaration { type: 'FunctionDeclaration'; id: Identifier || null; params: FunctionParameter[]; body: BlockStatement; generator: boolean; async: boolean; expression: false;}// AST 结构{ type: 'Program', sourceType: 'script', body: [ { type: 'FunctionDeclaration', id: { type: 'Identifier', name: 'fn' }, params: [ { type: 'Identifier', name: 'args' } ], body: { type: 'BlockStatement', body: [ { type: 'VariableDeclaration', declarations: [ { type: 'Identifier', name: 't } ], init: { type: 'AwaitExpression', argument: { type: 'Literal', value: 1, raw: 1 } }, kind: 'const' }, { type: 'ReturnStatement', 'argument': { type: 'Identifier', name: 't' } } ] }, generator: false, expression: false, async: true } ]} 根据 type: ‘FunctionDeclaration’, async: true 将函数转成Generator函数 将Generator函数AST, 编译成ES5代码]]></content>
<categories>
<category>Coding</category>
</categories>
<tags>
<tag>JS原理实现</tag>
</tags>
</entry>
<entry>
<title><![CDATA[First Day]]></title>
<url>%2F2019%2F06%2F09%2FFirst-Day%2F</url>
<content type="text"><![CDATA[经历 百度大数据部-数据科学平台前端开发, 主要负责平台数据可视化相关业务设计&开发 海云数据-数据可视化平台产品设计&开发]]></content>
<tags>
<tag>work</tag>
</tags>
</entry>
</search>