├── .gitignore ├── chrome-devtools.md ├── as_usage.md ├── centos.md ├── wechatapp.md ├── prettier.md ├── webpack.md ├── mobx └── README.md ├── vsc_usage.md ├── typescript_tips.md ├── vue_notebook ├── build_challenge.md ├── lifecycle_sequence.md ├── points.md ├── data-element-binding.md └── template_analysis_a5e27b1.md ├── webstorm_shortcut.md ├── linux ├── qa.md └── linux_cmd.md ├── web_attack.md ├── chrome ├── chrome_usage.md └── ext │ └── reading-octe-mate-source-code.md ├── ali-hooks.md ├── css └── flex.md ├── awesome_articles.md ├── css.md ├── intensive_read_understandinges6 └── README.md ├── code_usage.md ├── git_usage.md ├── ts_blog └── 4.3_beta_wip.md ├── ts └── howto-tips.md ├── atom_keybinding.md ├── mac_usage.md ├── nodejs └── module_resolve_algorithm.md ├── read_webpack_src └── README.md ├── vite └── read_src_commit_by_commit.md ├── read_parcel_src └── README.md ├── read_vuepress_src └── README.md └── read_vue3 └── 00001-3401f6.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode -------------------------------------------------------------------------------- /chrome-devtools.md: -------------------------------------------------------------------------------- 1 | ## debug 方法 2 | 3 | 如果想在控制台对某个方法入口打断点,可以使用 debug(方法变量) 4 | -------------------------------------------------------------------------------- /as_usage.md: -------------------------------------------------------------------------------- 1 | ## Gradle sync 失败 2 | 3 | 第一次打开 as,手贱设置了 http proxy?,去 gradle.properties 干掉没用的 http proxy 配置 4 | -------------------------------------------------------------------------------- /centos.md: -------------------------------------------------------------------------------- 1 | ### 怎样修改 CentOS 7 SSH 端口 2 | [怎样修改 CentOS 7 SSH 端口](https://sebastianblade.com/how-to-modify-ssh-port-in-centos7/) 3 | -------------------------------------------------------------------------------- /wechatapp.md: -------------------------------------------------------------------------------- 1 | 样式坑: 2 | 3 | - margin 负数只能偏移无法拉伸,例如 margin: -10px 无法把元素尺寸外扩 10px 4 | - cover-view 内的文字,偶尔尾巴被切掉1像素,添加「全角」空格,一个字体宽度,然后增加或减少边距(margin) 5 | -------------------------------------------------------------------------------- /prettier.md: -------------------------------------------------------------------------------- 1 | - [Your last ESLint config – Tomasz Netczuk – Medium](https://medium.com/@netczuk/your-last-eslint-config-9e35bace2f99) 2 | 3 | `eslint-config-prettier` 关闭跟 prettier 冲突的 eslint 规则 4 | -------------------------------------------------------------------------------- /webpack.md: -------------------------------------------------------------------------------- 1 | ## 希望样式中,图片路径查找是基于入口 CSS 文件的路径 2 | 3 | 使用 [bholloway/resolve-url-loader: Webpack loader that resolves relative paths in url() statements based on the original source file](https://github.com/bholloway/resolve-url-loader) 4 | -------------------------------------------------------------------------------- /mobx/README.md: -------------------------------------------------------------------------------- 1 | ## map 依赖响应粒度 2 | 3 | ``` 4 | // in store 5 | @observable private map = observable.map(); 6 | ... 7 | map.set('name', 'xxx') 8 | 9 | 10 | 11 | const Comp = () => { 12 | map; // 不 rerender 13 | } 14 | 15 | const Comp = () => { 16 | map.get('name'); // rerender 17 | } 18 | ``` 19 | -------------------------------------------------------------------------------- /vsc_usage.md: -------------------------------------------------------------------------------- 1 | # VSC 2 | 3 | - 禁止输入`.`后自动联想输入 4 | 5 | "editor.acceptSuggestionOnCommitCharacter": false 6 | 7 | [reactjs - Visual Studio Code autocomplete when I press "." key. Any way to disable? - Stack Overflow](https://stackoverflow.com/questions/48609354/visual-studio-code-autocomplete-when-i-press-key-any-way-to-disable) 8 | -------------------------------------------------------------------------------- /typescript_tips.md: -------------------------------------------------------------------------------- 1 | ## 参数是一个类 2 | 3 | ```ts 4 | // 1 5 | export function foo( 6 | clazz: new (...args: any[]) => T 7 | ): void 8 | 9 | // 2 10 | export function foo( 11 | object: T 12 | ): void 13 | 14 | class Person {} 15 | const obj = {} 16 | 17 | foo(Person, {}) // #1 18 | foo(obj, {}) // #2 19 | ``` 20 | -------------------------------------------------------------------------------- /vue_notebook/build_challenge.md: -------------------------------------------------------------------------------- 1 | # 编译困难 2 | 3 | 看 vue 最开始提交的代码,使用了 grunt + component 配合打包,你可能安装完相应库后发现奇怪的错误, 例如 4 | 5 | Fatal error: failed to lookup "seed"'s dependency "component-emitter" 6 | 7 | 这是因为 grunt 或者 component 版本不对,找到 vue 提交的日期,安装对应版本的 grunt 和 component (特别是component),参考 https://github.com/componentjs/component/releases?after=0.17.5 8 | 9 | npm install -g component@0.16.3 10 | 11 | -------------------------------------------------------------------------------- /webstorm_shortcut.md: -------------------------------------------------------------------------------- 1 | 导航 | 功能 2 | -------------------------------- | -------------------------------------------------- 3 | `Alt+F1` (select 1.Project View) | 编辑文件到导航文件树 4 | `F4` | 从导航文件树到编辑文件 5 | 6 | 7 | 窗口 | 功能 8 | -------------------------------- | -------------------------------------------------- 9 | `CMD+1` | 显示/隐藏 项目 窗口 10 | `CMD+9` | 显示/隐藏 版本控制 窗口 11 | -------------------------------------------------------------------------------- /linux/qa.md: -------------------------------------------------------------------------------- 1 | ## 怎么测网速 2 | 3 | raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py 4 | 5 | ## 如何让公网访问服务器转发到本地 pc 的端口 6 | 7 | 在 pc 端使用 ssh 命令,让远端服务器的端口映射到本地 pc 的端口 8 | 9 | [系统运维|如何通过反向 SSH 隧道访问 NAT 后面的 Linux 服务器](https://linux.cn/article-5975-1.html) 10 | [ip - How to make ssh tunnel open to public? - Super User](https://superuser.com/questions/588591/how-to-make-ssh-tunnel-open-to-public#) 11 | 12 | ## 查端口进程 13 | 14 | lsof -i :8080 15 | -------------------------------------------------------------------------------- /web_attack.md: -------------------------------------------------------------------------------- 1 | [owasp Category:Attack](https://www.owasp.org/index.php/Category:Attack) 2 | 3 | ## HTTP Response Splitting 4 | 5 | - [owasp HTTP_Response_Splitting](https://www.owasp.org/index.php/HTTP_Response_Splitting) 6 | - [再看 HTTP Response Splitting 攻击](http://huaidan.org/archives/2619.html) (`案例`) 7 | 8 | ## DNS Spoofing 9 | 10 | - [wiki DNS_spoofing](https://en.wikipedia.org/wiki/DNS_spoofing) 11 | - [DNS 缓存污染](http://ichuan.net/post/7/dns-cache-poisoning-in-china/) 12 | - [DNS缓存服务器投毒](https://www.91ri.org/2964.html) 13 | -------------------------------------------------------------------------------- /chrome/chrome_usage.md: -------------------------------------------------------------------------------- 1 | ## inspect 打开后一片空白 2 | 3 | - 打开 chrome://appcache-internals/# 移除缓存 4 | - 保证浏览器能访问 chrome-devtools-frontend.appspot.com (翻墙) 5 | 6 | ## 自动化测试时,自动打开的浏览器提示「...正在被自动化软件控制」导致无法继续 7 | 8 | - [Chrome Setup · nightwatchjs/nightwatch Wiki](https://github.com/nightwatchjs/nightwatch/wiki/Chrome-Setup) 9 | - [Python使用Splinter(Selenium)进行浏览器模拟测试 | 豪翔天下](https://haofly.net/python-splinter-selenium/) 10 | - [List of Chromium Command Line Switches « Peter Beverloo](http://peter.sh/experiments/chromium-command-line-switches/) 11 | 12 | 启动 chrome 时设置 --disable-infobars 参数 13 | 14 | ## 显示扩展应用的图标 15 | 16 | 在地址栏后面有个隐藏的 drag bar,往左拖动可以显示扩展的图标 17 | -------------------------------------------------------------------------------- /vue_notebook/lifecycle_sequence.md: -------------------------------------------------------------------------------- 1 | # 生命周期钩子执行顺序 2 | 3 | ``` 4 | App 5 | / \ 6 | A B 7 | /\ 8 | A-1 A-2 9 | ``` 10 | App mount 之后销毁 App,打印所有生命周期钩子顺序 11 | 12 | ``` 13 | before create A 14 | create A 15 | before mount A 16 | 17 | before create A-1 18 | create A-1 19 | before mount A-1 20 | 21 | before create A-2 22 | create A-2 23 | before mount A-2 24 | 25 | before create B 26 | create B 27 | 28 | before mount B 29 | 30 | mount A-1 31 | mount A-2 32 | mount A 33 | mount B 34 | 35 | before destroy A 36 | before destroy A-1 37 | destroy A-1 38 | 39 | before destroy A-2 40 | destroy A-2 41 | destroy A 42 | 43 | before destroy B 44 | destroy B 45 | ``` 46 | -------------------------------------------------------------------------------- /ali-hooks.md: -------------------------------------------------------------------------------- 1 | # ali-hooks阅读笔记 2 | 3 | ## ****useMemoizedFn**** 4 | 5 | useMemoizedFn( fn, [dep1,…]) 6 | 7 | ### 场景: 8 | 9 | 在使用 useCallback 来保证一个函数 prop 的地址不变,避免无谓重复渲染,当遇到需要 deps 有变化的时候,useCallback 也会返回新的地址,保证函数内引用最新的 deps。 10 | 11 | useMemoizedFn 就是保证返回的地址不变,然后函数内也能访问到最新的 state。 12 | 13 | ### 实现: 14 | 15 | 1)考虑返回地址不变,那么 hook 返回的需要一个 Ref 值 16 | 17 | 2)Ref 的 current 只赋值一次,为一个函数,函数内再去调用另一个 Ref 18 | 19 | 3)内 Ref 函数则在组件函数内每次都设置为最新的函数,保证闭包访问最新的 state 20 | 21 | 总结:一个ref保证返回地址不变,另一个ref可以保证持续访问到最新的函数引用(从而访问最新的闭包内容) 22 | 23 | ## ****useCreation**** 24 | 25 | useMemoizedFn( factoryFn, [dep1,…]) 26 | 27 | ### 场景: 28 | 29 | 等同于语义上的 useMemo,React 文档提到,不能完全依赖 useMemo 语义上的意思来做性能优化,未来有可能会重新计算,例如处理离开屏幕的组件,重新回到屏幕之后做计算。另外比起 useRef 还有一个优势,useRef( factoryFn) 传入一个构造器,其实每次都会执行构造器,只是会把后续传入的值忽略,相当于是 `const a = factoryFn(); useRef(a)` 30 | 31 | ### 实现: 32 | 33 | 1)useRef 一个引用对象,存放三个属性:返回值、是否初始化、依赖数组 34 | 35 | 2)重新计算时机:首次初始化,或者依赖有变化时 36 | -------------------------------------------------------------------------------- /vue_notebook/points.md: -------------------------------------------------------------------------------- 1 | ## 判断一个函数是否通过 new 执行 2 | 3 | ``` 4 | function Vue (options) { 5 | if (process.env.NODE_ENV !== 'production' && 6 | !(this instanceof Vue) 7 | ) { 8 | warn('Vue is a constructor and should be called with the `new` keyword') 9 | } 10 | this._init(options) 11 | } 12 | ``` 13 | 14 | ## 运行时的性能监控 15 | 16 | [Performance - Web API 接口参考 | MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Performance) 17 | 18 | ```javascript 19 | Vue.prototype.$mount = function ( 20 | el?: string | Element, 21 | hydrating?: boolean 22 | ): Component { 23 | //... 24 | mark('compile') 25 | const { render, staticRenderFns } = compileToFunctions(template, { 26 | //... 27 | mark('compile end') 28 | measure(`vue ${this._name} compile`, 'compile', 'compile end') 29 | //... 30 | } 31 | 32 | 33 | const perf = inBrowser && window.performance 34 | if ( 35 | perf && 36 | perf.mark && 37 | perf.measure && 38 | perf.clearMarks && 39 | perf.clearMeasures 40 | ) { 41 | mark = tag => perf.mark(tag) 42 | measure = (name, startTag, endTag) => { 43 | perf.measure(name, startTag, endTag) 44 | perf.clearMarks(startTag) 45 | perf.clearMarks(endTag) 46 | } 47 | } 48 | 49 | 50 | ``` 51 | -------------------------------------------------------------------------------- /css/flex.md: -------------------------------------------------------------------------------- 1 | flex 杂记 2 | --- 3 | 4 | [CSS Flexible Box Layout - CSS: Cascading Style Sheets | MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout) 5 | 6 | ## container (默认 row) 7 | 8 | align-content 多行,纵轴方向布局(对 flex-wrap:nowrap 无效) 9 | space-between / space-around / space-evenly 区别: [圖解 Flexbox 基本屬性 | Summer。桑莫。夏天](https://cythilya.github.io/2017/04/04/flexbox-basics/) 10 | 在任意属性值后面跟上 safe 可以确保元素不会溢出容器 11 | 12 | align-items 单行 13 | align-self 当 align-items 是对容器所有元素在 cross axis 上的布局,align-self 则设置某个 item 14 | 15 | xx-items 作用在容器内所有元素上 16 | xx-content 作用在容器上,所有元素当作整体 17 | xx-self 作用在具体单个元素 18 | 三类的区别参考里面的图 [css3 - The difference between justify-self, justify-items and justify-content in CSS Grid - Stack Overflow](https://stackoverflow.com/questions/48535585/the-difference-between-justify-self-justify-items-and-justify-content-in-css-gr?answertab=active#tab-top) 19 | 20 | 21 | justify-content 主轴元素排列 22 | 23 | ## item 24 | 25 | flex = flex-grow flex-shrink flex-basis 26 | 27 | flex-basis 设置了绝对值,则优先于主轴方向的尺寸 (width / height),决定盒子模型 content-box 的尺寸 28 | [前端 - flex设置成1和auto有什么区别 - SegmentFault 思否](https://segmentfault.com/q/1010000004080910) 29 | 30 | flex-flow = flex-direction flex-wrap 31 | 32 | 33 | -------------------------------------------------------------------------------- /awesome_articles.md: -------------------------------------------------------------------------------- 1 | ## 异步 2 | 3 | - [Tasks, microtasks, queues and schedules - JakeArchibald.com](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/) 4 | - [HTML Standard](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#microtask-queuing) 5 | - [What the heck is the event loop anyway? | Philip Roberts | JSConf EU - YouTube](https://www.youtube.com/watch?v=8aGhZQkoFbQ) 6 | - [Node.js design pattern : Reactor (Event Loop) - 掘金](https://juejin.im/post/5a5e03eef265da3e5033c5b9) 7 | - [The Node.js Event Loop, Timers, and process.nextTick() | Node.js](https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/) 8 | - [Node和浏览器之事件循环/任务队列/异步顺序/数据结构 - 彭湖湾 - 博客园](https://www.cnblogs.com/penghuwan/p/11699164.html) 9 | - [用一道大厂面试题带你搞懂事件循环机制 - 程序员成长指北 - SegmentFault 思否](https://segmentfault.com/a/1190000020159229) 10 | - [浅析Node.js的Event Loop - 动机在未来 - 博客园](https://www.cnblogs.com/yzfdjzwl/p/8182749.html) 11 | - [深入理解js事件循环机制(浏览器篇) - lynnelv's blog](http://lynnelv.github.io/js-event-loop-browser) 12 | 13 | ## 数字存储 14 | 15 | - [前端应该知道的JavaScript浮点数和大数的原理 - 知乎](https://zhuanlan.zhihu.com/p/66949640) 16 | - [JavaScript 浮点数陷阱及解法 · Issue #9 · camsong/blog](https://github.com/camsong/blog/issues/9) 17 | - [浮点数的二进制表示 - 阮一峰的网络日志](http://www.ruanyifeng.com/blog/2010/06/ieee_floating-point_representation.html) 18 | 19 | ## HTTP 20 | 21 | - [专题 | JerryQu 的小站](https://imququ.com/post/series.html) 22 | 23 | ## WebRTC 24 | 25 | - [Real time communication with WebRTC](https://codelabs.developers.google.com/codelabs/webrtc-web/) 26 | - [Real time communication with WebRTC](https://codelabs.developers.google.com/codelabs/webrtc-web/#0) 27 | -------------------------------------------------------------------------------- /css.md: -------------------------------------------------------------------------------- 1 | ### flex:1 下的文字无法 ellipsis/overflow:scroll 2 | 3 | 上层的所有 flex:1 带上 min-width:0(ellipsis) min-height:0(overflow:scroll) 4 | 5 | refers: 6 | - [javascript - text-overflow: ellipsis and flex - Stack Overflow](https://stackoverflow.com/questions/39472747/text-overflow-ellipsis-and-flex) 7 | - [html - Text-overflow: ellipsis causes flex item to overflow flex container - Stack Overflow](https://stackoverflow.com/questions/41286912/text-overflow-ellipsis-causes-flex-item-to-overflow-flex-container) 8 | - [CSS Flexible Box Layout Module Level 1](https://www.w3.org/TR/css-flexbox-1/#intrinsic-item-contributions) 9 | 10 | ### inline-block 的孩子上方总是有空隙 11 | 12 |  父容器设置 font-size:0; 或者首个不包含文字的孩子设置 font-size: 0 13 | 14 |  refers: 15 | [html - display: inline-block leaves gap with respect to height after div element - Stack Overflow](https://stackoverflow.com/questions/27730680/display-inline-block-leaves-gap-with-respect-to-height-after-div-element) 16 | 17 | ### 移除元素之间的空白 18 | 19 | [Fighting the Space Between Inline Block Elements | CSS-Tricks](https://css-tricks.com/fighting-the-space-between-inline-block-elements/) 20 | 21 | ### 宽高等比 22 | 23 | padding-top/padding-bottom 设置百分比是根据宽度的,所以设置元素 `padding-top 3/4*100%` 表示宽高比 4:3 24 | 25 | ### align-self:flex-end 无法置于底部 26 | 27 | [css - Flexbox column align self to bottom - Stack Overflow](https://stackoverflow.com/questions/24697267/flexbox-column-align-self-to-bottom#answer-35125244) 28 | 29 | 给置底元素加上 `margin-top: auto;` 30 | 31 | ### flex:1 元素作为容器,无法垂直滚动 32 | 33 | https://stackoverflow.com/questions/36247140/why-dont-flex-items-shrink-past-content-size#36247448[html - Why don't flex items shrink past content size? - Stack Overflow](https://stackoverflow.com/questions/36247140/why-dont-flex-items-shrink-past-content-size#36247448) 34 | -------------------------------------------------------------------------------- /intensive_read_understandinges6/README.md: -------------------------------------------------------------------------------- 1 | # 第十三章 模块 2 | 3 | ## 为什么要有模块 4 | 5 | 回忆一下过去我们 js 的文件加载后,代码加载的变量方法,本质上都在同一个全局共享空间,我们通过一些编码习惯,约定一些命名空间,避免了一些代码污染等问题,那么 ES6 的模块,帮助我们解决作用域的问题 6 | 7 | ## 什么是模块 8 | 9 | 1. 默认 strict mode,没得改 10 | - 模块内顶级作用域的变量不会添加到全局,只存在于模块内顶级作用域 11 | - 模块内top作用域里的 this,是 undefined 12 | - 不支持 html 注释风格 13 | 14 | ``` 15 | \ 16 | 22 | \ 23 | ``` 24 | 25 | 现在的浏览器还有不支持 js 么,这个古老的东西, es6 就抛弃了 26 | 27 | - 想要给外部使用的一定要 export 28 | - 模块可以引用其他模块的binding //TODO 这里binding 用词是什么意思,我以为就是模块中的功能罢了 29 | 30 | ## 简单 export 31 | 32 | 33 | ``` 34 | // 模块代码 35 | export const name = x 36 | export function foo () {} 37 | export class A {} 38 | function bar (){ } 39 | export { 40 | bar 41 | } 42 | 43 | // 使用外部模块代码 44 | import { name, foo, bar } from 45 | // 此时 name, foo.. 都是 read-only,不能赋值,像 const 46 | 47 | import * as x from 48 | // 此时 x 就跟普通定义的对象一样了,可以访问 x.name, x.foo,也可以修改赋值 49 | ``` 50 | 51 | 直接在带名称 function / class / 变量前面加 export,或者直接 export 一个对象, key 就是名称, 52 | 53 | ## 模块的怪异 54 | 55 | 如果 A 模块 import B 的变量,在 A 里面不能直接修改,但是如果 B 模块方法调用引起的 B 的变量发生变化,那么在 A 里面也会跟着同步 56 | 57 | ## import 到不同模块,同名如何处理 58 | 59 | ``` 60 | import { name as nameForB } 61 | 62 | export { username as name} 63 | ``` 64 | 65 | ## export 已经 import 的 66 | 67 | ``` 68 | export { sum } from 'x.js' 69 | 70 | 等同于 71 | 72 | import { sum } from "x.js"; 73 | export { sum } 74 | ``` 75 | 76 | ``` 77 | export * from "x.js" 78 | 这里会忽略 x.js 里面的 defualt 79 | ``` 80 | 81 | 如果需要 export 别的模块里面的 default,就要import 之后再去声明 export 82 | 83 | ## 浏览器中 module script 的执行时机 84 | 85 | 默认以 defer 行为加载执行 86 | 87 | - 按顺序执行 88 | - 保证所有依赖资源(import),加载完之后再执行 89 | 90 | 91 | -------------------------------------------------------------------------------- /code_usage.md: -------------------------------------------------------------------------------- 1 | Visual Studio Code 2 | === 3 | 4 | ⌘ ⌥ ⇧ ⌃ ⌫ ⌦ ↩︎ 5 | 6 | | 通用 | 功能 | 7 | | ---- | ------------ | 8 | | F1 | 打开命令输入 | 9 | | ⌘ P | 快速打开 | 10 | 11 | | 编辑 | 功能 | 12 | | ----- | ---------- | 13 | | ⌥ ⇧ A | 注释代码块 | 14 | 15 | | 文件 | 功能 | 16 | | ---------------- | -------------------- | 17 | | ⌘ O | 打开文件浏览选择 | 18 | | ⇧ ⌘ T | 重新打开已关闭 | 19 | | ⌃ R | 打开最近的文件 | 20 | | ⇧ ⌘ F | 在文件中搜索 | 21 | | ⇧ ⌘ J [搜索] | 切换更多搜索条件 | 22 | | ⇧ ⌘ H | 在文件中替换 | 23 | | ⌃ Tab 或 ⌃ ⇧ Tab | 在当前打开的文件切换 | 24 | | ⌃ - / ⌃ ⇧ - | 关注点 退后/向前 | 25 | | | | 26 | | | | 27 | | | | 28 | | | | 29 | | | | 30 | | | | 31 | | | | 32 | | | | 33 | | | | 34 | | | | 35 | | | | 36 | | | | 37 | 38 | 39 | | 导航 | 功能 | 40 | | ----- | ----------------------- | 41 | | ⌘ T | 搜索符号(方法...) | 42 | | ⇧ ⌘ O | 显示符号跳转(方法...) | 43 | | ⌃ G | 跳到第几行 | 44 | | ⌃- 或 ⌃⇧- | 向前/向后 45 | 46 | | 编辑管理 | 功能 | 47 | | ----------------- | ----------------------------- | 48 | | ⌘ \ | 拆分编辑器 | 49 | | ⌘ 1 或 ⌘ 2 或 ⌘ 3 | 选中(或打开)第 n 个拆分编辑器 | 50 | | ⌘ 单击导航文件 | 拆分编辑器打开文件 | 51 | | ⌃ ↩︎ [导航] | 拆分编辑器打开文件 | 52 | | cmd + alt + [ | 折叠代码块 | 53 | | cmd + alt + ] | 收缩代码块 | 54 | | cmd + k, cmd + 数字 | 按照级别折叠, 0 则全部折叠 | 55 | | cmd + k, cmd + j | 全部展开 | 56 | 57 | 58 | | 界面展示 | 功能 | 59 | | ------------ | --------------------- | 60 | | ⌘ B | 切换 Sidebar 显示隐藏 | 61 | | ⇧ ⌘ C [导航] | 命令行打开 | 62 | 63 | ## 我要 64 | 65 | - Sidebar 中在终端打开,设置为 iTerm 打开 66 | 67 | 设置 `"terminal.external.osxExec": "iTerm.app"` 68 | -------------------------------------------------------------------------------- /git_usage.md: -------------------------------------------------------------------------------- 1 | ## Story 2 | 3 | ## 修改前几次的提交信息 4 | 5 | ``` 6 | git rebase -i 父提交 7 | ``` 8 | 9 | 使用交互式的 rebase 操作,背后就是搞出 detached-Head (理解成 checkout 出某个提交,此时没有分支),然后修改完,refs/heads/master 再指向它, 10 | 这就是当我们要修改某个提交的 message 时,至少需要 rebase 它的父提交 11 | 12 | ## 查看 暂存区 跟 HEAD 的差异 13 | 14 | ``` 15 | git diff --cached 16 | ``` 17 | 18 | ## 工作区文件恢复跟暂存区一致 19 | 20 | ``` 21 | git checkout -- [file] 22 | ``` 23 | 24 | ## 暂存区文件退出暂存 25 | 26 | ``` 27 | git reset HEAD -- [file] 28 | ``` 29 | 30 | ## 把最近一堆提交干掉 31 | 32 | ``` 33 | git reset --hard [commit] 34 | 35 | git reset --hard HEAD // 暂存区&工作区都恢复跟头指针一样 36 | ``` 37 | 38 | ## 忽然接到紧急任务,手上有改动但是没完成代码怎么办 39 | 40 | ``` 41 | git stash 当前改动入缓存栈 42 | git stash apply 只是使用栈顶缓存的改动,但是不出栈 43 | git stash pop 弹出改动 44 | git stash list 查看 stash 栈 45 | ``` 46 | 47 | ## 备份本地仓库 48 | 49 | ``` 50 | git clone --bare file:///本地仓库路径/.git 备份目标路径.git 51 | ``` 52 | 53 | 这样创建的裸仓库没有工作区(就是没有源文件),之后把这个 备份目标路径.git 作为一个远程仓库,push 到这个备份.git 54 | 55 | ``` 56 | git remote add backup 备份目标路径.git 57 | ``` 58 | 59 | 60 | ## Command 61 | 62 | ### git diff 63 | 64 | git diff HEAD 65 | // 工作目录与最近一次提交的区别, HEAD 是最近一次提交id的别名 66 | --- 67 | 68 | git diff -stat 69 | // 更改过的文件列表和 diff 总览 70 | --- 71 | 72 | 73 | ### git rm 74 | 75 | git rm --cache 76 | // 移除对某个文件的管理,但是想把它留在文件夹里 77 | --- 78 | 79 | ### git mv 80 | > 移动 81 | 82 | 83 | ### git log 84 | 85 | git log --oneline 86 | // 提交摘要(id+描述) 87 | --- 88 | 89 | git log --stat 90 | // 包括涉及的文件和改动概要 91 | --- 92 | 93 | git log --patch 94 | // 包括详细改动 95 | --- 96 | 97 | git log -- /see/this/file/only.txt 98 | // 查看某个路径相关的日志 99 | --- 100 | 101 | git log -M --follow -- /a/path 102 | // 文件如果移动后,要追踪显示移动之前的日志 103 | --- 104 | 105 | 106 | 107 | ### git add 108 | 109 | git add -A . 110 | // 当你在电脑上移动一堆文件,避免手动 rm&add, 批量更新 rename 状态 111 | 112 | //note: 如果修改了再移动,有坑 113 | 1)修改 114 | 2)手动移动文件目录 115 | 3)执行 `git add -A .` 116 | 4)`git status`只显示 renamed,不会提示 modified 117 | --- 118 | 119 | 120 | 121 | 122 | ### 额~ 123 | 124 | - 你在文件夹窗口删除了一堆文件,避免手动一个个 `git rm` 125 | 126 | git add -u . 127 | 这个命令是逐层扫描当前目录下可以添加的文件,然后添加,在当前场景下,就相当于寻找所有以及删除的文件 -------------------------------------------------------------------------------- /ts_blog/4.3_beta_wip.md: -------------------------------------------------------------------------------- 1 | ## Separate Write Types on Properties 2 | 3 | ``` 4 | class Thing { 5 | #size = 0; 6 | 7 | get size(): number { 8 | return this.#size; 9 | } 10 | 11 | set size(value: string | number | boolean) { 12 | let num = Number(value); 13 | 14 | // Don't allow NaN and stuff. 15 | if (!Number.isFinite(num)) { 16 | this.#size = 0; 17 | return; 18 | } 19 | 20 | this.#size = num; 21 | } 22 | } 23 | ``` 24 | 25 | before: 26 | 27 | ![image](https://user-images.githubusercontent.com/4012276/117753485-1fc18680-b24b-11eb-8162-52a32b315bf5.png) 28 | 29 | after: 30 | 31 | - 读写类型分开 32 | - setter 类型 包含 getter 类型 33 | 34 | ## override and the --noImplicitOverride Flag 35 | 36 | >https://github.com/microsoft/TypeScript/pull/39669 37 | 38 | 效果: 39 | 1. 启用 noImplicitOverride 才会生效 40 | 2. 重写的成员需要加 override 41 | 42 | ## template string types 43 | 44 | 新的类型:模板字符串 45 | 46 | ``` 47 | function bar(s: string): `hello ${string}` { 48 | return `hello ${s}`; 49 | } 50 | ``` 51 | 52 | before: 报错,认为 return 后面是个 string 导致报错 53 | 54 | after: 探测到返回是模板字符串,就认为是个模板字符串类型 55 | 56 | ## ECMAScript #private Class Elements 57 | 58 | 真正的私有属性,实例访问不了带 # 的成员 59 | 60 | ## Always-Truthy Promise Checks 61 | 62 | 开启 `strictNullChecks` 后,Promise 值不能用于条件判断,永远为布尔真 63 | 64 | ``` 65 | async function foo(): Promise { 66 | return false; 67 | } 68 | if(foo()) // 报错 69 | ``` 70 | 71 | ## static Index Signatures 72 | 73 | 对索引变量属性,支持 static 74 | ``` 75 | Class Foo { 76 | static prop = true; // 报错,必须属于下面的 string | number 类型之一 77 | static [props: string]: string | number 78 | } 79 | ``` 80 | 81 | ## Import Statement Completions 82 | 83 | ``` 84 | from 文件 import {xx} 85 | ``` 86 | 87 | ## Editor Support for @link Tags 88 | 89 | ``` 90 | /** 91 | * This function depends on {@link bar} 编辑里面点击注释,可以跳转到对应方法 92 | */ 93 | function foo() {} 94 | 95 | function bar() {} 96 | ``` 97 | 98 | ## Union Enums Cannot Be Compared to Arbitrary Numbers 99 | 100 | 枚举类型,默认是 union 值 101 | 102 | after:枚举类型不能任意整数比较 103 | 104 | ``` 105 | enum E { 106 | A = 0, 107 | B = 1, 108 | } 109 | 110 | function doSomething(x: E) { 111 | // after: 不可以,-1 和 枚举值没有重叠,会报错 112 | if (x === -1) { 113 | } 114 | 115 | // 可以 116 | if(x === 1){ 117 | } 118 | } 119 | ``` 120 | 121 | 122 | -------------------------------------------------------------------------------- /ts/howto-tips.md: -------------------------------------------------------------------------------- 1 | ## Giving specify the tuple type explicitly and derive the union from it 2 | ```js 3 | const ALL_SUITS = ['hearts', 'diamonds', 'spades', 'clubs'] as const; 4 | type SuitTuple = typeof ALL_SUITS; // readonly ['hearts', 'diamonds', 'spades', 'clubs'] 5 | type Suit = SuitTuple[number]; // "hearts" | "diamonds" | "spades" | "clubs" 6 | ``` 7 | ## Union to intersection 8 | 9 | >- [Type inference in conditional types by ahejlsberg · Pull Request #21496 · microsoft/TypeScript](https://github.com/Microsoft/TypeScript/pull/21496) 10 | >- [typescript - Transform union type to intersection type - Stack Overflow](https://stackoverflow.com/questions/50374908/transform-union-type-to-intersection-type) 11 | 12 | ```js 13 | type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; 14 | 15 | type Result = UnionToIntersection; // T1 & T2 16 | ``` 17 | 18 | ## How to build a type inheritance class and include its static fields 19 | 20 | ```ts 21 | type Flatten = { [K in keyof T]: T[K] } 22 | 23 | class Animal { 24 | static animalStaticField = 'foo'; 25 | } 26 | 27 | interface Dog extends Flatten {} 28 | 29 | // Property 'animalStatic' is missing in type '{ prototype: typeof Animal; }' 30 | // but required in type 'Flattern'.(2741) 31 | const dogA: Dog = {prototype: Animal} 32 | ``` 33 | ## How to ensure that the generic type is an Class (Object) type and not a primitive type 34 | 35 | ```ts 36 | export type Prop = { new (...args: any[]): T & object } 37 | ``` 38 | {primitive type} & object will be never type 39 | 40 | ## How to define the type of this inside a method 41 | 42 | ```ts 43 | type A ={ 44 | [key: string]: (this: {name:string}) => void 45 | } 46 | 47 | const a = { 48 | say(){ 49 | console.log(this.name) 50 | } 51 | } 52 | ``` 53 | 54 | ## How to get the constructor type of a class 55 | 56 | ```ts 57 | interface ComponentConstructor { 58 | new (): This 59 | } 60 | ``` 61 | 62 | [wip: mixins · vuejs/core@3a7d11c](https://github.com/vuejs/core/commit/3a7d11ca153ba84bb2f0bae430b6c79224b3f9d4#diff-d011753005cf797f7f183dce1b0c7fb9cdd6597a70981f02b05b130fe645b65d) 63 | 64 | ## How to define process.env.NODE_ENV 65 | 66 | ``` 67 | declare global { 68 | // eslint-disable-next-line @typescript-eslint/no-namespace 69 | namespace NodeJS { 70 | interface ProcessEnv { 71 | NEXTAUTH_URL?: string 72 | } 73 | } 74 | } 75 | ``` 76 | 77 | 78 | -------------------------------------------------------------------------------- /vue_notebook/data-element-binding.md: -------------------------------------------------------------------------------- 1 | # 数据绑定实验 2 | 3 | > 源码来自 https://github.com/vuejs/vue/blob/871ed9126639c9128c18bb2f19e6afd42c0c5ad9/explorations%2Fgetset.html 4 | 5 | ## 总览 6 | 7 | - 提供模板 8 | 9 |
10 |

{{msg}}

11 |

{{msg}}

12 |

{{msg}}

13 |

{{what}}

14 |

{{hey}}

15 |
16 | 17 | - 通过js绑定数据 18 | 19 | new Element('test', {msg: 'hello}) 20 | 21 | - 输出视图 22 | 23 |
24 |

hello

25 |

hello

26 |

hello

27 |

28 |

29 |
30 | 31 | ## 解剖绑定过程(伪码) 32 | 33 | function Element () { 34 | //1) 给模板添加一些自定义占位符标志 35 | 36 | //2) 根据占位符标找到dom元素 37 | 38 | //3) 生成映射 {msg: msg对应的一些dom元素} 39 | 40 | //4) 准备数据对象 obj 41 | 42 | //5) 使用 Object.defineProperty 定义数据对象的 set, get 43 | 44 | //6) 修改数据对象的值 45 | } 46 | 47 | - 1) 给模板添加一些自定义占位符标志 48 | 49 | 将 50 | 51 |

{{msg}}

52 | 53 | 变成 54 | 55 |

56 | 57 | 其中 data-element-binding 是自定义的属性(占位符),通过这个属性把 dom 元素和 `{{msg}}` 关联起来 58 | 59 | - 2) 根据占位符标找到dom元素 60 | 61 | 62 | var els = el.querySelectorAll('[data-element-binding="msg"]'); 63 | 64 | - 3) 生成映射 {msg: msg对应的一些dom元素} 65 | 66 | var bindings = {msg: els}; 67 | /* 68 | 映射对象 => 69 | {msg: { 70 | els: [msg元素1, msgs元素2, ...], 71 | value: null //对应数据对象的值 72 | }} 73 | */ 74 | - 4) 准备数据对象 obj 75 | 76 | data = {}; 77 | 78 | - 5) 使用 Object.defineProperty 定义数据对象的 set, get 79 | 80 | for variable in 映射对象 81 | 82 | // variable 为 'msg' 83 | Object.defineProperty(data, variable, { 84 | set: function (newVal) { 85 | // 遍历元素 86 | bindings[variable].value = 87 | e.textContent = newVal 88 | }, 89 | get: function () { 90 | return bindings[variable].value 91 | } 92 | }) 93 | 94 | - 6) 修改数据对象的值 95 | 96 | 97 | data.msg = 'hello world'; 98 | // 检测到数据对象属性变化 99 | // 执行对应 set 方法 100 | 101 | // html 被修改 => 102 |
103 |

hello

104 |

hello

105 |

hello

106 |

107 |

108 |
109 | 110 | // 映射对象更新 => 111 | {msg: { 112 | els: [msg元素1, msgs元素2, ...], 113 | value: 'hello world' 114 | }} 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /vue_notebook/template_analysis_a5e27b1.md: -------------------------------------------------------------------------------- 1 | # 最初的模板解析&数据绑定 2 | 3 | > 参考这次提交:[a5e27b1174e9196dcc9dbb0becc487275ea2e84c](https://github.com/vuejs/vue/commit/a5e27b1174e9196dcc9dbb0becc487275ea2e84c) 4 | 5 | ## 代码解析 6 | 7 | var app = Seed.create({ 8 | id: 'test', 9 | // template 10 | scope: { 11 | msg: 'hello', 12 | hello: 'WHWHWHW', 13 | changeMessage: function () { 14 | app.scope.msg = 'hola' 15 | } 16 | } 17 | }) 18 | 19 | `id` 指定模板的根容器,`scope` 是需要绑定的数据和方法 20 | 21 | Seed 的核心工作就是解析模板,找到自定义的属性,绑定相关数据和方法,下面看下 Seed 的实现(部分伪代码替换源码) 22 | 23 | //... 24 | 25 | function Seed (opts) { 26 | // 1) 【扫描】包含指令(形如 sd-xx) 的元素 27 | 28 | // 2) 【处理节点】遍历处理每个包含指令的元素 和 根节点 29 | 30 | // 3) 【初始化】 31 | } 32 | 33 | - Seed#【扫描】 34 | 35 | 扫描所有包含形如 `sd-xx[-yy]` 的元素, els = [elA, elB, elC, ...] 36 | 37 | - Seed#【处理节点】 38 | 39 | - 1) 生成节点的【属性数组】 40 | 41 |

42 | 43 | => 44 | 45 | attrs = [ 46 | {name:'sd-text',value:'msg | capitalize'} 47 | ] 48 | 49 | 50 | - 2) 【解析指令】遍历 attrs, 解析 sd 开头的属性 51 | 52 | `sd-definition-argument ="key | filters#1 | filters#2"` 53 | 54 | update: Directives#definition 55 | [如果是个对象,则找到对象的 update 方法] 56 | 57 | + 例子1 58 | 59 | 对于 `{name:'sd-text',value:'msg | capitalize'}` 60 | 61 | 返回指令对象 62 | 63 | directive = { 64 | el: dom节点, 65 | attr: {name:'sd-text',value:'msg | capitalize'}, 66 | key: 'msg',//绑定变量 67 | filters: [ 'capitaliz'], 68 | definition: Directives.text方法, 69 | argument: null, 70 | update: Directives.text方法, 71 | } 72 | 73 | + 例子2 74 | 75 | 对于 `sd-on-click="changeMessage | .button"` 76 | 77 | directive = { 78 | ..., 79 | argument:"click", 80 | filters: ['.button'], 81 | update: Directives.on对象.update方法 82 | } 83 | 84 | - 3) 【组成绑定对象】 85 | 86 | binding = { 87 | ..., 88 | msg : { 89 | directives: [directive#1, directive#2], 90 | value: //内部实时值(用来更新dom值) 91 | }, 92 | } 93 | 94 | - 4) 【定义 get/set】 95 | 96 | get: return binding['msg'].value 97 | set: function (value) { 98 | 更新 binding['msg'].value 99 | 遍历 binding['msg']. directives 100 | // 如果 directive.filters 则应用过滤器 101 | // 使用过滤器之后的值,调用 directive.update 102 | }) 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /atom_keybinding.md: -------------------------------------------------------------------------------- 1 | # 编辑 2 | 3 | 4 | 基本 | 当前: 当前光标所在 5 | ------------------ | -------------------------------------------------- 6 | `Cmd+J` | 连接 下行到当前行末尾 7 | `Cmd+Ctrl+Up/Down` | 上移/下移 当前行 8 | `Cmd+Shift+D` | 复制 当前行 9 | `Cmd+K` `Cmd+U` | 大写 所在单词或选择内容 10 | `Cmd+K` `Cmd+L` | 小写 所在单词或选择内容 11 | `Ctrl+T` | 好换 光标两侧单字符 12 | `Alt+Cmd+Q` | 段落排版,根据 `editor.preferredLineLength` (每行字符数)重排当前段落 13 | 14 | 删除 | >> 15 | ---------------------- | -------- 16 | `Ctrl+Shift+K` | 删除 当前行 17 | `Alt+Delete` / `Alt+D` | 删除 至单词开始 18 | `Cmd+Delete` | 删除 至单词结束 19 | `Ctrl+K` | 删除 至行结束 20 | `Cmd+Backspace` | 删除 至行开始 21 | 22 | 多选光标 | >> 23 | -------------------- | ------------------- 24 | `Cmd+Click` | 添加光标 在点击处 25 | `Ctrl+Shift+Up/Down` | 添加光标 在当前的 上/下 (列编辑) 26 | `Cmd+D` | 多选 当前选中文本 27 | `Cmd+Ctrl+G` | 全选 当前选中文本 28 | `Cmd+Shift+L` | 将选中的多行进入多行光标编辑 29 | 30 | 闭合 | 包括 [] () and {} "" '' "" '' «» ‹› ... 31 | ------------ | ------------------------------------- 32 | `Ctrl+M` | 跳到最近的闭合处,多次摁键则在闭合 开始/结束 切换 33 | `Cmd+Ctrl+M` | 选中 当前括号中的所有内容 34 | `Alt+Cmd+.` | 闭合 当前 XML/HTML tag 35 | 36 | 查找替换 | >> 37 | ------------- | ------------------------ 38 | `Cmd+F` | 查找当前打开文件 39 | `Cmd+Shift+F` | 查找整个项目 40 | `Cmd+G` | 调到并选中 下一个匹配项 (配合`Cmd+F`) 41 | 42 | 折叠 | >> 43 | ----------------- | --------------------------- 44 | `Alt+Cmd+[` | 折叠 最近可闭合区 45 | `Alt+Cmd+]` | 展开 最近可闭合区 46 | `Alt+Cmd+Shift+[` | 折叠 所有闭合区 47 | `Alt+Cmd+Shift+]` | 展开 所有闭合区 48 | `Cmd+K Cmd+0-9` | `1`-折叠所有, `0`-展开所有, 其他-按层折叠 49 | 50 | 语言 | >> 51 | -------------- | ------------------------ 52 | `Ctrl+Shift+L` | 列表 当前可选语言(JS、CSS ...) 53 | `Cmd+Shift+;` | 拼写检测,给出修改建议(单词出现红线则拼写有误) 54 | `Alt+Shift+S` | 选择列表 当前语言可用的 snippet 55 | 56 | GIT | >> 57 | ----------- | ------------------------------------------------- 58 | `Alt+Cmd+Z` | 重置 当前文件的暂存区内容,回到 `HEAD` commit,类似 checkout, reset 59 | 60 | 文件 | >> 61 | ------------- | --------------------- 62 | `Cmd+T` | 选择列表>项目文件 63 | `Cmd+B` | 选择列表>已经打开的文件 64 | `Cmd+Shift+B` | 选择列表>git 状态为修改/新增 的文件 65 | 66 | Git | >> 67 | -------------- | ----------------------- 68 | `Alt+G` `D` | 当前文件 git diff 展示 69 | `Alt+G` `Down` | 下一个 diff 展示 70 | `Alt+G` `Up` | 上一个 diff 展示 71 | `Alt+G` `O` | 打开 GitHub 网站相应文件页面 72 | `Alt+G` `B` | 打开 GitHub 网站相应文件的原生内容页面 73 | `Alt+G` `H` | 打开 GitHub 网站相应文件的历史视图 74 | `Alt+G` `C` | 复制 GitHub 网站相应文件的路径 75 | `Alt+G` `R` | 打开 GitHub 分支比较也页面 76 | 77 | ## 视图 78 | 79 | 基本 | >> 80 | ---------------------------- | ----------- 81 | `Cmd+K` `Up/Down/Left/Right` | 按照方向分出多个编辑区 82 | `Cmd+W` | 关闭当前 Tab 83 | `Ctrl+Shift+M` | 预览 Markdown 84 | `Alt+Cmd+I` | 调出控制台 85 | `Cmd+Shift+\` | reveal in treeview 86 | -------------------------------------------------------------------------------- /mac_usage.md: -------------------------------------------------------------------------------- 1 | ## mac 快捷 2 | 3 | - 系统 4 | 5 | * 休眠 cmd + ctrl + q 6 | 7 | - 切换 8 | 9 | * cmd + tab 10 | * tab 向右切换 11 | * ~ 向左切换 12 | * 松开前,摁住 option 可以把最小化的窗口打开 13 | * cmd + shift + tab 从右向左切换 14 | * cmd + ~ 同个程序多个窗口间切换 15 | * 四指上滑(control + up),切换程序 16 | * 四指下滑 (control + down),切换某程序多窗口 17 | 18 | - 窗口 19 | 20 | * cmd + H 隐藏 app,四指下滑切换程序时不可见(可以 Tab 切换显示) 21 | * cmd + option + H 只显示当前应用窗口,其他隐藏 22 | * cmd + option + H + M 最小化所有窗口 23 | * cmd + M 最小化当前程序的当前窗口,四指下滑切换程序可见(Tab 切换需要长摁 option) 24 | * cmd + option + M 最小化当前程序的所有窗口,四指切换程序可见(Tab 切换需要长摁 option) 25 | 26 | [在OS X中,最小化窗口(cmd+M)和隐藏窗口(cmd+H)在设计初衷上有何区别? - Frendo的回答 - 知乎](https://www.zhihu.com/question/19966890/answer/143035156) 27 | 28 | * cmd + W 关闭当前窗口 29 | * cmd + Q 退出当前程序 30 | 31 | - 截图 32 | 33 | * cmd + shift +3 全屏 34 | * cmd + shift + 4 选定 35 | * space 进入窗口模式 36 | * 选择范围松开鼠标前,shift(切换) 锁定 X/Y 轴 37 | * 选择范围松开鼠标前,option 锁定圆心 38 | 39 | 截图时摁住 control 则进入剪贴板 40 | 41 | - 屏幕放大 42 | 43 | * 摁住 control + 双指触屏滑动 44 | 45 | - emoji 46 | 47 | * 默认 ⌘ ⌃ + space 打开特殊符号选择 48 | 49 | - finder 50 | 51 | + ⌘⇧. 切换显示隐藏文件 52 | 53 | ## bash 文件 54 | 55 | file | means 56 | ------------- | ------------- 57 | /etc/profile | 此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行.并从/etc/profile.d目录的配置文件中搜集shell的设置. 58 | /etc/bashrc | 为每一个运行bash shell的用户执行此文件.当bash shell被打开时,该文件被读取. 59 | ~/.bash_profile | 每个用户都可使用该文件输入专用于自己使用的shell信息,当用户登录时,该文件仅仅执行一次!默认情况下,他设置一些环境变量,执行用户的.bashrc文件. 60 | ~/.bashrc | 该文件包含专用于你的bash shell的bash信息,当登录时以及每次打开新的shell时,该该文件被读取. 61 | ~/.bash_logout | 当每次退出系统(退出bash shell)时,执行该文件. 62 | 63 | ## 更换 shell 为 zsh 64 | 65 | https://github.com/robbyrussell/oh-my-zsh 66 | 67 | ## 命令 68 | 69 | ### echo 70 | 71 | - 在终端打印内容,如打印当前环境变量 72 | 73 | ``` 74 | echo $PATH 75 | ``` 76 | 77 | - 把某些内容打印到某个文件,例如修改某个 bash 配置文件 78 | 79 | ``` 80 | echo "export PATH=xxxxxx:$PATH" >> ~/.bash_profile' 81 | ``` 82 | 83 | ## 剪贴板 84 | 85 | - 复制内容到剪贴板 86 | 87 | ``` 88 | pbcopy < in.txt 89 | ``` 90 | 91 | - 输出剪贴板内容 92 | 93 | ``` 94 | pbpaste >> out.txt 95 | ``` 96 | 97 | ## 我要 98 | 99 | ### ssh 设置登录别名 100 | 101 | ~/.ssh/config 文件写入 102 | 103 | Host theName 104 | HostName xx.xx.xx.xx 105 | Port 22 106 | User root 107 | 108 | 之后就可以 ssh theName 快速登录了 109 | 110 | ### 设置默认打开方式 111 | 112 | - 方式一:选中文件 -> control + 单点 -> 摁住 alt -> 选择始终以此方式打开(我的mac居然无效) 113 | - 方式二:选中文件 -> cmd + i -> 更改打开方式 -> 点击全部更改(靠谱) 114 | 115 | ### 在命令行用 sublime 打开文件 116 | 117 | 1. 对 sublime 的启动脚本建立一个软连接,放在可执行目录下 118 | 119 | ``` 120 | ln -f -s /Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl /usr/local/bin/ 121 | ``` 122 | 123 | 2. 当命令行需要文本窗口输入时,默认使用 sublime 124 | 125 | ``` 126 | echo "EDITOR='subl -w'" >> ~/.bash_profile 127 | ``` 128 | 129 | btw:查看 sublime 参数 用 subl -help 命令 130 | 131 | ### 给自带词典添加离线字典 132 | 133 | [导入工具](https://github.com/jjgod/mac-dictionary-kit) 134 | [字典列表](http://abloz.com/huzheng/stardict-dic/zh_CN/) 135 | 136 | 在字典列表下载离线字典,然后打开导入工具,把字典文件拖入, 137 | 然后在词典软件的 preference (偏好设置)把新增的字典勾选。 138 | 139 | ### 屏幕内容不小心放大,溢出屏幕 140 | 141 | ctrl + 双指下滑 142 | 143 | 144 | ### 强制关闭 145 | 146 | - 方式一:Cmd + Alt + Esc 关闭 147 | - 方式二:打开 Activity Monitor 选中,点击关闭按钮 148 | - 方式三: 149 | + 命令行 top,找到 pid 150 | + q 退出 151 | + kill \ 152 | -------------------------------------------------------------------------------- /nodejs/module_resolve_algorithm.md: -------------------------------------------------------------------------------- 1 | > [Modules: CommonJS modules | Node.js v18.2.0 Documentation](https://nodejs.org/api/modules.html) 2 | 3 | > [Modules: Packages | Node.js v18.2.0 Documentation](https://nodejs.org/api/packages.html) 4 | 5 | ## 在路径 Y 的模块内 require(X): 6 | 1. 如果 X 是内置模块, 7 | 1. 返回内置模块 8 | 2. 结束 9 | 2. 如果 X 以 '/' 开头 10 | - 设置 Y = 根目录 11 | 3. 如果 X 以 './' 或者 '/' 或者 '../' 开头 12 | 1. LOAD_AS_FILE(Y + X) 13 | 2. LOAD_AS_DIRECTORY(Y + X) 14 | 3. 抛出错误 "not found" 15 | 4. 如果 X 以 '#' 开头 16 | - LOAD_PACKAGE_IMPORTS(X, dirname(Y)) 17 | 5. LOAD_PACKAGE_SELF(X, dirname(Y)) 18 | 6. LOAD_NODE_MODULES(X, dirname(Y)) 19 | 7. 抛错 "not found" 20 | 21 | ## LOAD_AS_FILE(X) 22 | 1. 如果 X 是一个文件,以它本身的文件格式加载。结束 23 | 2. 如果 X.js 是一个文件,加载 X.js 作为 JS 代码。结束 24 | 3. 如果 X.json 是一个文件,解析 X.json 为 JS 对象。结束 25 | 4. 如果 X.node是一个文件,则将X.node作为二进制插件加载。 结束 26 | 27 | ## LOAD_INDEX(X) 28 | 1. 如果 X/index.js 是一个文件, 加载 X/index.js 作为 JS 代码。结束 29 | 2. 如果 X/index.json 是一个文件,解析 X/index.json 为 JS 对象。结束 30 | 3. 如果 X/index.node是一个文件,则将X/index.node作为二进制插件加载。 结束 31 | 32 | ## LOAD_AS_DIRECTORY(X) 33 | 1. 如果有 X/package.json 文件 34 | 1. 解析 X/package.json, 找到 "main" 字段。 35 | 2. 如果 "main" 是一个布尔假的值, GOTO 2. 36 | 3. let M = X + (main 字段) 37 | 4. LOAD_AS_FILE(M) 38 | 5. LOAD_INDEX(M) 39 | 6. 抛错误 "not found" 40 | 2. LOAD_INDEX(X) 41 | 42 | ## LOAD_NODE_MODULES(X, START) 43 | 1. let DIRS = NODE_MODULES_PATHS(START) 44 | 2. for each DIR in DIRS: 45 | 1. LOAD_PACKAGE_EXPORTS(X, DIR) 46 | 2. LOAD_AS_FILE(DIR/X) 47 | 3. LOAD_AS_DIRECTORY(DIR/X) 48 | 49 | ## NODE_MODULES_PATHS(START) 50 | 51 | // 从一个目录开始从下往上寻找所有存在 node_modules 子目录的情况, 52 | 53 | // 优先级:从近到远的所有 node_modules 目录,加上全局 node_modules 目录 54 | 55 | 1. let PARTS = path split(START) 56 | 2. let I = count of PARTS - 1 57 | 3. let DIRS = [] 58 | 4. while I >= 0, 59 | 1. if PARTS[I] = "node_modules" CONTINUE 60 | 2. DIR = path join(PARTS[0 .. I] + "node_modules") 61 | 3. DIRS = DIR + DIRS 62 | 4. let I = I - 1 63 | 5. return DIRS + GLOBAL_FOLDERS 64 | 65 | ##LOAD_PACKAGE_IMPORTS(X, DIR) 66 | 1. 从目录 DIR 开始找到最近的包目录 SCOPE 67 | 2. 如果没找到, return. 68 | 3. 如果 SCOPE/package.json 的 "imports" 是空,return。 69 | 4. let MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE), 70 | ["node", "require"]) ESM 包解析器定义的. 71 | 5. RESOLVE_ESM_MATCH(MATCH). 72 | 73 | ## LOAD_PACKAGE_EXPORTS(X, DIR) 74 | 1. 尝试把 X 当作是 NAME/子路径,其中 NAME 是带有 @scope/ 前缀的,子路径就是后面 / 开头的。 75 | 2. 如果 X 匹配不到这种模式,或者 DIR/NAME/package.json 不存在, 76 | return。 77 | 3. 解析 DIR/NAME/package.json,找到 "exports" 字段。 78 | 4. 如果 "exports" 空, return。 79 | 5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(DIR/NAME), "." + SUBPATH, 80 | `package.json` "exports", ["node", "require"]) ESM 包解析器定义的。 81 | 6. RESOLVE_ESM_MATCH(MATCH) 82 | 83 | ## LOAD_PACKAGE_SELF(X, DIR) 84 | 1. 从目录 DIR 开始找到最近的包目录 SCOPE 85 | 2. 如果没找到, return. 86 | 3. 如果 SCOPE/package.json 的 "exports" 是空,return。 87 | 4. 如果 SCOPE/package.json 的 "name" 不是 X 的第一段路径部分,return。 88 | 5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(SCOPE), 89 | "." + X.slice("name".length), `package.json` "exports", ["node", "require"]),ESM 包解析器定义的参数. 90 | 6. RESOLVE_ESM_MATCH(MATCH) 91 | 92 | ## RESOLVE_ESM_MATCH(MATCH) 93 | 1. let { RESOLVED, EXACT } = MATCH 94 | 2. let RESOLVED_PATH = fileURLToPath(RESOLVED) 95 | 3. 如果 EXACT 为 true, 96 | - 如果 RESOLVED_PATH 路径对应文件存在, 按照文件本身格式加载。结束 97 | 4. 否则 98 | 1. LOAD_AS_FILE(RESOLVED_PATH) 99 | 2. LOAD_AS_DIRECTORY(RESOLVED_PATH) 100 | 5. 抛出错误 "not found" 101 | -------------------------------------------------------------------------------- /chrome/ext/reading-octe-mate-source-code.md: -------------------------------------------------------------------------------- 1 | # chrome 插件 [Octo Mate](https://github.com/camsong/chrome-github-mate) 源码分析 2 | 3 | ## manifest 4 | 5 | ### permissions 权限 6 | 7 | 各种 permission 对应功能范围可以在 [Permission Warnings - Google Chrome](https://developer.chrome.com/apps/permission_warnings#warnings) 查到 8 | 9 | ``` 10 | "permissions" : [ 11 | "*://github.com/*", 12 | "tabs", 13 | "webNavigation" 14 | ], 15 | ``` 16 | 17 | - **tabs** `chrome.tabs` `chrome.windows` 18 | - **webNavigation** `chrome.webNavigation` 19 | - **\*://github.com/\*** 只有 gitub.com 的页面才有权限访问 chrome api 20 | 21 | ### web_accessible_resources 资源白名单 22 | 23 | ``` 24 | "web_accessible_resources": ["page.js"], 25 | ``` 26 | 27 | 在 version 2 manifest 中,只有配置在这里的资源才能在 web page 跨域访问 28 | 29 | ![20170907150477037043180.png](http://odoiwipoe.bkt.clouddn.com/20170907150477037043180.png) 30 | 31 | 在 web page 的 devtools 中跨域访问 `page.js` 成功,但是访问其他资源就不行了 32 | 33 | ### content_scripts 页面脚本 34 | 35 | ``` 36 | "content_scripts": [ 37 | { 38 | "matches": ["https://github.com/*"], 39 | "run_at": "document_end", 40 | "css": ["style.css"], 41 | "js": ["jquery-2.1.3.min.js", "page.js", "config.js", "script.js", "show-size.js", "tab-size.js", "panel.js", "ignore-spaces.js"] 42 | } 43 | ], 44 | ``` 45 | 46 | - ` "run_at": "document_end"` 表示在 Dom ready 和 window.onload 之间 47 | - `js` 会按照数组顺序加载执行 48 | 49 | ### options_page 配置页面 50 | 51 | ## content script 代码分析 52 | 53 | ### page.js 54 | 55 | 将 document 的 `pjax:end` 事件转成 not bubble, not cancelable 的 `_pajax:end` 时间 56 | 57 | 「疑问?」他是知道 github 在某些时候会触发 `pajax:end` 事件么 58 | 59 | 「答」[defunkt/jquery-pjax: pushState + ajax = pjax](https://github.com/defunkt/jquery-pjax) 原来就是 github 创始人的作品,github 目前也是用 pjax 的技术,所以在 github 内浏览器页面,加载后都会触发 pjax:end 事件,这都行! 60 | 61 | TODO pr【bug】page.js 会 load 两次 62 | 63 | ### config.js 64 | 65 | 获取用户配置,merge 默认配置 66 | 67 | ### scripts.js 68 | 69 | - 「增加 UI」 70 | 71 | + 增加目录页面文本文件图标 hover popt 72 | + 增加文件详情页面,download 按钮 73 | 74 | - 「增加 UI」的时机 75 | + js 加载 76 | + _pjax:end 事件(每次局部页面刷新) 77 | + popstate 事件(浏览器前进后退,pushState/replaceState不会触发这个事件) 78 | 79 | - 「增加的事件」 80 | 81 | + 目录页面点击文本图标 82 | 83 | - 「下载机制」 84 | + 借助 A 标签的 download="文件名" 属性,浏览器就会下载 href 链接,如果页面没有 A 标签,那么可以创建一个,触发 click 事件 85 | 86 | 87 | - 「通信获取配置是否有效」 88 | 89 | `chrome.runtime.sendMessage({key: "feature-1-enable"}, function(response) {` 90 | 91 | 内部发起通信,估计是配置管理器接收到 key 为 xx 的信息,就 reponse 一个 true/false 之类的, 92 | 然后这里如果没有启动相关功能,则不去注册相关事件 93 | 94 | ### show-resize.js 95 | 96 | 主要用来显示:下载包大小、ghpage按钮、缓存仓库信息 97 | 98 | 哈哈!有的项目会在 summary-bar(` .overall-summary`) 底部显示黄条 `.overall-summary-bottomless` 99 | 100 | ![](http://odoiwipoe.bkt.clouddn.com/20170907150478248861795.png) 101 | 点击黄条会出现下面 102 | ![](http://odoiwipoe.bkt.clouddn.com/20170907150478249645836.png) 103 | 104 | 这个文件是否启用的标准就是有没有 `.overall-summary-bottomless`,这就尴尬了, 105 | 106 | ### tab-size.js 107 | 108 | 配置切换 `tab` 字符表示的空格,原来 css 还有 `tab-size` 属性 [tab-size - CSS | MDN](https://developer.mozilla.org/zh-CN/docs/Web/CSS/tab-size),不过 IE 不支持 109 | 110 | ### panel.js 111 | 112 | 创建右侧收藏列表、outline 列表 113 | 114 | ### ignore-space.js 115 | 116 | 在 提交、PR 等 diff 页面可以按钮,切换「是否隐藏差异中的空白字符」,通过 url 带上 query `w=1` 实现(这是 github 支持) 117 | 118 | 总结一句:content scripts 就是增强 web page 页面上的交互 119 | 120 | ## bg script 121 | 122 | ``` 123 | //background.html 124 | 125 | 126 | 127 | 128 | ``` 129 | 130 | 主要处理两件事情:「管理保存配置」「处理 unread badge」 131 | 132 | ### 管理保存配置 133 | 134 | 这里引用了 config.js,所以要在一个独立于 web page 的 context 中保存配置,对比一下 web page 和 bg page 的 localstorage 135 | 136 | web page: 137 | ![](http://odoiwipoe.bkt.clouddn.com/20170908150483797047178.png) 138 | 139 | bg page: 140 | ![](http://odoiwipoe.bkt.clouddn.com/20170908150483802850597.png) 141 | 142 | `gm_config` 保存在 bg page 的 localstorage 143 | 144 | ### 读取 unread 145 | 146 | 通过获得 `https://github.com/notifications` 这份 html 页面,解析得到 147 | 148 | 149 | ## options page 配置页 150 | 151 | 从 `options.js` 看到,它也是写 local storage 的 `gm_config` 来保存,所以跟 bg 是同一个 context 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /linux/linux_cmd.md: -------------------------------------------------------------------------------- 1 | > CentOS 7.x 2 | 3 | ## df 4 | 5 | df 6 | //扫描磁盘使用情况 7 | 8 | --- 9 | 10 | 11 | # 文件处理 12 | 13 | ## ls 14 | 15 | ls /xx/yy 16 | //指定列出 /xx/yy 目录下的文件和目录,不指定则默认指定当前目录 17 | --- 18 | 19 | ls -a 20 | //all 所有文件(包括隐藏) 21 | --- 22 | 23 | ls -lh 24 | /* 25 | -l long list 长列表(包含详细信息),信息依次为 26 | 类型权限 | 硬连接引用次数 | 所属者 | 所属组 | 大小 | 创建日期 | 名称 27 | */ 28 | //-h human 人性化显示(例如大小单位) 29 | --- 30 | 31 | ls -id 32 | //-d 展示目录本身,而不展示其下... 33 | //-i 查看 i 节点(文件/目录 的 id) 34 | 35 | ## mkdir 36 | 37 | mkdir -p 38 | // -p 递归创建目录 39 | 40 | ## pwd 41 | 42 | pwd 43 | // 显示当前目录 44 | 45 | ## rmdir 46 | 47 | rmdir 48 | // 删除空目录 49 | --- 50 | 51 | ## cp 52 | 53 | cp file1 file2 file3 /to/dir 54 | // 拷贝多个文件 55 | 56 | cp -rp /from/dir /to/dir 57 | // -r 拷贝目录 58 | // -p 保留文件/目录属性(例如创建时间) 59 | 60 | ## mv 61 | 62 | mv /source/file/or/dir /to/dir 63 | //剪切 或 更名 64 | 65 | ## rm 66 | 67 | rm file 68 | // 删除文件 69 | --- 70 | 71 | rm -r /dir 72 | // -r 删除目录 73 | --- 74 | 75 | rm -rf /not/empty/dir 76 | // -f 强制删 77 | 78 | ## touch 79 | 80 | touch newfile.txt 81 | // 创建文件 82 | --- 83 | 84 | touch "programe files" 85 | // 特殊命名要带引号,不建议 86 | 87 | ## cat 88 | 89 | cat -n /to/watch/this/file 90 | // 查看文件内容 91 | // -n 带行号 92 | 93 | ## more 94 | 95 | more /to/watch 96 | // 查看文件, 空格-翻页, 回车-下一行,q-退出 97 | 98 | ## less 99 | 100 | //查看文件, 可以往回翻 101 | //可以搜索关键词,输入斜杠搜索关键字 102 | 103 | ## head 104 | 105 | head -n 3 file.txt 106 | // 查看文件前三行 107 | 108 | ## tail 109 | 110 | tail -n 3 file.txt 111 | // 查看文件后三行 112 | 113 | tail -f file.txt 114 | // -f 动态显示文件末尾 115 | 116 | ## ln 117 | 118 | ln -s /src/file //hard.link 119 | // 生成硬链接 120 | --- 121 | 122 | ln -s /src/file /soft.link 123 | // 生成软链接 124 | --- 125 | 126 | ## mount 127 | > 挂载 128 | 129 | mount -t iso9660 /dev/src0 /mnt/mount 130 | 131 | // 设备/dev/src0 挂到 /mnt/mount 132 | // -t 指定文件系统 (如果是系统分配的设备名,通常不需要指定) 133 | --- 134 | 135 | ## umount 136 | > 卸载挂载点 137 | 138 | # 权限管理 139 | 140 | ## chmod 141 | 142 | > change ... mode of permission 更改权限 143 | 144 | chmod u+r /target/file 145 | 146 | // u-表示所有者,还可以是 g-所属组, o-其他 147 | // '+' 表示添加权限,还可以是 '-' 或 '=' 148 | // r 是 rwx 中的一个 149 | /* 150 | 权限 对文件 对目录 151 | ------------------------------ 152 | r 读 查看文件内容 列出目录中的内容 153 | w 写 修改文件内容 目录中创建、删除文件 154 | x 执行 执行文件 可以进入目录 155 | 156 | */ 157 | --- 158 | 159 | chmode -R 777 /target/dir 160 | 161 | // r:4, w:2, x:1, rwxrw-r--:764 162 | // -R 表示递归,不加则默认只会改变目录本身权限 163 | 164 | ## chown 165 | 166 | > change file onwership 改变文件所有者, root 用户才可以 167 | 168 | change newowner /target 169 | // 改变 /target 的所有者为 newowner 170 | 171 | 172 | ## chgrp 173 | 174 | > change file group ... 改变文件的所有组,文件所有者才可以 175 | 176 | ## umask 177 | 178 | > the user file-creation mask 新建文件的缺省权限 179 | > 180 | > ps: 无论怎么设置,新建文件是没有 x 权限的,目录可以有 181 | 182 | umask -S 183 | 184 | //用 rwx 形式显示... 185 | --- 186 | 187 | umask 023 188 | 189 | // 设置为 rwxr-xr-- 190 | /* 191 | 需求:设置为 rwxr-xr-- 192 | 1)换算为 754 193 | 2)777 - 754 = 023 194 | 3)输入 023(掩码) 195 | */ 196 | 197 | # 文件搜索 198 | 199 | ## find 200 | > 扫描硬盘,插在文件 201 | 202 | find /search/from/dir -name ?key* 203 | 204 | // -name 表示按名称搜索 205 | // -iname 不区分大小写 206 | // ? 表示单个字符, * 表示任意个 207 | --- 208 | 209 | find /dir -size 204800 210 | 211 | // -size 按大小搜索,单位是数据块 (512字节) 212 | // -size +10 表示大于 10个数据块 213 | // -size -10 表示小于 10哥数据块 214 | --- 215 | 216 | find /dir -user username 217 | 218 | // -user 根据所有者 219 | // -group 根据所属组 220 | --- 221 | 222 | 223 | find /dir -cnmin -5 224 | 225 | // 搜索5分钟内文件属性属性被修改过的 226 | // -amin 访问时间(access) 227 | // -cmin 文件属性(change) 228 | // -mmin 文件内容(modify) 229 | // +5 表示超过5分钟 230 | --- 231 | 232 | find /dir -size +10 -a -size -100 233 | 234 | // -a 表示条件并 235 | // -o 表示条件或 236 | --- 237 | 238 | find /dir -type f 239 | 240 | // -type 表示根据文件类型查找 241 | // f:文件 d:目录 l:软链接 242 | --- 243 | 244 | find -inum 32112 245 | 246 | // 根据文件 i 节点查找 247 | // hack: 可以找硬链接 248 | --- 249 | 250 | find ./ -name notebook -exec ls -l {} \; 251 | 252 | // 对查询结果执行命令 253 | 254 | 255 | ## locate 256 | > 在文件资料库中查找文件,不是扫描硬盘,资料库不包含某些目录(/tmp) 257 | 258 | updatedb 259 | // 更新文件资料库 260 | --- 261 | 262 | ## which 263 | > 搜索命令所在的目录和别名信息 264 | 265 | ## whereis 266 | > 搜索命令所在的目录和帮助文档路径 267 | 268 | ## grep 269 | > 关键词逐行搜索文件内容 270 | 271 | grep -i keyword file.txt 272 | 273 | // -i 不区分大小写 274 | // -v 『排除关键词』搜索 275 | 276 | # 帮助 277 | 278 | ## man 279 | > 查看命令的帮助信息,查看配置文件的帮助信息 280 | 281 | man ls 282 | 283 | // 查看 ls 命令的帮助文档 284 | // 空格:往下翻页,b:往上翻页 285 | /* 286 | ➜ ~ whereis ls 287 | ls: /usr/bin/ls /usr/share/man/man1/ls.1.gz 288 | 形如 /man/man1/xx.1.gz 这类带 『1』 的帮助文档都是命令相关 289 | 带 『5』 的则是配置文件的帮助文档 290 | man 优先显示命令的帮助 291 | */ 292 | --- 293 | 294 | man services 295 | 296 | // 查看 /etc/services 配置文件的帮助信息 297 | // ps: man 后面的配置文件不需要带绝对路径 298 | // 如果有同名的命令和配置文件,那么需要 299 | // man 5 password 指定显示配置文件的帮助信息 300 | --- 301 | 302 | ## whatis 303 | > 查询类似命令的简单描述 304 | 305 | whatis ls 306 | 307 | // 显示所有带 ls 的命令的简要说明 308 | 309 | ## apropos 310 | > 查询类似配置文件的简单描述 311 | 312 | ## info 313 | > 跟 man 差不多,内容格式排版不一样 314 | 315 | ## help 316 | > 查看内置命令(找不到路径的命令)的帮助 317 | 318 | help cd 319 | // cd 是内置命令 320 | // which cd 321 | // cd: shell built-in command 322 | 323 | 324 | # 用户管理 325 | 326 | ## useradd 327 | > 添加用户 328 | 329 | useradd peter 330 | --- 331 | 332 | ## passwd 333 | > 为用户设置密码 334 | 335 | passwd peter 336 | 337 | // 用 peter 设置密码 338 | 339 | ## who 340 | > 查看当前登陆的用户们 341 | 342 | ➜ ~ who 343 | root pts/0 2016-04-28 04:23 (183.237.64.198) 344 | peter pts/1 2016-04-28 05:29 (183.237.64.198) 345 | // pts:远程终端登录 tty:本地终端登 346 | 347 | ## w 348 | > 查看谁登录以及 uptime 信息 349 | 350 | ## uptime 351 | > 系统运行摘要 352 | 353 | # 压缩 354 | 355 | ## gzip 356 | > GUN zip, 压缩为 .gz, 357 | 358 | gzip srcfile 359 | 360 | // 只能压缩文件(windows压啥都行) 361 | // 不保留源文件 362 | 363 | ## gunzip 364 | > 解压缩 365 | 366 | ## tar 367 | > 打包压缩 / 解包解压 368 | 369 | tar -cvf result.tar /src/sth 370 | 371 | // -c 打包, 可以是目录(通常后缀为 .tar) 372 | // -v 显示详情 373 | // -f 指定结果名 374 | // -z 打包同时使用zip压缩(通常后缀为 .tar.gz) 375 | // -j 打包同时使用bzip压缩(通常后缀为 .tar.bz2) 376 | --- 377 | 378 | tar -xvfz result.tar.gz 379 | 380 | // -x 解包 381 | // -f 指定文件 382 | // -z gzip 解压 383 | // -j bz2 解压 384 | // -v 显示详情 385 | --- 386 | 387 | ## zip 388 | > 压缩为 .zip, 文件/目录,保留原文件 389 | 390 | zip -r result.zip /src/dir 391 | 392 | // -r 目录 393 | --- 394 | 395 | ## unzip 396 | > 解压缩 397 | 398 | ## bzip2 399 | > gzip压缩升级版, 后缀通常为 .bz2 400 | 401 | bzip2 -k /src/any 402 | 403 | // -k 保留原文件 404 | --- 405 | 406 | # 网络 407 | 408 | ## write 409 | > 古老的向用户发信息 410 | 411 | write username 412 | > blablabla 413 | > ctrl+d 保存发送 414 | --- 415 | 416 | ## wall 417 | > 广播 418 | 419 | ## ping 420 | > 测试网络连通性 421 | 422 | ping -c 10 192.168.1.1 423 | 424 | // -c 发送次数 425 | --- 426 | 427 | ## ifconfig 428 | > interface configure 429 | 430 | ifconfig 431 | // 查看网卡情况(ip) 432 | 433 | --- 434 | 435 | ifconfig et0 192.x.x.x 436 | //设置设备临时ip(重启后无效) 437 | 438 | ## last 439 | > 列出目前和过去每次登录的信息 440 | 441 | ## lastlog 442 | > 列出所有用户最后一次登录 443 | 444 | lastlog -u nameorid 445 | 446 | // -u 指定查看某个用户 447 | 448 | ## traceroute 449 | > 探测到达主机间的路径 450 | 451 | ## netstat 452 | > 显示网络相关信息 453 | 454 | // -t TCP协议 455 | // -u UDP协议 456 | // -l 监听 457 | // -r 路由 458 | // -n 显示ip和端口 459 | --- 460 | 461 | netstat -tlun 462 | // 查看本机监听的端口 463 | --- 464 | 465 | netstat -an 466 | // 本机所有网络连接 467 | --- 468 | 469 | netstat -rn 470 | // 本机路由表 471 | 472 | ## setup 473 | > redhat 系提供的图形配置网络 474 | 475 | 476 | ## firewall-cmd 477 | 478 | firewall-cmd --zone=public --add-port=22/tcp --permanent 479 | firewall-cmd --reload 480 | //打开端口 481 | 482 | 483 | 484 | -------------------------------------------------------------------------------- /read_webpack_src/README.md: -------------------------------------------------------------------------------- 1 | webpack 源码从头到尾 2 | === 3 | 4 | # 2e14600 5 | 6 | > Initial commit 7 | 8 | ## webpack 加载器剖析 9 | 10 | 源码长这样: 11 | 12 | ```javascript 13 | var a = require("a"); 14 | var b = require("b"); 15 | require.ensure(["c"], function(require) { 16 | require("b").xyz(); 17 | var d = require("d"); 18 | }); 19 | ``` 20 | 21 | 经过 webpack 构建之后: 22 | 23 | ``` 24 | |- output.js 25 | |- 1.output.js 26 | ``` 27 | 28 | 每个输出文件叫做一个 chunk,chunk 内的各个模块叫做 module 29 | 30 | ### 入口 chunk ( output.js ) 31 | 32 | ``` 33 | (function(modules) { 34 | var parentJsonpFunction = window["webpackJsonp"]; 35 | 36 | // chunk 的异步加载后执行入口,类似 jsonp 的 callback 方法 37 | window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) {...}; 38 | var installedModules = {}; 39 | var installedChunks = { 40 | 0: 0 41 | }; 42 | // 加载 module 的方法 43 | function __webpack_require__(moduleId) {} 44 | 45 | // 加载 chunk 的方法 46 | __webpack_require__.e = function requireEnsure(chunkId, callback) {}; 47 | 48 | // 缓存每个 module 对方的方法 49 | // 初始化为入口 chunk 中定义的 modules 50 | __webpack_require__.m = modules; 51 | 52 | // 已经安装的 modules 信息 53 | __webpack_require__.c = installedModules; 54 | 55 | // 资源访问的基路径 56 | __webpack_require__.p = ""; 57 | return __webpack_require__(0); 58 | }) 59 | ([ 60 | /* 0 */ 模块 0 对应 output 本身这个模块 61 | function(module, exports, __webpack_require__) { 62 | var a = __webpack_require__(1); 63 | var b = __webpack_require__(2); 64 | 65 | // 这里加载另一个 chunk 1 66 | __webpack_require__.e /* nsure */ (1, function(require) { 67 | __webpack_require__(2).xyz(); 68 | var d = __webpack_require__(4); 69 | }); 70 | }, 71 | /* 1 */ 72 | function(module, exports) { // module a }, 73 | /* 2 */ 74 | function(module, exports) { // module b } 75 | ]); 76 | ``` 77 | ### \_\_webpack\_require\_\_ 78 | 79 | 加载模块的方法 80 | 81 | ```javascript 82 | function __webpack_require__(moduleId) { 83 | 84 | if (installedModules[moduleId]) 85 | return installedModules[moduleId].exports; 86 | 87 | // 创建一个新模块,然后放到缓存对象中 88 | // module 简单理解成 node 的 module 呗 89 | var module = installedModules[moduleId] = { 90 | exports: {}, 91 | id: moduleId, 92 | loaded: false 93 | }; 94 | 95 | // 执行模块对应方法,进行安装 96 | modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 97 | 98 | module.loaded = true; 99 | 100 | return module.exports; 101 | } 102 | ``` 103 | 104 | ### requireEnsure 105 | 106 | ```javascript 107 | function requireEnsure(chunkId, callback) { 108 | // "0" 表示 "已经加载安装完",其他情况可能表示加载中 109 | if (installedChunks[chunkId] === 0) 110 | return callback.call(null, __webpack_require__); 111 | 112 | // 如果是 0 以外的值,那就是 "当前正在加载的" 113 | if (installedChunks[chunkId] !== undefined) { 114 | // 如果之前处于『正在加载』,那么把回调塞入『加载后』的执行队列 115 | installedChunks[chunkId].push(callback); 116 | } else { 117 | // 表明正在加载 118 | installedChunks[chunkId] = [callback]; 119 | 120 | // 创建 script 标签加载资源 121 | var head = document.getElementsByTagName('head')[0]; 122 | var script = document.createElement('script'); 123 | script.type = 'text/javascript'; 124 | script.charset = 'utf-8'; 125 | script.async = true; 126 | script.src = __webpack_require__.p + "" + chunkId + ".output.js"; 127 | head.appendChild(script); 128 | } 129 | }; 130 | ``` 131 | 132 | 入口文件本身也算一个 chunk (0), 133 | 134 | ## webpack 命令 135 | 136 | - `single`: false 表示不分割分件(code spliting) 137 | - `min`: false 表示不压缩代码(uglifyjs) 138 | - `filenames`:false 表示不把文件名输出到最终文件 139 | - `options`:指定一个 json 配置文件表示各参数 140 | - `script-src-prefix`:资源路径前缀 141 | - `libary`:指定一个变量名,最终会 export 为这个全局变量 142 | 143 | ## webpack 源码 144 | 145 | ``` 146 | | 147 | |- webpack.js 148 | |- resolve.js 149 | |- parse.js 150 | ``` 151 | 152 | ### webpack.js 153 | 154 | 基于 `buildDeps.js` 解析构建的模块树,写文件,对于 `chunkId` 为 0 的入口文件,会写入 webpack 加载器的相关代码,使用 `wirteChunk` 和 `wirteSource` 写入 chunk 文件 155 | 156 | ### resolve.js 157 | 158 | 解析出文件 或者 node 模块的文件绝对路径 159 | 160 | ### parse.js 161 | 162 | 找出文件中的 require 和 ensure 依赖的模块 163 | 164 | ```javascript 165 | var a = require("a"); 166 | var b = require("b"); 167 | require.ensure(["c"], function(require) { 168 | require("b").xyz(); 169 | var d = require("d"); 170 | }); 171 | ``` 172 | ===> 173 | 174 | ```json 175 | { 176 | "requires": [{ 177 | "name": "a", 178 | "nameRange": [16, 18], 179 | "line": 1, 180 | "column": 8 181 | }, { 182 | "name": "b", 183 | "nameRange": [38, 40], 184 | "line": 2, 185 | "column": 8 186 | }], 187 | "asyncs": [{ 188 | "requires": [{ 189 | "name": "c" 190 | }, { 191 | "name": "b", 192 | "nameRange": [98, 100], 193 | "line": 4, 194 | "column": 11 195 | }, { 196 | "name": "d", 197 | "nameRange": [130, 132], 198 | "line": 5, 199 | "column": 12 200 | }], 201 | "namesRange": [59, 63], 202 | "line": 3, 203 | "column": 0 204 | }] 205 | } 206 | ``` 207 | 其中 `asyncs` 数组的一个元素对应一个 chunk,这里只有一个元素,意思就是会从该模块文件分出一个 chunk 208 | 209 | ### buildDeps.js 210 | 211 | 构建依赖树信息 212 | 213 | **模块**:入口文件叫(入口)模块,其他 `require('xx')` 为 `xx` 模块 214 | 215 | 构建依树的步骤: 216 | 217 | ``` 218 | buildDeps(context, mainModule, options, callback) { 219 | ... 220 | var depTree = { 221 | modules: {}, // 222 | modulesById: {}, 223 | chunks: {}, // 224 | nextModuleId: 0, 225 | nextChunkId: 0, 226 | chunkModules: {} // used by checkObsolete 227 | } 228 | var mainModuleId; 229 | # 1 230 | addModule(depTree, context, mainModule, options, function(err, id) { 231 | ... 232 | mainModuleId = id; 233 | buildTree(); 234 | }); 235 | function buildTree() { 236 | # 2 237 | addChunk(depTree, depTree.modulesById[mainModuleId], options); 238 | for(var chunkId in depTree.chunks) { 239 | # 3 240 | removeParentsModules(depTree, depTree.chunks[chunkId]); 241 | # 4 242 | removeChunkIfEmpty(depTree, depTree.chunks[chunkId]); 243 | # 5 244 | checkObsolete(depTree, depTree.chunks[chunkId]); 245 | } 246 | callback(null, depTree); 247 | } 248 | } 249 | ``` 250 | 251 | 1. 入口文件(模块)`example.js` 解析开始,深度优先地使用 `parse.js` 对每个模块解析出 `requires` 和 `asyncs` 252 | 2. 解析完模块后,通过 `addChunk` 方法,新增入口模块对应的 chunk,在这个过程中,对于模块对象中的 `asyncs` 数组的每个元素,分别增加一个 chunk,每个 chunk 依赖的模块对应相应信息中的 `requires` 包含的模块,并且在 chunk 信息中,对于包含的模块标识一个 *'include'* 253 | 254 | ``` 255 | "1": { 256 | "id": 1, 257 | "modules": { 258 | "2": "include", 259 | "3": "include", 260 | "4": "include" 261 | }, 262 | "context": { 263 | "requires": [{ 264 | "name": "c", 265 | "id": 3 266 | }, { 267 | "name": "b", 268 | "nameRange": [120, 122], 269 | "line": 5, 270 | "column": 11, 271 | "id": 2 272 | }, { 273 | "name": "d", 274 | "nameRange": [152, 154], 275 | "line": 6, 276 | "column": 12, 277 | "id": 4 278 | }], 279 | "namesRange": [81, 85], 280 | "line": 4, 281 | "column": 0, 282 | "chunkId": 1, 283 | "chunks": [1] 284 | }, 285 | "parents": [0] 286 | } 287 | ``` 288 | 289 | 3. 根据 chunk 的分割父子关系,如果子 chunk 包含的模块在父 chunk 中出现,则 'include' -> 'in-parent' 290 | 4. 如果模块为空(找不到文件),则移除 291 | 5. 如果多个 chunk 包含的模块一样,则会将其余的 chunk 的 chunkId 等信息设置为其中一个的 chunkId 292 | 293 | 总结,依赖树包含下列信息: 294 | 295 | 1. 每个模块的依赖元信息 296 | 2. 每个 chunk 依赖了哪些模块,哪些是自己包含,哪些已经在 父 chunk 中 297 | 3. chunk 之间的 "父子" 关系 298 | 4. chunk 的上下文信息,对应 入口模块 / 分割 ,前者为入口模块 parse 后的信息,后者为 `asyncs` 中的一个元素 299 | 300 | ### wirteChunk 301 | 302 | 遍历 `chunk` 中 include 的 modules,将模块逐个 chunk 中,下面就是一个 module 的轮廓,其中 module 的代码内容则用 `writeSource.js` 写入 303 | 304 | ``` 305 | /******/ 306 | /******/2: function(module, exports, require) { 307 | 308 | // writeSource.js 309 | 310 | /******/}, 311 | /******/ 312 | ``` 313 | 314 | ### writeSource 315 | 316 | 主要是将 `require('xx')` 中的模块名替换为 module.id,将 `require.ensure` 异步加载的分块(模块)名替换为 chunkId 317 | 318 | 319 | ## 涨姿势 320 | 321 | **npm** 322 | 323 | - [substack/node-optimist: Light-weight option parsing for node.js](https://github.com/substack/node-optimist) 命令行参数解析 324 | - [jquery/esprima: ECMAScript parsing infrastructure for multipurpose analysis](https://github.com/jquery/esprima) JS 语法解析 325 | 326 | **refer** 327 | 328 | - [SpiderMonkey Parser API](https://developer.mozilla.org/en/SpiderMonkey/Parser_API) Esprima 的 API 兼容 SpiderMonkey 的 API 329 | 330 | 331 | -------------------------------------------------------------------------------- /vite/read_src_commit_by_commit.md: -------------------------------------------------------------------------------- 1 | ## init 820c2cf Evan You 2020年4月20日 下午4:00 2 | 3 | 大致思路:监听本地文件、拦截请求、浏览器热更新 4 | 5 | 启动一个 server,通过 vue 相关中间件对于 .vue 文件的请求拦截,返回编译改造后的 vue 组件代码 6 | 7 | 监听本地 .vue 文件,如果有改动,通过 websocket 发送事件实现热更新等 8 | 9 | 页面需引入一个 js,用于创建 ws,监听 server 发送的一些事件,执行相应更新操作 10 | 11 | 12 | vue 中间件: 13 | 14 | 对于一个 .vue 文件 15 | ``` 16 | 19 | 20 | 29 | 30 | ``` 31 | 32 | 通过 http://localhost:3000/demo.vue 后,会得到 33 | 34 | ``` 35 | const script = { 36 | data() { 37 | return { 38 | name: 'peter' 39 | } 40 | } 41 | } 42 | 43 | export default script 44 | import { render } from "/demo.vue?type=template" 45 | script.render = render 46 | script.__hmrId = "/demo.vue" 47 | ``` 48 | 49 | 访问 /demo.vue?type=template 这种带有参数的连接,那就返回带有 render 方法的代码 50 | 51 | ``` 52 | ... 53 | export function render(_ctx, _cache) { 54 | return (_openBlock(), _createBlock("div", _hoisted_1, "xx" + _toDisplayString(_ctx.name), 1 /* TEXT */)) 55 | } 56 | ``` 57 | 58 | ## feat: auto inject hmr client 4a04d81 Evan You 2020年4月20日 下午4:45 59 | 60 | `parseSFC` 方法默认不走缓存,考虑热更新的场景,如果走缓存就没办法拿到最新的 61 | 62 | # feat: module rewrite 33488fe Evan You 2020年4月20日 下午5:32 63 | 64 | 变量名带上 __ 前缀 65 | 66 | `moduleRewriter.js` 专门改造 sfc-compile 生成的代码: 67 | `.vue` 返回的 script 不是简单的拼凑,通过 babel/parser 解析,找到 `import` 和 `export default`,对于 import npm 包的,统一改为 '/__modules/xx' 68 | 69 | `moduleMiddleware.js` 就是返回 node_module 包的代码 70 | 71 | ## refactor: use async fs + expose createServer API bfb4b91 Evan You 2020年4月21日 上午6:14 72 | 73 | 读取文件 fs 改成异步 74 | 75 | resp 返回文件内容改成 stream 76 | 77 | 模块查找基于 cwd 路径 78 | 79 | 对于 __modules/xx 请求 return moduleMiddleware(pathname.replace('/__modules/', ''), res) 80 | 81 | ## refactor: use TS 91d76bf Evan You 2020年4月21日 上午7:13 82 | 83 | @npm 84 | - `npm-run-all` 并行/串行 执行 npm script 85 | 86 | ts 配置 87 | ``` 88 | { 89 | "compilerOptions": { 90 | "sourceMap": false, 91 | "target": "esnext", 92 | "moduleResolution": "node", 93 | 94 | "esModuleInterop": true, // 增加一些辅助代码,保证 import default 和 import * 对于 commonjs 代码不会出错 95 | 96 | "declaration": true, // 生成 d.ts 97 | "allowJs": false, 98 | 99 | "allowSyntheticDefaultImports": true, // false 则:对于没有 export default 的 xx.js ,那么 import x from xx.js 就会报错 100 | 101 | "noUnusedLocals": true, 102 | "strictNullChecks": true, // null和 undefined值不包含在任何类型里,只允许用它们自己和 any来赋值(有个例外, undefined可以赋值到 void) 103 | "noImplicitAny": true, // true: 有隐含的 any类型时报错 104 | "removeComments": false, 105 | "lib": [ 106 | "esnext", 107 | "DOM" 108 | ] 109 | } 110 | } 111 | ``` 112 | [typescript - Understanding esModuleInterop in tsconfig file - Stack Overflow](https://stackoverflow.com/questions/56238356/understanding-esmoduleinterop-in-tsconfig-file) 113 | 114 | ## add tests e718bd5 Evan You 2020年4月21日 上午8:57 115 | 116 | @npm 117 | - `execa` 是可以调用shell和本地外部程序的javascript封装。会启动子进程执行。支持多操作系统,包括windows。如果父进程退出,则生成的全部子进程都被杀死 118 | 119 | 120 | 121 | `test/test.js` 122 | ``` 123 | server.kill('SIGTERM', { 124 | forceKillAfterTimeout: 2000 125 | }) 126 | ``` 127 | SIGTERM 程序结束(terminate)信号,与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出。shell命令kill缺省产生这个信号。SIGTERM is the default signal sent to a process by the kill or killall commands. 128 | 129 | 所以这里 forceKillAfterTimeout: 2000 就表示,如果 2 秒了, server 进程还没自己正常退出,那就强杀 130 | 131 | 整个测试的思路就是,准备一个项目 132 | ``` 133 | xx.vue 134 | main.js 135 | index.html 136 | ``` 137 | 然后拷贝到临时目录,启动 server,用 puppeteer 做 e2e 138 | 139 | ## remove git add from lint-staged 3e64a74 Evan You 2020年4月21日 上午8:58 140 | 141 | ## properly handle cwd 93286b9 Evan You 2020年4月21日 上午10:04 142 | 143 | @npm 144 | - `resolve-from` Resolve the path of a module like require.resolve() but from a given path, 代替 `resolve-cwd` 145 | - `minimist`轻量命令行参数解析器 146 | 147 | ### `moduleResolver.js` 148 | 149 | 增强了 module 查找能力 150 | 151 | ``` 152 | modulePath = resolve(cwd, `${id}/package.json`) 153 | ``` 154 | 155 | 支持 `.map` soucemap 文件 156 | 157 | ### `server.js` 158 | 159 | createServer 带上 cwd 参数,后续所有跟启动路径相关的地方都传入,这样别的地方就不存在 `process.cwd()` 160 | 161 | ## refactor types f4382f1 Evan You 2020年4月21日 上午11:08 162 | 163 | 164 | `vueCompiler.js` 把 compile script 和 compile template 封装 165 | 166 | ## feat: style hot reload 140f2b2 Evan You 2020年4月21日 下午12:37 167 | 168 | @npm 169 | - `hash-sum` blazing fast unique hash generator; 输出一个变量/function,计算 hash 170 | 171 | ### `watcher.js` 172 | 173 | style hotreload 的处理: 174 | 175 | descriptor.styles 前后两次比较, 176 | - 如果有 `scoped` 改变,则发送 `reload`事件 177 | - 顺序遍历比较, 如有不同发送 `style-update' 指定索引 178 | - 新的更长,则旧的部分删除,发送 `style-remove` 指定 `{id: `${hash_sum(resourcePath)}-${i + nextStyles.length}`}` 179 | 180 | 比较两个 block 是不是一样: 181 | - 强等于比较 182 | - keys 长度比较 183 | - 遍历 keys 比较属性 184 | 185 | ### `vueCompiler.js` 186 | 187 | `compileSFCMain()` 188 | 189 | 对于有 scoped 的 style block,请求 `.vue` 的代码包含 190 | ``` 191 | import "xxx?type=style&index={i}&${timestap}" 192 | ``` 193 | 194 | `compileSFCTemplate()` 195 | 196 | 带上参数 id: `data-v-${id}`, 其中 ${id} 就是请求的 `pathname` 197 | 198 | `compileSFCStyle()` 199 | 200 | 请求 type=style,就编译,拼接创建 style 标签的代码,标签 id 为 `vue-style-${id}-${index}` 201 | 202 | ## rename be00e79 Evan You 2020年4月21日 下午1:04 203 | 204 | vds -> vite 205 | 206 | ## include bin a47c406 Evan You 2020年4月21日 下午1:06 207 | 208 | package.json 209 | ``` 210 | "files": [ 211 | "bin", 212 | "dist" 213 | ], 214 | ``` 215 | 216 | 指定 npm 包含哪些内容 217 | 218 | ## ci c76ca14 Evan You 2020年4月21日 下午1:21 219 | 220 | 顺手学了下 circle workflow 221 | 222 | `yarn --frozen-lockfile` 表示不生成/修改 yarn.lock 223 | 224 | ## test: fix puppeteer on ci 97de06e Evan You 2020年4月21日 下午1:47 225 | 226 | [puppeteer/troubleshooting.md at main · puppeteer/puppeteer](https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#setting-up-chrome-linux-sandbox) 227 | 228 | 229 | ## fix: use correct vue & compiler-sfc 0d5a2a4 Evan You 2020年4月21日 下午11:39 230 | 231 | vue compiler 也需要从 cwd 用户本地去找,而不是直接从 vite 依赖的 npm 里面加载 232 | 233 | ## refactor: use koa c74b24e Evan You 2020年4月22日 上午5:01 234 | 235 | ## use custom history fallback a307eeb Evan You 2020年4月22日 上午5:32 236 | 237 | ## feat: support import rewriting in index.html 4ed433a Evan You 2020年4月22日 上午6:06 238 | 239 | 支持 index.html 中 240 | ``` 241 | 351 | 352 | 353 | - userHeadTags 调用 renderToString 传入的 354 | - renderResourceHints() 此方法返回当前要渲染的面,所需的 `` 资源,包括 preload 所需的 JavaScript 和 CSS 文件、prefetch 异步 JavaScript chunk,之后可能会用于渲染 355 | - renderStyles() 返回内联