├── .npmrc ├── .prettierignore ├── .gitignore ├── docs ├── react │ ├── assets │ │ ├── run.png │ │ ├── run-iphone.png │ │ ├── xcodeproject.png │ │ ├── react-devtools.jpeg │ │ └── debugger-chrome.jpeg │ ├── react-native-1.environment-mac.md │ ├── react-eslint.md │ ├── react-component-best.md │ ├── function-program.md │ ├── react-native-2.debug-skill.md │ └── react-redux-actions.md ├── temp │ ├── es6-import.md │ ├── 前端mac设置指南.md │ ├── react-router.md │ ├── js-design-pattern-principle.md │ ├── node-frame.md │ ├── webpack优化策略.md │ ├── php.md │ ├── linux-command.md │ ├── rust-entry.md │ ├── react │ │ ├── autoTrack.md │ │ ├── immer.md │ │ ├── useMemo-useCallback.md │ │ └── createContext.md │ ├── english-word.md │ ├── chrome-dev-proformance.md │ ├── v8-webapi.md │ ├── ramda.md │ ├── linux-ssh.md │ ├── ele-ui │ │ ├── 在线材料汇总.md │ │ ├── ai组件库会议记录.md │ │ ├── 可视化系统搭建.md │ │ └── ai组件库设计会议记录2.md │ ├── node-event-loop.md │ ├── quicklink源码解析.md │ ├── 反编译代码工具.md │ ├── tree.md │ ├── Node-http-server与pm2搭建应用级服务器 .md │ ├── mini-app-analysis.md │ ├── unit-test.md │ ├── 1.async函数原理简述.md │ ├── react-hooks.md │ ├── 一图胜千言.md │ ├── react-code-diff.md │ ├── react-code-3.event.md │ ├── standard-pullrequest.md │ ├── 前端监控搭建调研.md │ ├── web-components.md │ ├── beta │ │ └── photography.md │ ├── jsPlumb.md │ ├── node-base-code.md │ ├── solution │ │ ├── npm-upgrad-latest.md │ │ └── house-select.md │ ├── http-perfermance.md │ ├── jumpter-lab.md │ ├── practice-dva.md │ ├── clear-myself-code.md │ ├── react-packages.md │ ├── vue-cli3-proxy.md │ ├── react-redux-code-analysis2.md │ ├── 1.vue-router.md │ ├── temp-vue3.md │ ├── node-source.md │ ├── vue-code-slot.md │ ├── koa-and-express-plugin.md │ ├── mirco-frontend.md │ ├── Webpack升级史 .md │ ├── heap-stack.md │ ├── vue-vuex.md │ ├── flutter-start.md │ ├── atemp.md │ ├── cookie-google-rule.md │ ├── react-code-temp.md │ ├── mysql-mongoose.md │ ├── vue3-vite-2.md │ ├── nei源码阅读.md │ ├── rollup.md │ └── 在线酒店经营.md ├── .vuepress │ ├── public │ │ ├── favicon.ico │ │ └── circle.yml │ └── styles │ │ └── palette.styl ├── README.md ├── project │ ├── grow-up-life-it.md │ ├── puppeteer.md │ ├── grow-up-front.md │ ├── postcss.md │ ├── ts-vue-shortcoming.md │ ├── minipack-code-analysis.md │ └── deep-learn-term.md ├── team-standard │ ├── recommend-vuerouter.md │ ├── recommend-vue-api-order.md │ ├── 0.standard-ai-summary.md │ ├── 1.standard-ai-git.md │ ├── recommend-vue-component.md │ ├── recommend-vue-project-structure.md │ ├── 1.standard-ai-css.md │ ├── recommend-css-font.md │ └── recommend-code200.md ├── node │ ├── redis.md │ └── node-vscode-debug.md ├── read-books │ ├── book-design-for-all.md │ ├── book-head-first-design-patterns.md │ ├── book-soft-skills.md │ ├── 0.how-to-improve-reading.md │ ├── book-http-diagram.md │ └── book-agile-software.md ├── tools │ ├── vscode.md │ ├── charles-tool.md │ ├── yarn-vs-npm.md │ ├── npm script.md │ └── vscode-plugin-develop.md ├── js │ ├── different-for-in-for-of.md │ ├── think-different-MVC-MVP-MVVM.md │ ├── regex.md │ ├── es6-latest-feature.md │ ├── es6-4.module.md │ ├── js-base-2.extend.md │ ├── canvas-base.md │ ├── es6-1.new-feature.md │ └── http-cross-domain.md ├── vue │ ├── vue-code-7.inject-provide.md │ ├── vue-code-0.frame.md │ └── vue-code-6.nextTick.md └── interview │ ├── interview-webpack.md │ └── interview-mini-app.md ├── .circleci └── config.yml ├── .prettierrc.js └── package.json /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npm.taobao.org -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | docs/.vuepress/dist -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .vscode/ 3 | dist 4 | *.pdf 5 | *.sketch 6 | *.dmg 7 | *.xmind -------------------------------------------------------------------------------- /docs/react/assets/run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lq782655835/blogs/HEAD/docs/react/assets/run.png -------------------------------------------------------------------------------- /docs/react/assets/run-iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lq782655835/blogs/HEAD/docs/react/assets/run-iphone.png -------------------------------------------------------------------------------- /docs/temp/es6-import.md: -------------------------------------------------------------------------------- 1 | # ES6 Import 2 | 3 | import 和 require 导入的区别: 4 | 关键点:1. 前者是值的引用,后者是值的拷贝。 2.前者编译时输出接口,后者运行时加载。 -------------------------------------------------------------------------------- /docs/.vuepress/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lq782655835/blogs/HEAD/docs/.vuepress/public/favicon.ico -------------------------------------------------------------------------------- /docs/react/assets/xcodeproject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lq782655835/blogs/HEAD/docs/react/assets/xcodeproject.png -------------------------------------------------------------------------------- /docs/react/assets/react-devtools.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lq782655835/blogs/HEAD/docs/react/assets/react-devtools.jpeg -------------------------------------------------------------------------------- /docs/react/assets/debugger-chrome.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lq782655835/blogs/HEAD/docs/react/assets/debugger-chrome.jpeg -------------------------------------------------------------------------------- /docs/.vuepress/styles/palette.styl: -------------------------------------------------------------------------------- 1 | // 显示默认值 2 | $accentColor = #5b3cc4 3 | $textColor = #2c3e50 4 | $borderColor = #eaecef 5 | $codeBgColor = #282c34 -------------------------------------------------------------------------------- /docs/temp/前端mac设置指南.md: -------------------------------------------------------------------------------- 1 | # 前端mac设置指南 2 | 3 | ## mac读单词设置 4 | system设置--> Accessibility图标-->左边侧边栏选择Speech-->右下角勾选并自定义全局热键-->选中单词,快捷键体验吧。 5 | 6 | ## 参考文章 7 | 8 | * [知乎-Mac 自带字典“读”单词](https://www.zhihu.com/question/30134586) 9 | 10 | -------------------------------------------------------------------------------- /docs/temp/react-router.md: -------------------------------------------------------------------------------- 1 | # React-Router 2 | 3 | react-router-dom依赖react-router,所以我们使用npm安装依赖的时候,只需要安装相应环境下的库即可,不用再显式安装react-router。基于浏览器环境的开发,只需要安装react-router-dom 4 | 5 | * react-router npm The core of React Router 6 | * react-router-dom npm DOM bindings for React Router -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: http://img.ijtoo.com/at/img/127132/700500011/0.jpg?247 4 | description: Welcome to personal site 5 | actionText: 开始 → 6 | actionLink: /team-standard/0.standard-ai-summary 7 | footer: MIT Licensed | Copyright © 2018-present springleo 8 | --- 9 | -------------------------------------------------------------------------------- /docs/temp/js-design-pattern-principle.md: -------------------------------------------------------------------------------- 1 | # JS设计模式 - 设计原则(二) 2 | 3 | ## 好莱坞原则 4 | 5 | ”不要来找我,我会给你打电话“,非常形象。 6 | 7 | 1. 模版方法模式 8 | * 子类失去对自己的控制权,而是父类通知子类 9 | 1. 发布-订阅模式 10 | * 推送了消息,才去执行。 11 | 2. 回调函数 12 | * 如ajax,回调函数的执行,被另外一个函数控制着。 13 | 14 | ## 单一职责 15 | 16 | ## 开闭原则 -------------------------------------------------------------------------------- /docs/temp/node-frame.md: -------------------------------------------------------------------------------- 1 | express 和 koa 的中间件是用于处理 http 请求和响应的,但是二者的设计思路确不尽相同。大部分人了解的express和koa的中间件差异在于: 2 | 3 | * express采用“尾递归”方式,中间件一个接一个的顺序执行, 习惯于将response响应写在最后一个中间件中; 4 | * 而koa的中间件支持 generator, 执行顺序是“洋葱圈”模型。 5 | 6 | 不过实际上,express 的中间件也可以形成“洋葱圈”模型,在 next 调用后写的代码同样会执行到,不过express中一般不会这么做,因为 express的response一般在最后一个中间件,那么其它中间件 next() 后的代码已经影响不到最终响应结果了; 7 | 8 | -------------------------------------------------------------------------------- /docs/project/grow-up-life-it.md: -------------------------------------------------------------------------------- 1 | # 一个程序员的成长之路 2 | 3 | > 做技术过程中,曾经疑惑技术的核心竞争力是什么?是青春饭?是否最终都要转管理岗?随着年纪和角色的改变,自己也在不断探索这些问题。当看到@fouber的这篇[演讲](https://github.com/fouber/blog/issues/41)时,内心产生很多共鸣,现在精简整理出来,希望能让更多技术人看到自己的方向和定位。 4 | 5 | 以下是全名直播CTO张云龙在FDCon2018的演讲,以思维导图精简整理而来。 6 | 7 | ![image](https://user-images.githubusercontent.com/6310131/45275022-f164d980-b4ec-11e8-9ff7-2ce74a960840.png) 8 | -------------------------------------------------------------------------------- /docs/temp/webpack优化策略.md: -------------------------------------------------------------------------------- 1 | # Webpack优化实践 2 | 3 | 1. 插件工具分析:`webpack-bundle-analyzer`,发现问题。 4 | * vue-cli3使用最新的webpack4打包,打包速度还不错。 5 | * webpack4有`mode: production`选项,会做一些默认的优化。比如代替CommonsChunkPlugin 插件的 `splitChunks`选项 6 | 2. 问题1: element-ui打包后巨大 7 | * 按照官网按需加载 8 | 2. 问题2: chunk文件太多,而且大部分是小文件。 9 | * 减少路由懒加载。路由的懒加载是个有用的功能,但不能滥用,因为每条路由import()加载,都会生成chunk文件,异步加载是需要时间的。所以我们修改为只在一级路由懒加载,一级路由下的二级、三级都是同步。 -------------------------------------------------------------------------------- /docs/temp/php.md: -------------------------------------------------------------------------------- 1 | # PHP 语法 2 | 3 | ``` php 4 | 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/temp/linux-command.md: -------------------------------------------------------------------------------- 1 | 1. 安装虚拟机,增加虚拟机器 2 | 1. 找ubantu10.6镜像(百度ubantu release) 3 | 1. 启动虚拟机器,并制定系统镜像,设置相关系统参数 4 | 1. 1.设置网络为桥接模式(重启才有效)。 2.修改root密码:sudo passwd root 5 | 1. 安装软件 6 | * 安装nginx:agt-get install nginx 7 | * [安装nodejs/npm](https://tecadmin.net/install-latest-nodejs-npm-on-ubuntu/) 8 | 1. 方便设置 9 | * 开启粘贴板共享 10 | * 开启多个terminator 11 | 1. 问题解决 12 | * [Ubuntu Server 命令行下全是菱形](https://blog.csdn.net/wszll_Alex/article/details/50095409) -------------------------------------------------------------------------------- /docs/temp/rust-entry.md: -------------------------------------------------------------------------------- 1 | # Rust 2 | 3 | ``` 4 | cargo new folderName // 等同npm init 5 | cargo run // 等同npm start 6 | cargo build // 等同npm install 7 | ``` 8 | 9 | ``` js 10 | 11 | use std::io; 12 | 13 | fn main() { 14 | println!("Guess the number!"); 15 | 16 | println!("Please input your guess."); 17 | 18 | let mut guess = String::new(); 19 | 20 | io::stdin() 21 | .read_line(&mut guess) 22 | .expect("Failed to read line"); 23 | 24 | println!("You guessed: {}", guess); 25 | } 26 | ``` -------------------------------------------------------------------------------- /docs/temp/react/autoTrack.md: -------------------------------------------------------------------------------- 1 | # 自动埋点 2 | 3 | 方案一: 4 | 5 | 6 | 7 | 8 | 9 | 10 | 优势: 11 | 12 | 13 | https://zhuanlan.zhihu.com/p/270189082 14 | 15 | 方案二: 16 | 17 | const handleClick = after(() => { })(() => {log}}) 18 | 19 | https://github.com/Qquanwei/trackpoint-tools 20 | 21 | 方案三: 22 | 23 | 改写React.createElement 24 | 25 | original = React.createElement 26 | warper 27 | 28 | https://github.com/azoth1991/autoTrack/blob/master/AutoTrack.js 29 | 30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /docs/temp/english-word.md: -------------------------------------------------------------------------------- 1 | # 英语进阶 2 | 3 | ## 音标 4 | 5 | 英语元音和辅音的定义 6 | 7 |   元音,又称母音。元音是在发音过程中由气流通过口腔而不受阻碍发出的音。按前后分类为高 、中、低元音。按音节分,可分为单元音和双元音。 8 | 9 |   气流在口腔或咽头受到阻碍而形成的音叫做辅音,辅音又叫子音。共分为清辅音、浊辅音、鼻音、舌侧音 、半元音五种不同类型。其中鼻音、舌侧音 、半元音为浊辅音。 10 | 11 |   英语元音和辅音在英语发音中扮演着重要的角色,英语元音和辅音组合起来就成为英语音标,共48个音位,是英语发音的基础。 12 | 13 | ![image](https://user-images.githubusercontent.com/6310131/69052269-98be9400-0a41-11ea-8f2b-31b3f62b9823.png) 14 | 15 | ## 单词 16 | 17 | * expensive 18 | * increased 19 | * desired 20 | * quality 21 | * mechanism 22 | 23 | ## 资料 24 | 25 | * 元音:https://www.youtube.com/watch?v=REwSJEaVmUg 26 | * 辅音:https://www.youtube.com/watch?v=zRJhj4JFcD0 27 | -------------------------------------------------------------------------------- /docs/temp/chrome-dev-proformance.md: -------------------------------------------------------------------------------- 1 | # Chrome 性能分析 2 | 3 | ![](https://developers.google.com/web/tools/chrome-devtools/network/imgs/resource-timing-api.png) 4 | 5 | ![image](https://developers.google.com/web/tools/chrome-devtools/network/imgs/resource-timing-data.png) 6 | 7 | 1. `Queueing`: 请求排队等待时间 8 | 1. Stalled: 连接前的等待时间 9 | 1. DNS Lookup: 解析DNS时间 10 | 1. `Initial connection`: 建立连接,包括TCP三次握手1. ttp) 11 | 1. SSL:完成SSL握手(Https) 12 | 1. `Request sent`: 开始发起请求 13 | 1. `Waiting`: 等待服务器,知道收到第一个字节时间 14 | 1. `Ccontent` Download: 下载完整时间 15 | 16 | * [Understanding Resource Timing](https://developers.google.com/web/tools/chrome-devtools/network/understanding-resource-timing) 17 | 18 | 19 | 20 | 当一个 HTML 文档被加载和解析完成后,DOMContentLoaded 事件便会被触发。 -------------------------------------------------------------------------------- /docs/temp/v8-webapi.md: -------------------------------------------------------------------------------- 1 | # Javascript v8引擎和Web API 2 | 3 | 我一直在阅读Javascript的内部知识(在chrome浏览器中),但我有一些问题似乎找不到适当的答案。 4 | 5 | 根据我的理解: 6 | 7 | V8中包含了 8 | * Core Javascript(根据ECMA规范)引擎。 9 | * 浏览器的Web API提供了类似于settimeout的功能。 10 | * V8引擎包括一个调用堆栈和任何将要使用的Javascript。被执行的被压入该堆栈。 11 | * 然后通过Web API调用非标准函数。 12 | * 这些完成时将被推送到回调队列。 13 | * 一旦堆栈为空,回调队列中的所有内容都会被推送,通过事件循环进入堆栈。 14 | 15 | 问题:当`V8引擎解释Javascript代码时,如何知道某个特定功能来自Web API`? Web API实际如何与引擎链接? 16 | 17 | ## 最佳答案 18 | 19 | 像setTimeout()这样的API已添加到Javascript的global对象中。当JS引擎寻求解析符号时,它从本地范围开始,并向上延伸一系列范围。链的最末端是global范围。 20 | 21 | `宿主环境可以作为初始化V8引擎的一部分,将其自己的API添加到V8引擎的全局范围中,这正是浏览器针对V8尚未内置的功能所做的事情。` 22 | 23 | > 浏览器内核包括:渲染引擎 + JS引擎(比如V8(Blink内核)、JavaScriptCore(WebKit内核))。注:Blink内核(Google)是WebKit内核(Safari)的分支 -------------------------------------------------------------------------------- /docs/temp/ramda.md: -------------------------------------------------------------------------------- 1 | Ramda 的函数是自动柯里化的 。自动柯里化使得 "通过组合函数来创建新函数" 变得非常容易。因为 API 都是函数优先、数据最后(先传函数,最后传数据参数),你可以不断地组合函数,直到创建出需要的新函数,然后将数据传入其中。 2 | 3 | ``` js 4 | var obj = { 5 | a: 1, 6 | b: 2, 7 | b2: 3 8 | } 9 | 10 | // pipe函数要求:除第一个函数可以有多个参数,后面的参数都只支持1个参数 11 | var fn = R.pipe( 12 | R.pickBy((_, key) => key.startsWith('b')), 13 | R.toPairs, 14 | // 对上个返回数据先map(map中第一个参数接受函数,对每条数据如何处理),map中每条数据进行join 15 | R.map(R.join(':')) 16 | ) 17 | console.log(fn(obj)) // ["b:2","b2:3"] 18 | ``` 19 | 20 | ``` js 21 | var pickByResult = R.pickBy((_,key) => key.startsWith('b'), obj) // {"b":2,"b2":3} 22 | var toPairsResult = R.toPairs(pickByResult) // [["b",2],["b2",3]] 23 | var mapResult = R.map(R.join(':'), toPairsResult) // ["b:2","b2:3"] 24 | ``` -------------------------------------------------------------------------------- /docs/temp/linux-ssh.md: -------------------------------------------------------------------------------- 1 | # SSH 2 | 3 | ssh:登陆到跳板机(需要申请权限??) 4 | * 基本语法:ssh user@server 5 | * ssh是一种服务工具,需要单独安装和启动。 6 | * 登陆方式:口令登陆 和 公钥登陆(需要把本地弓) 7 | * 所谓"公钥登录",原理很简单,就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录shell,不再要求密码。 8 | * ssh-keygen: 提供生成本地公钥和私钥 9 | 10 | ## 本地端口转发 11 | 12 | 假定host1是本地主机,host2是远程主机。由于种种原因,这两台主机之间无法连通。但是,另外还有一台host3,可以同时连通前面两台主机。因此,很自然的想法就是,通过host3,将host1连上host2。 13 | 14 | ``` sh 15 | ssh -L 2121:host2:21 host3 # 访问本机的2121端口,会转发到host2的21端口(等同于代理) 16 | ``` 17 | 18 | 命令中的L参数一共接受三个值,分别是"本地端口:目标主机:目标主机端口",它们之间用冒号分隔。这条命令的意思,就是指定SSH绑定本地端口2121,然后指定host3将所有的数据,转发到目标主机host2的21端口(假定host2运行FTP,默认端口为21)。 19 | 20 | ## 参考 21 | 22 | * https://www.ruanyifeng.com/blog/2011/12/ssh_remote_login.html 23 | -------------------------------------------------------------------------------- /docs/temp/ele-ui/在线材料汇总.md: -------------------------------------------------------------------------------- 1 | 2 | 在线开源材料汇总 3 | 4 | * 基础组件库 5 | * [cloud-ui](https://github.com/vusion/cloud-ui) 6 | * [vusion](https://github.com/vusion/vusion) 7 | * 业务库开发 8 | * 基于ElementUI业务库:https://github.com/lq782655835/rds-vue 9 | * 配置化Form组件:https://github.com/lq782655835/el-form-plus 10 | * 配置化Table组件:https://github.com/lq782655835/el-table-plus 11 | * Vue插件:编程式创建Vue Dialog:https://github.com/lq782655835/el-dialog-helper 12 | * 类库开发 13 | * VSCode插件工具:https://github.com/lq782655835/common-utils 14 | * 通用函数库: https://github.com/lq782655835/30-seconds-of-code 15 | * 通用缓存管理库:https://github.com/lq782655835/cache-manage-js 16 | * 工具开发 17 | * 基于NEI的json2ts:https://github.com/lq782655835/json2ts 18 | * 基于模板自动生成CURD代码:https://github.com/lq782655835/auto-code 19 | -------------------------------------------------------------------------------- /docs/temp/node-event-loop.md: -------------------------------------------------------------------------------- 1 | 2 | 定义: 3 | ``` 4 | When Node.js starts, it initializes the event loop, processes the provided input script (or drops into the REPL, which is not covered in this document) which may make async API calls, schedule timers, or call process.nextTick(), then begins processing the event loop. 5 | ``` 6 | 7 | 当Node.js启动时会初始化event loop, 每一个event loop都会包含按如下顺序六个循环阶段, 8 | 9 | * timers 阶段: 这个阶段执行setTimeout(callback) and setInterval(callback)预定的callback; 10 | * I/O callbacks 阶段: 执行除了 close事件的callbacks、被timers(定时器,setTimeout、setInterval等)设定的callbacks、setImmediate()设定的callbacks之外的callbacks; 11 | * idle, prepare 阶段: 仅node内部使用; 12 | * poll 阶段: 获取新的I/O事件, 适当的条件下node将阻塞在这里; 13 | * check 阶段: 执行setImmediate() 设定的callbacks; 14 | close callbacks 阶段: 比如socket.on(‘close’, callback)的callback会在这个阶段执行. 15 | -------------------------------------------------------------------------------- /docs/temp/quicklink源码解析.md: -------------------------------------------------------------------------------- 1 | # quicklink源码解析 2 | 3 | 最近看github star增长趋势项目,发现有个google chrome lab 开源的quicklink非常火爆,故详细看了下项目源码。源码简单清晰,总共只有3个文件,但涉及的知识点都比较前卫和实用,故整理记录下。 4 | 5 | ## quicklink介绍 6 | quicklink 是一个通过预加载资源来提升后续方案速度的轻量级工具库。旨在提升浏览过程中,用户访问后续页面时的加载速度。 7 | 8 | 说到这可能大家首先想到的是html5 prefetch api,它的实现原理是利用浏览器的空闲时间去先下载用户指定需要的内容,然后缓存起来,这样用户下次加载时,就直接从缓存中取出来,从而可以快速响应出结果。但prefetch有几个限制,首先是兼容性问题,支持的浏览器有限。再者是prefetch只支持在header中以\ 方式加载,不支持页面中a标签以及js方式预加载。quicklink给我们一个轻量级兼容所有浏览器的预加载方案。 9 | 10 | Quicklink工作原理 通过以下方式加快后续页面的加载速度: 11 | * 检测视区中的链接(使用 Intersection Observer)。 12 | * 等待浏览器空闲(使用 requestIdleCallback)。 13 | * 确认用户并未处于慢速连接(使用 navigator.connection.effectiveType)或启用省流模式(使用 navigator.connection.saveData)。 14 | * 预获取视区内的 URL(使用 或 XHR)。可根据请求优先级进行控制(若支持 fetch() 可进行切换)。 15 | 16 | ## 参考文章 17 | * [prefetch](https://www.w3.org/TR/resource-hints/#prefetch) -------------------------------------------------------------------------------- /docs/temp/反编译代码工具.md: -------------------------------------------------------------------------------- 1 | # webpack反编译代码工具reverse-sourcemap 2 | 3 | github 地址:https://github.com/davidkevork/reverse-sourcemap 4 | 5 | ## 安装 6 | 7 | ``` bash 8 | sudo npm install --global reverse-sourcemap 9 | ``` 10 | 11 | ## 使用 12 | 13 | 前置条件,已有webpack打包生成的dist文件(主要是js文件反编译)以及开启sourcemap(生成xxx.js.map文件) 14 | 15 | 直接把整个`dist/js`目录自动反编译解析: 16 | 17 | ``` bash 18 | reverse-sourcemap dist/js -o sourcecode 19 | ``` 20 | 21 | 解释:以上命令直接解析dist/js中所有的xxx.map.js文件,并把源代码输出到当前路径到sourcecode文件夹中。 22 | 23 | ## 源码解析 24 | 25 | 本质上reverse-sourcemap工具,只是[source-map](https://github.com/mozilla/source-map)库的包装。 26 | 27 | ### source-map库源码解析规则 28 | 29 | 源码地址:https://github.com/mozilla/source-map/blob/master/lib/source-map-consumer.js#L173 30 | 31 | 1. 先找到sources关键字:里面存放了模块的文件路径(相当于书的目录) 32 | 1. 再找sourcesContent关键字:里面存放了所有文件的源码(书的实体内容) 33 | 1. 提供api:sourceContentFor(source),根据文件路径,拿到具体的源码(根据目录,直接找到章节内容) 34 | -------------------------------------------------------------------------------- /docs/temp/tree.md: -------------------------------------------------------------------------------- 1 | # 二叉树 2 | 3 | 1. 每个节点包含值,并且拥有左节点或右节点 4 | 2. 排序二叉树:左节点值小于父节点值,右节点值大于左边节点值 5 | 6 | ``` js 7 | // 二叉树基本节点数据结构 8 | function Node(key) { 9 | this.key = key 10 | this.left = null 11 | this.right = null 12 | } 13 | ``` 14 | 15 | ``` js 16 | sortNode([1, 3, 7, 4, 6]) 17 | 18 | function sortNode(arr) { 19 | arr.forEach(num => insert(num)) 20 | } 21 | 22 | var root = null 23 | function insert(key) { 24 | if (root === null) { 25 | root = new Node(key) 26 | } else { 27 | sortNode(root, new Node(key)) 28 | } 29 | } 30 | function sortNode(parentNode, childNode) { 31 | if (parentNode.key > childNode.key) { 32 | parentNode.left === null ? parentNode.left = childNode : sortNode(parent.left, childNode) 33 | } else { 34 | parentNode.right === null ? parentNode.right = childNode : sortNode(parent.right, childNode) 35 | } 36 | } 37 | ``` -------------------------------------------------------------------------------- /docs/temp/Node-http-server与pm2搭建应用级服务器 .md: -------------------------------------------------------------------------------- 1 | # http-server与pm2搭建应用级服务器 2 | 3 | ## [http-server](https://github.com/indexzero/http-server)概念 4 | 5 | http-server ./dist -p 3000 6 | -p 指定开启服务的地址 7 | -P 没有匹配到路径时,代理到设置到target地址 8 | 9 | ## pm2概念 10 | 11 | pm2 start app.js 12 | 13 | ### pm2-runtime 14 | 15 | pm2 list 16 | 17 | ## http-server与pm2搭配 18 | 19 | 对于是node命令的应用程序,使用`pm2-runtime`命令代替pm2。pm2-runtime的目标是将您的应用包装到合适的Node.js生产环境中。**这里有个注意点,对于node命令的参数,一定要在参数前加入`--`**。 20 | 21 | ``` js 22 | server: "pm2-runtime http-server -- ./dist -p 3000", 23 | // or 24 | "server:start": "http-server ./dist -p 3000", 25 | "sever": "pm2-runtime npm -- run server:start" 26 | ``` 27 | 28 | ## 参考文章 29 | 30 | * [running-nodejs-http-server-forever-with-pm2](https://stackoverflow.com/questions/31804966/running-nodejs-http-server-forever-with-pm2) 31 | 32 | * [docker-pm2-nodejs](http://pm2.keymetrics.io/docs/usage/docker-pm2-nodejs/) -------------------------------------------------------------------------------- /docs/temp/mini-app-analysis.md: -------------------------------------------------------------------------------- 1 | # 小程序运行机制 2 | 3 | ## 什么是WebView 4 | 5 | > WebView objects allow you to display web content as part of your activity layout, but lack some of the features of fully-developed browsers. A WebView is useful when you need increased control over the UI and advanced configuration options that will allow you to embed web pages in a specially-designed environment for your app. 6 | 7 | ## 双线程模型 8 | 9 | 类似于微信 JSSDK 这样的 Hybrid 技术,微信小程序的界面主要由成熟的 Web 技术渲染,辅之以大量的接口提供丰富的客户端原生能力。 10 | 11 | 每个小程序页面都是用不同的 WebView 去渲染,这样可以提供更好的交互体验,更贴近原生体验,也避免了单个 WebView 的任务过于繁重。此外,界面渲染这一块我们定义了一套内置组件以统一体验,并且提供一些基础和通用的能力,进一步降低开发者的学习门槛。 12 | 13 | 我们需要像Web 技术那样,有一份随时可更新的资源包放在云端,通过下载到本地,动态执行后即可渲染出界面。 14 | web缺陷:UI渲染跟 JavaScript 的脚本执行都在一个单线程中执行,这就容易导致一些逻辑任务抢占UI渲染的资源。 15 | 16 | 17 | js代码在 18 | 19 | ![双线程模型](https://user-gold-cdn.xitu.io/2019/5/17/16ac48abdb826898?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 20 | -------------------------------------------------------------------------------- /docs/temp/unit-test.md: -------------------------------------------------------------------------------- 1 | # 单元测试 2 | 3 | ## 单测类型 4 | 1. 测试驱动开发 TDD 5 | 2. 行为驱动开发 BDD 6 | ``` js 7 | // TDD 8 | suite('Array', function() { 9 | setup(function() { 10 | }); 11 | 12 | test('equal -1 when index beyond array length', function() { 13 | assert.equal(-1, [1,2,3].indexOf(4)); 14 | }); 15 | }); 16 | 17 | // BDD 18 | describe('Array', function() { 19 | before(function() { 20 | }); 21 | 22 | it('should return -1 when no such index', function() { 23 | [1,2,3].indexOf(4).should.equal(-1); 24 | }); 25 | }); 26 | ``` 27 | 28 | ## 框架选择 29 | 提供一套 API 帮助开发者更方便的测试代码。推荐使用Mocha测试框架 + chai断言库。如果需要测试覆盖率,推荐istanbul 30 | 31 | 断言库 优点 缺点 备注 32 | Node.js 核心库 Assert 无需第三方依赖 语法较弱 - 33 | Should.js API 非常语义 文档太烂 - 34 | expect.js - - 比较中庸 35 | chai 大而全的 API - 36 | 37 | # 参考文章 38 | 39 | [Mocha+Chai单元测试并没有想象中难](http://www.dengzhr.com/node-js/1282) 40 | 41 | [Chai.js API](https://www.chaijs.com/api/bdd/#method_ok) 42 | -------------------------------------------------------------------------------- /docs/temp/1.async函数原理简述.md: -------------------------------------------------------------------------------- 1 | # async函数原理简述 2 | 3 | 这两天拜读了阮一峰的[ES6入门](http://es6.ruanyifeng.com/),其中Generator和Async两章写的太精彩了,层次结构清晰,叙事深入浅出,感触阮老师的写作水平之深。本文在阮老师文章以及源码分析上进行总结提炼,方便大家速通该部分知识。 4 | 5 | ## 为什么要async 6 | 7 | 因为js是单线程的,当一段任务依赖于上一个任务结果,此时异步就产生了。程序在执行第二段任务前,必须等待第一段任务结果。回调函数是js对异步的解决方案。但该解决方案会有“地狱回调”问题。 8 | 9 | ``` js 10 | fs.readFile(fileA, 'utf-8', function (err, data) { 11 | fs.readFile(fileB, 'utf-8', function (err, data) { 12 | fs.readFile(fileB, 'utf-8', function (err, data) { 13 | ... 14 | }) 15 | }); 16 | }); 17 | ``` 18 | 19 | ## Promise 20 | 21 | Promise是ES6引入的原生对象,是对`异步编程解决方案`的补充,它比传统回调函数和事件更加强大以及合理。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。 22 | 23 | ``` js 24 | var readFile = require('fs-readfile-promise'); 25 | 26 | readFile(fileA) 27 | .then(data => readFile(fileB)) 28 | .then(data => readFile(fileC)) 29 | ... 30 | ``` 31 | 32 | ## Generator 函数 33 | 34 | Generator 函数将 JavaScript 异步编程带入了一个全新的阶段。 -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | orbs: 3 | node: circleci/node@1.1.6 4 | jobs: 5 | build-and-test: 6 | executor: 7 | name: node/default 8 | steps: 9 | - add_ssh_keys: 10 | fingerprints: 11 | - '54:19:54:8f:1d:d3:d7:c3:8c:06:09:c0:b3:36:5e:08' 12 | - checkout 13 | - node/with-cache: 14 | steps: 15 | - run: npm install 16 | - run: npm run build 17 | - run: # git push 18 | name: push-github 19 | command: | 20 | git config --global user.email "782655835@qq.com" 21 | git config --global user.name "lq782655835" 22 | npm run ghpages 23 | workflows: 24 | build-and-test: 25 | jobs: 26 | - build-and-test 27 | -------------------------------------------------------------------------------- /docs/team-standard/recommend-vuerouter.md: -------------------------------------------------------------------------------- 1 | # 推荐-Vue-Router写法 2 | 3 | 使用路由懒加载,实现方式是结合Vue异步组件和Webpack代码分割功能。 4 | 5 | 优点: 6 | 7 | * 减小包体积,提高加载速度 8 | * 当页面>20个时,组件定义需要拉到编辑器顶部才知道具体路径 9 | 10 | ### bad 11 | 12 | ``` js 13 | import IntentionList from '@/pages/intention/list' 14 | import Variable from '@/pages/variable' 15 | ... 16 | 17 | { 18 | path: '/intention/list', 19 | name: 'ilist', 20 | component: IntentionList 21 | }, 22 | { 23 | path: '/variable', 24 | name: 'variable', 25 | component: Variable 26 | } 27 | ``` 28 | 29 | ### good 30 | 31 | ``` js 32 | { 33 | path: '/intention/list', 34 | name: 'ilist', 35 | component: () => import('@/pages/intention/list') 36 | }, 37 | { 38 | path: '/variable', 39 | name: 'variable', 40 | component: () => import('@/pages/variable') 41 | } 42 | ``` 43 | 44 | > import语法需要Babel添加`syntax-dynamic-import`插件。最新当vue-cli 3.0中默认添加该特性,不需要额外引用。另外,合理控制异步模块的数量。 45 | -------------------------------------------------------------------------------- /docs/temp/react-hooks.md: -------------------------------------------------------------------------------- 1 | # React Hooks 2 | 3 | 利用 useState 创建 Redux 4 | 5 | ``` js 6 | // 这就是 Redux 7 | function useReducer(reducer, initialState) { 8 | const [state, setState] = useState(initialState); 9 | 10 | function dispatch(action) { 11 | const nextState = reducer(state, action); 12 | setState(nextState); 13 | } 14 | 15 | return [state, dispatch]; 16 | } 17 | // 一个 Action 18 | function useTodos() { 19 | const [todos, dispatch] = useReducer(todosReducer, []); 20 | 21 | function handleAddClick(text) { 22 | dispatch({ type: "add", text }); 23 | } 24 | 25 | return [todos, { handleAddClick }]; 26 | } 27 | 28 | // 绑定 Todos 的 UI 29 | function TodosUI() { 30 | const [todos, actions] = useTodos(); 31 | return ( 32 | <> 33 | {todos.map((todo, index) => ( 34 |
{todo.text}
35 | ))} 36 | 37 | 38 | ); 39 | } 40 | ``` -------------------------------------------------------------------------------- /docs/node/redis.md: -------------------------------------------------------------------------------- 1 | # Redis简介 2 | 3 | Redis是一个高性能的key-value数据库。Redis有以下特点: 4 | * 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 5 | * 不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储 6 | * 支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用 7 | 8 | **Redis像mysql、mongodb数据库一样,你需要安装它的环境,它有自己的语法;如果要使用java等后端语言对接,需要安装对应驱动包。** 9 | 10 | ## 常用语法 11 | 12 | * String 13 | * SET key value 14 | * GET key 15 | * Hash 16 | * HGET key field 17 | * HSET key field value 18 | * HDEL key field1 [field2] 19 | * HEXISTS key field 20 | * List 21 | * LPUSH key value1 [value2] 将一个或多个值插入到列表头部 22 | * LLEN key 获取列表长度 23 | * LPOP key 移出并获取列表的第一个元素 24 | * RPOP key 移除列表的最后一个元素,返回值为移除的元素。 25 | * LRANGE key start stop 获取列表指定范围内的元素 26 | * Set 27 | * SADD key member1 [member2] 向集合添加一个或多个成员 28 | * SMEMBERS key 返回集合中的所有成员 29 | * sorted set 30 | * ZADD key score1 member1 [score2 member2] 向有序集合添加一个或多个成员,或者更新已存在成员的分数 31 | * ZCARD key 获取有序集合的成员数 32 | -------------------------------------------------------------------------------- /docs/temp/react/immer.md: -------------------------------------------------------------------------------- 1 | # immer 2 | 3 | 无API形式的,把state修改为newState(纯函数),类似Vue可直接修改Object对象(原理也确实使用Proxy和Object.defineProperty) 4 | 5 | 解释和源码:https://zhuanlan.zhihu.com/p/122187278 6 | 7 | ## immer 降级方案 8 | ``` js 9 | // https://github.com/immerjs/immer/blob/439e4f1d7d/src/core/immerClass.ts 10 | export function createProxy( 11 | immer: Immer, 12 | value: T, 13 | parent?: ImmerState 14 | ): Drafted { 15 | // precondition: createProxy should be guarded by isDraftable, so we know we can safely draft 16 | const draft: Drafted = isMap(value) 17 | ? getPlugin("MapSet").proxyMap_(value, parent) 18 | : isSet(value) 19 | ? getPlugin("MapSet").proxySet_(value, parent) 20 | : immer.useProxies_ 21 | ? createProxyProxy(value, parent) 22 | : getPlugin("ES5").createES5Proxy_(value, parent) 23 | 24 | const scope = parent ? parent.scope_ : getCurrentScope() 25 | scope.drafts_.push(draft) 26 | return draft 27 | } 28 | ``` -------------------------------------------------------------------------------- /docs/read-books/book-design-for-all.md: -------------------------------------------------------------------------------- 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 | ![image](https://user-images.githubusercontent.com/6310131/51229437-6a8ea480-1997-11e9-8b38-725e604b970f.png) -------------------------------------------------------------------------------- /docs/temp/一图胜千言.md: -------------------------------------------------------------------------------- 1 | # 一图胜千言 2 | 3 | ## 原型 4 | ![](https://cdn-images-1.medium.com/max/1600/1*425LxRkFEldC5CJWyhZRBg.png) 5 | 6 | ``` js 7 | cat1.__proto__ === Cat.prototype 8 | // cat1的构造函数找不到,会去找原型中的构造函数(Cat.prototype.constructor),最终找到Cat 9 | cat1.constructor === Cat.prototype.constructor === Cat 10 | ``` 11 | ## 原型链 12 | 13 | ![](https://images2015.cnblogs.com/blog/752003/201701/752003-20170120135801843-1947643869.jpg) 14 | 15 | ## 前端优化 16 | ![](https://user-gold-cdn.xitu.io/2017/10/14/041436b6f1575010917b7bb6530cf507?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 17 | 18 | ## offsetTop/offsetHeight 19 | ![](https://images0.cnblogs.com/i/486719/201405/081742092766389.jpg) 20 | 21 | * offsetWidth/offsetHeight 对象的可见宽度 22 | * clientWidth/clientHeight 内容的可见宽度 23 | * scrollWidth/scrollHeight元素完整的高度和宽度,overflow:hidden的部分也计算在内。 24 | 25 | * offsetLeft/offsetTop 当前元素距浏览器边界的偏移量,以像素为单位。 26 | * clientTop/clientLeft 这个属性测试下来的结果就是border。 27 | * scrollLeft/scrollTop 设置或返回已经滚动到元素的左边界或上边界的像素数。 28 | 29 | ## 一语道破 30 | 31 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 100, // 设置prettier单行输出(不折行)的(最大)长度 3 | 4 | tabWidth: 4, // 设置工具每一个水平缩进的空格数 5 | 6 | useTabs: false, // 使用tab(制表位)缩进而非空格 7 | 8 | semi: false, // 在语句末尾添加分号 9 | 10 | singleQuote: true, // 使用单引号而非双引号 11 | 12 | trailingComma: 'none', // 在任何可能的多行中输入尾逗号 13 | 14 | bracketSpacing: true, // 在对象字面量声明所使用的的花括号后({)和前(})输出空格 15 | 16 | arrowParens: 'avoid', // 为单行箭头函数的参数添加圆括号,参数个数为1时可以省略圆括号 17 | 18 | // parser: 'typescript', // 指定使用哪一种解析器 19 | 20 | jsxBracketSameLine: true, // 在多行JSX元素最后一行的末尾添加 > 而使 > 单独一行(不适用于自闭和元素) 21 | 22 | rangeStart: 0, // 只格式化某个文件的一部分 23 | 24 | rangeEnd: Infinity, // 只格式化某个文件的一部分 25 | 26 | filepath: 'none', // 指定文件的输入路径,这将被用于解析器参照 27 | 28 | requirePragma: false, // (v1.7.0+) Prettier可以严格按照按照文件顶部的一些特殊的注释格式化代码,这些注释称为“require pragma”(必须杂注) 29 | 30 | insertPragma: false, // (v1.8.0+) Prettier可以在文件的顶部插入一个 @format的特殊注释,以表明改文件已经被Prettier格式化过了。 31 | 32 | proseWrap: 'preserve' // (v1.8.2+) 33 | } 34 | -------------------------------------------------------------------------------- /docs/temp/react-code-diff.md: -------------------------------------------------------------------------------- 1 | ``` js 2 | processUpdates: function(parentNode, updates) { 3 | for (var k = 0; k < updates.length; k++) { 4 | var update = updates[k]; 5 | switch (update.type) { 6 | case 'INSERT_MARKUP': 7 | insertLazyTreeChildAt( 8 | parentNode, 9 | update.content, 10 | getNodeAfter(parentNode, update.afterNode), 11 | ); 12 | break; 13 | case 'MOVE_EXISTING': 14 | moveChild( 15 | parentNode, 16 | update.fromNode, 17 | getNodeAfter(parentNode, update.afterNode), 18 | ); 19 | break; 20 | case 'SET_MARKUP': 21 | setInnerHTML(parentNode, update.content); 22 | break; 23 | case 'TEXT_CONTENT': 24 | setTextContent(parentNode, update.content); 25 | break; 26 | case 'REMOVE_NODE': 27 | removeChild(parentNode, update.fromNode); 28 | break; 29 | } 30 | } 31 | }, 32 | ``` -------------------------------------------------------------------------------- /docs/temp/react-code-3.event.md: -------------------------------------------------------------------------------- 1 | 2 | * ReactDOMComponent._updateDOMProperties(oldProps, props) 3 | * enqueuePutListener(this, propKey, nextProp, transaction); 4 | * listenTo(registrationName, doc); 5 | * 6 | * EventPluginHub.putListener 7 | 8 | ``` js 9 | var EventListener = { 10 | // 冒泡阶段执行事件 p -> body -> html 11 | listen: function listen(target, eventType, callback) { 12 | target.addEventListener(eventType, callback, false); 13 | return { 14 | remove: function remove() { 15 | target.removeEventListener(eventType, callback, false); 16 | } 17 | }; 18 | }, 19 | // 捕获阶段 html -> body -p 20 | capture: function capture(target, eventType, callback) { 21 | target.addEventListener(eventType, callback, true); 22 | return { 23 | remove: function remove() { 24 | target.removeEventListener(eventType, callback, true); 25 | } 26 | }; 27 | }, 28 | ``` 29 | 30 | ![](http://zhenhua-lee.github.io/img/react/react-event.jpg) -------------------------------------------------------------------------------- /docs/temp/ele-ui/ai组件库会议记录.md: -------------------------------------------------------------------------------- 1 | # 组件库讨论 2 | > 2018-11-20 3 | 4 | # 设计原则 5 | * size写法 6 | 1. attribute + css属性选择器 (兼容性较差) 7 | 2. class + 匹配对应css (*选用,优化class写法-格式化字符串) 8 | * type,列出具体的可选项 9 | * 颜色 10 | * 命名与赋值 11 | * 自定义的界限? 12 | * 命名方式统一 13 | 14 | # ai-link 15 | * 站内站外写法 16 | * 形式变为mixin供其他组件使用 17 | 18 | # ai-button 19 | * 扩展link功能后无法满足全部需求[改动见ai-link] 20 | 21 | # ai-icon 22 | * 组件内图标库,单独打包,放在外部 23 | * svg-icon作为推荐第三方组件 24 | 25 | # ai-input 26 | * mixin regex功能 27 | 28 | # ai-select 29 | * 多选暂放在一个组件中 30 | * render方法在组件内区分两种组件 31 | 32 | # ai-table 33 | * 考虑自定义表头的添加 34 | 35 | # ai-modal 36 | * 单例的修改 37 | * 复杂功能使用mixin 38 | 39 | # ai-layout 40 | * 在文档中说明,暂不改变css attrbiute方式 41 | 42 | # ai-checkbox 43 | * 添加v-model属性 44 | * chose事件名统一 45 | 46 | # ai-steps 47 | * 添加v-model 48 | 49 | # ai-upload 50 | * VIP功能:NOS上传 51 | 52 | # ai-switch 53 | * 需要从片段中移植进来 54 | * 添加v-model 55 | 56 | # ai-tree 57 | * 需要从片段中移植进来 58 | * 添加v-model 59 | 60 | # 第三方组件推荐 61 | * svg处理: svg-icon 62 | * 日期选择: vue-datepicker-local 63 | * 表单验证: vuelidate 64 | -------------------------------------------------------------------------------- /docs/.vuepress/public/circle.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | orbs: 3 | node: circleci/node@1.1.6 4 | jobs: 5 | build-and-test: 6 | executor: 7 | name: node/default 8 | steps: 9 | - add_ssh_keys: 10 | fingerprints: 11 | - '54:19:54:8f:1d:d3:d7:c3:8c:06:09:c0:b3:36:5e:08' 12 | - checkout 13 | - node/with-cache: 14 | steps: 15 | - run: npm install 16 | - run: npm run build 17 | - run: # git push 18 | name: push-github 19 | command: | 20 | git config --global user.email "782655835@qq.com" 21 | git config --global user.name "lq782655835" 22 | npm run ghpages 23 | workflows: 24 | build-and-test: 25 | jobs: 26 | - build-and-test: 27 | filters: 28 | branches: 29 | only: master 30 | -------------------------------------------------------------------------------- /docs/temp/standard-pullrequest.md: -------------------------------------------------------------------------------- 1 | 为何需要codereview 2 | 1. 消除显而易见的bug,消除测试。 3 | 1. 代码规范,包括风格、写法,学习优秀源码。 4 | 1. 产品内容至少两人了解,方便可能的合作。 5 | 6 | ## PR规范 7 | 8 | 1. 原则:一个pr只做一件事情,对应一个jira/issue(如果有)。 9 | 1. 提pr前先同步主干分支(一般为develop,分支规范看[AI前端Git规范]())。 10 | 1. 正文介绍这个pr大概做了哪些内容,贴上相关jira/issues链接(如果有),方便以后查阅。 11 | 1. @相关人员对你的pr进行review。 12 | 1. 针对别人的评论对你的代码做出修改或说明原因。 13 | 1. pr review完,评论“LGTM”。 14 | 15 | * 建议: 16 | 1. 建议从新项目启动就开始,小改动(hotfix、小需求等)可不提PR。 17 | 1. 当项目需要迭代上线时,可自行merge,审核人review不影响。 18 | * 技巧: 19 | 1. 正在做的任务标题前加`[WIP]`(此时无法被merge) 20 | 1. 一个pr如果有多次提交,rebase你的pr,保证pr只有一次commit,并且描述清晰。 21 | * 其他 22 | * [Creating a pull request](https://help.github.com/en/articles/creating-a-pull-request) 23 | * [Merging a pull request](https://help.github.com/en/articles/merging-a-pull-request) 24 | * [10 tips for better Pull Requests](https://blog.ploeh.dk/2015/01/15/10-tips-for-better-pull-requests/) 25 | * [Pull Request 书写指南](https://github.com/amio/pull-request-guide) 26 | 27 | > LGTM = Look Good To Me! 28 | 29 | > [WIP] = Work In Process 30 | -------------------------------------------------------------------------------- /docs/tools/vscode.md: -------------------------------------------------------------------------------- 1 | # VSCode快捷键及常用插件 2 | 3 | >本文集合VSCode常用的快捷键和插件,希望能提高读者的开发效率。 4 | 5 | * 快捷键 6 | * 常用插件 7 | 8 | ## 快捷键 9 | 10 | #### `Command + shift + P` 11 | 12 | 打开命令面板,可以执行VSCode的任何一条命令 13 | 14 |  `Command + P` 15 | 16 | 1. 直接输入文件名,快速打开文件 17 | 1. ? 列出当前可执行的动作 18 | 1. ! 显示Errors或Warnings,也可以Ctrl+Shift+M 19 | 1. : 跳转到行数,也可以Ctrl+G直接进入 20 | 1. @ 跳转到symbol(搜索变量或者函数),也可以Ctrl+Shift+O直接进入 21 | 1. @:根据分类跳转symbol,查找属性或函数,也可以Ctrl+Shift+O后输入:进入 22 | 1. \# 根据名字查找symbol,也可以Ctrl+T 23 | 1. \> 可以回到主命令框模式 24 | 25 | `Commond + +`  字体调大一号 26 | `Commond + -`  字体调小一号 27 | 28 | ## 常用插件 29 | 30 | ### Common 31 | 32 | 1. Eslint 33 | 34 | > eslint代码检查插件,注意前提需要安装eslint  `npm install eslint -g` 35 | 36 | 1. stylelint 37 | 38 | 1. Project Manager 39 | 40 | 1. Auto Import 41 | 42 | 1. Auto Const 43 | 44 | 1. Auto Close Tag 45 | 46 | 1. HTML Snippets 47 | 48 | 1. vscode-icons 49 | 50 | 1. Auto-Open Markdown Preview 51 | 52 | 1. SVG Viewer 53 | 54 | 1. highlight-matching-tag 55 | 56 | 1. Sort lines 57 | 58 | 1. Markdown PDF 59 | 60 | ### Vue 61 | 1. Vetur (推荐) 62 | 1. VueHelper 63 | 64 | ### React 65 | 1. React-Native/React/Redux snippets for es6/es7 -------------------------------------------------------------------------------- /docs/temp/前端监控搭建调研.md: -------------------------------------------------------------------------------- 1 | # 前端监控搭建 2 | 3 | 两种模式:`监控数据存储在自己服务器`和`监控数据存储在第三方服务器`。 4 | 5 | 如果是监控数据`存储在自己服务器`,意味着自己要搭建数据库和后台,好处是所有监控都可以自己control。 6 | 7 | 如果是监控数据`存储在第三方服务器`,优点是无需搭建数据库,只需要接入api即可,同时提供web面板查看 8 | 9 | ## 1.监控数据存储在自己服务器 10 | 11 | ### 1.1 web-monitoring:https://github.com/kisslove/web-monitoring 12 | 13 | * 前端:Angular5+,ant-design 14 | * 后端:Nodejs+Express 15 | * 数据库:Mongoose+MongoDB 16 | 17 | ### 1.2 scs-monitor:https://github.com/scscms/scs-monitor 18 | 19 | 技术栈:Nodejs + Koa + Vue + Mysql 20 | 21 | ### 1.3 bombayjs:https://github.com/bombayjs/bombayjs 22 | 23 | bombayjs是前端监控解决方案,包括bombayjs、bombayjs-server、bombayjs-admin三个项目 24 | 25 | 采用eggjs + mongodb + redis + kafka + elk架构 26 | 27 | 项目地址: 28 | 29 | * https://github.com/bombayjs/bombayjs (web sdk) 30 | * https://github.com/bombayjs/bombayjs-server (服务端,用于提供api) 31 | * https://github.com/bombayjs/bombayjs-admin (后台管理系统,可视化数据等) 32 | 33 | ## 2. 监控数据存储在第三方服务器 34 | 35 | ### 2.1 sentry(开源,可自己搭建私有服务器):https://github.com/getsentry/sentry 36 | 37 | ### 2.2 阿里云前端监控:https://help.aliyun.com/document_detail/66404.html?spm=a2c4g.11186623.6.667.675154516X9GJd 38 | 39 | ### 2.3 GoogleAnalysis(直接使用web产品) 40 | 41 | ### 2.4 百度统计(直接使用web产品) -------------------------------------------------------------------------------- /docs/temp/web-components.md: -------------------------------------------------------------------------------- 1 | # Web Components 2 | 3 | Web Components 是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的web应用中使用它们。 4 | 5 | * customElements是Window对象上的一个只读属性,接口返回一个CustomElementRegistry 对象的引用, 可以用于注册一个新的custom elements ,并且可以用于获取之前定义过的自定义元素的信息。 6 | 7 | ``` js 8 | window.customElements.define('element-details', 9 | class extends HTMLElement { 10 | constructor() { 11 | super(); 12 | const template = document 13 | .getElementById('element-details-template') 14 | .content; 15 | const shadowRoot = this.attachShadow({mode: 'open'}) 16 | .appendChild(template.cloneNode(true)); 17 | } 18 | }); 19 | ``` 20 | 21 | > 根据规范,自定义元素的名称必须包含连词线,用与区别原生的 HTML 元素。 22 | 23 | ## 使用生命周期回调函数 24 | 25 | 在custom element的构造函数中,可以指定多个不同的回调函数,它们将会在元素的不同生命时期被调用: 26 | 27 | * connectedCallback:当 custom element首次被插入文档DOM时,被调用。 28 | * disconnectedCallback:当 custom element从文档DOM中删除时,被调用。 29 | * adoptedCallback:当 custom element被移动到新的文档时,被调用。 30 | * attributeChangedCallback: 当 custom element增加、删除、修改自身属性时,被调用。 31 | 32 | ## 参考文章 33 | 34 | * [Web Components 入门实例教程](http://www.ruanyifeng.com/blog/2019/08/web_components.html) 35 | * [Web Components Tutorial for Beginners [2019]](https://www.robinwieruch.de/web-components-tutorial) -------------------------------------------------------------------------------- /docs/team-standard/recommend-vue-api-order.md: -------------------------------------------------------------------------------- 1 | # 推荐-Vue实例选项顺序 2 | 3 | > 在Vue中,export default对象中有很多约定的API Key。每个人的顺序排放都可能不一致,但保持统一的代码风格有助于团队成员多人协作。 4 | 5 | Vue官网文档中也有推荐[顺序](https://cn.vuejs.org/v2/style-guide/index.html#%E7%BB%84%E4%BB%B6-%E5%AE%9E%E4%BE%8B%E7%9A%84%E9%80%89%E9%A1%B9%E7%9A%84%E9%A1%BA%E5%BA%8F-%E6%8E%A8%E8%8D%90),文档中对选项顺序做了许多分类。但从工程项目角度思考,需要更加精简以及合理的排序。推荐如下规则进行排序: 6 | 7 | 1. Vue扩展: extends, mixins, components 8 | 1. Vue数据: props, model, data, computed, watch 9 | 1. Vue资源: filters, directives 10 | 1. Vue生命周期: created, mounted, destroy... 11 | 1. Vue方法: methods 12 | 13 | 以下推荐顺序,基于团队小伙伴@akimoto整理的顺序: 14 | 15 | ``` js 16 | export default { 17 | name: '', 18 | /*1. Vue扩展 */ 19 | extends: '', // extends和mixins都扩展逻辑,需要重点放前面 20 | mixins: [], 21 | components: {}, 22 | /* 2. Vue数据 */ 23 | props: {}, 24 | model: { prop: '', event: '' }, // model 会使用到 props 25 | data () { 26 | return {} 27 | }, 28 | computed: {}, 29 | watch:{}, // watch 监控的是 props 和 data,有必要时监控computed 30 | /* 3. Vue资源 */ 31 | filters: {}, 32 | directives: {}, 33 | /* 4. Vue生命周期 */ 34 | created () {}, 35 | mounted () {}, 36 | destroy () {}, 37 | /* 5. Vue方法 */ 38 | methods: {}, // all the methods should be put here in the last 39 | } 40 | ``` -------------------------------------------------------------------------------- /docs/temp/beta/photography.md: -------------------------------------------------------------------------------- 1 | # 摄影总结 2 | 3 | 成像原理来自“小孔成像”,将外界的光线保存在感光元器内,再模拟转换为数码信号进行保存。快门就是控制记录的入口。 4 | 成像三要素:光圈 + 快门 + ISO。 5 | 6 | ![](https://pic1.zhimg.com/80/v2-a7aaac7dc6ade101f9ffb3e8b373e778_hd.jpg) 7 | 8 | ## 1. 基本认识 9 | 10 | ### 认识相机 11 | 12 | * `光圈优先`:光圈就是镜头的进光口,大小决定在单位时间内进去的光量。光圈越大,代表开孔越大,进光量多,快门相对应自动越大。 13 | * F4以下是大光圈,F4-8为中等光圈,F8以上是小光圈。 14 | * `快门优先`:快门越快,说明拍摄时间短(进光量小),所以需要光圈自动调节到足够大。 15 | * 快门是控制镜头打开和关闭的,可以控制进光量时间长短。 16 | * `ISO`:对光的灵敏度。越高质量越差。日光充足,尽量选100-400 17 | * 当光圈和快门调到极限,都无法满足获得正常曝光的照片时,这时适合使用ISO。 18 | * `曝光补偿`:在光线不足/过足时,进行调节。(正数则白的越白,负数则黑的越黑) 19 | * `测光模式`:常见有平均测光模式、点测光模式、中央重点模式。 20 | * 平均测光原理:根据画幅所有测光相加之后,进行加权平均(焦点处权重高),这样使得整体明暗差别不会太大。 21 | * 点测光原理:保证焦点位置曝光正常,其他地方不会过多考虑。 22 | * 大部分场景适合使用平均测光,如风景照,照片整体明暗深浅差不多,但逆光适合不适合(强暗分布太不均匀);点测光适合突出主题,如人像、夕阳剪影,因为聚焦的地方曝光正常,而比它亮的地方过曝,比它暗很多的地方就会漆黑一片。所以在拍摄夕阳剪影时,使用点测光+焦点在天空。 23 | 24 | > 拍摄风景照阳光充足时,可减小光圈(星芒效果)+ ISO提高。 25 | 26 | ### 光圈与景深 27 | 28 | **景深与光圈、焦距(镜头决定)、拍摄距离有关**。控制其他条件不变,三者对景深的影响总结如下: 29 | 30 | 光圈越大,景深越小,适合做背景虚化效果,如人像;反之光圈越小,景深越大。 31 | 焦距对景深也有影响,通常焦距越大,景深越小,如长焦镜头的景深比较小,而广角镜头的景深都比较大。 32 | 拍摄距离越远,景深越大;距离越近,景深越小。 33 | 34 | * 光圈大 =数字小 =背景模糊/景深浅 =普通拍人像 35 | * 光圈小 =数字大 =背景清楚/景深深 =普通拍风景 36 | 37 | ## 2. 构图 38 | 39 | **原则:突出照片主旨,适当进行留白,线条不要太混乱。** 40 | 41 | * 井字构图 42 | * 三分线构图 43 | * 延伸线构图 44 | 45 | 人像技巧:人像拍照时,脚贴边,更显腿长;单腿往前伸,利用进大远小构造大长腿。 -------------------------------------------------------------------------------- /docs/temp/jsPlumb.md: -------------------------------------------------------------------------------- 1 | # jsPlumb 2 | 3 | ## 基本概念 4 | 5 | * Source: 源对象。jsPlumb 通过元素的 id 属性获取对象。 6 | 7 | * Target: 目标对象。jsPlumb 通过元素的 id 属性获取对象。 8 | 9 | * Source 和 Target 都可以是任何元素,区别是,Source 是起点,Target 是终点。 例如,connector 中的箭头总是从 Source 指向 Target。 10 | 11 | * Anchor:锚点。是 Source 和 Target 对象上可以连接 Connector 的点。Anchor 并不是一个视觉概念,它是不可见的。 12 | 13 | * Connector: 连接线。 14 | 15 | * Endpoint: 端点。需要注意的是,箭头并不是一种端点样式,它是通过 overlay 添加的。 16 | 17 | * Overlay: 添加到连接线上的附件。例如箭头和标签。 18 | 19 | ![](https://github.com/wangduanduan/jsplumb-chinese-tutorial/blob/master/images/20180227151857_Pu4O9c_jsPlumb-Connector-Components.jpeg) 20 | 21 | 22 | ## [Event](http://jsplumb.github.io/jsplumb/events.html) 23 | 24 | endpoint:支持自定义设置样式、设置isSource/isTarget、默认的可connect连接 + 回调。 25 | 26 | * connection(info, originalEvent): 新的连接事件回调 27 | * connectionDrag:connection 28 | * connectionDragStop:connection 29 | * connectionDragMoved:params.connection 30 | * beforeDetach:connection 31 | connectionDetached(info, originalEvent): 断开时事件回调 32 | * click:connection 33 | 34 | ### 方法 35 | 36 | * instance.batch(function() {}) 37 | * instance.addEndpoint(...args) 38 | 39 | * instance.connect({uuids}) 编码连线 40 | * instance.draggable(target, setting) 可拖拽node节点 41 | 42 | ### connection 43 | 44 | connection.getOverlay('label').setLabel(str) 45 | 46 | ## 参考 47 | 48 | https://www.cnblogs.com/ysx215/p/7615677.html -------------------------------------------------------------------------------- /docs/temp/node-base-code.md: -------------------------------------------------------------------------------- 1 | 2 | ## req 3 | 原生req属性 4 | * req.url 完整URL地址,包括路径和查询部分,hash部分会丢弃。在请求报文第一行 5 | * req.method 请求报文第一行第一个单词 6 | ``` js 7 | const url = require('url) 8 | const querystring = require('querystring) 9 | // 类express实现源码 10 | // https://github.com/expressjs/express/blob/master/lib/request.js#L412 11 | req.path = url.parse(req.url).pathname 12 | req.query = url.parse(req.url, true).query 13 | //或 req.query = querystring.parse(url.parse(req.url)).query 14 | ``` 15 | 16 | ``` js 17 | // 类body-parse实现源码 18 | // 拿到raw数据 19 | // row-body源码:https://github.com/stream-utils/raw-body/blob/master/index.js#L240 20 | function (req, res, next) { 21 | if (hasBody(req)) { 22 | var buffers = [] 23 | req.on('data', chunk => buffers.push(chunk)) 24 | req.on('end', () => { 25 | req.rawBody = Buffer.concat(buffers).toString() 26 | next() 27 | }) 28 | } else { 29 | next() 30 | } 31 | } 32 | 33 | // json处理 34 | // body-parse源码:https://github.com/expressjs/body-parser/blob/master/lib/types/json.js#L89 35 | function(req, res, next) { 36 | if (mime(req) === 'application/json') { 37 | req.body = JSON.parse(req.rawBody) 38 | next() 39 | } 40 | 41 | next() 42 | } 43 | ``` 44 | 45 | ## 获取工具当前路径 46 | 47 | ``` js 48 | const appDirectory = fs.realpathSync(process.cwd()) 49 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath) 50 | ``` -------------------------------------------------------------------------------- /docs/temp/solution/npm-upgrad-latest.md: -------------------------------------------------------------------------------- 1 | # 记一次npm升级最新包过程 2 | 3 | ## 问题: 4 | node以前版本是8.6.0,匹配的npm是5.4。npm全局安装包目录会在`/usr/local/Cellar/node/`,生成command命令会软链到`/usr/local/bin`(这也是~/.ssh/.zshrc里指定的命令查询路径,所以可以不输入完整路径即可找到)。 5 | 6 | 但有次macOS系统升级了,node版本也自动升级到v13.6版本了,所以导致npm install -g vusion会把vusion包放在新的node@v13.6包下的node_modules位置。不会覆盖以前node@v5.4目录下安装的vusion。 7 | 8 | 但npm没有对应的升级。 9 | 10 | ## 分析 11 | 12 | 使用某个版本(@version)的node时,执行npm install -g,会把包放在/usr/local/Cellar/node/@verson/lib/node_modules下,同时生成软链接到`/usr/local/bin`。 13 | 当切换node版本时,再想升级以前的包,就找不到对应位置了,所以此时要想升级vusion包,只能先删除以前的,再到新的node环境新建个新的。 14 | 15 | 16 | ## 解决方案 17 | 18 | 首先要升级npm,这是必需的。因为node已经为新的了,npm最好升级下。 19 | 20 | 把以前的vusion包以及软链接删除,再在新的node下安装新的vusion包以及软链。 21 | 22 | 1. 切换到指定的node@13.6,升级npm:(node都升级了,npm最好也升级下) 23 | ``` 24 | sudo npm install -g npm 25 | ``` 26 | 2. 删除老版本vusion包以及对应软链接。 27 | ``` 28 | rm -rf /usr/local/bin/vusion // 删除软链接 29 | rm -rf /usr/local/Cellar/node/8.6.0/lib/node_modules/vusion //删除源文件 或者nvm use 8.6.0 -> npm uninstall -g vusion 30 | ``` 31 | 3. 创建新的vusion包 32 | ``` 33 | nvm use 13.6 34 | npm install -g vusion 35 | ``` 36 | 4. ~/.zshrc下手动绑定软链/usr/local/Cellar/node/13.6.0/bin/ 37 | ``` 38 | source ~/.zshrc // 使得.zshrc有效 39 | ``` 40 | 41 | 42 | /usr/local/Cellar/node/13.6.0/bin/vusion -> /usr/local/Cellar/node/13.6.0/lib/node_modules/vusion/bin/vusion 43 | /Users/liaoqiao/.nvm/versions/node/v12.13.0/bin/vusion -> /Users/liaoqiao/.nvm/versions/node/v12.13.0/lib/node_modules/vusion/bin/vusion -------------------------------------------------------------------------------- /docs/team-standard/0.standard-ai-summary.md: -------------------------------------------------------------------------------- 1 | # 前端团队规范总结 2 | 3 | 团队规范对于团队合作非常重要,大大减少项目之间的沟通,提高整体解决方案。有些规范能让lint做强制审核的,尽量用lint;有些需要约定俗成的,则记录成文档形成规范。 4 | 5 | ### 工程化 6 | 7 | * [yiai-cli脚手架](https://github.com/lq782655835/yiai-cli) 8 | 9 | * [Vue标准项目](https://github.com/lq782655835/standard-vue-project) 10 | 11 | * [Vue Electron标准客户端项目](https://github.com/lq782655835/electron-vue-template) 12 | 13 | * [Vue Nuxt标准官网SSR项目](https://github.com/lq782655835/official-website-template) 14 | 15 | * [Vue 小程序标准项目](https://github.com/lq782655835/mpvue-project) 16 | * [AI前端工程工具链](https://github.com/lq782655835/blogs/issues/8) 17 | 18 | ### 规范 19 | 20 | * [AI前端JS规范](https://github.com/lq782655835/blogs/issues/24) 21 | 22 | * [AI前端CSS规范](https://github.com/lq782655835/blogs/issues/25) 23 | 24 | * [AI前端Vue规范](https://github.com/lq782655835/blogs/issues/26) 25 | 26 | * [AI前端Git规范](https://github.com/lq782655835/blogs/issues/27) 27 | 28 | ### 代码风格 29 | * [AI JavaScript 风格指南](./clean-code-javascript.md) 30 | ### 推荐 31 | 32 | * [Vue项目目录结构推荐](https://github.com/lq782655835/blogs/issues/28) 33 | 34 | * [Vue实例选项顺序推荐](https://github.com/lq782655835/blogs/issues/12) 35 | 36 | * [Vue-Router写法推荐](https://github.com/lq782655835/blogs/issues/20) 37 | 38 | * [200错误统一处理推荐](https://github.com/lq782655835/blogs/issues/21) 39 | 40 | * [优雅引用字体推荐](./recommend-css-font.md) 41 | ## 预览 42 | ![image](https://user-images.githubusercontent.com/6310131/46003078-3f2f3380-c0e2-11e8-9625-798f19c498da.png) 43 | -------------------------------------------------------------------------------- /docs/temp/http-perfermance.md: -------------------------------------------------------------------------------- 1 | # perfermance API 2 | 3 | ## performance.mark() 4 | 5 | 根据标准,调用 performance.mark(markName) 时,发生了如下几步: 6 | 7 | * 创建一个新的 PerformanceMark 对象(以下称为条目); 8 | * 将 name 属性设置为 markName; 9 | * 将 entryType 属性设置为 'mark'; 10 | * 将 startTime 属性设置为 performance.now() 的值; 11 | * 将 duration 属性设置为 0; 12 | * 将条目放入队列中; 13 | * 将条目加入到 performance entry buffer 中; 14 | * 返回 undefined 15 | 16 | 上述过程,可以简单理解为,“请浏览器记录一条名为 markName 的性能记录” 17 | 18 | ## performance.measure() 19 | 20 | mark可以理解为一个打点数据,只记录当前打点的时间戳记录。 21 | 但更多时候我们需要记录从 A点-> B点的during。所以可以在两个mark时间戳后,调用measure,自动计算两个点的运行时长差值。 22 | 23 | ## performance.getEntriesByName() 24 | 25 | mark() 和 measure() 两个api都是打点记录。如果用户要查看,使用getEntriesByName() 方法即可拿到详细信息。 26 | 27 | ``` js 28 | const measure = (fn, name = fn.name) => { 29 | const startName = prefixStart(name) 30 | const endName = prefixEnd(name) 31 | performance.mark(startName) 32 | fn() 33 | performance.mark(endName) 34 | // 调用 measure 35 | performance.measure(name, startName, endName) 36 | } 37 | const getDuration = entries => { 38 | // 直接获取 duration 39 | const [{ duration }] = entries 40 | return duration 41 | } 42 | const retrieveResult = key => getDuration(performance.getEntriesByName(key)) 43 | 44 | // 使用时 45 | function foo() { 46 | // some code 47 | } 48 | measure(foo) 49 | const duration = retrieveResult('foo') 50 | console.log('duration of foo is:', duration) 51 | ``` 52 | 53 | ## 参考 54 | 55 | * https://juejin.im/post/5b7a51886fb9a019ea01f593 56 | -------------------------------------------------------------------------------- /docs/team-standard/1.standard-ai-git.md: -------------------------------------------------------------------------------- 1 | # AI前端Git规范 2 | 3 | ### Git分支命名 4 | 5 | * master:主分支,负责记录上线版本的迭代,该分支代码与线上代码是完全一致的。 6 | * develop:开发分支,该分支记录相对稳定的版本,所有的feature分支和bugfix分支都从该分支创建。其它分支为短期分支,其完成功能开发之后需要删除 7 | * feature/*:特性(功能)分支,用于开发新的功能,不同的功能创建不同的功能分支,功能分支开发完成并自测通过之后,需要合并到 develop 分支,之后删除该分支。 8 | * bugfix/*:bug修复分支,用于修复不紧急的bug,普通bug均需要创建bugfix分支开发,开发完成自测没问题后合并到 develop 分支后,删除该分支。 9 | * release/*:发布分支,用于代码上线准备,该分支从develop分支创建,创建之后由测试同学发布到测试环境进行测试,测试过程中发现bug需要开发人员在该release分支上进行bug修复,所有bug修复完后,在上线之前,需要合并该release分支到master分支和develop分支。 10 | * hotfix/*:紧急bug修复分支,该分支只有在紧急情况下使用,从master分支创建,用于紧急修复线上bug,修复完成后,需要合并该分支到master分支以便上线,同时需要再合并到develop分支。 11 | 12 | ![](https://user-gold-cdn.xitu.io/2018/7/9/1647e5710a461adc?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 13 | 14 | ### Git Commit Message格式 15 | type : subject 16 | 17 | #### type 提交类型: 18 | * feature: 新特性 19 | * fix: 修改问题 20 | * style: 代码格式修改 21 | * test: 测试用例修改 22 | * docs: 文档修改 23 | * refactor: 代码重构 24 | * misc: 其他修改, 比如构建流程, 依赖管理 25 | 26 | #### subject 提交描述 27 | 对应内容是commit 目的的简短描述,一般不超过50个字符 28 | 29 | ### 参考链接 30 | 31 | * [A successful Git branching model](https://nvie.com/posts/a-successful-git-branching-model/ 32 | ) 33 | * [约定式提交](https://www.conventionalcommits.org/zh/v1.0.0-beta.2/) 34 | * [必须知道的 Git 分支开发规范](https://juejin.im/post/5b4328bbf265da0fa21a6820) 35 | * [Git 在团队中的最佳实践--如何正确使用Git Flow](http://www.open-open.com/lib/view/open1451353135339.html) 36 | * [优雅的提交你的 Git Commit Message](https://zhuanlan.zhihu.com/p/34223150) -------------------------------------------------------------------------------- /docs/js/different-for-in-for-of.md: -------------------------------------------------------------------------------- 1 | # for in和for of区别 2 | 3 | ## for in 4 | 5 | 以任意顺序遍历一个对象的[可枚举属性](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Enumerability_and_ownership_of_properties)。遍历数组时,key为数组下标字符串;遍历对象,key为对象字段名。 6 | 7 | ### 数组 8 | 9 | ``` js 10 | let arr = [{age: 1}, {age: 5}, {age: 100}, {age: 34}] 11 | for (let key in arr) { 12 | console.log(key, arr[key]) 13 | } 14 | // 打印 15 | // 0 {age: 1} 16 | // 1 {age: 5} 17 | // 2 {age: 100} 18 | // 3 {age: 34} 19 | ``` 20 | 21 | ### 对象 22 | 23 | ``` js 24 | let obj = {f1: 'test1', f2: 'test2'} 25 | for (let key in obj) { 26 | console.log(key, obj[key]) 27 | } 28 | // 打印 29 | // f1 test1 30 | // f2 test2 31 | ``` 32 | 33 | ### for in 缺点 34 | 35 | 1. for in 迭代顺序依赖于执行环境,不一定保证顺序 36 | 1. for in 不仅会遍历当前对象,还包括原型链上的可枚举属性 37 | 1. for in 没有break中断 38 | 1. for in 不适合遍历数组,主要应用为对象 39 | 40 | ## for of 41 | 42 | ES6引入的新语法。在[可迭代对象](Iteration_protocols)(包括 Array,Map,Set,String,TypedArray,arguments对象,NodeList对象)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。 43 | 44 | `Object对象不是可迭代对象,故for of 不支持。`for of有个很大的特点是支持数组的break中断。 45 | 46 | ### 数组 47 | 48 | ``` js 49 | let arr = [{age: 1}, {age: 5}, {age: 100}, {age: 34}] 50 | for(let {age} of arr) { 51 | if (age > 10) { 52 | break // for of 允许中断 53 | } 54 | console.log(age) 55 | } 56 | // 打印 57 | // 1 58 | // 5 59 | ``` 60 | 61 | ### for of 优点 62 | 63 | 1. for of 有与for in 一样的简洁语法(这也是两者容易混乱的点),但没有for in的缺点 64 | 1. for of 保证顺序且不会仅遍历当前对象 65 | 1. for of 可与break,continue,return配合 66 | -------------------------------------------------------------------------------- /docs/project/puppeteer.md: -------------------------------------------------------------------------------- 1 | # Puppeteer翻页爬虫 2 | 3 | > Puppeteer(中文翻译”木偶”) 是 Google Chrome 团队官方的无界面Chrome 工具。 4 | 5 | [pagination-crawler](https://github.com/lq782655835/crawler/blob/master/pagination-crawler.js) 是一个使用puppeteer操控搜狗输入法页面翻页,爬取每个页面数据的小爬虫。 6 | 7 | ## 常用API 8 | 9 | page.$ 和page.$$区别:page.$ 等于同于document.querySelector,page.$$ 等同于 document.querySelectorAll。同理page.$eval和page.$$eval区别 10 | 11 | page.$ 和page.$eval区别:page.$返回elementHandle,这是puppteer包装的对象(非dom对象),而page.$eval第二个参数是根据第一个参数选择拿到的DOM对象。相对来说,page.$eval有更大的dom操作能力 12 | 13 | 14 | 15 | * 开始 16 | * browers = await puppeteer.launch({headless:bool}) 17 | * page = await browers.newPage() 18 | * page.goto(url) 19 | * browers.close() 20 | * page 21 | * page.waitFor(2000 or '.selector') 等待时间或某个元素出现 22 | * page.waitForSelector('.selector').then(() => todo) 等待元素出现后执行todo 23 | * page.click('.selector') 24 | 25 | * 获取/操作页面元素 26 | * 获得被包装的elementHandle值 27 | * page.$('.el') 被包装成elementHandle的document.querySelector值 28 | * page.$('.el') 被包装成elementHandle的document.querySelectorAll值 29 | * 选择,第二个参数为dom 30 | * page.$eval('.el', dom => dom) 获取document.querySelector值后,在浏览器环境处理 31 | * await page.$$eval('.el', doms => doms) 获取document.querySelectorAll值后,在浏览器环境处理 32 | * 执行脚本 类似于在控制台中执行指令 33 | * await page.evaluate(body => body.innerHTML, await page.$('body')); 浏览器环境下执行的代码 34 | * 拦截请求 35 | * page.setRequestInterception(true) 开启拦截请求 36 | * page.on('request',interceptedRequest => interceptedRequest.abort()) 拦截逻辑 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blogs", 3 | "version": "1.0.0", 4 | "repository": "https://github.com/lq782655835/blogs.git", 5 | "author": "782655835@qq.com", 6 | "scripts": { 7 | "dev": "npm run config && vuepress dev docs", 8 | "build": "npm run config && vuepress build docs", 9 | "config": "npm run auto && npm run format", 10 | "auto": "vuepress-auto-read --blogUrl=https://lq782655835.github.io/blogs", 11 | "format": "prettier --write './**/*.{js,ts,vue,json}'", 12 | "ghpages": "gh-pages -d docs/.vuepress/dist" 13 | }, 14 | "folderTitleMap": { 15 | "team-standard": "团队规范", 16 | "project": "Project", 17 | "js": "JS", 18 | "node": "NodeJS", 19 | "vue": "Vue", 20 | "react": "React", 21 | "tools": "效率工具", 22 | "read-books": "读书笔记" 23 | }, 24 | "license": "MIT", 25 | "devDependencies": { 26 | "@vuepress/plugin-back-to-top": "^1.0.0-alpha.0", 27 | "@vuepress/plugin-google-analytics": "^1.2.0", 28 | "@vuepress/plugin-medium-zoom": "^1.0.0-alpha.0", 29 | "gh-pages": "^2.2.0", 30 | "node-vuepress-auto-read": "^0.1.0", 31 | "prettier": "^1.15.3", 32 | "vuepress": "1.2.0", 33 | "vuepress-plugin-code-copy": "^1.0.4", 34 | "vuepress-plugin-flowchart": "^1.4.3", 35 | "vuepress-plugin-reading-progress": "^1.0.8", 36 | "vuepress-theme-vuesax": "^1.0.0" 37 | }, 38 | "dependencies": { 39 | "vuepress-theme-antdocs": "^1.0.1" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /docs/temp/solution/house-select.md: -------------------------------------------------------------------------------- 1 | # 久尚云筑选房评测 2 | 3 | ![](https://pic2.zhimg.com/80/v2-6d8c8cb6950532112979175516fee3ed_hd.jpg) 4 | 5 | 基本信息: 6 | 2,3,7三栋楼总共184套房(124套100平以下),选房顺序125号。 7 | 7栋楼靠中间,2、3靠马路。建议楼栋次序:7>2>3 8 | 9 | * 小区门口 10 | * 有门禁、自己的物业 11 | * 物业费3.6,小贵 12 | * 小区内 13 | * 人车分流 14 | * 带跑道 15 | * 容积率2.2,得房率83%(97*83%=81,赠送面积7平),实际大概88平左右 16 | * 2020年12月交房,精装修3500 17 | * 周边配套 18 | * 地铁:翁梅站,走路800米(10min)到 19 | * 商场:艺尚魔法,18w平规划,已开了一半,剩余一半马上开 20 | * 学校:杭师大文正小学(在楼盘东面,正对)、幼儿园较多 21 | * 规划地块的三甲医院(在楼盘东面,正对)、公园 22 | * 在建的菜市场(在楼盘西面,正对)、高中等 23 | 24 | 缺点: 25 | * 周边公交少,都是局部公交。只依赖地铁出行 26 | * 地铁9号线,10min一趟 27 | * 距离地铁走路800m(10min步行) 28 | * 纯居住盘,没有楼盘底下商业。对面小区有底商 29 | * 医院和大型公园都是规划用地,落成没那么快 30 | 31 | 总结:局部居住小圈子,有生活配套和教育资源,但没有产业,周边交通依赖地铁,界面布局被三条高速切割。 32 | 33 | 34 | 35 | > 一般而言,容积率分为 独立别墅为0.2~0.5, 联排别墅为0.4~0.7, 6层以下多层住宅为0.8~1.2, 11层小高层住宅为1.5~2.0, 18层高层住宅为1.8~2.5, 19层以上住宅为2.4~4.5 36 | 37 | ## 买房流程 38 | 39 | ## 材料准备 40 | 41 | 合作银行:中行,5.39利率,省直公积金贷款 42 | 43 | 1. 选好准确房间号,付定金,回家准备首付吧(一般首付要3-7个工作日付完) 44 | 1. 材料准备: 45 | * 银行贷款资料: 46 | * 身份证 原件、复印件 47 | * 户口本(含首页,户主页,本人页) 原件、复印件 48 | * 最近征信报告 复印件 49 | * 最近银行流水 原件 50 | * 收入证明 原件 51 | * 无房证明 复印件 52 | * 社保证明 复印件 53 | * 首付证明(这个在楼盘办理首付后,可以马上拿到) 54 | * 省直公积金贷款资料:(办理地点:凤起路169号) 55 | * 公积金对账单 56 | * 存贷证明 57 | 2. 去楼盘付首付(最主要的),然后一般有驻场银行马上办贷款 58 | * 先向开发商付完首付,记得给自己卡设置较高的POS限额 59 | * 付完首付马上办理贷款,主要是交以上材料 + 签各种文件,接下来就是银行审批 60 | 3. 由于目前是网签,所以等一段时间楼盘销售人员找你过去签正式合同 61 | 62 | 办理公积金贷款需要材料:?? 63 | * 身份证 原件、复印件 64 | * 户口本(含首页,户主页,本人页) 原件、复印件 65 | * 收入证明 原件 66 | *《商品房房买卖合同》 67 | * 预付款收据原件及复印件 -------------------------------------------------------------------------------- /docs/temp/react/useMemo-useCallback.md: -------------------------------------------------------------------------------- 1 | # 什么时候使用useMemo和useCallback 2 | 3 | ## Hooks性能问题举例 4 | 5 | 以下例子,每次渲染都会重新执行`buzz(options)`副作用,而不管options里的bar/baz是否有变化。 6 | ``` js 7 | function Foo({bar, baz}) { 8 | const options = {bar, baz} 9 | React.useEffect(() => { 10 | buzz(options) 11 | }, [options]) // we want this to re-run if bar or baz change 12 | return
foobar
13 | } 14 | 15 | function Blub() { 16 | return 17 | } 18 | ``` 19 | 20 | 这里有问题的原因是因为 useEffect 将对每次渲染中对 options 进行引用相等性检查,并且由于JavaScript的工作方式,每次渲染 options 都是新的,所以当React测试 options 是否在渲染之间发生变化时,它将始终计算为 true,意味着每次渲染后都会调用 useEffect 回调,而不是仅在 bar 和 baz 更改时调用。(本质上还是JS语言:{} != {}) 21 | 22 | ## 优化方案1 23 | 24 | 只需要把bar/baz提取成依赖项即可: 25 | 26 | ``` js 27 | // option 1 28 | function Foo({bar, baz}) { 29 | React.useEffect(() => { 30 | const options = {bar, baz} 31 | buzz(options) 32 | }, [bar, baz]) // we want this to re-run if bar or baz change 33 | return
foobar
34 | } 35 | ``` 36 | 37 | 如果 bar 或者 baz 是(非原始值)对象、数组、函数,上面的解决方案就不行了。那该如何优化呢?这就引入了本期的useMemo和useCallback了 38 | 39 | ## 优化方案2 40 | 41 | ``` js 42 | function Foo({bar, baz}) { 43 | React.useEffect(() => { 44 | const options = {bar, baz} 45 | buzz(options) 46 | }, [bar, baz]) 47 | return
foobar
48 | } 49 | 50 | function Blub() { 51 | const bar = React.useCallback(() => {}, []) 52 | const baz = React.useMemo(() => [1, 2, 3], []) 53 | return 54 | } 55 | ``` 56 | 57 | ## 参考 58 | 59 | * [什么时候使用 useMemo 和 useCallback](https://jancat.github.io/post/2019/translation-usememo-and-usecallback/) 60 | -------------------------------------------------------------------------------- /docs/temp/jumpter-lab.md: -------------------------------------------------------------------------------- 1 | 2 | # jumpter lab 3 | 4 | ## 1. 安装 5 | 6 | ### 一:安装conda(推荐) 7 | 8 | conda隔离环境作用(有点类似虚拟机)。 9 | * 记住安装位置 10 | * 把conda命令放在zsh中。 11 | 12 | ``` 13 | # 创建虚拟环境并下载jupyterlab cookiecutter nodejs git。 14 | conda create -n jupyterlab-ext --override-channels --strict-channel-priority -c conda-forge -c anaconda jupyterlab cookiecutter nodejs git 15 | 16 | # 激活虚拟环境 17 | # 此时安装的jupyterlab是在其env环境内,使用which pip命令就理解了 18 | conda activate jupyterlab-ext 19 | ``` 20 | 21 | ### 二:或者直接安装jupyterlab 22 | 23 | ``` 24 | pip install jupyterlab cookiecutter 25 | ``` 26 | 27 | > 使用conda虚拟环境时,以下所有的操作都要执行`conda activate jupyterlab-ext`以后 28 | 29 | ## 2. 启动jupyterlab 30 | 31 | ``` bash 32 | jupyter lab # 开启jupyter lab 33 | jupyter lab build # 手动编译application。当完成插件修改时,手动build可以自动install 所有插件 34 | ``` 35 | 36 | ## 3. 插件开发 37 | 38 | ### 下载模板代码 39 | 40 | ``` 41 | # cookiecutter工具下载 42 | cookiecutter https://github.com/jupyterlab/extension-cookiecutter-ts --checkout v1.0 43 | 44 | cd jupyterlab_apod 45 | 46 | # 初始项目依赖并构建项目 47 | jlpm install 48 | jupyter labextension install . # 插件安装到jupyterlab工具上 49 | ``` 50 | 51 | ### jupyter插件应用到jupyterlab上 52 | 53 | https://jupyterlab.readthedocs.io/en/stable/user/extensions.html 54 | 55 | 类似于npm插件: 56 | * install 安装没带--no-build时,会重启lab进程; 57 | * install支持多个package一起安装 58 | 59 | ``` bash 60 | jupyter labextension list # 已安装的list 61 | 62 | jupyter labextension install my-extension # npm包(安装后自动会编译application) 63 | jupyter labextension install . --no-build # 安装本地包,同时不编译application 64 | 65 | jupyter labextension uninstall my-extension 66 | ``` 67 | 68 | ## 参考 69 | 70 | https://juejin.im/post/5dc2658d6fb9a04a6d7f1e0d -------------------------------------------------------------------------------- /docs/temp/practice-dva.md: -------------------------------------------------------------------------------- 1 | # DVA 2 | 3 | React-Router 4 | * Link 5 | * Switch/Route 6 | * BrowserRouter 7 | * Redirect 8 | 9 | * useParams: { id } 10 | * useRouteMatch: { url } 11 | 12 | ## React跳转方式 13 | 14 | ### 1. withRouter 15 | 16 | 官方推荐:https://reactrouter.com/web/api/withRouter 17 | 18 | withRouter增强函数:props: { location, history, match, route } 19 | 20 | ```js 21 | import React, { useState, useEffect } from 'react'; 22 | import withRouter from 'umi/withRouter'; // 底层来自react-router 23 | import PropTypes from 'prop-types'; // props 24 | 25 | function VipDetail(props) { 26 | const { location, history, match, route } = props 27 | history.push(url) // 跳转 28 | return
29 | } 30 | 31 | 32 | VipDetail.propTypes = { 33 | location: PropTypes.shape({ query: PropTypes.objectOf(PropTypes.string) }).isRequired, 34 | } 35 | 36 | // withRouter增强函数:props: { location, history, match, route } 37 | export default withRouter(VipDetail); 38 | ``` 39 | 40 | > dva中每个page自动带withRouter,可省略手动withRouter 41 | 42 | ### 2. Link 43 | 44 | ``` js 45 | import { BrowserRouter as Router,Link} from 'react-router-dom'; 46 | 首页 47 | ``` 48 | 49 | ### 3. Redirect 50 | 51 | 类似Link 52 | 53 | ``` js 54 | import { Redirect } from 'react-router-dom'; 55 | 56 | ``` 57 | 58 | ### 4. umi中页面跳转 59 | 60 | ``` js 61 | // 1 基于 umi/link,通常作为 React 组件使用。 62 | import Link from 'umi/link'; 63 | 64 | export default () => ( 65 | Go to list page 66 | ); 67 | 68 | // 2. 基于 umi/router,通常在事件处理中被调用。 69 | import router from 'umi/router'; 70 | 71 | function goToListPage() { 72 | router.push('/list'); 73 | } 74 | ``` -------------------------------------------------------------------------------- /docs/read-books/book-head-first-design-patterns.md: -------------------------------------------------------------------------------- 1 | # 《Head First Design Patterns》 2 | 3 | 这是一本十分经典的关于设计模式的书,以前阅读过这本书,这次重新阅读又增加了新的理解。书本中一些经典案例的讲解,联系到平时工作中的应用,对设计模式有了更加深入的思考。这里给出工作中总结的[常用JS设计模式](../js/js-design-pattern.md) 4 | 5 | ## 设计原则 6 | 7 | 1. 找出应用中可能需要变化之处,把它们独立出来,不要把和那些不变化的代码混合在一起 8 | 9 | ``` java 10 | // bad 11 | // 飞行技能和叫声不是每只鸭子都会的(比如橡皮鸭),所以不能放在鸭子Duck基类中 12 | class Duck { 13 | // common 14 | eat(); 15 | sleep(); 16 | 17 | // special 18 | fly() 19 | quack() 20 | } 21 | ``` 22 | 23 | 2. 针对接口编程,而不是针对实现编程 24 | 25 | ``` java 26 | Dog d = new Dog() // 针对实现编程 27 | Animal d = new Dog() // 针对接口编程(Dog extend Animal) 28 | ``` 29 | 30 | 3. 多用组合,少用继承 31 | 32 | ``` js 33 | // bad 34 | // 飞行和叫声是接口,需要每次都实现,不好 35 | class RedHeadDuck extend Duck interfaces IFly, IQuack { 36 | 37 | } 38 | 39 | // good 40 | class RedHeadDuck extend Duck { 41 | FlyBehavior fly; 42 | QuackBehavior quack; 43 | } 44 | ``` 45 | 46 | ## 设计模式 47 | ### 观察者模式 48 | * 特点是一对多 49 | * 需要有订阅关系(这样发布才通知的到) 50 | 51 | ### 迭代器模式 52 | * 提供一种方法顺序访问一个聚合对象的各个元素,而又不暴露其内部的表示。 53 | * 即不同的迭代方式换成统一的iterator.next() 54 | 55 | ### 组合模式 56 | * 允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象与组合对象。 57 | 58 | ### 模版方法 59 | * 在一个方法中定义算法的骨架,而将一些步骤延迟到子类。 60 | * 模板方法可以在不改变结构算法的基础上,重新定义算法中的某些步骤 61 | 62 | ### 命令模式 63 | * 将“请求“封装成对象,以便使用不同的请求来参数化其他对象。 64 | * eg:n种缓存方式 + 缓存管理类(管理类面向最终用户,但内部调用还是使用缓存) 65 | 66 | ``` js 67 | // n种缓存方式 68 | class LocalStorage extend ICache{} 69 | class ObjectStorage extend ICache{} 70 | ... 71 | 72 | // 管理类 73 | class CacheManage { 74 | constructor(ICache cache){ 75 | this.cache = cache 76 | } 77 | 78 | get() { 79 | return this.cache.get() 80 | } 81 | } 82 | 83 | ``` -------------------------------------------------------------------------------- /docs/temp/clear-myself-code.md: -------------------------------------------------------------------------------- 1 | 2 | ## Functions should only be one level of abstraction 3 | 4 | ``` js 5 | function parseBetterJSAlternative(code) { 6 | const REGEXES = [ 7 | // ... 8 | ]; 9 | 10 | const statements = code.split(" "); 11 | const tokens = []; 12 | REGEXES.forEach(REGEX => { 13 | statements.forEach(statement => { 14 | // ... 15 | }); 16 | }); 17 | 18 | const ast = []; 19 | tokens.forEach(token => { 20 | // lex... 21 | }); 22 | 23 | ast.forEach(node => { 24 | // parse... 25 | }); 26 | } 27 | 28 | // good 29 | function parseBetterJSAlternative(code) { 30 | const tokens = tokenize(code); 31 | const syntaxTree = parse(tokens); 32 | syntaxTree.forEach(node => { 33 | // parse... 34 | }); 35 | } 36 | 37 | function tokenize(code) { 38 | const REGEXES = [ 39 | // ... 40 | ]; 41 | 42 | const statements = code.split(" "); 43 | const tokens = []; 44 | REGEXES.forEach(REGEX => { 45 | statements.forEach(statement => { 46 | tokens.push(/* ... */); 47 | }); 48 | }); 49 | 50 | return tokens; 51 | } 52 | 53 | function parse(tokens) { 54 | const syntaxTree = []; 55 | tokens.forEach(token => { 56 | syntaxTree.push(/* ... */); 57 | }); 58 | 59 | return syntaxTree; 60 | } 61 | ``` 62 | 63 | ## Don't use flags as function parameters 64 | 65 | ``` js 66 | // bad 67 | function createFile(name, temp) { 68 | if (temp) { 69 | fs.create(`./temp/${name}`); 70 | } else { 71 | fs.create(name); 72 | } 73 | } 74 | 75 | // good 76 | function createFile(name) { 77 | fs.create(name); 78 | } 79 | 80 | function createTempFile(name) { 81 | createFile(`./temp/${name}`); 82 | } 83 | ``` 84 | 85 | -------------------------------------------------------------------------------- /docs/temp/react-packages.md: -------------------------------------------------------------------------------- 1 | ## react-router-redux 2 | 配合react-router 3.x及以下 3 | 4 | > connected-react-router适合4.x版本。 5 | 6 | https://github.com/reactjs/react-router-redux/blob/master/examples/basic/app.js 7 | 8 | history + store (redux) → react-router-redux → enhanced history → react-router 9 | 10 | ``` js 11 | import { Router, Route, IndexRoute, browserHistory } from 'react-router' 12 | import { syncHistoryWithStore, routerReducer } from 'react-router-redux' 13 | 14 | const reducer = combineReducers({ 15 | ...reducers, 16 | routing: routerReducer 17 | }) 18 | 19 | const history = syncHistoryWithStore(browserHistory, store) 20 | 21 | ReactDOM.render( 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
, 33 | document.getElementById('mount') 34 | ) 35 | 36 | ---- 37 | import React from 'react' 38 | import { Link, browserHistory } from 'react-router' 39 | 40 | export default function App({ children }) { 41 | return ( 42 |
43 |
44 | Links: 45 | {' '} 46 | Home 47 | {' '} 48 | Foo 49 | {' '} 50 | Bar 51 |
52 |
53 | // feature 54 | 55 |
56 |
{children}
57 |
58 | ) 59 | } 60 | ``` -------------------------------------------------------------------------------- /docs/temp/vue-cli3-proxy.md: -------------------------------------------------------------------------------- 1 | # vue-cli3中proxy配置不完全支持webapck 2 | 3 | 4 | ![image](https://user-images.githubusercontent.com/6310131/54813522-b24be800-4cc8-11e9-8b7d-749199a6ed8e.png) 5 | ## 结论 6 | 1. 目前vue-cli3只支持string和object方式,不支持context数组方式。相关issue 7 | * [Add more options for proxy context](https://github.com/vuejs/vue-cli/issues/2320) 8 | * [增强proxy: 对齐webpack devServer的proxy](https://github.com/vuejs/vue-cli/issues/2868) 9 | 10 | 11 | ## webpack proxy 12 | [官方文档](https://webpack.js.org/configuration/dev-server/#devserverproxy) 13 | webpack-dev-server代理是基于[http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware),更多使用方式可以查看其github 14 | ### 一 15 | 请求到 /api/xxx 现在会被代理到请求 http://localhost:3000/api/xxx 16 | ``` js 17 | mmodule.exports = { 18 | //... 19 | devServer: { 20 | proxy: { 21 | '/api': 'http://localhost:3000' 22 | } 23 | } 24 | }; 25 | ``` 26 | 27 | ### 二 28 | 代码多个路径代理到同一个target下, 你可以使用由一个或多个「具有 context 属性的对象」构成的数组 29 | ``` js 30 | module.exports = { 31 | //... 32 | devServer: { 33 | proxy: [{ 34 | context: ['/auth', '/api'], 35 | target: 'http://localhost:3000', 36 | }] 37 | } 38 | }; 39 | ``` 40 | 41 | ### 三 42 | 传递参数 43 | ``` js 44 | module.exports = { 45 | //... 46 | devServer: { 47 | proxy: { 48 | '/api': { 49 | target: 'http://localhost:3000', 50 | changeOrigin: true, 51 | secure: false, 52 | pathRewrite: {'^/api' : ''}, 53 | bypass: function() {...} 54 | } 55 | } 56 | } 57 | }; 58 | ``` 59 | 60 | ## 参考文章 61 | * [vue-cli3 devserver-proxy](https://cli.vuejs.org/zh/config/#devserver-proxy) -------------------------------------------------------------------------------- /docs/tools/charles-tool.md: -------------------------------------------------------------------------------- 1 | # 前端抓包神器Charles 2 | 3 | ## Q: Charles是什么 4 | A: Charles是一个抓包工具,类似工具还有Fiddler。Charles相当于一个插在服务器和客户端之间的“过滤器”;当客户端向服务器发起请求的时候,先到Charles进行过滤,然后Charles在把最终的数据发送给服务器; 5 | 6 | ## Q: Charles能干什么 7 | 8 | A: 常做以下这些事: 9 | * 抓取 Http 和 Https 的请求和响应,抓包是最常用的了。 10 | * 重发网络请求,方便后端调试,复杂和特殊情况下的一件重发还是非常爽的(捕获的记录,直接repeat就可以了,如果想修改还可以修改)。 11 | * 修改网络请求参数(客户端向服务器发送的时候,可以修改后再转发出去)。 12 | * 网络请求的截获和动态修改。 13 | * 支持模拟慢速网络,主要是模仿手机上的2G/3G/4G的访问流程。 14 | * 支持本地映射和远程映射,比如你可以把线上资源映射到本地某个文件夹下,这样可以方面的处理一些特殊情况下的bug和线上调试(网络的css,js等资源用的是本地代码,这些你可以本地随便修改,数据之类的都是线上的环境,方面在线调试); 15 | * 可以抓手机端访问的资源(如果是配置HOST的环境,手机可以借用host配置进入测试环境) 16 | 17 | ## Q: 安装Charles 18 | 19 | A:从[官网](http://www.charlesproxy.com/download)直接下载,并用以下账号注册 20 | ``` 21 | Registered Name: https://zhile.io 22 | 23 | License Key: 48891cf209c6d32bf4 24 | ``` 25 | 26 | ## Q: 如何抓包http请求 27 | 28 | A:开启mac的代理功能即可,这样会把chrome所有的请求都代理到charles中。操作: 29 | 1. Proxy --> 勾选macOS Proxy 30 | 2. 同时确认Proxy-> Proxy settings ->proxies :勾选enable transparent http proxying 31 | 32 | ## Q: 如何抓包https请求 33 | 34 | A:没有设置任何东西,一般请求到的https是unknown,这是因为https需要信任的本地证书。所以需要在本地安装证书,操作: 35 | 1. `安装并信任证书`:Help --> SSL Proxying --> Install Charles Root Certificate会自动安装证书 --> Mac需要输入密码信任证书,找到Charles开头的证书,双击选择信任即可。 36 | 2. `添加https代理`:Proxy-> SSL Proxying Settings->SSL Proxying:勾选Enable SSL Proxying + 添加Location(host设置为“*”,Port设置为“443”) 37 | 38 | ## Q: 关闭Charles后,无法访问网络 39 | 40 | A: 这是因为在使用Charles后,把Mac作为了代理。退出Charles应用程序并不会自动关闭掉Mac的代理,所以需要我们手动关闭Mac代理。操作: 41 | 1. Go to Applications > System Preferences > Network > Advanced > Proxies and deselect any proxies that have been selected. 42 | 43 | ## 参考文章 44 | * [抓包工具Charles的使用教程](https://www.cnblogs.com/jiayuchn-test/p/8875105.html) 45 | * [mac下配置Charles,安装证书](https://blog.csdn.net/windy135/article/details/79086270) -------------------------------------------------------------------------------- /docs/react/react-native-1.environment-mac.md: -------------------------------------------------------------------------------- 1 | # ReactNative Mac开发环境搭建 2 | 3 | 本说明旨在帮助读者快速搭建开发RN应用,考虑到部门大部分是Mac,故奉上Mac-IOS开发环境,Window请戳[这里](https://reactnative.cn/docs/0.51/getting-started.html) 4 | 5 | ## 工具依赖 6 | 7 | - Node/yarn 8 | 9 | - XCode 10 | 11 | - Watchman 12 | 13 | - react-native-cli 14 | 15 | ### 安装Node 16 | 17 | 1. 推荐使用homebrew 18 | 19 | ```shell 20 | /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 21 | ``` 22 | 23 | 2. 安装node/yarn 24 | 25 | ```shell 26 | brew install node yarn 27 | ``` 28 | 29 | 没有翻墙请使用淘宝镜像以加速下载 30 | 31 | ``` 32 | npm config set registry https://registry.npm.taobao.org --global 33 | npm config set disturl https://npm.taobao.org/dist --global 34 | ``` 35 | 36 | ### XCode 37 | 38 | 提供IDE和**Simulator模拟器**,AppStore或[官网](https://developer.apple.com/xcode/downloads/)下载,React Native目前需要Xcode 8.0 或更高版本。 39 | 40 | ### Watchman 41 | 42 | 监视文件变更工具,热加载需要用到该工具 43 | 44 | ``` 45 | brew install watchman 46 | ``` 47 | 48 | ### react-native-cli 49 | 50 | react native的cli工具,通常都使用该工具创建项目模板 51 | 52 | ```shell 53 | npm install -g react-native-cli 54 | ``` 55 | 56 | ## Hello World 57 | 58 | 一般0.44.3版本比较稳定,最新版本如果没有翻墙,很可能有些库无法下载,导致编译错误。所以如果在家没翻墙,建议创建项目使用0.44.3版本, 59 | 60 | ```shell 61 | react-native init HelloRN --0.44.3 62 | cd AwesomeProject 63 | react-native run-ios 64 | ``` 65 | 66 | ### 运行RN项目 67 | 68 | #### 第一种方式: Xcode可视化方式(推荐,速度快/查看报错方便) 69 | 70 | 1. 进入RN项目下ios目录,打开.xcodeproj后缀文件,自动会xcode打开 71 | 72 | ![xcodeproj后缀文件](./assets/xcodeproject.png) 73 | 74 | 2. 如下选择模拟器机型,启动项目即可预览App 75 | 76 | ![xcodeproj后缀文件](./assets/run.png) 77 | 78 | 79 | #### 第二种方式: 命令行方式 80 | 81 | ```shell 82 | cd HelloRN 83 | react-native run-ios 84 | ``` 85 | 86 | ### 推荐链接: 87 | 88 | [React中文网](https://reactnative.cn/docs/0.51/getting-started.html#content) 89 | -------------------------------------------------------------------------------- /docs/temp/react-redux-code-analysis2.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### redux中间件机制 4 | 5 | koa洋葱模型核心原理: 6 | 7 | ``` js 8 | Promise.resolve(middleware1(context, async() => { // 注意async关键字不能省略 9 | return Promise.resolve(middleware2(context, async() => { 10 | return Promise.resolve(middleware3(context, async() => { 11 | return Promise.resolve(); 12 | })); 13 | })); 14 | })) 15 | ``` 16 | 17 | 18 | 19 | ## React之调度更新 20 | 21 | 记得很早之前,尤雨溪的一篇访谈里谈论react和vue的异同时,提到了`react是一个pull based的框架而vue是一个push based的框架`,两种设计理念没有孰好孰坏之分,只有不同场景下看谁更适合而已,push based可以让框架主动分析出数据的更新粒度和拆分出渲染区域不同依赖,所以对于初学者来说不用关注细节就能更容易写出一些性能较好的代码。 22 | 23 | react感知到数据变化的入口是setState,用户主动触发这个接口,框架拉取到最新的数据从而进行视图更新,但是其实从react角度来看没有感知到数据变化一说,因为你只要显式的调用了setState就表示要驱动进行新一轮的渲染了。 24 | 25 | ### React&Redux之发布订阅 26 | 27 | react-redux提供了connect装饰器来帮助组件完成检测过程,以便决定组件是否需要被更新。 28 | 29 | 我们来看一个典型的使用了redux的组件: 30 | ``` js 31 | const mapStateToProps = state => { 32 | return { loginName: state.login.name, product: state.product }; 33 | } 34 | 35 | @connect(mapStateToProps) 36 | class Foo extends React.Component { 37 | render() { 38 | const { loginName, product } = this.props; 39 | // 渲染逻辑略 40 | } 41 | } 42 | ``` 43 | 44 | mapStateToProps其实是一个状态选择操作,挑出想要的状态映射到实例的props上,变化检测发生哪一步呢?通过源码我们会知道`connect通过高阶组件,在包裹层完成了订阅操作以便监听store数据变化,订阅的回调函数计算出当前组件该不该渲染`,我们实例化的组件时其实是包裹后的组件,该组件实现了shouldComponentUpdate行为,在它重渲染期间会按照react的生命周期流程调用到shouldComponentUpdate以决定当前组件实例是否需要更新。 45 | 46 | 注意我们提到了一个订阅机制,`因为redux自身的实现原理,当单一状态树上任何一个数据节点发生改变时,其实所有的高阶组件的订阅回调都会被执行`,具体组件该不该更新,回调函数里会浅比较前一刻的状态和后一刻状态来决定当前实例需不要更新,所以这也是为什么redux强调如果状态改变了,一定总是要返回新的状态,以便辅助浅比较能够正常工作,当然顺带实现了时间回溯功能,但是大多数时候我们的应用本身是不需要此功能的,而redux-dev-tool倒是非常依赖单一状态在不同时间的快照来实现重放功能。 47 | 48 | > redux按需更新 49 | 50 | ### 参考 51 | 52 | * Redux状态管理之痛点、分析与改良:https://segmentfault.com/a/1190000009540007 53 | * 【Concent杂谈】精确更新策略:https://cloud.tencent.com/developer/article/1573642 -------------------------------------------------------------------------------- /docs/temp/ele-ui/可视化系统搭建.md: -------------------------------------------------------------------------------- 1 | 2 | # 可视化系统搭建 3 | 4 | ## 业务痛点 5 | 6 | * 页面类似: 页面布局和业务逻辑较固定. 7 | * 需求高频: 每周甚至每天有多个这种需求. 8 | * 迭代快速: 开发时间短, 上线时间紧. 9 | * 开发性价比低: 开发任务重复, 消耗各方的沟通时间和人力. 10 | 11 | ![](https://user-gold-cdn.xitu.io/2019/2/12/168e0b5f69a0c244?imageslim) 12 | 13 | ## 前提条件 14 | 15 | * **拥有丰富完整的物料库**。 16 | * **搭建的目标业务不复杂**,因为核心还是利用组件 + 配置文件完成全流程。 17 | 18 | ## 技术难点 19 | 20 | 1. 页面组件化(基础) 21 | * 组件树组合, 页面可视化搭建变成页面组件的可视化组合. 22 | * 组件配置编辑, 将对页面内容的编辑转化为对组件的配置属性(props)修改. 23 | 1. 页面可视化编辑 24 | * 动态地给页面源码添加组件, 然后重新打包生成页面. 25 | * 使用配置表单来填入配置数据. 26 | 1. 组件层级关系 27 | * 非嵌套组件方式,实现简单,但单一,只针对某些有限场景。 28 | * 嵌套组件方式,实现复杂,考虑场景多。如一个父组件为行内组件, 给其添加一个块级组件作为子组件, 渲染后可能会导致行内组件被块级组件撑开. 29 | 1. 实时预览 30 | * 页面挂载方式。优点:实时预览,快;缺点:编译需引入组件库源码,耦合,而且组件库样式可能会污染编辑器页面。[commit](https://github.com/lq782655835/vuepress-theme-antdocs/commit/cca961eb775732fc35bab38caa7f32eb140962e1) 31 | * 后台渲染方式(iframe嵌入)。优点:只需要配置内容即可(编辑器和组件库解耦),后台ssr生成页面 。 缺点:ssr技术攻艰,以及后台渲染速度要快(预览实时性) 32 | 1. 页面打包 33 | * 输出实时预览的页面。所以上面的步骤一般选择后台渲染方式(iframe)。 34 | * 如何搭配自动发布系统?安全性保证? 35 | 1. 约定和约束(贡献业务组件,供上层可视化工具) 36 | * 页面可视化搭建工具要支持业务现有的前端框架. 37 | * 组件和模板的编写方式需遵循较简单的编写约定, 避免开发人员难上手和写起来不舒服. 38 | * 组件在开发模式下进行调试和测试 39 | 40 | ### 核心难点 41 | 1. 技术难点:页面可视化搭建工具与页面前端框架解偶. 42 | 1. 技术难点: 如何用最简单的方式生成配置数据编辑表单. 43 | 1. 技术难点: 如何组织页面组件的层级关系. 44 | 1. 技术难点: 如何实现组件库的快速后台渲染, 从而实现编辑器和组件库前端框架的分离. 45 | 46 | ### 其他问题 47 | 48 | 1. 业务单一 49 | 1. 业务组件升级 50 | 1. 搭配发布控制系统(安全保证) 51 | 1. 物料库(基础) 52 | 1. 一定会用到ssr 53 | 1. 本质上是组件 + schema校验。核心还是Vue的is 54 | 1. 预览如何拿到页面地址 55 | 56 | ### 更多 57 | 58 | * https://www.yunfengdie.com/ 59 | * https://page-pipepline.github.io/pipeline-editor/dist/#/pipeline?templateId=1 60 | * https://github.com/CntChen/cntchen.github.io/issues/17 61 | * https://ice.work/docs/guide/intro 62 | 63 | 64 | 65 | client.config -> main.js -> 浏览器代码 66 | server.config -> main-server -> ssr 67 | 68 | 69 | npm run server 生成ssr目标代码 70 | npm run render: 引入ssr schame,测试渲染 -------------------------------------------------------------------------------- /docs/team-standard/recommend-vue-component.md: -------------------------------------------------------------------------------- 1 | # 组件设计风格 2 | 3 | ### 要求 Element 元素统一使用El后缀 4 | 5 | ``` js 6 | // ✗ bad 7 | const elem = this.$el; 8 | const element = e.target; 9 | const input = this.$refs.input 10 | 11 | // ✓ good 12 | const el = this.$el; 13 | const el = e.target; 14 | const inputEl = this.$refs.input; 15 | ``` 16 | 17 | ### 要求 Vue 实例统一使用VM后缀 18 | 19 | ``` js 20 | // ✗ bad 21 | const instance = this; 22 | const form = this.$refs.form; 23 | this.$emit('select', { 24 | item, 25 | }); 26 | 27 | // ✓ good 28 | const vm = this; 29 | const formVM = this.$refs.form; 30 | this.$emit('select', { 31 | item, 32 | itemVM: selectedVM, 33 | }); 34 | ``` 35 | 36 | ### 被动接收事件方法使用on前缀 37 | 38 | ```js 39 | // ✗ bad 40 | { 41 | methods: { 42 | input() { 43 | // ... 44 | }, 45 | handleValidate() { 46 | // ... 47 | }, 48 | }, 49 | } 50 | 51 | // ✓ good 52 | { 53 | methods: { 54 | onInput() { 55 | // ... 56 | }, 57 | onValidate() { 58 | // ... 59 | }, 60 | }, 61 | } 62 | ``` 63 | 64 | ### slot 只在对应名称的 class 内设置 65 | 66 | ``` js 67 | // ✗ bad 68 | 69 |
70 | 71 |
72 | {{ title }} 73 |
74 |
75 |
76 |
77 |
78 | 79 | // ✓ good 80 |
81 | 82 |
83 | {{ title }} 84 |
85 |
86 |
87 |
88 | ``` 89 | 90 | ### 变量命名 91 | 92 | * 常见状态:default, primary, info, success, warning, error, disabled, muted, ... 93 | * 大小分级:mini, small, base, large, huge, ... 94 | * 颜色分级:darkest, darker, dark, base, light, lighter, lightest, ... 95 | -------------------------------------------------------------------------------- /docs/temp/1.vue-router.md: -------------------------------------------------------------------------------- 1 | # Vue Router原理 2 | 3 | * 组件 4 | * view 5 | * 路由匹配到对应components后,render使用h(component, data, children)渲染生成 6 | * link 7 | * render渲染组件时,内置click事件,h('a', data, this.$slots.default) 8 | * 逻辑 9 | * HashHistory:popstate监听事件 10 | * HTML5History:popstate/hashchange(老浏览器)监听事件 11 | * AbstractHistory(适用Node端):模拟栈行为 12 | 13 | ## 基本 API 14 | 15 | * $router.push/replace() 所有跳转都是依据push/replace,router-link onclick事件底层也是这样调用 16 | * $router.go/back/forward 17 | * $router.resolve ( 18 | to: RawLocation, 19 | current?: Route, 20 | append?: boolean 21 | ): { 22 | location: Location, 23 | route: Route, 24 | href: string, 25 | // for backwards compat 26 | normalizedTo: Location, 27 | resolved: Route 28 | } 29 | 30 | ## 源码解析 31 | 32 | * hashHistory: popstate/hashchange event (use `/#` tag) 33 | * export: popstate event(use `/` tag) 34 | 35 | ``` js 36 | switch (mode) { 37 | case 'history': 38 | this.history = new HTML5History(this, options.base) 39 | break 40 | case 'hash': 41 | this.history = new HashHistory(this, options.base, this.fallback) 42 | break 43 | case 'abstract': 44 | this.history = new AbstractHistory(this, options.base) 45 | break 46 | default: 47 | if (process.env.NODE_ENV !== 'production') { 48 | assert(false, `invalid mode: ${mode}`) 49 | } 50 | } 51 | 52 | get currentRoute (): ?Route { 53 | return this.history && this.history.current 54 | } 55 | 56 | push (location: RawLocation, onComplete?: Function, onAbort?: Function) { 57 | this.history.push(location, onComplete, onAbort) 58 | } 59 | 60 | replace (location: RawLocation, onComplete?: Function, onAbort?: Function) { 61 | this.history.replace(location, onComplete, onAbort) 62 | } 63 | 64 | go (n: number) { 65 | this.history.go(n) 66 | } 67 | 68 | back () { 69 | this.go(-1) 70 | } 71 | 72 | forward () { 73 | this.go(1) 74 | } 75 | ``` -------------------------------------------------------------------------------- /docs/temp/temp-vue3.md: -------------------------------------------------------------------------------- 1 | ## toRefs 2 | 3 | ``` js 4 | function useMousePosition() { 5 | const pos = reactive({ 6 | x: 0, 7 | y: 0 8 | }) 9 | 10 | // ... 11 | return toRefs(pos) 12 | } 13 | 14 | // x & y are now refs! 15 | const { x, y } = useMousePosition() 16 | 17 | // 以下是丢失响应式方式 18 | // reactivity lost! 19 | const { x, y } = useMousePosition() 20 | return { 21 | x, 22 | y 23 | } 24 | 25 | // reactivity lost! 26 | return { 27 | ...useMousePosition() 28 | } 29 | 30 | // this is the only way to retain reactivity. 31 | // you must return `pos` as-is and reference x and y as `pos.x` and `pos.y` 32 | // in the template. 33 | return { 34 | pos: useMousePosition() 35 | } 36 | ``` 37 | 38 | ``` js 39 | // 源码:object rest会丢失响应式,使用toRefs 40 | export function toRefs( 41 | object: T 42 | ): { [K in keyof T]: Ref } { 43 | const ret: any = {} 44 | for (const key in object) { // for in 展开一层 45 | ret[key] = toProxyRef(object, key) 46 | } 47 | return ret 48 | } 49 | 50 | function toProxyRef( 51 | object: T, 52 | key: K 53 | ): Ref { 54 | return { 55 | _isRef: true, 56 | get value(): any { 57 | return object[key] 58 | }, 59 | set value(newVal) { 60 | object[key] = newVal 61 | } 62 | } 63 | } 64 | ``` 65 | 66 | ## Composition API 67 | 68 | ``` js 69 | const count = reactive({ num: 0 }) // state 70 | const num = ref(0) 71 | // computed依赖于count.num,也意味着该computed是count.num的依赖项 72 | const computedNum = computed(() => 2 * count.num)) 73 | count.num = 7 74 | 75 | // effect默认没带lazy参数,先会执行effect 76 | effect(() => { 77 | // effect用到对应响应式数据时,count.num get就已经收集好了该effect依赖 78 | // 同理,使用computed api时, 79 | console.log(count.num) 80 | }) 81 | 82 | function increment() { // methods 83 | count.value++ 84 | } 85 | 86 | watchEffect(() => console.log(count.value)) // watch 87 | 88 | onMounted(() => console.log('mounted!')) // life cycle 89 | onUnMounted(() => console.log('unmounted!')) 90 | ``` -------------------------------------------------------------------------------- /docs/read-books/book-soft-skills.md: -------------------------------------------------------------------------------- 1 | # 程序员软技能指南 2 | 3 | 周末在家读了《软技能-代码之外的生存指南》这本书,感触良多。这本书意在传达技术之外的能力,也就是所谓的软技能。作为程序员,大家都听过软技能,但软技能有哪些实例和特征,估计没几个人能说的清楚。本书作者根据自身成功经历(33岁就已经财务自由),讲述了程序员需要提升的软技能,包括但不限于自我提升、理财、健身等方面。笔者根据书中观点,结合自身感悟,总结如下要点。 4 | 5 | ## 学习 6 | * `快速学习方法` 7 | 1. 了解全局。对主题相关内容有个全局的了解 8 | 1. 确定范围 9 | * 将庞大的主题分解为小而聚焦的主题 10 | * 抵住主题下的子主题诱惑,尽可能保持专注 11 | * 考虑时间因素。更长的时间可以攻克更大的主题 12 | 1. 定义目标。平衡全局和细节,找个目标点 13 | 1. 寻找资源。包括但不限于如下: 14 | * 图书 15 | * 博客 16 | * 视频 17 | * 源代码 18 | * 示例项目 19 | 1. 创建学习计划 20 | 1. 筛选资源。第四部集齐的很多资源都是重复的,找出品质最高的资源。 21 | 1. 开始学习,浅尝辄止。 22 | * 抵制主所有资源的诱惑,不要让学习变得失控。 23 | * 例如学习MacOS操作系统,刚开始你只要知道基本用法,可以办公即可可。 24 | 1. 动手操作,边玩边学。学习过程中遇到很多未能完全理解的问题,先记录下来,往后会有机会找出这些问题的答案。 25 | 1. 全面掌握,学以致用。好奇心驱动,会让你懂得更多。 26 | 1. 乐为人师,融会贯通。 27 | * `寻找导师` 28 | * 寻找导师的checklist 29 | 1. 他们做到了我想去做的? 30 | 1. 他们曾经帮助他人做到了我想做的? 31 | 1. 他们现在取得了什么可以展示的成就? 32 | 1. 你能和这个人和睦相处?他充满智慧吗? 33 | * 虚拟导师 - 好书 34 | * `成为导师` 35 | * 经验传授或从旁观者视角提供解决方案 36 | * 成为导师的好处 37 | * 会比自己的学生学习的更多,也是自身的一个成长 38 | * 探求答案过程中越加深入思考 39 | * 帮助的每一个人,终有一天会超越你并回馈于你 40 | * `发现知识短板` 41 | * 找出知识短板checklist 42 | 1. 哪些工作话费时间最多? 43 | 1. 可以改进的重复性劳动 44 | 1. 自己没有完全理解的东西 45 | 1. 回答不上来的面试题 46 | * 消除知识短板 47 | * 随时记录问题的app/记事本 48 | 49 | ## 个人品牌 50 | * `创建自己的博客` 51 | * 定时更新,每周至少一篇 52 | * 文章质量慢慢提升,先写 53 | * 提升访问量。比如去其他博客下评论,并导链到自己博客 54 | * `演讲` 55 | * 小规模场合做起,完善演讲技能 56 | * 勇敢走出去,别害怕让自己出丑 57 | 58 | ## 晋升 59 | * `承担责任` 60 | * 不受重视的项目扛起来 61 | * 帮助团队新人快速成长 62 | * 没人做的项目愿意承担下来,并走自动化 63 | * `令自己引人注目` 64 | * 保证曝光度 - 让领导知道每周你做了什么。 65 | * 发表意见 - 在会议上就发表一些自己的意见(刷存在感)。 66 | * 做好导师 - 提高自己,帮助他人,何乐不为。 67 | * 提供演讲或培训 - 选择一个有用的话题。 68 | * `自学` 69 | * 快速学习东西 70 | * 分享自己学到的东西 71 | * 写blog 72 | * codereview 73 | * 技术大会 74 | * `成为问题的解决者` 75 | 76 | ## 其他 77 | * `不要陷入对技术对狂热中`。程序员往往有个误解:自己选择的技术就认为是最好的。 78 | * `理财` 79 | * `健身` 80 | -------------------------------------------------------------------------------- /docs/read-books/0.how-to-improve-reading.md: -------------------------------------------------------------------------------- 1 | # 如何做到十倍阅读量 2 | 3 | 为什么要提高阅读量? 4 | * 提升才华,写作能力提高 5 | * 深入思考能力 6 | * 丰富知识,扩展边界 7 | * 增加一点书卷气,保持心静如水 8 | * 修身养性,让人滤除浮躁 9 | * 享受人生 10 | 11 | 以上只是列举了一些读书带来的好处,实际上带给你的远不止如此。读书是一种品质,读书是一种责任,读书是一种情怀,读书是一种境界。阅读是一个通过共鸣、共振来自我求证的过程。真正使阅读成为一种深刻而愉悦的体验的,是你在书中找到并塑造了自己。 12 | 13 | 可能大家都觉察到了,越来越多的人出了学校,读书总量却还停留在出校门时间。是因为大家都不感兴趣吗?还是被工作时间挤压的不够?我想应该有很多种理由吧。由于各种因素,总是阅读不完一本书,从而产生挫折感,使得一年难得看完几本书,阅读量有限。笔者之前也是深受其扰,现在跟随速读的人学习,从根本上改变了一些问题,现在一周的阅读量是以前两个月的阅读量。现在结合自身实践情况,总结如下几点方法给大家,希望能帮助大家进行快速阅读,以达到提高阅读量的目的。 14 | 15 | ## 1. 强烈的目标导向 16 | 带着目的去看书,有的人可能本能的讨厌这样的读书,觉得很功利。其实不然,无论是工具书还是一本小说,一定要带着很强烈的目标去读书。当你带着目的去看书的时候,你就知道你真正想得到什么东西。当你知道真正想到什么东西的时候,你就能很快的阅读完这本书。 17 | 18 | 你会从书中找到对应的内容进行学习,而不是从头开始一页一页的翻。所以强烈的目标导向是速读的第一要素。如果你拿起一本书,却不知道为什么拿起这本书,那么你可以先放下这本书,直到你直到为什么拿起这本书。 19 | > 确定读书的目的, What am I reading for?阅读感兴趣的书,筛选同类优秀书籍短时间看完(目标导向,先有兴趣点)。 20 | 21 | ## 2. 习惯用各种介质看书 22 | 有些人喜欢纸质书那种一页一页翻动的实体感觉,其实我以前也是此类用户的重度者。但这种方式费钱+效率低。当你需要阅读大量书籍,这种方式比较局限,而通过不同介质去读书,能让你短时间快速辨别出一些优质书籍出来,同时不限时间地点。 23 | 24 | 特别目前各类介质平台较多,比如kindle、ipad、mac。而且目前各类软件也十分人性化,比如我目前经常用的kindle阅读软件,能帮你统一所有书籍,不管是手机还是平板还是电脑,都可以在登录账号后阅读导入的书籍。而且kindle有一些人性化的操作,比如给导入的文件自动完成目录,十分易于阅读。所以当需要大量阅读时,一个好的介质能让你有更高的效率。 25 | > 不同介质都可以 What type of work am I reading? 26 | 不管是纸质书还是电子稿,都可以。只要是你能收集到的所有资源,横向对比,选出同类题材书籍中质量最高的前2~3本。 27 | 28 | ## 3. 乱序读书 29 | 大部分人从头到位看不完,要有目标的导向,没有哪本书是需要从头看到尾的。所以推崇电子阅读器,可能15min就看完你最感兴趣的内容,然后再根据读完的这部分内容再确定你是否再继续读下去。 30 | 31 | 不用从头看到尾,在读书开始之前,先阅读下书的目录。了解这本书所需要阐述的内容跟你的兴趣点是吻合的。同时结构性的了解书本所要阐述的大概内容。 32 | > 看目录找兴趣点,不是所有的书都要读完。 33 | 34 | ## 4. 集中阅读 35 | 短时间集中精力快速阅读完书籍中感兴趣的内容,能帮助你快速构建你的知识体系。如果一本书一个月都没有读完,说明你的读书时间是被切片的,经常阅读不连续,导致刚记忆的一些内容马上又忘了。 36 | 37 | 快速阅读,看不下去不强看。很多工具书籍读取第一次只是作为一个脑海印象,而不是看过书就马上了解其内在原理。 38 | 39 | ## 5. 总结你的阅读 40 | 如果你看完书不去总结,很容易忘记你所看过的,特别是过上一段时间,大部分内容会在1周之内忘掉。从心理学角度将,读书本来就是一个低效的学习方式。如何记住所看书的内容?总结,写书评或脑图总结等,指出书中的要点以及学习到的内容。 41 | 42 | 快速阅读的弊端就是只有一个印象停留在记忆里,但人的记忆是有限的,很多看过而没有记录或实践的书,在1个月以上就忘了90%。而脑图总结是一个快速的方式让你记忆内容,从而只需要看一眼脑图,就知道这本书里讲述了哪些东西与原理。 43 | > 脑图总结,读书才能系统化 44 | 45 | ## 6. 读以致用 46 | 讲给别人听,分享你的知识。 47 | 分享知识是一个深化理解的过程,它帮助你更好的总结出一些要点,并且在叙述过程中加深印象以及有更深的理解层次思考。 48 | -------------------------------------------------------------------------------- /docs/temp/node-source.md: -------------------------------------------------------------------------------- 1 | # Node源码 2 | 3 | V8 是 Google 的开源项目,由 C++ 编写,除了 Chrome 使用了 V8 之外,还有大名鼎鼎的 Nodejs! 4 | 5 | V8 设计之初的目的是为了提升浏览器执行 JavaScript 代码的性能。为了获取速度,V8 并没有采用标准的解释器,而是通过把 JavaScript 代码编译成效率更高的机器码。 6 | V8 和很多现代 JavaScript 引擎(比如:SpiderMonkey、Rhino)一样,通过 JIT 编译器把 JavaScript 代码编译成机器码。这里的主要区别就是 V8 不会产生任何的字节码或者中间代码。 7 | 8 | V8主要工作就是:「把 JavaScript 直译成机器码,然后运行」。但这中间,往往是一个复杂的过程,它需要处理很多的难题,诸如:编译优化、内存管理、垃圾回收。[V8 javascript engine代码阅读](http://eternalsakura13.com/2018/07/09/zujian/) 9 | 10 | [Node的源码目录](https://github.com/nodejs/node/blob/master/lib/path.js) 11 | ``` 12 | ├── ... 13 | ├── deps 14 | │ ├── ... 15 | │ ├── v8 16 | │ ├── ... 17 | ├── ... 18 | ├── lib 19 | │ ├── ... 20 | │ ├── buffer.js 21 | │ ├── child_process.js 22 | │ ├── console.js 23 | │ ├── ... 24 | ├── node -> out/Release/node 25 | ├── ... 26 | ├── out 27 | │ ├── ... 28 | │ ├── Release 29 | | ├── node 30 | | ├── node.d 31 | | ├── obj 32 | | └── gen 33 | | ├── ... 34 | | ├── node_natives.h 35 | | ├── ... 36 | │ ├── ... 37 | ├── src 38 | │ ├── ... 39 | │ ├── debug-agent.cc 40 | │ ├── debug-agent.h 41 | │ ├── env-inl.h 42 | │ ├── env.cc 43 | │ ├── ... 44 | ├── 45 | ... 46 | ``` 47 | 48 | /deps/v8:这里是V8源码所在文件夹,你会发现里面的目录结构跟V8源码十分相似。NodeJS除了移植V8源码,还在增添了一些内容。 49 | > 基本由c++写成,常见数据结构和优化这里都会有体现。v8主要工作是编译优化、内存管理、垃圾回收,简单讲就是你给出js语法字符串,v8解释给机器,让机器跑起程序。 50 | 51 | /src:由C/C++编写的核心模块所在文件夹,由C/C++编写的这部分模块被称为「Builtin Module」 52 | 53 | `/lib`:由`JavaScript编写的核心模块所在文件夹`,这部分被称为「Native Code」,在编译Node源码的时候,会采用V8附带的js2c.py工具,把所有内置的JavaScript代码转换成C++里面的数组,生成out/Release/obj/gen/node_natives.h文件。有些 Native Module 需要借助于 Builtin Module 实现背后的功能。 54 | > node内置的模块源码,基本都放在这里。 55 | 56 | /out:该目录是Node源码编译(命令行运行make)后生成的目录,里面包含了Node的可执行文件。当在命令行中键入node xxx.js,实际就是运行了out/Release/node文件。 57 | 58 | ![img](http://huang-jerryc.com/image/blog/the-v8-what-javascripter-should-konw/85B39636DBC008CDB299B1BB6E45883B.png) 59 | 60 | Node在启动的时候,就已经把 Native Module,Builtin Module 加载到内存里面了。后来的 JavaScript 代码,就需要通过 V8 进行动态编译解析运行。 61 | > 最终都是加载进内存中。 -------------------------------------------------------------------------------- /docs/tools/yarn-vs-npm.md: -------------------------------------------------------------------------------- 1 | # 你必须知道的yarn 2 | 3 | [yarn的起源](https://code.fb.com/web/yarn-a-new-package-manager-for-javascript/)已经解释了为什么要创建一个新的javascript包管理器,这里笔者也推荐大家从npm切换为yarn。npm4就不说了,速度太慢了,npm5借鉴了很多yarn的机制,比如简单的版本锁、重写cache模块等,减少了与yarn的差距。但依然有些地方做的不如yarn,[这篇文章](https://jobs.stratsys.com/blog/posts/9244-npm5-vs-yarn-which-one-is-better)记录了npm5和yarn的实验对比,结论是:在没有缓存时,yarn和npm5速度差不多;在有缓存时,yarn比npm5快2倍。 4 | 5 | ## yarn优势 6 | 以下针对npm5前: 7 | * `yarn 离线安装。` 8 | 下载的时候 Yarn 缓存了所有的包以至于不需要再次从网络下载 9 | * `yarn并行下载,使得时间更快。` 10 | 通过并行操作最大限度地提高资源利用率,以至于再次下载的时候安装时间比之前更快。npm5之前是等上一个安装完后再执行下一个,串行下载。 11 | * `yarn锁包yarn-lock,保证引用包正确。` 12 | yarn.lock 文件准确的锁定了所有被下载和项目依赖的包版本。通过这个文件,你能确定你的工程师团队的每一位成员都能安装准确的包,并且可以更容易的部署,而没有意外 bug出现。 13 | 14 | ## 指令 15 | * 常用 16 | * `yarn init`,初始化项目,相当于npm init。 17 | * `yarn install` 简写为yarn ,相当于npm install(i),用来安装。 18 | ``` bash 19 | // 不生成 yarn.lock 鎖定文件,並且,如果需要更新則會失敗。 20 | // 服务器上编译通常加该参数,以保证yarn-lock.json一致 21 | yarn install --frozen-lockfile 22 | ``` 23 | * `yarn add` ,安装模块,相当于npm install 。 24 | 25 | * `yarn run`,运行项目,相当于npm run。 26 | * 其他 27 | * `yarn remove`,卸载模块,相当于npm uninstall(un)。 28 | * `yarn upgrade`,更新模块,相当于npm update。 29 | * `yarn link` 这指令需要在你想要依赖的依赖包中执行,该指令会注册成功一个package名称 30 | * `yarn link [package]` 这指令应用到你的项目包中 31 | ``` bash 32 | $ cd react 33 | $ yarn link 34 | success Registered "react". 35 | ``` 36 | ``` bash 37 | $ cd ../react-relay 38 | $ yarn link react 39 | ``` 40 | * `yarn bin`, 打印出执行脚本的位置,可以被yarn run执行。相当于npm bin 41 | * `yarn login/yarn publish` npm登录和发布。相当于npm login/npm publish 42 | * `yarn cache clean`,清除缓存,相当于npm cache clean。 43 | * `yarn list` 列出当前所有依赖的包 44 | * `yarn config list` 显示所有配置设置 45 | ``` bash 46 | yarn config set version-git-tag 1.0.0 47 | yarn config get version-git-tag 48 | yarn config delete version-git-tag 49 | ``` 50 | 51 | ## 参考文章 52 | * [5 things you can do with Yarn](https://auth0.com/blog/five-things-you-can-do-with-yarn/) 53 | * [Yarn - CLI Introduction](https://yarnpkg.com/en/docs/cli/) -------------------------------------------------------------------------------- /docs/project/grow-up-front.md: -------------------------------------------------------------------------------- 1 | # 谈谈前端天花板问题 2 | 3 | ## 问题 4 | 前端到底能干什么,它的天花板到底在哪?对于许多从事前端开发的人来说,这问题一直萦绕在心头。互联网行业这几年飞速发展,加上前端入门门槛低,会HTML、JS、CSS就属于前端范畴,前端人员大量扩展。 5 | 6 | 从工程角度而言,现在公司层面项目较多使用Vue、React作为View层库,两者的生态都很好,提供了方便的cli脚手架工具以及其他优秀的配套工具,开箱即用。从工程结果看,一两年的开发人员和3-n年资深前端开发人员差距不大,都能很好的完成项目开发以及准时上线,无非是代码组织以及质量可能不同而已。那如何体现前端人员之间的差距,以及该往哪些方向提升自己的能力? 7 | 8 | 个人认为,应该从两个方向突破这问题:`硬实力`和`软实力` 9 | 10 | ## 硬实力 11 | 12 | 硬实力很好理解,就是一步一步的技术成长。作为技术人员,应该多积累一些项目经验以及技术沉淀。推荐读者可以在团队中多做一些技术分享以及codereview,分享受益最大的一定是分享者。另外github是一个很好的学习分享平台,从中有取之不尽的学习资源。 13 | 14 | 对技术学习方向有迷茫的同学,可以先了解下Javascript发展史。JS 23年(1995年)发展过程中,前18年不怎么受待见,仅仅是在网页中添加一些互动脚本,完全没有工程化的痕迹。主要原因有以下: 15 | 16 | * `性能不佳`,执行速度不够快。经过几次浏览器大战,厂商的JS执行引擎执行速度大大提高,最出名的就是google的V8 JS引擎。 17 | 18 | * `语法特性的缺失`,缺少Class等Java面向对象特性。ES6的标准给JS语言带来质的提升,优雅的Class、Decorator等Java语言有的,JS也可以有。 19 | 20 | * `缺少模块化`,难以复用。ES6模块化、commonjs模块化解决方案带来代码复用,npm这个超级生态更是给所有前端开发一个福音。 21 | 22 | * `缺少优秀的IDE以及配套调试工具`,动态语言容易导致无法预料的bug。ESLint、Stylelint等代码检查工具,以及Chrome Developer Tools调试工具,让前端开发更舒适,减少了bug出错率以及调试效率。 23 | 24 | * `应用场景局限在web浏览器中`,无法做到跨端跨平台。随着NodeJS的诞生,JS可以应用更多场景:Web前端、后端(NodeJS)、桌面端(ElectronJS、NW)、移动端(React-Native、Weex、Cordova)、嵌入式( Duktape)、机器学习(Tensorflow.js)。大前端 25 | 26 | 从现在前端规模看,以上问题都很好的解决了。以前只能在其他语言做的桌面端、移动端、机器学习等内容,现在也在大前端的范畴了。所以现在前端工程建立起来了,前后端分离也更加彻底了。同时这也给前端人员提供了更广大的舞台以及更高的要求:前端除传统的HTML、JS、CSS三驾马车外,还需要了解后端服务知识,比如http协议细节、Nginx服务器知识、数据库知识等。**总之,前端开发人员,可以从以上维度挑一些感兴趣的方向进行研究**,比如V8引擎的设计、ES6语法糖原理、多端应用的工程化:NodeJS、Electron、ReactNative等。 27 | 28 | ## 软实力 29 | 30 | 软实力指团队合作中,作为技术人员与产品、交互、视觉、测试、市场等相关人员的沟通以及协作。 31 | 32 | 简单理解,就是你可以与任何一个非技术人员讨论相关工作内容。有人会说这是`情商`,我认为情商只是作为软实力的必备之一。举个例子,有些开发人员总是很烦恼产品或交互在提测快上线时,加新需求或改动需求,然后开始上线前的讨价还价沟通。造成这种局面仅是产品或交互的问题吗?为什么团队在开始阶段就不先确定规则:提测阶段不改需求?前端真的从开始就有好好理解产品需求并适当提供改进意见?交互、视觉、测试等中间职责链有对产品进行思考?一个好的团队知道效率的重要性,有效的沟通会大大增加团队的执行效率。 33 | 34 | 窥一斑而知全豹,前端人员也是半个产品,半个交互,半个测试,对工程的每个环节有自己的理解和思考。至此,也不能用前端来形容这个角色了。以为软实力就是更好更有效率的把工作完成?no,这只是其中之一,软实力更多的影响你的生活,你的人生,包括你的做事风格,思维方式等。这条路上,我也是个学习者。 35 | 36 | ## 总结 37 | 38 | 每个公司都有相应职级对应不同的技术title,就像一个梯子,硬实力逐级向上。一步一个脚印,踏实积累经验,多分享与学习,达到对应职级所具有的硬实力。相比较于硬实力的明线,软实力是个看不见并难以感觉的暗线。但软实力对整个人生影响巨大,每个人的软实力都是独特的。愿你我都是一个不断成长的人。 -------------------------------------------------------------------------------- /docs/temp/vue-code-slot.md: -------------------------------------------------------------------------------- 1 | # Vue slot源码解析 2 | 3 | slot本质是把父组件生成的vnode/function,延迟到子组件渲染中。作用域插槽中,由于依赖于子组件的数据和上下文,所以父组件中存储的是function函数(返回vnode),当子组件渲染时,带入子组件的props,function返回vnode,最终渲染在子组件。 4 | 5 | ``` js 6 | let AppLayout = { 7 | template: '
' + 8 | '
' + 9 | '
默认内容
' + 10 | '
' + 11 | '
' 12 | } 13 | 14 | let vm = new Vue({ 15 | el: '#app', 16 | template: '
' + 17 | '' + 18 | '

{{title}}

' + 19 | '

{{msg}}

' + 20 | '

{{desc}}

' + 21 | '
' + 22 | '
', 23 | data() { 24 | return { 25 | title: '我是标题', 26 | msg: '我是内容', 27 | desc: '其它信息' 28 | } 29 | }, 30 | components: { 31 | AppLayout 32 | } 33 | }) 34 | ``` 35 | 36 | app父组件编译最终生成的代码如下: 37 | ``` js 38 | // app是父组件,父组件中放的是要渲染的真实内容 39 | with(this){ 40 | return _c('div', 41 | [_c('app-layout', // app-layout是子组件,子组件放虚拟占位slot 42 | [_c('h1',{attrs:{"slot":"header"},slot:"header"}, 43 | [_v(_s(title))]), 44 | _c('p',[_v(_s(msg))]), 45 | _c('p',{attrs:{"slot":"footer"},slot:"footer"}, 46 | [_v(_s(desc))] 47 | ) 48 | ]) 49 | ], 50 | 1)} 51 | ``` 52 | 53 | app-layout子组件编译生成代码: 54 | ``` js 55 | with(this) { 56 | return _c('div',{ 57 | staticClass:"container" 58 | },[ 59 | _c('header',[_t("header")],2), // _t函数会去拿父组件的vnode 60 | _c('main',[_t("default",[_v("默认内容")])],2), 61 | _c('footer',[_t("footer")],2) 62 | ] 63 | ) 64 | } 65 | ``` 66 | 67 | 我们了解了普通插槽和作用域插槽的实现。它们有一个很大的差别是`数据作用域`,普通插槽是在父组件编译和渲染阶段生成 vnodes,所以数据的作用域是父组件实例,子组件渲染的时候直接拿到这些渲染好的 vnodes。而对于`作用域插槽`,父组件在编译和渲染阶段并不会直接生成 vnodes,而是在父节点 vnode 的 data 中保留一个 scopedSlots 对象,存储着不同名称的插槽以及它们对应的渲染函数,只有在编译和渲染子组件阶段才会执行这个渲染函数生成 vnodes,由于是在子组件环境执行的,所以对应的数据作用域是子组件实例。 68 | 69 | 简单地说,两种插槽的目的都是让子组件 slot 占位符生成的内容由父组件来决定,但数据的作用域会根据它们 vnodes 渲染时机不同而不同。 70 | 71 | [Vue.js 技术揭秘 - slot](https://ustbhuangyi.github.io/vue-analysis/extend/slot.html) -------------------------------------------------------------------------------- /docs/vue/vue-code-7.inject-provide.md: -------------------------------------------------------------------------------- 1 | # Vue2.x源码分析 - inject/provide 2 | 3 | Vue组件传递数据方式 4 | * `prop` 5 | * `$parent` 6 | * **`inject/provide`** 本质还是通过$parent向上查找祖先节点数据 7 | 8 | ``` js 9 | // src/core/instance/init.js 10 | Vue.prototype._init = function (options?: Object) { 11 | const vm: Component = this 12 | ... 13 | vm._self = vm 14 | initLifecycle(vm) 15 | initEvents(vm) 16 | initRender(vm) 17 | callHook(vm, 'beforeCreate') 18 | initInjections(vm) // resolve injections before data/props 19 | initState(vm) 20 | initProvide(vm) // resolve provide after data/props 21 | callHook(vm, 'created') 22 | 23 | if (vm.$options.el) { 24 | vm.$mount(vm.$options.el) 25 | } 26 | } 27 | } 28 | ``` 29 | 30 | ``` js 31 | // inject里的key,通过$parent向上找到provide值,再进行响应式监听 32 | export function initInjections (vm: Component) { 33 | const result = resolveInject(vm.$options.inject, vm) 34 | if (result) { 35 | Object.keys(result).forEach(key => { 36 | defineReactive(vm, key, result[key]) // 响应式数据 37 | }) 38 | } 39 | } 40 | 41 | export function resolveInject (inject: any, vm: Component): ?Object { 42 | if (inject) { 43 | const result = Object.create(null) 44 | const keys = Object.keys(inject) 45 | 46 | for (let i = 0; i < keys.length; i++) { 47 | const key = keys[i] 48 | const provideKey = inject[key].from 49 | let source = vm 50 | // 循环向上,直到拿到祖先节点中的provide值 51 | while (source) { 52 | if (source._provided && hasOwn(source._provided, provideKey)) { 53 | result[key] = source._provided[provideKey] // provide是在initProvide中设置的 54 | break 55 | } 56 | source = source.$parent // 关键代码 57 | } 58 | } 59 | return result 60 | } 61 | } 62 | ``` 63 | 64 | ``` js 65 | // 单纯把provide值,赋值给vm._provided。initInject中会使用到 66 | export function initProvide (vm: Component) { 67 | const provide = vm.$options.provide 68 | if (provide) { 69 | vm._provided = typeof provide === 'function' 70 | ? provide.call(vm) 71 | : provide 72 | } 73 | } 74 | ``` -------------------------------------------------------------------------------- /docs/temp/koa-and-express-plugin.md: -------------------------------------------------------------------------------- 1 | # Koa Plugin With Express 2 | 3 | ## [Koa](https://koajs.com/#introduction) 4 | 5 | ``` js 6 | // used 7 | const Koa = require('koa') // koa v2 8 | const app = new Koa() 9 | 10 | app.use(async (ctx, next) => { 11 | console.log(ctx.req, ctx.res, ctx.method, ctx.url) 12 | await next() 13 | console.log('洋葱模型') 14 | }) 15 | app.use(( ctx ) => { 16 | ctx.body = 'hello world!' 17 | }) 18 | 19 | app.listen(3000) 20 | ``` 21 | 22 | ctx代理了一些ctx.req和ctx.res的内容:https://koajs.com/#context 23 | 24 | ``` 25 | Request aliases 26 | The following accessors and alias Request equivalents: 27 | 28 | ctx.header 29 | ctx.headers 30 | ctx.method 31 | ctx.method= // set 32 | ctx.url 33 | ctx.url= 34 | ctx.originalUrl 35 | ctx.origin 36 | ctx.href 37 | ctx.path 38 | ctx.path= 39 | ctx.query 40 | ctx.query= 41 | ctx.querystring 42 | ctx.querystring= 43 | ctx.host 44 | ctx.hostname 45 | ctx.fresh 46 | ctx.stale 47 | ctx.socket 48 | ctx.protocol 49 | ctx.secure 50 | ctx.ip 51 | ctx.ips 52 | ctx.subdomains 53 | ctx.is() 54 | ctx.accepts() 55 | ctx.acceptsEncodings() 56 | ctx.acceptsCharsets() 57 | ctx.acceptsLanguages() 58 | ctx.get() 59 | 60 | Response aliases 61 | The following accessors and alias Response equivalents: 62 | 63 | ctx.body 64 | ctx.body= 65 | ctx.status 66 | ctx.status= 67 | ctx.message 68 | ctx.message= 69 | ctx.length= 70 | ctx.length 71 | ctx.type= 72 | ctx.type 73 | ctx.headerSent 74 | ctx.redirect() 75 | ctx.attachment() 76 | ctx.set() 77 | ctx.append() 78 | ctx.remove() 79 | ctx.lastModified= 80 | ctx.etag= 81 | ``` 82 | 83 | ## [Express](https://expressjs.com/en/4x/api.html) 84 | 85 | ``` js 86 | var app = express() 87 | 88 | app.use((req, res, next) => { 89 | console.log(req.path, req.method, req.params) 90 | next() // 尾递归 91 | }) 92 | app.get('/', (req, res) => { 93 | // res.send('hello world') 94 | // res.status(500).send('bad param') 95 | // res.set('Content-Type', 'application/json') 96 | res.json({name: 1}) 97 | }) 98 | app.listen(3000) 99 | ``` 100 | -------------------------------------------------------------------------------- /docs/team-standard/recommend-vue-project-structure.md: -------------------------------------------------------------------------------- 1 | # 推荐-Vue项目目录结构 2 | 3 | 目录结构保持一致,使得多人合作容易理解与管理,提高工作效率。[Vue标准项目](https://github.com/lq782655835/standard-vue-project) 4 | 5 | ## 简要说明 6 | 7 | * `main.js`主入口,`router.js`路由划分 8 | * `plugins` 自己或第三方插件,包括但不限于components、directives、filters、third lib 9 | * `pages` 所有路由页面。原则:轻page,重component 10 | * `components` 所有组件。包括原子组件、业务公用组件、页面独有组件 11 | * `server` api引入入口 12 | * `assets` sass、图片资源入口,不常修改数据 13 | * `utils` 工具文件夹 14 | * `store` 标准vuex格式,非必须 15 | 16 | ## 详细说明 17 | ``` 18 | project 19 | └───src 20 | │ │ app.vue // 主页面 21 | │ │ main.js // 主入口 22 | | | router.js // 所有路由 23 | │ │ 24 | │ |____assets // css、image、svg等资源 25 | │ | |____css // 所有sass资源 26 | | | | | reset.scss // 兼容各浏览器 27 | | | | | global.scss // 全局css 28 | | | | | variable.scss // sass变量和function等 29 | │ | |____img // image图标库 30 | | | |____svg // svg图标库 31 | | | 32 | | |____components // 组件 33 | │ | |____common // common自注册组件 34 | │ | |____base // 原子组件(如果是引入第三方,该文件夹可省略) 35 | │ | | ... // 业务公用组件 36 | │ | |____entity // entity页面组件 37 | │ | |____about // about页面组件 38 | | | 39 | | |____pages // UI层(原则:轻page,重component) 40 | | | |____entity 41 | | | | | list.vue // 列表页 42 | | | | | create.vue // 新增页 43 | | | | | edit.vue // 修改页 44 | | | | main.vue 45 | | | 46 | | |____plugins // 自己或第三方插件 47 | | | | index.js // 插件入口文件 48 | | | | directives.js // 所有Vue指令 49 | | | | filters.js // 所有Vue过滤 50 | | | 51 | | |____server // 接口层 52 | | | | index.js // 所有接口 53 | | | | http.js // axios二次封装 54 | | | 55 | | |____store // vuex数据 56 | | | | index.js 57 | | | 58 | | |____utils // 工具层 59 | | | | config.js// 配置文件,包括常量配置 60 | | 61 | └───public // 公用文件,不经过webpack处理 62 | │ │ favicon.ico 63 | │ │ index.html 64 | │ vue.config.js // vue-cli3主配置 65 | │ babel.config.js// babel配置 66 | │ .eslintrc.js // eslint配置 67 | │ .prettierrc.js // perttier配置 68 | │ package.json // npm配置 69 | │ README.md // 项目说明 70 | ``` -------------------------------------------------------------------------------- /docs/tools/npm script.md: -------------------------------------------------------------------------------- 1 | # npm script技巧 2 | 3 | > npm不仅是js包管理工具,还可以为作为代码库配置工具。有些时候需要一些小脚本来约定规则或者监听文件变化,这时候npm script起到重要作用。 4 | 5 | ## 1. 串行和并行 6 | 7 | 使用`&&`将多个命令串行执行。比如我们经常提交代码时,先perriter格式化代码,然后检查eslint以及stylelint,最后再进行commitlint。依次执行,前面执行为false则停止。使用`&`将多个命令并行执行。 8 | 9 | ``` json 10 | "scripts": { 11 | "precommit": "npm run format && npm run eslint && npm run stylelint && git add ." 12 | } 13 | ``` 14 | 15 | ## 2. 通配符执行相似指令 16 | 通配符需要配合[`npm-run-all`](https://github.com/mysticatea/npm-run-all/blob/master/docs/npm-run-all.md)包(更轻量和简洁的多命令运行)。--parallel参数表示并行 17 | 18 | ``` json 19 | "scripts": { 20 | "precommit": "npm-run-all --parallel lint:*", 21 | "lint:js": "eslint --ext .js,.vue --ignore-path .gitignore", 22 | "lint:commit": "commitlint -e $GIT_PARAMS"," 23 | } 24 | ``` 25 | 26 | ## 3. 原生钩子 27 | 28 | npm脚本有pre和post两个钩子。eg:build脚本命令的钩子就是prebuild和postbuild。 29 | 30 | ``` json 31 | "scripts": { 32 | "build": "webpack", 33 | "prebuild": "echo before build", 34 | "postbuild": "echo after build" 35 | } 36 | ``` 37 | > 执行build时按照如下顺序执行: 38 | 39 | > npm run prebuild && npm run build && npm run postbuild 40 | 41 | npm 默认提供如下命令钩子: 42 | 43 | + install 44 | + uninstall 45 | + start 46 | + restart 47 | + build 48 | + test 49 | + stop 50 | + version 51 | 52 | ## 4. 监听文件变动 53 | 54 | gulp中watch非常实用,但npm script也能实现文件变动后自动运行npm脚本。这就需要安装[`onchange`](https://www.npmjs.com/package/onchange)包。onchange帮助我们在文件增删改时执行对应npm命令,非常实用。 55 | 56 | 安装onchange: 57 | 58 | ``` shell 59 | npm install onchange --save-dev 60 | ``` 61 | 62 | scripts监听(示例监听svg文件变化,以处理最新svg文件): 63 | 64 | ``` json 65 | "scripts": { 66 | "dev": "webpack & npm run watch:svg", 67 | "watch:svg": "onchange 'assets/svg/*.svg' -- npm run svg", 68 | "svg": "vsvg -s ./assets/svg -t ./assets/icon", 69 | } 70 | ``` 71 | > npm中`--`后面代表着参数 72 | ## 5. git钩子 73 | 74 | 这也是非常实用功能之一,可以利用git钩子构建代码约束。经常用到的工具包是[`husky`](https://github.com/typicode/husky),通过husky源码知道,它替换了项目中.git/hooks钩子。项目中常用钩子是`precommit`,`prepush`, `commit-msg` 75 | 76 | 安装husky: 77 | 78 | ``` shell 79 | npm install husky --save-dev 80 | ``` 81 | 82 | 约束: 83 | 84 | ``` json 85 | "scripts": { 86 | "precommit": "npm run format && npm run eslint" 87 | } 88 | ``` -------------------------------------------------------------------------------- /docs/js/think-different-MVC-MVP-MVVM.md: -------------------------------------------------------------------------------- 1 | # MVC、MVP、MVVM区别 2 | 3 | 软件中最核心的,最基本的东西是什么? 是数据,我们写的所有代码,都是围绕数据的。 4 | 5 | 围绕着数据的产生、修改等变化,出现了业务逻辑。 6 | 7 | 围绕着数据的显示,出现了不同的界面技术。 8 | 9 | ## MVC 10 | 11 | 网上很多资料对MVC看似有矛盾,其实是因为MVC模式主流分为`主动MVC`和`被动MVC`两种。 12 | 13 | ### 主动MVC 14 | 15 | 主动MVC也是对应着传统MVC理论思想,其中的主动是表示,Model变化会主动通知View更新。 16 | 17 | `Modal`: 封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。不要认为Modal是数据库的Entity层,其实理解为业务层更恰当。 18 | 19 | `View`: 负责数据的展示,因为是Modal主动更新View,所以View需要事先订阅Modal的变化 20 | 21 | `Controller`: M和V之间的连接器,接受View层的变化并更新到Modal上 22 | 23 | ![](https://blog.nodejitsu.com/content/images/2014/Feb/mvc.png) 24 | 25 | ### 被动MVC 26 | 27 | 这是常规Web MVC框架使用的模式,如ASP .NET MVC,Struts。Controller是一个核心层,负责管理View和Modal。 28 | 29 | 被动MVC中,模型Modal对视图View和控制器Controller一无所知,仅仅是被使用。视图也不会主动订阅Modal的更新。视图的显示是根据控制器来决定。 30 | 31 | ![image](https://user-images.githubusercontent.com/6310131/48696408-d2857080-ec1c-11e8-8a8a-ce665ba00fc1.png) 32 | 33 | ### 实际项目应用MVC 34 | 35 | 实际项目中,对MVC的应用往往采用更灵活的方式,除了每层各司其职外,还需要加入用户的交互指令。 36 | 37 | 如果你熟悉`ASP .NET MVC`,一定对以下这张图不陌生。 38 | 39 | ![image](https://user-images.githubusercontent.com/6310131/48696231-46734900-ec1c-11e8-9f95-c1edee6d0abf.png) 40 | 41 | 如果你熟悉`Backbond`,则更复杂些。用户既可以通过发送DOM事件到View,View再要求Model发生改变。也可以通过URL改变触发Controller层,从而改变View。Backbond View层比较重而Controller比较轻。 42 | 43 | ![image](https://user-images.githubusercontent.com/6310131/48553500-b4ff9080-e916-11e8-93b3-7b1d33ae9326.png) 44 | 45 | ## MVP 46 | 47 | MVP是MVC的一种变种,其跟传统的MVC不同的表现在: 48 | 49 | * **View层和Modal层没有直接关系,都是通过Presenter传递** 50 | 51 | * **Presenter与View层通信是双向的** 52 | 53 | ![](https://blog.nodejitsu.com/content/images/2014/Feb/mvp.png) 54 | 55 | ## MVVM 56 | 57 | MVVM跟MVP基本类似,Presenter替换为ViewModal。其区别是**MVVM通过双向数据绑定(通过事件同步到ViewModal和View)来进行View和ViewModal的同步**。Vue、Angular、Ember都是采用这种模式。 58 | 59 | MVVM使得前后端分离更加彻底。前端不再仅仅是UI层展示,可以将后端更多的业务逻辑搬到前端进行处理,后台除了提供常规的数据库业务数据,有更多的精力去专注于保持系统稳定和可扩展性。 60 | 61 | ![](https://blog.nodejitsu.com/content/images/2014/Feb/mvvm.png) 62 | ## 参考文章: 63 | 64 | [Scaling Isomorphic Javascript Code](https://blog.nodejitsu.com/scaling-isomorphic-javascript-code/) 65 | 66 | [阮一峰 MVC,MVC,MVP 和 MVVM 的图示](http://www.ruanyifeng.com/blog/2015/02/mvcmvp_mvvm.html) 67 | 68 | [开发中的MVVM模式及与MVP和MVC的区别](https://www.jianshu.com/p/ffcb84dc4ebc) -------------------------------------------------------------------------------- /docs/project/postcss.md: -------------------------------------------------------------------------------- 1 | # PostCSS 2 | 3 | ## 概念 4 | 5 | PostCSS 本身是一个功能比较单一的工具。它提供了一种方式用 JavaScript 代码来处理 CSS。它负责把 CSS 代码解析成抽象语法树结构(Abstract Syntax Tree,AST),再交由插件来进行处理。PostCSS 的强大之处在于其不断发展的插件体系。 6 | 7 | 开发者一个常见的误解是,PostCSS是另一个像SASS和LESS的预处理器。相信很多人使用PostCSS插件,会把注意力放到模仿其它预处理特性上,如变量,条件语句,循环和混入。随着PostCSS的发展,许多其他功能的插件被开发出来,有许多完全和传统的预处理器不同的新特性被引入。 8 | 9 | **你可以把PostCSS,当成像SASS和LESS这样的预处理器使用,你也可以用一些像SASS,LESS的扩展,来升级你的工具集。** 10 | 11 | ## 常用插件 12 | 13 | * [autoprefixer](https://github.com/postcss/autoprefixer) 给css加前缀 14 | * [precss](https://github.com/jonathantneal/precss) 提供类似sass语法,告别sass包 15 | * [cssnext](https://github.com/MoOx/postcss-cssnext) 将未来CSS特性编译为现今支持的特性 16 | * [px2rem-postcss](https://github.com/songsiqi/px2rem-postcss) 将px转为rem工具。`移动端强烈推荐` 17 | 18 | ## Webpack配置PostCSS插件 19 | 20 | 如下例子,webpack配置了precss和autoprefixer插件: 21 | 22 | ``` js 23 | // Wepack 4.x in webpack.config.js 24 | module.exports = { 25 | module: { 26 | rules: [ 27 | { 28 | test: /\.css$/, 29 | use: ['style-loader', 'css-loader', 'postcss-loader'] 30 | } 31 | ] 32 | } 33 | } 34 | 35 | // postcss.config.js 36 | module.exports = { 37 | plugins: [ 38 | "precss": {}, 39 | "autoprefixer": {} 40 | ] 41 | } 42 | 43 | // 或者 44 | module.exports = { 45 | module: { 46 | rules: [ 47 | { 48 | test: /\.css$/, 49 | use: [ 50 | 'style-loader', 51 | 'css-loader', 52 | { 53 | loader: 'postcss-loader', 54 | options: { 55 | ident: 'postcss', 56 | plugins: [ 57 | require('precss')({...options}), 58 | require('autoprefixer')({...options}) 59 | ] 60 | } 61 | } 62 | ] 63 | } 64 | ] 65 | } 66 | } 67 | ``` 68 | 69 | ## 与Sass配合 70 | 71 | 如果你对PostCSS的各种特性很感兴趣,但又不想放弃熟练使用的Sass。不用担心,你可以完全把Sass与PostCSS结合使用. 72 | 73 | 1. 安装LibSass:npm install node-sass --save-dev 74 | 2. 在配置文件中先对.scss文件进行处理后再用PostCSS进行处理 -------------------------------------------------------------------------------- /docs/team-standard/1.standard-ai-css.md: -------------------------------------------------------------------------------- 1 | # AI前端CSS规范 2 | 3 | ### 分号 4 | 5 | 每个属性声明后面都要加分号。 6 | 7 | ### 命名 8 | 9 | 1. 不使用id选择器 10 | 2. 适用有意义的名词命名 11 | 3. 单词全部小写,名词超过1个时,使用`-`分隔符 12 | 13 | ### 属性声明顺序 14 | 15 | 原则:整体到局部,外部到内部,重要属性优先 16 | 17 | ``` css 18 | .element { 19 | display: block; 20 | float: left; 21 | position: absolute; 22 | top: 0; 23 | right: 0; 24 | bottom: 0; 25 | left: 0; 26 | z-index: 100; 27 | margin: 0 100px; 28 | padding: 50px; // padding习惯写到margin后面 29 | width: 100px; 30 | height: 100px; 31 | border: 1px solid #e5e5e5; border-radius: 3px; 32 | font: normal 13px "Helvetica Neue", sans-serif; 33 | color: #333; 34 | text-align: center; 35 | line-height: 1.5; 36 | background-color: #f5f5f5; 37 | opacity: 1; 38 | } 39 | ``` 40 | 41 | ### 其他规范 42 | 43 | 使用prettier格式化工具约束,推荐配置如下: 44 | 45 | * 格式自动化 46 | * 4个缩进 47 | * 全部单引号 48 | * 属性`:`后有空格 49 | * 颜色全部小写 50 | * 小数点前面0自动添加 51 | 52 | ``` js 53 | module.exports = { 54 | printWidth: 100, // 设置prettier单行输出(不折行)的(最大)长度 55 | 56 | tabWidth: 4, // 设置工具每一个水平缩进的空格数 57 | 58 | useTabs: false, // 使用tab(制表位)缩进而非空格 59 | 60 | semi: false, // 在语句末尾添加分号 61 | 62 | singleQuote: true, // 使用单引号而非双引号 63 | 64 | trailingComma: 'none', // 在任何可能的多行中输入尾逗号 65 | 66 | bracketSpacing: true, // 在对象字面量声明所使用的的花括号后({)和前(})输出空格 67 | 68 | arrowParens: 'avoid', // 为单行箭头函数的参数添加圆括号,参数个数为1时可以省略圆括号 69 | 70 | // parser: 'babylon', // 指定使用哪一种解析器 71 | 72 | jsxBracketSameLine: true, // 在多行JSX元素最后一行的末尾添加 > 而使 > 单独一行(不适用于自闭和元素) 73 | 74 | rangeStart: 0, // 只格式化某个文件的一部分 75 | 76 | rangeEnd: Infinity, // 只格式化某个文件的一部分 77 | 78 | filepath: 'none', // 指定文件的输入路径,这将被用于解析器参照 79 | 80 | requirePragma: false, // (v1.7.0+) Prettier可以严格按照按照文件顶部的一些特殊的注释格式化代码,这些注释称为“require pragma”(必须杂注) 81 | 82 | insertPragma: false, // (v1.8.0+) Prettier可以在文件的顶部插入一个 @format的特殊注释,以表明改文件已经被Prettier格式化过了。 83 | 84 | proseWrap: 'preserve' // (v1.8.2+) 85 | } 86 | ``` 87 | 88 | ### 参考连接 89 | 90 | [百度CSS规范指南](https://github.com/ecomfe/spec/blob/master/css-style-guide.md) 91 | 92 | [腾讯CSS规范指南](http://alloyteam.github.io/CodeGuide/#css) 93 | 94 | [Google CSS规范指南](http://iischajn.github.io/trans/htmlcss-guide/) -------------------------------------------------------------------------------- /docs/temp/mirco-frontend.md: -------------------------------------------------------------------------------- 1 | # 微前端 2 | 3 | 微前端方式: 4 | 1. 通过nginx配置导航不同系统 5 | * 常见方式,缺点:每次都刷新页面 6 | 1. 通过iframe组合 7 | * 常见方式,缺点:应用间交互麻烦 8 | 1. 通过路由进行应用拆分 9 | * 目前较为主流的方式,需要一点架构改造,比如:统一依赖,各子应用如何互相隔离(包括数据流隔离、css样式隔离等)、部署改造 10 | * 缺点:需要统一技术栈,因为实质还是通过主路由+懒加载实现。 11 | 1. [single-spa](https://github.com/CanopyTax/single-spa)解决方案 12 | * 能跨技术栈,但有一些缺点,目前阶段难用在生产阶段。生产使用可以考虑基于single-spa的框架,比如qiankun。 13 | 1. webcomponents 14 | * 较好的方式,引入js/style进行隔离(ShadowDOM) 15 | * 缺点:1. 技术新,兼容性不好,改造现有应用难;2. 不成熟的生态,组件间的数据通信是大问题。 16 | 17 | single-spa的原理就是,将子项目中的link/script标签和`
`插入到主项目,而这个操作的核心就是动态加载js和css。 18 | 19 | ## iframe的优缺点 20 | 21 | ### 缺点 22 | 23 | 1. 页面加载问题: 影响主页面加载,阻塞onload事件,本身加载也很慢,页面缓存过多会导致电脑卡顿。(无法解决) 24 | 25 | 2. 布局问题:iframe必须给一个指定的高度,否则会塌陷。解决办法:子系统实时计算高度并通过postMessage发送给主页面,主页面动态设置高度,修改子系统或者代理插入脚本。有些情况会出现多个滚动条,用户体验不佳。 26 | 27 | 3. 弹窗及遮罩层问题:只能在iframe范围内垂直水平居中,没法在整个页面垂直水平居中。 28 | 29 | * 解决办法1:通过与框架页面消息同步解决,将弹窗消息发送给主页面,主页面来弹窗,对原项目改动大且影响原项目的使用。 30 | * 解决办法2:修改弹窗的样式:隐藏遮罩层,修改弹窗的位置。修改的办法就是通过代理服务器插入css样式。 31 | * 补充:iframe里面的内容无法实现占满屏幕的弹窗(非全屏),他只能在iframe范围内全屏,无法跳出iframe的限制在主页面全屏,不过这种情况也很少。 32 | 33 | 4. 浏览器前进/后退问题:iframe和主页面共用一个浏览历史,iframe会影响页面的前进后退,大部分时候正常,iframe多次重定向则会导致浏览器的前进后退功能无法正常使用,不是全部页面都会出现,基本可以忽略。但是iframe页面刷新会重置(比如说从列表页跳转到详情页,然后刷新,会返回到列表页),因为浏览器的地址栏没有变化。 34 | 35 | 5. iframe的页面跳转到其他页面出问题,比如两个iframe之间相互跳转,直接跳转会只在iframe范围内跳转,所以必须通过主页面来进行跳转。不过iframe跳转的情况很少 36 | 37 | 6. 不同源的系统之间的通讯需要通过postMessage,存在一定的安全性 38 | 39 | 40 | ### 优点 41 | 42 | 1. 完全隔离了css和js,避免了各个系统之间的样式和js污染 43 | 1. 可以在子系统完全不修改的情况下嵌入进来 44 | 45 | 46 | 47 | 48 | 49 | ## qiankun解决方案 50 | 51 | 基于single-spa,但使用起来更方便 52 | 53 | single-spa缺点:single-spa要求所有js/css/images等资源,都统一打包到一个js文件中,过大。 54 | qiankun改进点:支持入口为index.html,因为qiankun内使用import-html-entry提取出了依赖的js、css文件等,更利于并行加载 55 | 56 | ### 1. [import-html-entry](https://github.com/kuitos/import-html-entry/blob/master/src/index.js#L216) 57 | 58 | 通过该工具函数,可解析html为入口 59 | 60 | import-html-entry源码解析: 61 | 1. 根据html的url,拿到所有html文档内容 62 | 1. 根据正则,拿到template/scripts/entry/styles等内容 63 | 1. 返回内容和处理方法 64 | 65 | 66 | ## 参考资料 67 | 68 | * https://zhuanlan.zhihu.com/p/39102712 69 | * https://tech.meituan.com/2018/09/06/fe-tiny-spa.html 70 | * [qiankun微前端解决方案](https://zhuanlan.zhihu.com/p/78362028) -------------------------------------------------------------------------------- /docs/team-standard/recommend-css-font.md: -------------------------------------------------------------------------------- 1 | # 推荐-优雅引用字体 2 | 3 | ## 编程式字体方法的好处 4 | 1. 学习视觉同学对于具体字体的考量,也许还能发现视觉同学的bug 5 | 2. 全局控制,避免样式散乱 6 | 3. 书写字体样式成为一门艺术 7 | 8 | ## bad 9 | 10 | ``` css 11 | .form-title { 12 | font: 'PingFang-SC-medium'; 13 | font-size: 18px; 14 | font-color: #22222; 15 | } 16 | 17 | .form-text { 18 | font: 'PingFang-SC-regular'; 19 | font-size: 14px; 20 | font-color: #333333; 21 | } 22 | ``` 23 | 24 | ## good 25 | 26 | variables.scss文件 27 | 28 | ``` scss 29 | $font-normal-color = #222222; // 字体颜色 30 | $font-light-color = #333333; 31 | 32 | @mixin font-class($fontFamily, $fontSize, $fontColor) { 33 | font-family: $fontFamily; 34 | font-size: $fontSize; 35 | color: $fontColor; 36 | } 37 | 38 | @mixin font-large($size: 14px, $color: $font-normal-color) { 39 | @include font-class($font-family-medium, $size, $color); 40 | } 41 | 42 | @mixin font-normal($size: 14px, $color: $font-light-color) { 43 | @include font-class($font-family-regular, $size, $color); 44 | } 45 | ``` 46 | 47 | 应用: 48 | ``` scss 49 | .form-title { 50 | @include font-large(18px, #22222); 51 | } 52 | 53 | .form-text { 54 | @include font-large(14px, #333333); 55 | } 56 | ``` 57 | > font-large/font-normal等公用mixins建议放在统一的`variables.scss`文件中,再通过sass-resource自动引入到所有组件中 58 | 59 | ## 最佳字体顺序参考 60 | 61 | ### PC端 62 | 63 | ``` 64 | $font-family-medium = 'PingFang-SC-medium', Helveti ca, Tahoma, Arial, 'Microsoft YaHei', 'Hiragino Sans GB', 'WenQuanYi Micro Hei', sans-serif; 65 | $font-family-regular = 'PingFang-SC-regular', Helvetica, Tahoma, Arial, 'Microsoft YaHei', 'Hiragino Sans GB', 'WenQuanYi Micro Hei', sans-serif; 66 | ``` 67 | ### 移动端 68 | ``` 69 | $font-family-ultralight = 'PingFangSC-Ultralight', 'Source Han Sans CN', "Helvetica Neue"; 70 | $font-family-regular = 'PingFangSC-Regular', 'Source Han Sans CN', "Helvetica Neue"; 71 | $font-family-medium = 'PingFangSC-Medium', 'Source Han Sans CN Medium', "Helvetica Neue"; 72 | $font-family-thin = 'PingFangSC-Thin', 'Source Han Sans CN Thin', "Helvetica Neue"; 73 | $font-family-light = 'PingFangSC-Light', 'Source Han Sans CN Light', "Helvetica Neue"; 74 | $font-family-semibold = 'PingFangSC-Semibold', 'Source Han Sans CN Light', "Helvetica Neue"; 75 | ``` 76 | -------------------------------------------------------------------------------- /docs/temp/react/createContext.md: -------------------------------------------------------------------------------- 1 | ## 为什么要Context 2 | 3 | **为了避免中间组件的props透传**,比如全局的皮肤theme字段。 4 | 5 | ``` js 6 | class App extends React.Component { 7 | render() { 8 | return ; 9 | } 10 | } 11 | 12 | function Toolbar(props) { 13 | // The Toolbar component must take an extra "theme" prop 14 | // and pass it to the ThemedButton. This can become painful 15 | // if every single button in the app needs to know the theme 16 | // because it would have to be passed through all components. 17 | return ( 18 |
19 | 20 |
21 | ); 22 | } 23 | 24 | class ThemedButton extends React.Component { 25 | render() { 26 | return 81 | ); 82 | } 83 | ``` -------------------------------------------------------------------------------- /docs/js/regex.md: -------------------------------------------------------------------------------- 1 | # 正则表达式 2 | 3 | 正则表达式对于每个开发者都非常重要,用的好能在一些关键时刻让自己变得轻松。推荐个正则可视化工具:[regulex](https://jex.im/regulex/#!flags=&re=%5E(a%7Cb)*%3F%24),帮助学习者直观验证。再推荐[Regex Tester](https://www.regextester.com/)进行在线调试。 4 | 5 | 原理: 6 | 1. 解析 7 | * 语法分析 8 | * 词法分析 9 | 1. 编译 10 | * 状态机 11 | 1. 运行 12 | 13 | ## 限定符 14 | ``` js 15 | * 0次或多次 16 | + 1次或多次 17 | ? 0次或1次 18 | {n} 匹配n次 19 | {n,} 至少n次 20 | {n, m} 至少n次,至多m次 21 | ``` 22 | 23 | ``` js 24 | \ 转义符 25 | . 任意字符 26 | | 或运算 27 | \d = [0-9] 28 | \D = [^0-9] 29 | \w = [a-zA-Z0-9_] 30 | \W = [^a-zA-Z0-9_] 31 | \s 空白字符 32 | \S 非空白字符 33 | ^ 定位符,字符串起始位置;当在[]号时代表取反。 34 | $ 定位符,字符串末尾位置。 35 | ``` 36 | 37 | ## 贪婪模式/非贪婪模式 38 | 贪婪模式:在整个表达式匹配成功的前提下,尽可能多的匹配。常用的有`*` 和 `+`限定符 39 | ``` js 40 | /.*/g 41 | ``` 42 | 43 | 非贪婪模式:在整个表达式匹配成功的前提下,尽可能少的匹配。 44 | ``` js 45 | // 遇到?,尽可能少的匹配 46 | /.*?/g 47 | ``` 48 | 49 | ## 正则方法 50 | 51 | ### regex.exec(string) 52 | 如果匹配成功,exec() 方法返回一个数组;匹配失败,返回 null 53 | ``` js 54 | let regexExec = /#(.*)$/.exec('http://localhost:8081/#/demo') 55 | /* 56 | [ '#/demo', 57 | '/demo', 58 | index: 22, 59 | input: 'http://localhost:8081/#/demo' ] 60 | */ 61 | ``` 62 | 63 | ### regex.test(string) 64 | 查看正则表达式与指定的字符串是否匹配。返回 true 或 false 65 | ``` js 66 | let regexTest = /#(.*)$/.test('http://localhost:8081/#/demo') // true 67 | ``` 68 | 69 | ### string.match(regex) 70 | 71 | * `在非全局匹配模式下`,类似regex.exec(string)。 72 | * 在`全局匹配模式下(使用g标志)`,则将返回与完整正则表达式匹配的所有结果(Array),但不会返回捕获组,或者未匹配 null。 73 | ``` js 74 | // 非全局匹配模式 75 | let stringMatch = 'http://localhost:8081/#/demo'.match(/#(.*)$/) 76 | // [ '#/demo', 77 | // '/demo', 78 | // index: 22, 79 | // input: 'http://localhost:8081/#/demo' ] 80 | 81 | // 全局匹配模式 82 | let stringMatch = 'http://localhost:8081/#/demo'.match(/#(.*)$/g) // [ '#/demo' ] 83 | ``` 84 | 85 | ### string.search(regex) 86 | 匹配成功,search() 返回正则表达式在字符串中首次匹配项的索引,否则返回 -1。类似regex.test() 87 | ``` js 88 | let stringSearch = 'http://localhost:8081/#/demo'.search(/#(.*)$/) // 22 89 | ``` 90 | 91 | ## 其他 92 | 其他正则相关语法@jawil总结的十分详细,故转载在此。 93 | 以下图片有个谬误,这里纠正下:`\w` 匹配一个单字字符(字母、数字或者下划线)。等价于 [A-Za-z0-9_]。 94 | 95 | ![](https://camo.githubusercontent.com/0c015371b3762c589971a7b227c47b17791b1123/68747470733a2f2f73332e353163746f2e636f6d2f7779667330322f4d30312f38452f35362f774b696f4c31692d4a7a65546650394541414f6c376749536d6a343938302e676966) -------------------------------------------------------------------------------- /docs/temp/Webpack升级史 .md: -------------------------------------------------------------------------------- 1 | # Webpack升级史 2 | 3 | 经历过自定义配置过webpack的人都知道,webpack的各种配置是一个难点。好在现在工程都在推崇零配置打包工具,Parcel快速涨星就能知道大家对零配置的迫切。好在webpack开发团队顺应潮流,最新的webpack4很大改变就是其配置变得简单的多。 4 | 5 | 网上有很多webpack配置教程结构相差很大,容易让人感到迷惑和不解。官方的[webpack changelog](https://github.com/webpack/webpack/releases)内容较多,不适合逐条查看。但从中也可以找到比较重要的[wepack3到webpack4大版本升级日志](https://github.com/webpack/webpack/issues/6064)。现在跟随笔者,看下webpack1到webpack4进行了哪些重要配置改变。 6 | 7 | ## [Rules vs Loaders in Webpack - What's the Difference?](https://stackoverflow.com/questions/43002099/rules-vs-loaders-in-webpack-whats-the-difference) 8 | 9 | question: Loaders is used in Webpack 1, and Rules in Webpack 2. They say that "Loaders" in the future it will be deprecated in favour of module.rules. 10 | 11 | Sample for `loaders` to `rules`: 12 | ``` js 13 | // Wepack 1.x 14 | module.exports = { 15 | module: { 16 | loaders: [ 17 | { 18 | test: /\.css$/, 19 | loader: "style-loader!css-loader!postcss-loader" 20 | } 21 | ] 22 | }, 23 | postcss: function () { 24 | return [precss, autoprefixer]; 25 | } 26 | } 27 | ``` 28 | 29 | ``` js 30 | // Wepack 4.x in webpack.config.js 31 | module.exports = { 32 | module: { 33 | rules: [ 34 | { 35 | test: /\.css$/, 36 | use: [ 37 | 'style-loader', 38 | 'css-loader', 39 | { 40 | loader: 'postcss-loader', 41 | options: { 42 | ident: 'postcss', 43 | plugins: [ 44 | require('precss')({...options}), 45 | require('autoprefixer')({...options}) 46 | ] 47 | } 48 | } 49 | ] 50 | } 51 | ] 52 | } 53 | } 54 | ``` 55 | 56 | ## 参考文章 57 | 58 | * [Rules vs Loaders in Webpack - What's the Difference?](https://stackoverflow.com/questions/43002099/rules-vs-loaders-in-webpack-whats-the-difference) 59 | 60 | * [To v2 or v3 from v1](https://webpack.js.org/migrate/3/#module-loaders-is-now-module-rules) 61 | 62 | * [To v4 from v3](https://webpack.js.org/migrate/4/#node-js-v4) -------------------------------------------------------------------------------- /docs/temp/heap-stack.md: -------------------------------------------------------------------------------- 1 | # 数据结构堆栈 vs 内存堆栈 2 | 3 | 数据结构中的堆和内存中的堆是两个完全不同的概念。前者是组织数据的一种手段(或者叫工具),后者只是指明数据存储在哪种内存区之上。 4 | 5 | 数据结构中的一般称“栈(stack)”,是一种后进先出的数据结构。它是一种概念,或者说是一种逻辑技术,与语言、平台无关。 6 | 7 | 内存管理中的“堆栈”其实是分为堆(heap)和栈(stack)的,以引用变量为例,引用变量本身存储在栈中,引用变量指向的值存储在堆中。 8 | 如int[] arr = {1, 2, 3}; 9 | 变量arr(数组名)存储在栈中,变量arr的值(数组元素)存储在堆中(普通结构)。 10 | 内存管理中的栈采用的就是数据结构中的栈的思想,即遵循后进先出的管理方法。 11 | 好比数据结构中的栈是一项先进的技术,在内存管理中采用了该技术。 12 | 13 | ## 数据结构中的堆栈 堆结构和栈结构 14 | 15 | `栈是一种特殊的列表`,栈内的元素只能通过列表的一端访问,这一端称为栈顶。 16 | 栈被称为是一种`后入先出`(LIFO,last-in-first-out)的数据结构。 17 | 由于栈具有后入先出的特点,所以任何不在栈顶的元素都无法访问。 18 | 为了得到栈底的元素,必须先拿掉上面的元素。 19 | 20 | `堆是一种经过排序的树形数据结构`,每个结点都有一个值。 21 | 通常我们所说的堆的数据结构,是指二叉堆。 22 | 堆的特点是根结点的值最小(或最大),且根结点的两个子树也是一个堆。 23 | 由于堆的这个特性,常用来实现优先队列,堆的存取是随意,这就如同我们在图书馆的书架上取书, 24 | 虽然书的摆放是有顺序的,但是我们想取任意一本时不必像栈一样,先取出前面所有的书, 25 | 我们只需要关心书的名字。 26 | 27 | ## 内存中的堆栈 堆内存 和栈内存 28 | 29 | ### 基本类型 30 | 31 | 有Undefined、Null、Boolean、Number 和String。这些类型在内存中分别占有固定大小的空间,`他们的值保存在栈空间`,我们通过按值来访问的。 32 | 33 | ### 引用类型 34 | 35 | Array,Function,Object都是引用数据类型。 36 | 37 | 当我们要访问堆内存中的引用数据类型时,实际上我们首先是从变量中获取了该对象的地址指针, 然后再从堆内存中取得我们需要的数据。 38 | 39 | ``` js 40 | // 基本数据类型-栈内存 41 | let a1 = 0; 42 | // 基本数据类型-栈内存 43 | let a2 = 'this is string'; 44 | // 基本数据类型-栈内存 45 | let a3 = null; 46 | 47 | // 对象的指针存放在栈内存中,指针指向的对象存放在堆内存中 48 | let b = { m: 20 }; 49 | // 数组的指针存放在栈内存中,指针指向的数组存放在堆内存中 50 | let c = [1, 2, 3]; 51 | ``` 52 | 53 | ![](https://user-gold-cdn.xitu.io/2019/6/25/16b8c0b5752823f6?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 54 | 55 | 56 | 57 | 在JS中,基本数据类型变量大小固定,并且操作简单容易,所以把它们放入栈中存储。 引用类型变量大小不固定,所以把它们分配给堆中,让他们申请空间的时候自己确定大小,这样把它们分开存储能够使得程序运行起来占用的内存最小。 58 | 59 | ## 为什么内存分配中要用栈这个数据结构 60 | 61 | `栈内存一般存储的是函数的调用信息和函数中申明的变量`。 62 | 63 | 因为函数的调用是递归的,`外层函数一定比内层被调用的函数先加载和执行`,而一定`等到内层被调用函数结束后才能结束`,这个先进后出的机制就是为什么叫栈内存的原因。 64 | 65 | > JavaScript中函数的执行过程,其实就是一个入栈出栈的过程。 66 | 67 | ## 闭包中的变量都保存在堆内存中 68 | 69 | 闭包中的变量并不保存在栈内存中,而是保存在堆内存中。 这也就解释了函数调用之后之后为什么闭包还能引用到函数内的变量。 70 | 71 | ``` js 72 | function A() { 73 | let a = 1; 74 | function B() { 75 | console.log(a); 76 | } 77 | return B; 78 | } 79 | let res = A(); 80 | ``` 81 | 82 | 函数 A 返回了一个函数 B,并且函数 B 中使用了函数 A 的变量,函数 B 就被称为闭包。 83 | 84 | `函数 A 弹出调用栈后,函数 A 中的变量这时候是存储在堆上的`,所以函数B依旧能引用到函数A中的变量。 85 | 现在的 JS 引擎可以通过逃逸分析辨别出哪些变量需要存储在堆上,哪些需要存储在栈上。 86 | 87 | 88 | https://juejin.im/post/5d116a9df265da1bb47d717b -------------------------------------------------------------------------------- /docs/js/es6-latest-feature.md: -------------------------------------------------------------------------------- 1 | # ES6-ECMAScript特性汇总 2 | ES6+不仅给javascript语言带来质的飞跃,也在语法层面对开发人员友好了很多,js代码可以写的简洁与工整。这里也推荐大家看笔者整理的[AI Javascript风格指南 ](https://lq782655835.github.io/blogs/team-standard/clean-code-javascript.html),有很多较长ES5代码转为简洁ES6+的例子。以下汇总ES6+新增的特性,帮助大家更好的在实际编码过程中,应用上这些特性。从[tc39/proposals](https://github.com/tc39/proposals/blob/master/finished-proposals.md)可以看到Javascript 发展委员会TC39已经纳入标准的提案。 3 | 4 | ## ES6 5 | 详见笔者另外一篇文章:[ES6-新增特性一览](https://lq782655835.github.io/blogs/js/ES6-1.new-feature.html) 6 | 7 | ## ES7 8 | * Array.prototype.includes() 9 | * 求幂运算符(**) 10 | ``` js 11 | [1,2].indexOf(3) // false 12 | Math.pow(4,2)== 4 ** 2 13 | ``` 14 | 15 | ## ES8 16 | * 字符串填充(padStart 和 padEnd) 17 | * Object.values() 18 | * Object.entries() 19 | * Object.getOwnPropertyDescriptors() 20 | * 函数参数列表和调用中的尾随逗号 21 | * Async Functions async/await(异步函数) 22 | * 共享内存 和 Atomics 23 | ``` js 24 | 'test'.padEnd(8, 'abcd') // 'testabcd' 25 | 26 | // Object.values()适用对象和数组 27 | Object.values({ name: 'Fred', age: 87 }) // ['Fred', 87] 28 | Object.values(['Fred', 'Tony']) // ['Fred', 'Tony'] 29 | 30 | // Object.entries()适用对象和数组 31 | Object.entries({ name: 'Fred', age: 87 }) // [['name', 'Fred'], ['age', 87]] 32 | Object.entries(['Fred', 'Tony']) // [['0', 'Fred'], ['1', 'Tony']] 33 | 34 | async function doSomething() { 35 | console.log(await doSomethingAsync()) 36 | } 37 | 38 | ``` 39 | 40 | ## ES9 41 | * 对象的Rest(剩余)/Spread(展开) 属性 42 | * Asynchronous iteration for-await-of(异步迭代) 43 | * Promise.prototype.finally() 44 | * 正则表达式改进 45 | * 先行断言(lookahead) 和 后行断言(lookbehind) 46 | * Unicode 属性转义 \p{…} 和 \P{…} 47 | * 命名捕获组(Named capturing groups) 48 | * 正则表达式的 ‘s’ 标志 49 | ``` js 50 | [first, second, ...others] = [1, 2, 3, 4, 5] // ES6开始支持数组rest 51 | const { first, second, ...others } = { first: 1, second: 2, third: 3, fourth: 4, fifth: 5 } // ES9支持对象rest 52 | 53 | // 异步迭代 54 | for await (const data of readFile(filePath)) { 55 | console.log(data) 56 | } 57 | 58 | // 无论成功还是失败,都会执行finally 59 | fetch('file.json') 60 | .then(data => data.json()) 61 | .catch(error => console.error(error)) 62 | .finally(() => console.log('finished')) 63 | ``` 64 | 持续更新... 65 | 66 | ## 参考文章 67 | * [JavaScript 完全手册(2018版)](https://www.css88.com/archives/9965) 68 | * [TC39 处理 ECMAScript 新特性的工作流程](https://www.css88.com/archives/7742) 69 | * [New ES2018 Features Every JavaScript Developer Should Know](https://css-tricks.com/new-es2018-features-every-javascript-developer-should-know/) -------------------------------------------------------------------------------- /docs/project/ts-vue-shortcoming.md: -------------------------------------------------------------------------------- 1 | # TypeScript在Vue2.x中的坑 2 | 3 | TypeScript是JS的超集,笔者非常建议在大型系统中使用TypeScript,因为它的类型系统使得代码可维护性提高了许多。但由于Vue2.x版本有设计断层,导致很多类型是通过declare方式推导出,而不是基于class的API,这也是为什么Vue3.0用typescript重写的原因之一。在期待Vue3.0的同时,来聊下在Vue2.x中使用TypeScript的一些坑。 4 | 5 | ## 1. 扩展Vue全局方法复杂 6 | 增加Vue全局方法,需要在'vue/types/vue'模块下扩展,十分突兀。 7 | 8 | ``` ts 9 | // 1. 确保在声明补充的类型之前导入 'vue' 10 | // 保证是在Vue模块下,使得不影响以前的Vue模块 11 | import Vue from 'vue' 12 | 13 | // 2. 定制一个文件,设置你想要补充的类型 14 | // 在 types/vue.d.ts 里 Vue 有构造函数类型 15 | declare module 'vue/types/vue' { 16 | // 3. 声明为 Vue 补充的东西 17 | interface Vue { 18 | $myProperty: string 19 | } 20 | } 21 | ``` 22 | 23 | ## 2. 全局mixin this问题 24 | 由于Vue有设计断层,所以this问题很苦恼。表现在全局mixin时,由于没有mixin类型,所以当单独抽取出globalMixin变量时,方法中this.$router会报错。 25 | ``` ts 26 | // bad 27 | const globalMixin = { 28 | methods: { 29 | $natigateTo(path: string, params?: any): void { 30 | // this.$router will throw error 31 | this.$router.push(withCluster) 32 | } 33 | } 34 | } 35 | Vue.mixin(globalMixin) 36 | ``` 37 | 38 | ``` ts 39 | // good for 2.x 40 | Vue.mixin({ 41 | methods: { 42 | $natigateTo(path: string, params?: any): void { 43 | this.$router.push(withCluster) 44 | } 45 | } 46 | }) 47 | ``` 48 | 49 | ## 3. 装饰器 this !== target 问题 50 | 正常的Class在加装饰器的时候使用method.apply(target, args)是没有问题的。但是vue在注册组件的时候会进行初始化,this在这个时候被改变了(class内部的的this变了,此时 this !== target了) 51 | 52 | ``` ts 53 | const log = (target, name, descriptor) => { 54 | const method = descriptor.value 55 | descriptor.value = function(...args) { 56 | console.log(`你点击了 ${name} 方法`) 57 | method.apply(target, args) 58 | } 59 | return descriptor 60 | } 61 | 62 | @Component 63 | export default class Home extends Vue { 64 | @State jobId: string 65 | @Mutation('SET_JOB_ID') setJobId: (jobid: string) => void 66 | 67 | @log 68 | startDashboard(row: JobItem): void { 69 | let { jobId } = row 70 | // below will throw error: 71 | // "TypeError: this.setJobId is not a function" 72 | // because now this !== target 73 | this.setJobId(jobId) 74 | console.log(this.jobId) 75 | } 76 | } 77 | ``` 78 | 79 | ## 参考文章 80 | * [TypeScirpt - Modules](https://www.typescriptlang.org/docs/handbook/modules.html) 81 | 82 | * [Vue - TypeScript](https://cn.vuejs.org/v2/guide/typescript.html) 83 | * [Element/Component name with vue-class-component](https://medium.com/haiiro-io/element-component-name-with-vue-class-component-f3b435656561) -------------------------------------------------------------------------------- /docs/tools/vscode-plugin-develop.md: -------------------------------------------------------------------------------- 1 | # VSCode插件开发实践 2 | 3 | 开发项目过程中,避免不了工具函数书写。当开发另外一个项目时,经常需要搜索项目,打开并找到对应工具函数拷贝。这种方式容易打断工作流程,而使用vscode插件可以在不离开IDE前提下,快速找到对应实用工具。 4 | 5 | > 以下实践代码可到github [lq782655835/common -utils](https://github.com/lq782655835/common-utils) 6 | 7 | ## 开发插件 8 | 9 | [官方开发插件教程](https://code.visualstudio.com/api/get-started/your-first-extension) 10 | 11 | 官方已经把插件模板集成到yeoman(类似cli工具)中,十分方便开发者开发新插件。安装工具和模版: 12 | ``` 13 | npm install -g yo generator-code 14 | yo code // 生成模版 15 | ``` 16 | 17 | 1. 生成模板后,最重要的是了解package.json,因为大部分设置都是在这设置。 18 | ``` js 19 | { 20 | "name": "xxx", // 插件名称 21 | "displayName": "xxx", // 插件显示名称 22 | "description": "Air Quaility", // 插件描述 23 | "version": "0.0.1", // 插件版本 24 | "publisher": "beanleecode", // 插件发布者 25 | "icon": "logo.png", // 插件的icon,不支持svg,最小图片大小128*128 26 | ... 27 | "activationEvents": [ // 活动事件列表(哪些命令是激活的) 28 | "onCommand:extension.sayHello" 29 | ], 30 | "main": "./extension", // 代码书写主入口 31 | "contributes": { 32 | "commands": [ // 对应命令列表 33 | { 34 | "command": "extension.sayHello", // 命令名,必须与上面activationEvents一致 35 | "title": "Hello World" // 命令显示解释 36 | } 37 | ] 38 | } 39 | } 40 | ``` 41 | 42 | 2. 调试F5 43 | 44 | 就是这么简单。VSCode 会启动一个新窗口供测试,十分方便。 45 | 46 | ## 发布插件 47 | 48 | [官方发布插件教程](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) 49 | 50 | 发布过程都依赖`vsce`工具,这是一个npm包,安装十分便捷: 51 | ``` 52 | npm i vsce -g 53 | ``` 54 | 55 | ### 发布方式 56 | 1. 直接把文件夹发给别人,让别人找到vscode的插件存放目录并放进去,然后重启vscode,一般不推荐; 57 | 1. 打包成vsix插件,然后发送给别人安装,如果你的插件涉及机密不方便发布到应用市场,可以尝试采用这种方式; 58 | 1. 注册开发者账号,发布到官网应用市场,这个发布和npm一样是不需要审核的。 59 | 60 | ## 本地打包方式 61 | 打包成vsix文件: 62 | ``` 63 | vsce package 64 | ``` 65 | 66 | > 当遇到全局安装了vsce,但vsce命令找不到,请检查是否使用了nvm等node管理工具,并切换到指定的node版本。 67 | 68 | ## 在线发布方式 69 | 70 | 1. 在[Visual Studio Team Services](https://account.microsoft.com/?lang=zh-CN&refd=account.live.com&refp=landing&mkt=ZH-CN) 创建一个账号 71 | 1. 创建token 72 | * 点击右上角的个人信息security,再点击create token 73 | * name随便填写,expires In最好选最长时间,`Accounts一定要选All accessible accounts`,Authorized Scopes选择 All Scopes。 74 | * create后会给你一个token值,它只显示一次,务必要保留,之后命令行操作会使用到。 75 | 1. 借助vsce工具上传到商店。 76 | 1. `vsce create-publisher (name)` 创建publisher,这个步骤会让你填写name、email以及上步获得的token 77 | 1. `vsce publish (version)` 发布到在线商店(依赖于上一步,已创建publisher并登录) 78 | 79 | #### 注意事项 80 | * 类似npm包,README.md文件默认会显示在插件主页; 81 | * 增量发布可以修改package.json的version外,还可以使用代号:major/minor/patch,如:vsce publish patch -------------------------------------------------------------------------------- /docs/interview/interview-webpack.md: -------------------------------------------------------------------------------- 1 | # Webpack 2 | 3 | [你必须知道的Webpack](./webpack-you-need-known.md) 4 | 5 | https://cloud.tencent.com/developer/article/1145103 6 | 7 | https://segmentfault.com/a/1190000008060440 8 | 9 | ### 流程概括 10 | 11 | Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程: 12 | 13 | 1. 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数; 14 | 2. 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译; 15 | 3. 确定入口:根据配置中的 entry 找出所有的入口文件; 16 | 1. 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理; 17 | 1. 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系; 18 | 1. 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会; 19 | 1. 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。 20 | 21 | 简版流程: 22 | Webpack 的构建流程可以分为以下三大阶段: 23 | 24 | 1.初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler。 25 | 26 | 2.编译:从 Entry 发出,针对每个 Module 串行调用对应的 Loader 去翻译文件内容,再找到该 Module 依赖的 Module,递归地进行编译处理。 27 | 28 | 3.输出:对编译后的 Module 组合成 Chunk,把 Chunk 转换成文件,输出到文件系统。 29 | 30 | 在每个大阶段中又会发生很多事件,Webpack 会把这些事件广播出来供给 Plugin 使用。 31 | 32 | ## Plugin 33 | 34 | ``` js 35 | class BasicPlugin{ 36 | // 在构造函数中获取用户给该插件传入的配置 37 | constructor(options){ 38 | } 39 | 40 | // Webpack 会调用 BasicPlugin 实例的 apply 方法给插件实例传入 compiler 对象 41 | apply(compiler){ 42 | compiler.plugin('compilation',function(compilation) { 43 | }) 44 | } 45 | } 46 | 47 | // 导出 Plugin 48 | module.exports = BasicPlugin; 49 | ``` 50 | 51 | ``` js 52 | // webpack配置plugin 53 | const BasicPlugin = require('./BasicPlugin.js'); 54 | module.export = { 55 | plugins:[ 56 | new BasicPlugin(options), 57 | ] 58 | } 59 | ``` 60 | 1. Webpack 启动后,在读取配置的过程中会先执行 new BasicPlugin(options) 初始化一个 BasicPlugin 获得其实例。 61 | 1. 在初始化 compiler 对象后,再调用 basicPlugin.apply(compiler) 给插件实例传入 compiler 对象。 62 | 1. 插件实例在获取到 compiler 对象后,就可以通过 compiler.plugin(事件名称, 回调函数) 监听到 Webpack 广播出来的事件。 并且可以通过 compiler 对象去操作 Webpack。 63 | 64 | ### Compiler 和 Compilation 65 | 在开发 Plugin 时最常用的两个对象就是 Compiler 和 Compilation,它们是 Plugin 和 Webpack 之间的桥梁。 Compiler 和 Compilation 的含义如下: 66 | 67 | * Compiler 对象包含了 Webpack 环境所有的的配置信息,包含 options,loaders,plugins 这些信息,这个对象在 Webpack 启动时候被实例化,它是全局唯一的,可以简单地把它理解为 Webpack 实例; 68 | * Compilation 对象包含了当前的模块资源、编译生成资源、变化的文件等。当 Webpack 以开发模式运行时,每当检测到一个文件变化,一次新的 Compilation 将被创建。Compilation 对象也提供了很多事件回调供插件做扩展。通过 Compilation 也能读取到 Compiler 对象。 69 | * Compiler 和 Compilation 的区别在于:Compiler 代表了整个 Webpack 从启动到关闭的生命周期,而 Compilation 只是代表了一次新的编译。 70 | 71 | 72 | 73 | > webpack插件:DllPlugin和DllReferencePlugin、MiniCssExtractPlugin、HtmlWebpackPlugin、webpack.DefinePlugin 74 | -------------------------------------------------------------------------------- /docs/js/es6-4.module.md: -------------------------------------------------------------------------------- 1 | # ES6-模块详解 2 | 3 | ## 编译时静态检查 4 | 5 | 在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。 6 | 7 | ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。 8 | 9 | ``` js 10 | // 错误:由于不是编译时检查,所以这种方式会报错,而commonjs就不会 11 | // 所以使用import和export都会放在最外层,而允许放在arr.forEach(key => import * from key)中 12 | let modulePath = './test' 13 | import test from modulePath 14 | 15 | // 正确 16 | import test from './test' 17 | ``` 18 | 19 | ### es6 module和commonjs不同 20 | 1. es6 module编译时输出接口,commonjs运行时加载。 21 | 2. es6 module输出引用,commonjs输出拷贝。 22 | 3. 循环引用处理不同。es6 module生成指向,内容自己保证;commonjs只输出已执行部分(说到底还是输出拷贝)。 23 | 24 | > 由于动态加载非常实用,故tc39引入了[import()](https://github.com/tc39/proposal-dynamic-import)提案,帮动态加载模块 25 | 26 | ## export 几种用法 27 | `export`命令用于规定模块的对外接口 28 | 29 | ``` js 30 | // 使用export {}不能使用import {name1, name2, …} from '...'方式导入 31 | export { name1, name2, …, nameN }; 32 | export { variable1 as name1, variable2 as name2, …, nameN }; 33 | 34 | // export type方式 35 | // 使用export type才是import {name1, name2, …} from '...'的正确使用方式 36 | export let name1, name2, …, nameN; 37 | export let name1 = …, name2 = …, …, nameN; 38 | export function FunctionName() {...} 39 | export class ClassName {...} 40 | 41 | // export default 方式。 default、 as 关键字 42 | export default expression; 43 | export default function (…) { … } 44 | export default function name1(…) { … } 45 | export { name1 as default, … }; // 等价于export default name1, ... 46 | 47 | // 模块继承,导入和导出在一行。 48 | // 这种方式非常适合重构时,帮助把大文件拆分成多个小文件 49 | export * from …; 50 | export { name1, name2, …, nameN } from …; 51 | export { import1 as name1, import2 as name2, …, nameN } from …; 52 | ``` 53 | 54 | ## import 几种用法 55 | 使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过`import`命令加载这个模块。 56 | 57 | ``` js 58 | // export default 方式 59 | import defaultName from 'modules.js'; 60 | 61 | // export type 方式 62 | import { export1, export2 } from 'modules'; 63 | import { export1 as ex1, export2 as ex2 } from 'moduls.js'; // as 关键字 64 | import * as moduleName from 'modules.js'; 65 | 66 | // 同时引入export default 和export type 67 | import defaultName, { expoprt1, export2 } from 'modules'; 68 | import defaultName, * as moduleName from 'modules'; 69 | 70 | // 引入无输出模块 71 | import 'modules'; 72 | ``` 73 | 74 | ## 参考文章 75 | 76 | * [阮一峰-ESMAScript6入门](http://es6.ruanyifeng.com/#docs/module) 77 | * [ES6 export * from import](https://stackoverflow.com/questions/38077164/es6-export-from-import) 78 | * [javascript中import和export用法总结](https://segmentfault.com/a/1190000016417637) -------------------------------------------------------------------------------- /docs/read-books/book-http-diagram.md: -------------------------------------------------------------------------------- 1 | # 《HTTP图解》 2 | 3 | ## Web及网络基础 4 | 5 | ### TCP/IP的分层管理 6 | 1. 应用层 - HTTP、FTP、DNS等 7 | * HTTP。 8 | * DNS协议。通过域名查找IP地址,或逆向从IP地址反查域名的服务。 9 | 2. 传输层。提供两台计算机之间的数据传输。 10 | * TCP协议。把HTTP请求报文进行**分割**(为了更容易传送大数据),并在报文上打上标记序号及端口号,发送给网络层。提供三次握手(TCP过程标志:SYN和ACK),确认数据最终送达到对方。 11 | 1. 网络层。处理网络上流动的数据包,规定了通过怎样的路径到达对方计算机,并把数据包传送给对方。 12 | * IP协议。增加作为通信目的地的MAC地址后,转发给链路层。 13 | * 使用ARP协议,反查出对应的MAC地址。(IP地址可变换,MAC地址基本不会变) 14 | 1. 链路层。处理连接网络等硬件部分,包括控制操作系统、硬件的设备驱动、网卡等物理可见部分。 15 | 16 | ### 各种协议与HTTP协议的关系: 17 | ![image](https://user-images.githubusercontent.com/6310131/60406595-3d524d80-9be9-11e9-904a-22495ac30f25.png) 18 | 19 | ### TCP协议三次握手: 20 | ![image](https://user-images.githubusercontent.com/6310131/60406519-d3d23f00-9be8-11e9-8390-38c80bb260c6.png) 21 | 22 | ## Http协议 23 | 24 | 请求:请求行 + 首部字段 +(空行)正文 25 | 26 | 响应: 状态行 + 首部字段 +(空行)正文 27 | 28 | ![image](https://user-images.githubusercontent.com/6310131/61171226-30e5d180-a5a7-11e9-84b4-b874617105a0.png) 29 | 30 | ### 响应状态 31 | * 2xx 成功 32 | * 200 ok 33 | * 204 没有内容 34 | * 206 部分内容 35 | * 3xx 重定向 36 | * 304 没有更新 37 | * 4xx 客户端错误 38 | * 403 禁止 39 | * 404 没有找到 40 | * 5xx 服务器错误 41 | * 500 服务器错误 42 | * 503 服务不可用,表明服务器正在停机维护 43 | 44 | ## Cookie 45 | 46 | Cookie会根据从服务器发送的响应报文内一个叫`Set-Cookie`的首部字段信息,通知客户端保存Cookie。当下次客户端再往该服务器发送请求时,客户端会自动在请求报文中加入Cookie的值发送出去。 47 | 48 | 服务器发现客户端发送过来的Cookie后,会去**检查究竟是从哪个客户端发来的连接请求**,然后对比服务器上的记录,最后得到之前的状态信息。 49 | 50 | Cookie风险: 51 | * 仅利用cookie中进行身份验证。比如在cookie中存isVip/userID等关键信息字段 52 | * 不设置httponly。如果攻击者拿到用户的cookie值(XSS攻击等手段),那可以通过js修改cookie,伪造用户。 53 | * secure问题。当secure的限制没有开启时,那么在一个https的网站中,一个xss还是能通过http读取到https下的cookie。 54 | 55 | Cookie防护: 56 | * 防止在 Cookies 中存放敏感信息 57 | * 关键数据放服务端(session),cookie保存口令值。 58 | * 对重要的值加上httponly标志 59 | * 强制要求开启HTTPS连接 60 | 61 | Session安全(防护升级): 62 | * `口令值通过私钥签名加密`。虽然Session不存敏感信息,找不到xss攻击拿到cookie的情况下,也有可能被伪造出。因为随机算法给出的口令值仍然可能被伪造(特别是用户量大的网站,难免被碰运气)。通过私钥签名可以解决这种情况。 63 | * `客户端独有信息(IP等) + 口令私钥签名`。有可能通过xss攻击,获得用户cookie中保存的口令值,以此伪造用户。通过客户端独有信息以及配合上面的口令私钥签名可以解决这种情况。 64 | 65 | ## Web安全 66 | 67 | 常用安全漏洞 68 | 1. 输出值转义不完全 69 | * `跨站脚本攻击XSS`(比如:url地址栏、动态生成HTML) 70 | * sql注入攻击(属于主动攻击) 71 | * os命令攻击(比如:发送邮件) 72 | * Http首部注入攻击,通过在响应字段内**插入换行**,添加任意响应首部的攻击 73 | 1. 设计上的缺陷 74 | * 会话劫持:拿到用户的绘画id(通常是XSS攻击盗取),并伪装成用户 75 | * `跨站请求伪造CSRF`。对已完成认证的用户进行非预期的信息更新(属于被动攻击)。加入留言板留言是get请求,攻击者可以发布恶意评论\,那所有访问这条评论的用户,都会发布自己都不知道的“123”评论 76 | 1. 其他 77 | * 点击劫持。常说的钓鱼网站,攻击者制作了一张游戏网页,引诱你去点击它的Play按钮,Play按钮下,隐藏的(通过iframe设置透明度即可)是你已经登录的网站(比如qq)。你点击Play有可能点击的是qq的注销功能。 78 | * Dos攻击。让运行的服务停止服务。比如集中请求资源,让服务器资源耗尽。 -------------------------------------------------------------------------------- /docs/vue/vue-code-0.frame.md: -------------------------------------------------------------------------------- 1 | # Vue2.x源码分析 - 框架结构 2 | 3 | > 看过Vue源码有一段时间了,对里面的研究也有一些心得。对于Vue源码分析文章,笔者以前不是很想写,因为网上有太多优质的Vue源码分析文章,笔者读源码时也受益于此。但思维的转变,发生在一次团队Code Review。笔者所在的团队主要使用Vue技术栈(团队小伙伴都是多面手,一专多能),但有些小伙伴受限于业务开发,Vue停留在API使用层。所以笔者组织大家对Vue进行源码分析,各自分工并且每周Code Review分享给所有人,借此提升团队的工作效率,也形成一个良好的团队氛围。故以文字记录下来,一方面沉淀自己,另一方面也为团队code review做备忘录。 4 | 5 | 笔者认为,读源码需要从全局到局部,先了解整个源码的目录结构,懂得项目里有哪些内容,然后列举出来,再通过个人兴趣研究点,逐个攻破。比如通读Vue目录结构,里面有core/compile/weex/ssr等,再提取出研究的web核心:双向数据绑定/组件系统/事件/生命周期/ast编译/vnode等。读源码切忌一行一行分析,这样容易陷入局部思考,也容易有挫折感。源码也不是只读一遍就能理解,所以需要反复回溯,细细思考。 6 | 7 | 源码分析顺序: 8 | 9 | * 目录结构 10 | * 核心内容 11 | * 重点突破 12 | * 回归溯源 13 | 14 | ## 目录结构 15 | 16 | 思维导图可以清晰的了解到全局内容。看目录结构,除去build打包,类型定义以及ssr/weex,能了解到我们研究的核心就`compiler、core、platforms.web`三个文件夹。 17 | 18 | ![image](https://user-images.githubusercontent.com/6310131/45196761-6daeb100-b290-11e8-89fd-31e965e1fcee.png) 19 | 20 | ## 核心内容 21 | 22 | 主要是Vue的几个核心:数据驱动、响应式原理、组件化、vdom diff算法等 23 | 24 | ![image](https://user-images.githubusercontent.com/6310131/45197180-85873480-b292-11e8-909d-37cc2dbdc5b2.png) 25 | 26 | ![image](https://user-images.githubusercontent.com/6310131/77979977-b3324380-7338-11ea-8145-bda4e4e25612.png) 27 | 28 | ![image](https://user-images.githubusercontent.com/6310131/77980169-36539980-7339-11ea-9219-857173dfe334.png) 29 | 30 | ![image](https://user-images.githubusercontent.com/6310131/77980253-6dc24600-7339-11ea-9ae8-c4c505936f3b.png) 31 | 32 | ![image](https://user-images.githubusercontent.com/6310131/77980772-b62e3380-733a-11ea-824c-ce45ce0df9c1.png) 33 | 34 | ## 整体流程 35 | 36 | 下图整理了vue初始化流程以及代码流转,建议配合[Vue.js技术揭秘](https://ustbhuangyi.github.io/vue-analysis/prepare/entrance.html#vue-%E7%9A%84%E5%85%A5%E5%8F%A3)文章去了解。 37 | 38 | ![image](https://user-images.githubusercontent.com/6310131/45197312-1231f280-b293-11e8-83e0-93c4844924c9.png) 39 | 40 | ## Debug Vue源码 41 | 42 | 单纯看代码,特别是一些复杂逻辑时,容易绕进去,使得源码阅读效率降低。而debug源码能很好的知道关键地方的输入和输出,加快理解源码的速度。这里笔者也建议clone Vue源码后,设置以下debug设置,从而更好的调试源码。 43 | 44 | ### Chrome调试 45 | 46 | 1. 在配置文件scripts/config.js中,增加`sourceMap: true`。这样最终生成的dist/vue.js打包文件会带上源码的路径。 47 | 48 | 2. 修改源码examples文件夹中案例,把引用的Vue包路径`改为“dist/vue.js”` 49 | 50 | ### VSCode调试 51 | 52 | 在VSCode编辑器内,调试源码更方便。VSCode基本设置以及参数说明在笔者另外一篇博文[Debug fro VSCode](../node/node-vscode-debug.md)有详细说明。如下给出最终vscode调试设置: 53 | 54 | ``` js 55 | { 56 | "version": "0.2.0", 57 | "configurations": [ 58 | { 59 | "type": "chrome", 60 | "request": "launch", 61 | "name": "Launch Chrome", 62 | "url": "file:///project-path/vue/examples/commits/index.html", // 这里的project-path是你电脑上的根路径 63 | "webRoot": "${workspaceFolder}/examples/commits/index.html" 64 | } 65 | ] 66 | } 67 | 68 | ``` -------------------------------------------------------------------------------- /docs/project/minipack-code-analysis.md: -------------------------------------------------------------------------------- 1 | # minipack源码解析 2 | 3 | 注释可以查看笔者fork自官方minipack项目:https://github.com/lq782655835/minipack 4 | 5 | > 结合笔者另外一篇文章[Webpack 模块打包原理](https://lq782655835.github.io/blogs/project/webpack4-1.module.html),会有更多编译底层知识。 6 | 7 | ## 整体流程 8 | 9 | 先递归找到依赖项 & 编译每个文件 -> 代码拼接方式生成最终代码。 10 | 11 | 流程细节:入口entry文件 -> 生成ATS -> ATS中找到依赖项 & babel编译代码(createAsset) -> 依赖项递归生成队列queue(createGraph) -> 代码拼接(build,基于CommonJS规范) 12 | 13 | ``` js 14 | const graph = createGraph('./example/entry.js'); // ATS分析图 15 | const result = bundle(graph); // 技巧拼接字符串 16 | ``` 17 | 18 | 核心代码分析如下: 19 | 20 | ## 1. babel编译代码 21 | 22 | babel编译代码(babylon.parse -> AST -> transformFromAst -> code 23 | 24 | ``` js 25 | const babylon = require('babylon'); 26 | const {transformFromAst} = require('babel-core'); 27 | 28 | // 生成ATS 29 | const ast = babylon.parse(content, { 30 | sourceType: 'module', 31 | }); 32 | 33 | // 最终code代码 34 | const {code} = transformFromAst(ast, null, { // springleo: babel编译代码(babylon.parse -> AST -> transformFromAst -> code) 35 | presets: ['env'], 36 | }); 37 | ``` 38 | 39 | ## 2. ATS可以获取依赖项 40 | 41 | ``` js 42 | const traverse = require('babel-traverse').default; 43 | 44 | const dependencies = []; 45 | 46 | traverse(ast, { 47 | // Every time we see an import statement we can just count its value as a 48 | // dependency. 49 | ImportDeclaration: ({node}) => { 50 | dependencies.push(node.source.value); // 通过ATS找到依赖项目 51 | }, 52 | }); 53 | ``` 54 | 55 | ## 拼接代码(核心) 56 | 57 | 技巧性利用CommonJS规范 58 | 59 | ``` js 60 | function bundle(graph) { 61 | let modules = ''; 62 | 63 | // 代码拼接 64 | graph.forEach(mod => { 65 | modules += `${mod.id}: [ 66 | function (require, module, exports) { 67 | ${mod.code} 68 | }, 69 | ${JSON.stringify(mod.mapping)}, 70 | ],`; 71 | }); 72 | 73 | // 入口0开始寻找require(1) 74 | const result = ` 75 | (function(modules) { 76 | function require(id) { 77 | const [fn, mapping] = modules[id]; 78 | 79 | // 自己实现的require本地包 80 | function localRequire(name) { 81 | return require(mapping[name]); // springleo: 从依赖包的名字,找到包编译的序列号ID 82 | } 83 | 84 | const module = { exports : {} }; 85 | 86 | fn(localRequire, module, module.exports); 87 | 88 | return module.exports; 89 | } 90 | 91 | require(0); 92 | })({${modules}}) 93 | `; 94 | 95 | return result; 96 | } 97 | ``` 98 | 99 | graph是解析的图数据结构: 100 | 101 | ![image](https://user-images.githubusercontent.com/6310131/132790449-d87054f7-3eed-4005-8969-3c8c4ee2bfcb.png) 102 | -------------------------------------------------------------------------------- /docs/node/node-vscode-debug.md: -------------------------------------------------------------------------------- 1 | # Node Debug for VSCode 2 | 3 | 调试对于任何一门语言都是及其重要的。好的调试工具能让人更有效率的开发以及查错。Node没有chrome developer tool这样的Web可视化集成调试工具,但VSCode默认集成了TS、Git、Debug等实用工具,而且使用非常方便。VSCode的插件生态,也让VSCode变成前端开发必备的利器。以下介绍VSCode下的Node调试。 4 | 5 | ## 基本用法 6 | 7 | 1. 进入VScode界面,点击界面左边的第四个类似虫子的按钮,进入调试界面: 8 | ![](https://segmentfault.com/img/bVMhsN?w=640&h=342) 9 | 2. 点击页面上方“没有配置”下拉菜单,选择“添加配置”。 10 | ![](https://segmentfault.com/img/bVMhsO?w=640&h=571) 11 | 3. 选择Node.js环境。 12 | ![](https://segmentfault.com/img/bVMhsP?w=640&h=107) 13 | 4. 选择完成之后,在项目的根目录中会生成一个.vscode的目录,这个目录中存放了各种各样的VScode编辑器的配置。VSCode根据你选择的环境,生成了对应的`配置文件lanuch.json`。Node内容如下: 14 | ``` json 15 | { 16 | "version": "0.2.0", 17 | "configurations": [ 18 | { 19 | "type": "node", 20 | "request": "launch", 21 | "name": "Launch Program", 22 | "program": "${workspaceFolder}/node/http.js" // 调试路径入口,需要根据自己项目进行配置 23 | } 24 | ] 25 | } 26 | ``` 27 | 5. 设置断点,点击开始调试按钮(绿色三角形),就可以开始调试。 28 | 29 | ## 调试参数配置 30 | lanuch.json配置项较多,可查看[官方文档](https://code.visualstudio.com/docs/nodejs/nodejs-debugging)详细了解。VSCode也集成了一些常用的调试配置片段,有Node、Chrome、Electron、Gulp等。以下说明几个重要参数: 31 | * `name`: 给该配置项取个名字 32 | * `type`: 通常有node、chrome等参数 33 | * `request`: launch/attach 34 | * launch模式,**由 vscode 来启动**一个独立的具有 debug 模式的程序 35 | * attach模式,是连接**已经启动的服务**。比如已经在外面将项目启动,突然需要调试,不需要关掉已经启动的项目再去vscode中重新启动,只要以attach的模式启动,vscode可以连接到已经启动的服务。 36 | * `program`: debug node入口文件的绝对路径。只在launch模式有效 37 | * `runtimeExecutable`: 执行器的绝对路径,默认是node。只在launch模式有效 38 | * `runtimeArgs`: 执行器参数。只在launch模式有效 39 | 40 | ## debug使用npm启动 41 | 42 | 以上Node调试方式有个问题,每次文件入口修改都需要改动lanuch.json配置文件。我们的方法是可以使用让npm script充当入口,让改动变成在package.json中。 43 | 44 | 以上需要改造两步: 45 | 1. 修改lanuch.json配置成npm命令方式: 46 | ``` json 47 | { 48 | "version": "0.2.0", 49 | "configurations": [ 50 | { 51 | "type": "node", 52 | "request": "launch", 53 | "name": "Launch via NPM", 54 | "runtimeExecutable": "npm", // npm 执行器。使用npm script方式作为入口 55 | "runtimeArgs": [ 56 | "run-script", 57 | "start:debug" 58 | ], 59 | "port": 5858 // 调试的端口指定,attach时用到 60 | } 61 | ] 62 | } 63 | ``` 64 | 2. 修改package.json的scripts配置 65 | ``` json 66 | { 67 | // 注意:需要配置上--inspect-brk=5858以attach到debugger 68 | "start:debug": "nodemon --inspect-brk=5858 node/http.js" 69 | } 70 | ``` 71 | 72 | ## 参考文章 73 | 74 | * [nodejs-debugging](https://code.visualstudio.com/docs/nodejs/nodejs-debugging) 75 | 76 | * [使用Visual Studio Code对Node.js进行断点调试](https://segmentfault.com/a/1190000009084576) 77 | 78 | * [What is the proper way to debug an npm script using vscode? 79 | ](https://stackoverflow.com/questions/43210203/what-is-the-proper-way-to-debug-an-npm-script-using-vscode) -------------------------------------------------------------------------------- /docs/temp/vue-vuex.md: -------------------------------------------------------------------------------- 1 | # Vuex 2 | 3 | ## 状态管理模式 4 | 状态自管理应用包含以下几个部分: 5 | 6 | * `state`,驱动应用的数据源; 7 | * `view`,以声明方式将 state 映射到视图; 8 | * `actions`,响应在 view 上的用户输入导致的状态变化。 9 | 10 | 以下是一个表示“单向数据流”理念的极简示意: 11 | 12 | ![](https://ustbhuangyi.github.io/vue-analysis/assets/vuex.png) 13 | 14 | ## 为什么需要状态库 15 | 16 | 多个组件共享状态时,单向数据流的简洁性很容易被破坏: 17 | * 多个视图依赖于同一状态。 18 | * 来自不同视图的行为需要变更同一状态。 19 | 20 | 对于问题一,传参的方法对于`多层嵌套的组件`将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。对于问题二,我们经常会采用父子组件直接引用或者通过事件来`变更和同步状态的多份拷贝`。 21 | 22 | **因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为。** 23 | 24 | 所以需要一个`状态库`:执行`单项数据流`同时,还能做到`应用简单`。 25 | 26 | ## Vuex 27 | Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。 28 | 29 | Vuex 和单纯的全局对象有以下两点不同: 30 | 31 | * `Vuex 的状态存储是响应式的`。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。 32 | * `不能直接改变 store 中的状态`。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。 33 | 34 | ![](https://ustbhuangyi.github.io/vue-analysis/assets/vuex1.png) 35 | 36 | ### API 37 | 38 | ``` js 39 | // app.js 40 | import store from './store' 41 | import App from './components/App.vue' 42 | new Vue({ 43 | store, // 设置store对象,使得所有组件都有该store 44 | el: '#app', 45 | render: h => h(App) 46 | }) 47 | ``` 48 | 49 | ``` js 50 | // store.js 51 | export default new Vuex.Store({ 52 | state: { 53 | todos: [] 54 | }, 55 | actions, 56 | mutations, 57 | plugins 58 | }) 59 | ``` 60 | 61 | ### 源码 62 | 63 | ``` js 64 | export default function (Vue) { 65 | Vue.mixin({ beforeCreate: vuexInit }) 66 | 67 | function vuexInit () { 68 | const options = this.$options 69 | if (options.store) { 70 | // 插入store 71 | // 组件中可以通过 this.$store 访问到这个实例。 72 | this.$store = typeof options.store === 'function' 73 | ? options.store() 74 | : options.store 75 | } else if (options.parent && options.parent.$store) { 76 | // 所有子组件都有该store 77 | this.$store = options.parent.$store 78 | } 79 | } 80 | } 81 | ``` 82 | 83 | Vuex的state数据,响应式根源,最终都是使用new Vue({data})进行响应式监听。当有state改动时,最终会触发Vue Watcher,从而更新DOM。 84 | ``` js 85 | // 响应式基础 86 | store._vm = new Vue({ 87 | data: { 88 | $$state: state // Vuex响应式来源于Vue响应式机制 89 | }, 90 | computed 91 | }) 92 | 93 | get state () { 94 | return this._vm._data.$$state 95 | } 96 | ``` 97 | 98 | ``` js 99 | // Vuex不能直接修改 $store 100 | function enableStrictMode (store) { 101 | store._vm.$watch(function () { return this._data.$$state }, () => { 102 | if (process.env.NODE_ENV !== 'production') { 103 | assert(store._committing, `do not mutate vuex store state outside mutation handlers.`) 104 | } 105 | }, { deep: true, sync: true }) 106 | } 107 | ``` -------------------------------------------------------------------------------- /docs/project/deep-learn-term.md: -------------------------------------------------------------------------------- 1 | # 深度学习平台术语 2 | 3 | 以下是开源深度学习平台kubeflow需要了解的相关术语。掌握它们,会更加理解搭建一个深度学习平台所需要的概念或框架。 4 | 5 | ## 1. RPC 6 | 7 | 提供远程调用对方的函数的框架。 8 | 9 | 远程过程调用带来的新问题: 10 | 1. Call ID映射。 11 | 1. 序列化和反序列化。 12 | 1. 网络传输 13 | 14 | https://www.zhihu.com/question/25536695 15 | 16 | ## 2. gRPC 17 | 18 | google出的RPC框架,基于http2。可对外提供grpc服务。 19 | 20 | ## 3. 微服务 21 | 22 | `微服务`有两个核心: 23 | * 微:服务的粒度要细,即服务要细化到API 24 | * 服务:提供好服务,要让用户感到好用(要做到这一点很不容易) 25 | 26 | ![](https://img-blog.csdn.net/20170103145438903?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VpZmVuZzMwNTE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 27 | 28 | 微服务特别简单(好的架构就应该简单),我们把服务再拆分成一个个API,API是一个完整的功能。然后我们把API扔到一个“云上”,然后用户就可以到“云上”获取所有API的服务,这个“云”保证能提供好的服务。 29 | 30 | `微服务的关键是服务网关`,所以,上面提到的“云”就是服务网关。服务网关之下,就是内部系统之间的服务治理,这就是另外一个话题了。 31 | 32 | 实现方案: 33 | 34 | ![](https://img-blog.csdn.net/20170103151255786?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VpZmVuZzMwNTE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 35 | 36 | https://blog.csdn.net/suifeng3051/article/details/53992560 37 | 38 | > Docker 和Kubernetes 技术的流行, 为Pass资源的分配管理和服务的部署提供了新的解决方案, 但是微服务领域的其他服务治理问题仍然存在. 39 | 40 | ## 4. 服务治理 41 | 42 | 服务化(常利用RPC框架实现)。但服务化还有挑战: 43 | * 服务越来越多,配置管理复杂 44 | * 服务间依赖关系复杂 45 | * 服务之间的负载均衡 46 | * 服务的拓展 47 | * 服务监控 48 | * 服务降级 49 | * 服务鉴权 50 | * 服务上线与下线 51 | 52 | 所以服务治理是关键(dubbo就是一个带有服务治理功能的RPC框架)。服务治理理应具有: 53 | * 服务注册, 服务发现 54 | * 服务伸缩 55 | * 健康检查 56 | * 快速部署 57 | * 服务容错: 断路器, 限流, 隔离舱, 熔断保护, 服务降级等等 58 | * 认证和授权 59 | * 灰度发布方案 60 | * 服务调用可观测性, 指标收集 61 | * 配置管理 62 | 63 | > 简单理解,大量的内部服务之间需要一个机制,去互相发现、流量管控等业务之外的问题,这个机制就是服务治理。Istio就是服务治理的框架实践(Service Mesh概念属于服务治理概念内,都在Istio中有实现)。 64 | 65 | ## 5. Istio 66 | 67 | 简单理解,k8s负责把服务部署起来,Istio负责服务之间的管理(包括访问、限流、安全等) 68 | 69 | ![](https://user-gold-cdn.xitu.io/2019/10/11/16db9347c2109b45?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 70 | 71 | ## 5. GPU 72 | 73 | GPU是显卡的处理器,称为图形处理器(Graphics Processing Unit,即GPU),又称显示核心、视觉处理器、显示芯片,是一种专门在个人电脑、工作站、游戏机和一些移动设备(如平板电脑、智能手机等)上图像运算工作的微处理器,它是显卡的“心脏”,与CPU类似,只不过**GPU是专为执行复杂的数学和几何计算而设计的**,这些计算是图形渲染所必需的。 74 | 75 | CPU是“主(host)”而GPU是“从(device)”,GPU无论发展得多快,都只能是替CPU分担工作,而不是取代CPU。GPU是显卡上的一块芯片,就像CPU是主板上的一块芯片。 76 | 77 | ### GUP与CPU 78 | 79 | CPU和GPU之所以大不相同,是由于其设计目标的不同,它们分别针对了两种不同的应用场景。CPU负责逻辑性强的事物处理和串行计算,GPU则专注于执行高度线程化的并行处理任务(大规模计算任务)。 80 | 81 | CPU需要很强的通用性来处理各种不同的数据类型,同时逻辑判断又会引入大量的分支跳转和中断的处理,这些都使得CPU的内部结构异常复杂。 82 | 83 | GPU面对的则是类型高度统一的、相互无依赖的大规模数据和不需要被打断的纯净的计算环境。 84 | 85 | CPU擅长逻辑控制,串行的运算。和通用类型数据运算不同,GPU擅长的是大规模并发计算,这也正是密码破解等所需要的。所以GPU除了图像处理,也越来越多的参与到计算当中来。 86 | 87 | 总而言之,CPU和GPU因为最初用来处理的任务就不同,所以设计上有不小的区别。而某些任务和GPU最初用来解决的问题比较相似,所以用GPU来算了。GPU的工作大部分就是这样,计算量大,但没什么技术含量,而且要重复很多很多次。GPU的运算速度取决于雇了多少小学生,CPU的运算速度取决于请了多么厉害的教授。教授处理复杂任务的能力是碾压小学生的,但是对于没那么复杂,但是量特别大的任务,还是顶不住人多。当然现在的GPU也能做一些稍微复杂的工作了,相当于升级成初中生高中生的水平。但还需要CPU来把数据喂到嘴边才能开始干活,究竟还是靠CPU来管的。 88 | -------------------------------------------------------------------------------- /docs/temp/flutter-start.md: -------------------------------------------------------------------------------- 1 | # Flutter 2 | 3 | ## 特点 4 | 5 | ### 优点 6 | 7 | * 跨平台(接近于原生的体验,更少的代码量,更低的成本) 8 | * 上手快、技术门槛低(dart类似js) 9 | * 中小App,对原生操作(比如摄像机应用、动画应用、视频应用、声音应用等等)使用少的,可以采用这种方式(适合轻量级) 10 | 11 | > Flutter 与 RN、Weex、小程序最大的不同就是 Flutter 是一个跨平台解决方案,而非一个动态化解决方案。 12 | 13 | 14 | ## Hello World 15 | 16 | ``` js 17 | import 'package:flutter/material.dart'; 18 | 19 | void main() => runApp(new MyApp()); 20 | 21 | // 该应用程序继承了 StatelessWidget,这将会使应用本身也成为一个widget。 在Flutter中,大多数东西都是widget,包括对齐(alignment)、填充(padding)和布局(layout) 22 | class MyApp extends StatelessWidget { 23 | @override 24 | Widget build(BuildContext context) { 25 | // Material是一种标准的移动端和web端的视觉设计语言 26 | return new MaterialApp( 27 | title: 'Welcome to Flutter', 28 | // Scaffold 是 Material library 中提供的一个widget, 它提供了默认的导航栏、标题和包含主屏幕widget树的body属性。widget树可以很复杂。 29 | home: new Scaffold( 30 | appBar: new AppBar( 31 | title: new Text('Welcome to Flutter'), 32 | ), 33 | body: new Center( 34 | child: new Text('Hello World'), 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | ``` 41 | 42 | * 本示例创建一个Material APP。Material是一种标准的移动端和web端的视觉设计语言。 Flutter提供了一套丰富的Material widgets。 43 | 44 | * main函数使用了(=>)符号, 这是Dart中单行函数或方法的简写。 45 | 46 | * 该应用程序继承了 StatelessWidget,这将会使应用本身也成为一个widget。 在Flutter中,大多数东西都是widget,包括对齐(alignment)、填充(padding)和布局(layout) 47 | 48 | * Scaffold 是 Material library 中提供的一个widget, 它提供了默认的导航栏、标题和包含主屏幕widget树的body属性。widget树可以很复杂。 49 | 50 | * widget的主要工作是提供一个build()方法来描述如何根据其他较低级别的widget来显示自己。 51 | 52 | * 本示例中的body的widget树中包含了一个Center widget, Center widget又包含一个 Text 子widget。 Center widget可以将其子widget树对其到屏幕中心。 53 | 54 | ## Stateless & Stateful widgets 55 | 56 | * Stateless widgets 是不可变的, 这意味着它们的属性不能改变 - 所有的值都是最终的. 57 | 58 | * Stateful widgets 持有的状态可能在widget生命周期中发生变化. 实现一个 stateful widget 至少需要两个类: 59 | 1. 一个 StatefulWidget类。 60 | 1. 一个 State类。 StatefulWidget类本身是不变的,但是 State类在widget生命周期中始终存在. 61 | 62 | ``` js 63 | class RandomWords extends StatefulWidget { 64 | @override 65 | createState() => new RandomWordsState(); 66 | } 67 | 68 | class RandomWordsState extends State { 69 | @override 70 | Widget build(BuildContext context) { 71 | // 每一次热更新,单词都会变化 72 | // 如果是Stateless widgets则每次都是固定同样的单词 73 | final wordPair = new WordPair.random(); 74 | return new Text(wordPair.asPascalCase); 75 | } 76 | } 77 | 78 | // 使用 79 | body: new Center( 80 | //child: new Text(wordPair.asPascalCase), 81 | child: new RandomWords(), 82 | ) 83 | ``` 84 | 85 | ## 参考 86 | 87 | 1. https://github.com/raunakhajela/Flutter-Tutorials 88 | 1. helloworld:https://github.com/iamshaunjp/flutter-beginners-tutorial/blob/lesson-15/ninja_id/lib/main.dart 89 | 2. stateful widget: https://github.com/iamshaunjp/flutter-beginners-tutorial/blob/lesson-30/world_time_app/lib/main.dart -------------------------------------------------------------------------------- /docs/interview/interview-mini-app.md: -------------------------------------------------------------------------------- 1 | # 小程序 2 | 3 | 互联网生态演进:超级 APP + 小程序成为「轻应用时代」下的新生态。 4 | 5 | * 双线程原因:对小程序的架构设计时的要求只有一个,就是要`快`,包括要渲染快、加载快等。 6 | * native方案不可能,要随wechat发版 7 | * web方案不可能,ui渲染和js都在单线程中执行,容易逻辑任务抢占UI渲染资源。 8 | * 以前Hybrid方案不可能:1. Hybrid都是浏览器内核渲染,逻辑与渲染混合在一起,慢。2. RN渲染底层使用原生渲染,坑多,不推荐。 9 | * 小程序方案:小程序是基于`双线程模型`的(变种的Hybrid方案),在这个模型中,小程序的逻辑层与渲染层分开在不同的线程运行,这跟传统的Web 单线程模型有很大的不同,使得小程序架构上多了一些复杂度,也多了一些限制。 10 | * 安全。ui和渲染隔离,使得不能通过js去获取ui的数据 11 | * 同时也能防止js跳转一些web页面 12 | * 双线程模型 13 | * 客户端系统有JavaScript 的解释引擎(在iOS下是用内置的 JavaScriptCore框架,在安卓则是用腾讯x5内核提供的JsCore环境),我们可以创建一个单独的线程去执行 JavaScript,在这个环境下执行的都是有关小程序业务逻辑的代码,也就是我们前面一直提到的逻辑层。而界面渲染相关的任务全都在WebView线程里执行,通过逻辑层代码去控制渲染哪些界面,那么这一层当然就是所谓的渲染层。这就是小程序双线程模型的由来。 14 | * 双线程影响 15 | * `每个小程序页面都是用不同的WebView去渲染`(所以最多不能超过6页) 16 | * 提供更好的交互体验,更贴近原生体验,避免单个webview任务过重。 17 | * 天生的延时 18 | 19 | ![](https://zhaomenghuan.js.org/assets/img/wechat-miniprogram-framework.ad156d1c.png) 20 | 21 | ![](https://zhaomenghuan.js.org/assets/img/weapp-architecture.7e7b2004.png) 22 | 23 | ![](https://zhaomenghuan.js.org/assets/img/lifecycle.45dd5c41.png) 24 | --- 25 | 26 | 小程序以离线包方式下载到本地,通过微信客户端载入和启动,开发规范简洁,技术封装彻底,自成开发体系,有 Native 和 H5 的影子,但又绝不雷同。小程序本身定位为一个简单的逻辑视图层框架。 27 | 28 | 1. 原理: 29 | * 小程序本质就是一个`单页面应用`,所有的页面渲染和事件处理,都在一个页面内进行,但又可以通过微信客户端调用原生的各种接口; 30 | * 它的架构,是`数据驱动的架构模式`,它的UI和数据是分离的,所有的页面更新,都需要通过对数据的更改来实现; 31 | * `功能可分为webview和appService两个部分`; 32 | webview用来展现UI,appService有来处理业务逻辑、数据及接口调用; 33 | 两个部分在两个进程中运行,通过系统层JSBridge实现通信,实现UI的渲染、事件的处理等。(this.data的属性是不可以同步到视图的,必须调用this.setData()方法) 34 | 2. 生命周期函数: 35 | * onLoad() 页面加载时触发,只会调用一次,可获取当前页面路径中的参数。 36 | * onShow() 页面显示/切入前台时触发,一般用来发送数据请求; 37 | * onReady() 页面初次渲染完成时触发, 只会调用一次,代表页面已可和视图层进行交互。 38 | * onHide() 页面隐藏/切入后台时触发, 如底部 tab 切换到其他页面或小程序切入后台等。 39 | * onUnload() 页面卸载时触发,如redirectTo或navigateBack到其他页面时。 40 | 3. `bind事件`绑定`不会阻止冒泡`事件向上冒泡,`catch事件`绑定可以`阻止`冒泡事件向上冒泡。 41 | 4. `mpvue` 修改了 Vue.js 的 `运行时框架 runtime` 和`代码编译器 compiler` 实现,使其可以运行在小程序环境中,从而为小程序开发引入了整套 Vue.js 开发体验。 42 | * `生命周期和数据同步`。在 Vue.js 触发数据更新时实现数据同步。数据更新发端于小程序,处理自 Vue.js,Vue.js 数据变更后再同步到小程序。 43 | * `事件代理机制`。小程序组件节点上触发事件后,只要找到虚拟 DOM 上对应的节点,触发对应的事件。另一方面,Vue.js 事件响应如果触发了数据更新,其生命周期函数更新将自动触发,在此函数上同步更新小程序数据,数据同步也就实现了。 44 | 45 | * https://developers.weixin.qq.com/ebook?action=get_post_info&docid=0006a2289c8bb0bb0086ee8c056c0a 46 | 47 | * https://zhaomenghuan.js.org/blog/wechat-miniprogram-principle-analysis.html 48 | 49 | ### Electron 50 | 官网里这么说:`electron 提供了一个 Nodejs 的运行时,专注于构建桌面应用,同时使用 web 页面来作为应用的 GUI`。你可以将其看作是一个由 JavaScript 控制的迷你版的 Chromium 浏览器。 51 | 52 | 翻译一下:它是一个运行时,可以像 Node 一样这样执行:electron app.js;也是一个使用 html + css + JavaScript 构建跨平台原生桌面应用的框架。本质上,electron 就是一个带了 Chrome 浏览器的壳子(无需考虑兼容性的问题)。 53 | 54 | electron 用 web 页面作为它的 GUI,而不是绑定了 GUI 库的 JavaScript。它结合了 Chromium、Node.js 和用于调用操作系统本地功能的 APIs(如打开文件窗口、通知、图标等)。 55 | 56 | `具有两个进程,分别是主进程,以及渲染进程`。 -------------------------------------------------------------------------------- /docs/js/js-base-2.extend.md: -------------------------------------------------------------------------------- 1 | # JavaScript继承 2 | 3 | 在上篇[JavaScript原型](./js-base-1.prototype.md)中,我们理解了原型的来源以及与其相关的constructor、new、prototype概念。下面我们来看看js是如何通过原型来实现面向对象的另外一个特征:继承。另外推荐《JavaScript高级程序设计》,js面向对象章节写的太棒了,层层递进,深入浅出。 4 | 5 | ## 组合继承 6 | 利用 call 继承父类上的属性,用子类的原型等于父类实例去继承父类的方法。 7 | 8 | 缺点:调用两次父类Person构造函数,造成性能浪费。 9 | 10 | ``` js 11 | // 组合继承,也叫经典继承 12 | function Person(name, languages) { 13 | this.name = name 14 | this.languages = languages 15 | } 16 | Person.prototype.sleep = function() { console.log(this.name + ' go to sleep') } 17 | 18 | function Developer(name, languages, codeLanguage) { 19 | // 构造函数内,使得Person实例的属性/方法,不会变成原型共享属性/方法 20 | Person.call(this, name, languages) // 构造函数终归也是函数 21 | this.codeLanguage = codeLanguage 22 | } 23 | // 继承原型 24 | // 原型链,使得Person实例拥有的属性/方法,都变成Developer原型的共享属性/方法 25 | // 单以下这一行代码会有“当属性为引用类型”的bug,所以需要配合上面Person.call 26 | Developer.prototype = new Person() 27 | Developer.prototype.constructor = Developer 28 | 29 | var jsCoder = new Developer('tom', ['Chinese', 'English'], ['js', 'css']) 30 | // Developer中没有定义sleep方法,但可以访问到,说明Develper继承了Person 31 | jsCoder.sleep() // tom go to sleep 32 | ``` 33 | 34 | ## 寄生组合继承 35 | 36 | 用空函数的原型去等于父类原型,再用子类的原型等于干净函数的实例,从而达到消除一次执行Person函数。 37 | 38 | ``` js 39 | // 寄生组合继承,也是最理想的继承方式 40 | function Person(name, languages) { 41 | this.name = name 42 | this.languages = languages 43 | } 44 | Person.prototype.sleep = function() { console.log(this.name + ' go to sleep') } 45 | 46 | function Developer(name, languages, codeLanguage) { 47 | Person.call(this, name, languages) 48 | this.codeLanguage = codeLanguage 49 | } 50 | 51 | // 利用空函数中介,实现继承 52 | Developer.prototype = object(Person.prototype) 53 | Developer.prototype.constructor = Developer 54 | function object(origin) { // object函数,等同于ES6中Object.create 55 | function F(){} 56 | F.prototype = origin 57 | return new F() 58 | } 59 | 60 | var jsCoder = new Developer('tom', ['Chinese', 'English'], ['js', 'css']) 61 | jsCoder.sleep() // tom go to sleep 62 | ``` 63 | 64 | ## 通用继承 65 | 66 | 以上我们能得到一种通用的js继承,而这种方式也是很多js库正在使用的继承函数。 67 | 68 | ``` js 69 | inheritPrototype(Developer, Person) 70 | 71 | function inheritPrototype(subClass, superClass) { 72 | subClass.prototype = Object.create(superClass.prototype) 73 | subClass.prototype.constructor = subClass 74 | // 或者一条代码 75 | // subClass.prototype = Object.create(superClass && superClass.prototype, { 76 | // constructor: { value: subClass, writable: true, configurable: true } 77 | // }); 78 | } 79 | ``` 80 | 81 | ## 参考文章 82 | 83 | * [avascript面向对象编程(二):构造函数的继承](http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html) 84 | * [JavaScript深入之继承的多种方式和优缺点](https://github.com/mqyqingfeng/Blog/issues/16) 85 | * [MDN - Object​.create()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create) -------------------------------------------------------------------------------- /docs/react/react-eslint.md: -------------------------------------------------------------------------------- 1 | # ESlint代码检查规范 - React/ReactNative 2 | 3 | 在前端编码时,为了规范每个成员的代码风格以及避免低级的错误,我们可以使用Eslint来制定规则.本文旨在帮助团队成员形成良好的React代码规范。推荐使用[Airbnb Eslint规范](https://github.com/airbnb/javascript/tree/master/react)+[自定义Rules](http://eslint.cn/docs/rules/)。 4 | 5 | ## Airbnb Eslint规范 6 | 7 | 目前使用eslint不再需要自己手动装太多npm包,社区已经在最新eslint初始化命令中自动安装。 8 | 9 | ### 安装Eslint 10 | 11 | 有全局安装和本地安装两种方式,推荐本地安装 12 | 13 | ``` 14 | npm install --save-dev eslint 15 | ``` 16 | 17 | ### 初始化Eslint 18 | 19 | 初始化会供用户很多可选的选择,这里推荐使用流行的`Airbnb Eslint`。安装完后,在`package.json`中会自动安装需要的依赖,分别为eslint-config-airbnb、eslint-plugin-import、eslint-plugin-jsx-a11y、eslint-plugin-react。同时也会创建`.eslintrc`配置文件 20 | 21 | ``` 22 | eslint --init 23 | ``` 24 | 25 | ![](https://upload-images.jianshu.io/upload_images/1474238-c0bc9d15756a4abe.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/555) 26 | 27 | ### Eslint检查 28 | 29 | eslint的[Command Line Interface](https://eslint.org/docs/user-guide/command-line-interface)有命令行调用接口,如何搭配命令行取决于项目风格。格式: 30 | 31 | ``` 32 | eslint [options] [file|dir|glob]* 33 | ``` 34 | 35 | eg:当前app目录下监测js并打印报错 36 | 37 | ``` 38 | eslint --quiet --ext .js app 39 | ``` 40 | 41 | ``` 42 | --ext [String] Specify JavaScript file extensions - default: .js 43 | --quiet Report errors only - default: false 44 | ``` 45 | 46 | >tips: 如果测试执行报错,可能你同时安装了本地和全局eslint,这里可以把eslint命令指定为本地路径:`./node_modules/.bin/eslint`,参考见该[issue](https://github.com/airbnb/javascript/issues/465) 47 | 48 | ## 自定义Rules 49 | 50 | 自定义Rules综合考虑了笔者部门小伙伴习惯的Vue风格,如不使用分号结尾,以及React特殊的JSX语法,形成以下推荐配置: 51 | 52 | ```eslint 53 | module.exports = { 54 | "extends": ["airbnb"], // 使用airbnb规则 55 | "parser": "babel-eslint",// React使用了大量ES6语法,使用babel-eslint解析器代替默认的Espree 56 | "globals": { // 全局变量设置 57 | "__DEV__": false // false 表示这个全局变量不允许被重新赋值 58 | }, 59 | "rules": { 60 | // 4个空格 61 | "indent": [2, 4], 62 | "react/jsx-indent": [2, 4], 63 | "react/jsx-indent-props": [2, 4], 64 | 65 | "semi": [2, "never"], // 是否使用分号结尾 66 | "no-console": 'off', // 允许console 67 | "max-len": "off", // 单行没有字数限制 68 | "object-curly-newline": "off", // 关闭大括号内换行符的一致性 69 | "comma-dangle": "off", // 关闭是否使用拖尾逗号 70 | "arrow-parens": "off", // 关闭箭头函数是否需要大括号 71 | 72 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], // 允许使用js/jsx文件扩展 73 | "react/sort-comp": "off", // 关闭sort 74 | "react/no-array-index-key": "off",// 允许使用index作为List的key 75 | "no-unused-expressions": "off",// 允许三元表达式 76 | "import/no-unresolved": "off",// 允许require image 77 | "react/no-multi-comp": "off", // 允许一个文件定义多个组件 78 | "react/display-name": "off", // 不需要给组件定义displayName 79 | } 80 | 81 | }; 82 | ``` 83 | 84 | >注意:需要额外安装babel-eslint以解析ES6语法:`npm install --save-dev babel-eslint` 85 | -------------------------------------------------------------------------------- /docs/react/react-component-best.md: -------------------------------------------------------------------------------- 1 | # React 组件最佳实践 2 | 3 | ## class组件 4 | ``` js 5 | import React, { Component } from 'react' 6 | import { observer } from 'mobx-react' 7 | import { string, object } from 'prop-types' 8 | // 分开本地导入和依赖导入 9 | import ExpandableForm from './ExpandableForm' 10 | import './styles/ProfileContainer.css' 11 | 12 | // 使用修饰器(如果有的话) 13 | @observer 14 | export default class ProfileContainer extends Component { 15 | state = { expanded: false } 16 | // 初始化state (ES7) 或者在构造函数(constructor)中初始化state (ES6) 17 | 18 | //使用静态属性(ES7)声明propTypes越早越好 19 | static propTypes = { 20 | model: object.isRequired, 21 | title: string 22 | } 23 | 24 | // 在propTypes后声明defaultProps 25 | static defaultProps = { 26 | model: { 27 | id: 0 28 | }, 29 | title: 'Your Name' 30 | } 31 | 32 | // 使用箭头函数绑定指向定义的上下文的this 33 | handleSubmit = (e) => { 34 | e.preventDefault() 35 | this.props.model.save() 36 | } 37 | 38 | handleNameChange = (e) => { 39 | this.props.model.name = e.target.value 40 | } 41 | 42 | handleExpand = (e) => { 43 | e.preventDefault() 44 | this.setState(prevState => ({ expanded: !prevState.expanded })) 45 | } 46 | 47 | render() { 48 | // 解构props成可读的 49 | const { 50 | model, 51 | title 52 | } = this.props 53 | return ( 54 | 58 | // 如果有2个以上props,分行写 59 |
60 |

{title}

61 | { model.name = e.target.value }} 65 | // 避免创造新的闭包,应该使用下面的方法。 66 | onChange={this.handleNameChange} 67 | placeholder="Your Name"/> 68 |
69 |
70 | ) 71 | } 72 | } 73 | ``` 74 | 75 | ## 函数组件 76 | 77 | ``` js 78 | import React from 'react' 79 | import { observer } from 'mobx-react' 80 | import { func, bool } from 'prop-types' 81 | // 分开本地导入和依赖导入 82 | import './styles/Form.css' 83 | 84 | // 在组件前声明propTypes 85 | ExpandableForm.propTypes = { 86 | onSubmit: func.isRequired, 87 | expanded: bool, 88 | onExpand: func.isRequired 89 | } 90 | 91 | // 推荐使用具名组件,箭头函数定义的函数组件其实是没有命名的 92 | // 解构props,通过函数入参默认值的方式设定defaultProps 93 | function ExpandableForm({ onExpand, expanded = false, children, onSubmit }) { 94 | const formStyle = expanded ? { height: 'auto' } : { height: 0 } 95 | return ( 96 |
97 | {children} 98 | 99 |
100 | ) 101 | } 102 | 103 | // 包装函数代替修饰器 104 | export default observer(ExpandableForm) 105 | ``` -------------------------------------------------------------------------------- /docs/temp/atemp.md: -------------------------------------------------------------------------------- 1 | 2 | ## Lerna 3 | 4 | * lerna init 5 | * lerna add package --scope xxx # 添加项目包 6 | * lerna run command --scope xxx # 执行包命令 7 | 8 | lerna bootstrap 9 | lerna publish 10 | 11 | * lerna exec --scope xxx -- ls 12 | 13 | 14 | # JavaScript 作用域 15 | 16 | * 闭包的应用 17 | * 垃圾回收机制基础 18 | 19 | ``` js 20 | var globalScope = true 21 | if (globalScope) { 22 | var innerScope = 123 23 | } 24 | console.log(innerScope) // 123 25 | ``` 26 | 27 | ``` js 28 | // let关键字隐式的将变量innerScope绑定到{..}作用域中 29 | let globalScope = true 30 | if (globalScope) { 31 | let innerScope = 123 32 | } 33 | console.log(innerScope) // innerScope is not defined 34 | ``` 35 | 36 | # js 滚动条 37 | 38 | 网页可见区域宽: document.body.clientWidth; 39 | 网页可见区域高: document.body.clientHeight; 40 | 网页可见区域宽: document.body.offsetWidth; (包括边线的宽) 41 | 网页可见区域高: document.body.offsetHeight; (包括边线的宽) 42 | 网页正文全文宽: document.body.scrollWidth; 43 | 网页正文全文高: document.body.scrollHeight; 44 | 网页被卷去的高: document.body.scrollTop; (当前滚动条距离顶部的距离) 45 | 网页被卷去的左: document.body.scrollLeft;(当前滚动条距离左边的距离) 46 | 47 | 判断滚动条到底部,需要用到DOM的三个属性值,即scrollTop、clientHeight、scrollHeight。 48 | 49 | scrollTop为滚动条在Y轴上的滚动距离。 50 | clientHeight为内容可视区域的高度。 51 | scrollHeight为内容可视区域的高度加上溢出(滚动)的距离。 52 | 从这个三个属性的介绍就可以看出来,滚动条到底部的条件即为scrollTop + clientHeight == scrollHeight。 53 | 54 | # JS技巧 55 | 56 | ## 数组 57 | 58 | ### 数组的对象解构 59 | 60 | 数组也可以对象解构,可以方便的获取数组的第n个值 61 | ``` js 62 | const csvFileLine = '1997,John Doe,US,john@doe.com,New York'; 63 | const { 2: country, 4: state } = csvFileLine.split(','); 64 | 65 | country // US 66 | state // New Yourk 67 | ``` 68 | 69 | ## 对象 70 | 71 | 在函数参数中解构嵌套对象 72 | ``` js 73 | var car = { 74 | model: 'bmw 2018', 75 | engine: { 76 | v6: true, 77 | turbo: true, 78 | vin: 12345 79 | } 80 | } 81 | const modelAndVIN = ({model, engine: {vin}}) => { 82 | console.log(`model: ${model} vin: ${vin}`); 83 | } 84 | modelAndVIN(car); // => model: bmw 2018 vin: 12345 85 | ``` 86 | 87 | ## Ice飞冰 88 | 89 | ### 物料开发 90 | 91 | https://ice.work/docs/materials/guide/usage 92 | 93 | 方案跟vusion一致,组件(基础/业务)通过npm发布,block作为代码片段可二次修改,模板通过脚手架生成。 94 | 95 | 完成以上本地编码后,可以统一注册到ice中心(token)(返回url),然后可在iceworks(IDE)上进行查看。 96 | 97 | React官方物料:https://github.com/alibaba-fusion/materials 98 | 99 | ### iceworks本地IDE 100 | 101 | https://ice.work/docs/iceworks/quick-start 102 | 103 | ## 制作一个vue-cli工具 104 | 105 | https://github.com/RaleighY/femi-scripts 106 | ``` js 107 | const config = require("../config/webpack.config") 108 | 109 | const compiler = Webpack(config) 110 | const devServer = new WebpackDevServer(compiler, { 111 | historyApiFallback: true, 112 | proxy: PackageJson.proxy, 113 | }) 114 | 115 | devServer.listen(4000, "localhost", err => { 116 | if (err) { 117 | return console.log(err) 118 | } 119 | }) 120 | ``` -------------------------------------------------------------------------------- /docs/temp/ele-ui/ai组件库设计会议记录2.md: -------------------------------------------------------------------------------- 1 | # 组件库设计 2 | 3 | ## 公用字典表 4 | 5 | * size: 大小标志,值有:xxl,xl,l,s,xs,xxs 6 | * dir: 方向标志,值有:h,v 7 | * 支持v-modal也同时支持value.async 8 | * v-modal尽量使用value/input等官方推荐语法糖 9 | 10 | ## ai-link 11 | 12 | #### Props 13 | * [x] href 14 | * [x] target 15 | * [x] to 16 | * [x] replace 17 | * [x] disabled 18 | * [ ] gaKey @讨论??? 19 | 20 | #### Events 21 | * [x] before-navigate 22 | * [x] navigate 23 | 24 | ## ai-button 25 | 26 | #### Props 27 | * [x] color 28 | * [ ] icon 29 | * [ ] size( for css) 30 | * [ ] 属性同ai-link,支持to,href,disabled等 31 | 32 | ## ai-icon @讨论??? 33 | 34 | * [ ] 组件内图标库 35 | * [ ] 通用解决方案:开源svg-icon 36 | 37 | ## ai-input 38 | 39 | #### Props 40 | * [x] v-modal 41 | * [x] value 42 | * [ ] value.number 43 | * [x] maxLength(v-bind="$attrs") 44 | * [x] type: password/textarea(v-bind="$attrs") 45 | * [x] disabled(v-bind="$attrs") 46 | * [ ] size( for css) 47 | * [ ] clearabled 48 | * [ ] regex @讨论??? 49 | 50 | #### Events 51 | * [ ] input 52 | * [ ] 原生blur等(v-on="$listeners") 53 | 54 | ## ai-select 55 | 56 | #### Props 57 | * [x] list 58 | * [x] v-modal 59 | * [x] value 60 | * [x] disabled 61 | * [ ] size( for css) 62 | * [ ] labelField 63 | * [ ] valueField 64 | * [ ] multiple @多选样式 讨论 65 | 66 | #### Events 67 | * [ ] input 68 | * [x] select 69 | 70 | ## ai-table 71 | 72 | #### Props 73 | * [x] list 74 | * [x] slot-scope 75 | * [ ] 自定义表头 76 | 77 | ## ai-pagination 78 | 79 | #### Props 80 | * [ ] page.sync 81 | * [ ] total 82 | * [ ] pageSize 83 | 84 | #### Events 85 | * [ ] update:page 86 | * [ ] changed 87 | 88 | ## ai-modal 89 | 90 | #### Props 91 | * [ ] visible.sync 92 | * [ ] title 93 | * [ ] content 94 | * [ ] size 95 | * [ ] okButton 96 | * [ ] cancelButton 97 | * [ ] $alert 98 | * [ ] $confirm 99 | 100 | #### Slots 101 | * [ ] title 102 | * [ ] default 103 | * [ ] foot 104 | 105 | #### Events 106 | * [ ] before-close 107 | * [ ] close 108 | 109 | ## ai-layout 110 | 111 | #### Props 112 | * [ ] dir 113 | * [ ] size (for css) 114 | * [ ] layout (for css) 115 | 116 | ## ai-tabs 117 | 118 | #### Props 119 | * [ ] v-modal 120 | 121 | #### Events 122 | * [ ] input 123 | * [ ] changed 124 | 125 | ## ai-tab 126 | 127 | #### Props 128 | * [x] title 129 | * [ ] to 130 | * [ ] disabled 131 | 132 | ## ai-toast 133 | 134 | #### Props 135 | * [x] text 136 | * [x] type 137 | * [x] delay 138 | 139 | ## ai-tooltip 140 | 141 | #### Props 142 | * [x] text 143 | * [ ] position 144 | 145 | ## ai-radio 146 | 147 | ## ai-radios 148 | 149 | ## ai-checkbox 150 | 151 | ## ai-checkboxs 152 | 153 | ## ai-form 154 | 155 | ## ai-form-item 156 | 157 | ## ai-label 158 | 159 | ## ai-badge 160 | 161 | ## ai-breadcrumb 162 | 163 | ## ai-progress 164 | 165 | ## ai-steps 166 | 167 | ## ai-menu 168 | 169 | ## ai-menu-item 170 | 171 | ## ai-upload 172 | 173 | ## ai-switch 174 | 175 | ## ai-tree 176 | 177 | ## ai-datepicker 178 | 179 | ## popup 180 | 181 | ## grid -------------------------------------------------------------------------------- /docs/temp/cookie-google-rule.md: -------------------------------------------------------------------------------- 1 | # Google Cookie策略对Web开发的影响 2 | 3 | Chrome将从2月的Chrome 80开始执行新的Cookie策略,对未设置“SameSite”的Cookie默认其值为Lax,即三方网站如果使用了该资源,在请求中是不会带上相关的Cookie的;对SameSite设置为None的Cookie,要求必须同时设置Secure,否则拒绝此Cookie。 4 | 5 | 6 | ### SameSite 7 | 这是一个用来决定Cookie的可用网站范围的属性。它有三个值: 8 | 9 | 1. Strict: 仅限于同站请求 10 | 1. Lax: 同站请求,或者跨站的GET的会导致页面URL发生变化的导航行为,包括链接、GET的Form提交。比如在 http://www.def.com 网站的页面中嵌入了一个链接 http://www.abc.com ,那么在用户点击此链接时发起的初始请求,会带上http://www.abc.com中设置为Lax的Cookie。而那些设置为Strict的Cookie是不会带上的。这种跨站链接还包括像嵌入到邮件中的链接。 11 | 2. None: 没有限制,同站及跨站请求中都会带上,与传统的方式一样。 12 | 13 | **在新的Chrome的Privacy Sandbox的策略中:** 14 | 15 | 1. 如果Cookie未设置SameSite,那把其值默认为Lax。 16 | 1. 如果Cookie设置SameSite=None,还需要同时设置Secure,否则此Cookie会被浏览器拒绝。设为Secure之后,其它三方网站对我方网站资源的引用必须使用HTTPS,否则还是不会带上此Cookie,这也要求我方网站必须支持HTTPS。 17 | 18 | ## 解释 19 | 20 | 假设有http://www.abc.com和http://www.def.com两个网站,通过之前的某些页面逻辑,浏览器中已经存储了如下Cookie: 21 | ``` 22 | Set-Cookie: abc1=ok; Domain=abc.com 23 | Set-Cookie: abc2=ok; Domain=www.abc.com 24 | Set-Cookie: abc3=ok; Domain=xyz.abc.com 25 | Set-Cookie: abc4=ok; Domain=abc.com; SameSite=Strict 26 | Set-Cookie: abc5=ok; Domain=abc.com; SameSite=Lax 27 | Set-Cookie: abc6=ok; Domain=abc.com; SameSite=None 28 | Set-Cookie: abc7=ok; Domain=abc.com; SameSite=None; Secure 29 | Set-Cookie: abc8=ok; Domain=xyz.abc.com; SameSite=None 30 | Set-Cookie: abc9=ok; Domain=xyz.abc.com; SameSite=None; Secure 31 | 32 | Set-Cookie: def1=ok; Domain=def.com 33 | ``` 34 | 35 | 假设当前在http://www.def.com这个网站中: 36 | ![](https://pic3.zhimg.com/80/v2-4a00716c99ba7f39a1a4a9fdfc75eee2_720w.jpg) 37 | 38 | ## 谷歌进行第三方Cookie规则更新的意义 39 | 40 | Cookie的不安全主要在三个方面: 41 | 42 | ### 1. 中间人效应 43 | 44 | Cookie的传输将会`经过多个路由器`,它们都将获得Cookie的内容。而Cookie的内容往往标识了登录状态。解决方法便是使用更安全的HTTPS协议进行传输。 45 | 46 | ### 2. XSS跨站脚本(Cross-Site Scripting) 47 | 48 | 使用跨站点脚本技术可以窃取cookie。当`网站允许使用javascript操作cookie`的时候,就会发生攻击者发布恶意代码攻击用户的会话,同时可以拿到用户的cookie信息。例子: 49 | 50 | ``` html 51 | 领取红包 52 | ``` 53 | 54 | 当用户点击这个链接的时候,浏览器就会执行onclick里面的代码,结果这个网站用户的cookie信息就会被发送到http://abc.com攻击者的服务器。攻击者同样可以拿cookie搞事情。 55 | 56 | 解决办法:可以通过cookie的HttpOnly属性,设置了HttpOnly属性,javascript代码将不能操作cookie。 57 | 58 | ### 3. CSRF跨站请求伪造(Cross-Site Request Forgery) 59 | 60 | 例如,SanShao可能正在浏览其他用户XiaoMing发布消息的聊天论坛。假设XiaoMing制作了一个引用ShanShao银行网站的HTML图像元素,例如: 61 | 62 | ``` html 63 | 64 | ``` 65 | 如果SanShao的银行将其认证信息保存在cookie中,并且cookie尚未过期,(当然是没有其他验证身份的东西),那么SanShao的浏览器尝试加载该图片将使用他的cookie提交提款表单,从而在未经SanShao批准的情况下授权交易。 66 | 67 | 这是利用网站漏洞和`用户未登出`第三方网站进行的攻击。在SameSite问世之前,我们可用通过HTTP头中的Referer字段进行校验。 68 | 69 | ### 补充:广告商Cookie应用案例 70 | 71 | 通常cookie的域和浏览器地址的域匹配,这被称为第一方cookie。那么**第三方cookie就是cookie的域和地址栏中的域不匹配**,这种cookie通常被用在第三方广告网站。为了跟踪用户的浏览记录,并且根据收集的用户的浏览习惯,给用户推送相关的广告。 72 | 73 | 更多:https://zhuanlan.zhihu.com/p/31852168 74 | 75 | ## 参考 76 | 77 | * https://zhuanlan.zhihu.com/p/103420328 78 | * https://zhuanlan.zhihu.com/p/31852168 79 | * https://maxket.com/chrome-samesite-none-update/ -------------------------------------------------------------------------------- /docs/react/function-program.md: -------------------------------------------------------------------------------- 1 | # 浅谈函数式编程 2 | 3 | 程序设计时,各语言只是实现的过程,目的都相同:清晰可读易扩展。表现在设计原则: 4 | * `可扩展` - 需求变了,能不改动以前的代码,而是拥有扩展的能力(设计模式就是从中总结出来的一些经验)。 5 | * `可重用` - 避免到处是重复的代码,万一逻辑变动,所有地方都得改。 6 | * `模块化` - 拆分模块,使得各模块各司其职,而不是杂柔在一起(大型项目必备) 7 | * `可推理` - 读代码次数比写代码次数多,易于维护 8 | * `可测试` - 代码健壮性 9 | 10 | 在符合以上基础原则上,可以使用任何语言去实现最终的程序。但现实是各语言都有各自特点以及适用场景,实现同一个功能,可能一个及其简单,一个复杂。比如JavaScript是一种动态类型语言,函数也是类型的一种(当作对象类型),所以可以把函数当作参数值进行传递(这就是FP(functional programming)中常说的函数天生是“一等公民”)。而Java这种强类型面向对象语言,是无法把定义的函数/方法当作一个参数,传入到另外一个函数/方法中。两者的编程风格区别看以下案例: 11 | 12 | ``` js 13 | // js函数式编程 14 | // 函数作为参数值传入,使得逻辑更清晰并且无污染 15 | [1, 2, 3] 16 | .filter(function(item) { return item !== 1}) 17 | .map(funciton(item) { return item * 2 }) 18 | 19 | // js命令式编程 20 | // 相比函数式,1. 更多的中间状态,如mapArr 2. 逻辑可读性差 3. 代码复用差 21 | var arr = [1, 2, 3] 22 | var mapArr = [] 23 | for(var i = 0; i < arr.length; i++) { 24 | if (arr[i] != 1) { 25 | mapArp.push(arr[i] * 2) 26 | } 27 | } 28 | ``` 29 | 30 | ``` java 31 | // java命令式编程 32 | // 定义的参数互相串行,复用性差 33 | int[] arr = {1, 2, 3}; 34 | // filter 35 | List filterArr = new List<>(); 36 | for(int i = 0; i < arr.length; i++) 37 | { 38 | if (arr[i] != 1) 39 | { 40 | filterArr.add(arr[i]); 41 | } 42 | } 43 | // map 44 | int[] result = ...MapArray(filterArr) 45 | ``` 46 | 47 | 以上得知,不同语言受限于语法不同,代码风格不一致。同一种语言(如:js)实现相同的功能,风格也大不一样,如上面的“函数式编程实现”以及“命令式编程实现”。所以`函数式编程是一种编程风格,也可以说是编程范式`。 48 | 49 | > 编程范式是如何编写程序的方法论。 50 | 51 | ## 函数式编程 52 | 53 | 以函数作为主要载体的编程方式,用函数去拆解、抽象一般的表达式。它的目的是使用函数来`抽象作用在数据之上的控制流和操作`,从而在系统中`消除副作用`以及`减少对状态的改变`。 54 | 55 | 函数式编程旨在尽可能的提高代码的无状态性和不变性。要做到这一点,就要学会使用纯函数。`纯函数,就是无副作用的函数`。所谓"副作用"(side effect),指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。 56 | 函数式编程强调没有"副作用",意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。 57 | 58 | 所以`函数式编程有如下特性`: 59 | 1. `函数是"第一等公民"`。指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。 60 | 1. `不修改状态`。不得修改外部变量的值 61 | 1. `引用透明`。同样的输入,那么函数总是返回同样的结果(单元测试梦寐以求的) 62 | 1. `无副作用`。调用函数只会计算出结果,不会出现其他效果。 63 | 64 | 以上决定了`函数式编程有如下优点`: 65 | * 语义更加清晰 66 | * 可复用性更高(函数为可调用的最小单位) 67 | * 可维护性更好(只需关注表达式的内部的实现,更易定位bug) 68 | * 作用域局限,副作用少 69 | 70 | > 面向对象编程通过封装变化使得代码易于理解。 71 | 72 | > 函数式编程通过最小变化使得代码易于理解。 73 | 74 | ## 常见的函数式编程模型 75 | 76 | 以函数作为主要载体的编程方式: 77 | 78 | * 闭包(Closure) 79 | * 高阶函数。接受1个或多个函数作为输入或输出一个函数的函数。简单说`高阶函数是操作其他函数的函数。` 80 | * map 81 | * filter 82 | * reduce 83 | * 柯里化(Currying) 84 | * Currying 为实现多参函数提供了一个递归降解的实现思路——把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数 85 | * 使用场景 86 | * 参数复用 87 | * 延迟执行 88 | * 实现方式 89 | * bind语法糖 使得JSX可以绑定数据,同时延迟执行 90 | * 箭头函数 使得JSX延迟执行 91 | * 自定义curry函数 92 | * 组合(Composing)/ 管道(Pipe) 93 | 94 | ## 参考文章 95 | 96 | * [我眼中的 JavaScript 函数式编程](http://taobaofed.org/blog/2017/03/16/javascript-functional-programing/) 97 | 98 | * [JavaScript 柯里化,了解一下](https://juejin.im/post/5af13664f265da0ba266efcf) 99 | 100 | * [函数式编程,真香](https://mp.weixin.qq.com/s/HDOXCMRk_nd59cY9rWXsOQ) -------------------------------------------------------------------------------- /docs/temp/react-code-temp.md: -------------------------------------------------------------------------------- 1 | componentWillMount,render,componentDidMount 都是在 mountComponent 中被调用 2 | 3 | 4 | ReactElement 5 | 数据类,只包含 props refs key 等 6 | 由 React.creatElement(ReactElement.js) 创建,React.createClass 中 render 中返回的实际也是个 ReactElement 7 | 8 | ReactComponent 9 | 控制类,包含组件状态,操作方法等 10 | 包括字符组件、原生 DOM 组件、自定义组件(和空组件) 11 | 12 | 13 | 函数式组件与基于Class声明的组件相比,其具有以下特性: 14 | 15 | * 不需要声明类,可以避免大量的譬如extends或者constructor这样的代码 16 | 17 | * 不需要显示声明this关键字,在ES6的类声明中往往需要将函数的this关键字绑定到当前作用域,而因为函数式声明的特性,我们不需要再强制绑定: 18 | 19 | * 应当避免在底层的展示性组件中混入对于状态的管理,而应该将状态托管于某个高阶组件或者其他的状态容器中。利用函数式声明组件可以彻底保证不会在组件中进行状态操作。 20 | 21 | * 易于理解与测试 22 | 23 | * 更佳的性能表现:因为函数式组件中并不需要进行生命周期的管理与状态管理,因此React并不需要进行某些特定的检查或者内存分配,从而保证了更好地性能表现。 24 | 25 | 26 | // 无状态的组件函数中,访问Context: 27 | ``` js 28 | const Text = (props, context) => 29 |

props.children

; 30 | Text.contextTypes = { 31 | fontFamily: React.PropTypes.string 32 | }; 33 | class App extends React.Component { 34 | static childContextTypes = { 35 | fontFamily: React.PropTypes.string 36 | } 37 | getChildContext() { 38 | return { 39 | fontFamily: 'Helvetica Neue' 40 | }; 41 | } 42 | render() { 43 | return Hello World; 44 | } 45 | } 46 | ``` 47 | 48 | 49 | 官方给出的 React 的定义是: 50 | 51 | A JavaScript library for building user interfaces. 52 | 53 | 即专注于构建 View 层的一个库。React 的核心开发者之一的 Sebastian Markbåge 认为: 54 | 55 | UI 只是把数据通过映射关系变成另一种形式的数据。给定相同的输入(数据)必然会有相同的输出(UI),即一个简单的纯函数。 56 | React 中的函数式思想的具体体现 57 | 58 | 虽说 View 层可以当成是数据的另外一种展现形式,但在实际的 React 开发中,除了数据的展示以外,更重要的是还有数据的交互,举个栗子: 59 | 60 | 这个一个典型的渲染列表的栗子,在这个栗子中除了渲染 PostList 外,还进行了数据的获取和事件的操作,也就意味着这个 PostList 组件不是一个「纯函数」。严格意义上来说这个组件还不是一个可复用的组件,比如说有这样一种业务场景,除了首页有 PostList 组件以外,在个人页面同样有个 PostList 组件,UI 一致但是交互逻辑不一致,这种情况下就无法复用首页的 PostList 组件了。为了解决这个问题,我们可以再次抽离一个真正意义上可复用的 View 层,它有一下几个特点: 61 | 62 | 给定相同的数据(由父组件通过 props 传递给子组件且是唯一数据来源),总是渲染相同的 UI 界面; 63 | 组件你内部不改变数据状态; 64 | 不处理交互逻辑。 65 | 66 | 浅拷贝: 67 | ``` js 68 | function shallowEqual(objA, objB) { 69 | if (is(objA, objB)) { 70 | return true; 71 | } 72 | 73 | if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { 74 | return false; 75 | } 76 | 77 | var keysA = Object.keys(objA); 78 | var keysB = Object.keys(objB); 79 | 80 | if (keysA.length !== keysB.length) { 81 | return false; 82 | } 83 | 84 | // Test for A's keys different from B. 85 | for (var i = 0; i < keysA.length; i++) { 86 | if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) { 87 | return false; 88 | } 89 | } 90 | 91 | return true; 92 | } 93 | 94 | ``` 95 | 96 | * [React 中的函数式思想](https://justclear.github.io/functional-in-react/) 97 | 98 | setState --> flushBatchedUpdates --> runBatchedUpdates -->performUpdateIfNecessary --> receiveComponent --> ... 99 | 100 | 为什么不要在jsx中使用bind(this)或箭头函数 101 | 102 | 因为每调用一次事件,会执行bind(this),它会返回一个新的函数,这个函数会绑定到VNode.props.onClick上(??),意味着前后的VNode的props浅对比是返回false,导致有和没有PureClass都是一样的。但其实这两个函数实现是一致的。 -------------------------------------------------------------------------------- /docs/read-books/book-agile-software.md: -------------------------------------------------------------------------------- 1 | # 《敏捷开发》读后总结 2 | 3 | 前端开发在软件工程中,占据着很重要的一环。上游对接的是视觉、设计、后端等,下游对应的是用户反馈。更多时候,开发不是单纯的写好代码完成需求就行,写出优雅可读性高的代码只是硬技术之一。还需要在整个软件生产过程中发挥个人软技能,比如如何让团队运作高效,如何让产品可持续优化迭代。毕竟所有人都需要对结果负责,而不是对过程负责。 4 | 5 | 在软件工程中,你可能已经应用上了该书的一些总结或技巧,只是不自知罢了,笔者在阅读这本书时产生了许多共鸣。以下跟随这本经典图灵奖丛书——《高效程序员的45个习惯-敏捷开发修炼之道》,为敏捷之道提供指引。 6 | 7 | ## 态度决定一切 8 | 9 | * **做事** 10 | * 把矛头对准解决问题的办法,而不是人 11 | * 敏捷团队重成果胜于重过程 12 | * **欲速则不达** 13 | * 不要坠入快速的简单修复中 14 | * 深层次的思考是区别优秀程序员和拙劣代码工人的区别 15 | * 投入时间和精力保持代码整洁、敞亮 16 | * **对事不对人** 17 | * 开会技巧 18 | 1. 设定最终期限。防止陷入无休止的争辩中。没有最好的方案,只有更合适的方案。设定期限能在为难时做出决断。 19 | 2. 逆向思维。每个人都需要意识到权衡的必要性。尽可能找到优点最多缺点最少的方案,少带个人情感。 20 | 3. 设立仲裁人。确保会议正常进行,打算大篇会议之外的讨论以及假大空式发言。 21 | 4. 支持已经做出的决定。 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 | 再好的敏捷项目,都会发生bug、错误等。这时候需要进行敏捷调试。调试时面对的真正问题,是无法用固定的时间来限制。有些项目可能调试一天也没有找到问题。对于一个项目来说,这种没有准确把握时间的消耗是不可接受的。 56 | 57 | * **记录解决问题的日志**。将曾经遇到的问题,记录在wiki上进行维护,大家一起维护这份日志,或许从里面会有一些解题思路。注意记录时的关键字。原则上记录问题的时间不能超过解决问题的时间。 58 | * **警告就是错误**。尽量减少错误信息 59 | * **对问题各个击破**。尽量减少耦合,得到单元测试的模块。 60 | * **报告所有的异常**。不要压制异常,及时抛出。 61 | * **提供有用的错误信息**。 62 | 63 | ## 敏捷协作 64 | 团队之间的协作,保持高效率。 65 | 66 | * **定期安排会面时间** 67 | * 立会可以让团队达成共识。 68 | * 保证会议短小精悍不跑题。 69 | * 时间尽量在早上,也不要在刚上班。 70 | * **架构师必须写代码** 71 | * 新系统的设计者必须亲自投入到实现中 72 | * **实行代码集体所有制** 73 | * **成为指导者** 74 | * 激励别人,让他们更出色,同时提升团队整体实力 75 | * 解释自己知道的,可以让自己理解更加深入 76 | * 别人提出问题,可以发现不同视角 77 | * 帮助团队成员提升水平同时提高自己 78 | * 不必局限自己团队,也可以是个人blog或一小段代码 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 | -------------------------------------------------------------------------------- /docs/js/canvas-base.md: -------------------------------------------------------------------------------- 1 | # Canvas基础 2 | 3 | canvas 看起来和 img 元素很相像,唯一的不同就是它并没有 src 和 alt 属性。实际上,canvas 标签只有两个属性—— width和height。 4 | 5 | canvas 元素创造了一个固定大小的画布,canvas起初是空白的。为了展示,首先脚本需要找到渲染上下文,然后在它的上面绘制。你可以通过使用它的getContext() 方法来访问绘画上下文。 6 | 7 | ``` js 8 | var canvas = document.getElementById('tutorial'); 9 | var ctx = canvas.getContext('2d'); // 画图基础 10 | ``` 11 | 12 | ## 概念 13 | ### xy 14 | 栅格的起点为左上角(坐标为(0,0))。所有元素的位置都相对于原点定位。 15 | ![](https://mdn.mozillademos.org/files/224/Canvas_default_grid.png) 16 | 17 | ## 绘制形状 18 | 19 | ### 1. 绘制矩形 20 | 21 | canvas只支持一种原生的图形绘制(马上显现在canvas上,即时生效):矩形。所有其他的图形的绘制都至少需要生成一条路径。canvas提供了三种方法绘制矩形: 22 | 23 | * `fillRect(x, y, width, height)` 24 | 绘制一个填充的矩形 25 | * `strokeRect(x, y, width, height)` 26 | 绘制一个矩形的边框 27 | * `clearRect(x, y, width, height)` 28 | 清除指定矩形区域,让清除部分完全透明。 29 | 30 | > x与y指定了在canvas画布上所绘制的矩形的左上角(相对于原点)的坐标。width和height设置矩形的尺寸。 31 | 32 | ### 2. 绘制路径 33 | 34 | 路径绘制图形步骤: 35 | * 首先,你需要创建路径起始点。 36 | * 然后你使用画图命令去画出路径。 37 | * 之后你把路径封闭。 38 | * 一旦路径生成,你就能通过描边或填充路径区域来渲染图形。 39 | 40 | 函数 41 | * `beginPath()`,路径的第一步。 42 | 新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。 43 | * `closePath()`,不是必需的。 44 | 闭合路径。 45 | * `stroke()`,没有闭合的形状不会自动闭合。 46 | 通过线条来绘制图形轮廓。 47 | * `fill()`,没有闭合的形状都会自动闭合。 48 | 通过填充路径的内容区域生成实心的图形。 49 | 50 | ### 总结 51 | 52 | 1. 画矩形 53 | * fillRect 54 | 2. 画路径 55 | * 基础 56 | * beginPath()。常和moveTo(将笔触移动到指定的坐标x以及y上)搭配 57 | * closePath() 和开始路径形成闭合路径。 58 | * fill() 填充 59 | * stroke() 画线 60 | * 画图形 61 | * arc(x, y, radius, startAngle, endAngle, anticlockwise) 62 | * lineTo(x, y) 绘制直线 63 | 64 | > 画路径,一定是遇到stroke/fill方法,才开始绘制。 65 | 66 | > 生成闭合路径(如三角形边框),需要closePath + stroke()组合。当然,如果你本身是不需要闭合的图形,则直接使用stroke即可。 67 | 68 | ``` js 69 | var ctx = canvas.getContext('2d'); 70 | 71 | ctx.beginPath(); 72 | ctx.moveTo(75, 50); 73 | ctx.lineTo(100, 75); 74 | ctx.lineTo(100, 25); 75 | ctx.fill(); 76 | ``` 77 | 78 | ## 样式和颜色 79 | 80 | * 颜色 81 | * `fillStyle` 82 | * `strokeStyle` 83 | * 样式 84 | * `globalAlpha` 85 | * `lineWidth` 86 | * `globalCompositeOperation` 遮盖策略 87 | 88 | ``` js 89 | // 这些 fillStyle 的值均为 '橙色' 90 | ctx.fillStyle = "orange"; 91 | ctx.fillStyle = "#FFA500"; 92 | ctx.fillStyle = "rgb(255,165,0)"; 93 | ctx.fillStyle = "rgba(255,165,0,1)"; 94 | 95 | // 设置透明度值 96 | ctx.globalAlpha = 0.2; 97 | 98 | // 当前绘线的粗细 99 | ctx.lineWidth = 1; 100 | 101 | // 设置遮盖策略:交集清除 102 | ctx.globalCompositeOperation = 'destination-out' 103 | ``` 104 | 105 | ## 绘制元素 106 | 107 | 1. 图片 108 | * `drawImage(imageOrCanvas, x, y)`。image 是 image 或者 canvas 对象,x 和 y 是其在目标 canvas 里的起始坐标。 109 | 1. 文本 110 | * `fillText(text, x, y [, maxWidth])` 111 | 在指定的(x,y)位置填充指定的文本,绘制的最大宽度是可选的. 112 | * `strokeText(text, x, y [, maxWidth])` 113 | 在指定的(x,y)位置绘制文本边框,绘制的最大宽度是可选的. 114 | 115 | ``` js 116 | var img = new Image(); 117 | img.src = 'images/backdrop.png'; 118 | img.onload = function(){ 119 | ctx.drawImage(img,0,0); 120 | } 121 | 122 | ctx.font = "48px serif"; 123 | ctx.fillText("Hello world", 10, 50); 124 | ``` -------------------------------------------------------------------------------- /docs/temp/mysql-mongoose.md: -------------------------------------------------------------------------------- 1 | # koa + mysql 2 | 3 | 更多:https://github.com/lq782655835/koa-mysql-best-experiment/blob/master/koa-sql/README.md 4 | 5 | * https://github.com/LFB/nodejs-koa-blog 6 | * api -> service -> dao -> model 7 | * jwt:(登录成功:jwt.sign,生成token)(每次带入token校验:obj = jwt.verify(token, secretKey)) 8 | * validator校验基础帮助库 9 | * https://github.com/wclimb/Koa2-blog/ 10 | * 使用createPool直接传入sql语法执行 11 | * 权限设计session。 教程:https://www.jianshu.com/p/f3df4ffe3301 12 | * https://github.com/Molunerfinn/vue-koa-demo/blob/master/app.js 13 | * jwt 14 | * https://github.com/unix/koa-ts 15 | * ts路由 16 | * api -> controller -> service -> model 17 | 18 | 19 | ## sequelize 20 | ORM框架,适配多个数据库。 21 | 22 | * 查询sql:https://demopark.github.io/sequelize-docs-Zh-CN/core-concepts/model-querying-basics.html 23 | * 外键:https://sequelize.org/v5/manual/associations.html 24 | * 支持直接原生sql查询:https://sequelize.org/v5/manual/raw-queries.html 25 | 26 | 常用 27 | * findAll({ where: {xxx}}) 28 | * findByPk(id) 29 | * findOne({ where: {title: 'aProject'} }) 30 | * create(model) // 或者修改model后,save() 31 | * destroy({ where: {xxx} }) 32 | * update(model, where: {xxx}) 33 | 34 | 数据类型:https://sequelize.org/v5/manual/data-types.html 35 | 36 | ## mysql 客户端语法 37 | 38 | 1.创建数据库语法: 39 | create databases 数据库名称 40 | 41 | 2.选择数据库语法 42 | use 数据库名称 43 | 44 | 3.显示数据库中所有表语法 45 | show tables; 46 | 47 | 4.删除数据库语法 48 | drop database 数据库名称 49 | 50 | 51 | 5.创建表语法 52 | create table 表名( 53 | 列名1:列的类型 54 | 列名2:列的类型 55 | 列名3:列的类型 56 | 57 | ) 58 | 59 | 6.显示表结构(desc语法) 60 | 代码测试: 61 | 62 | mysql> desc emp 63 | -> \g 64 | 65 | 66 | 7.显示创建表的语法 67 | show create table emp; 68 | 69 | 70 | > mysql:需要在命令行中 ,需要带上‘;’作为结尾。 71 | 72 | # mongoosejs 73 | 74 | 官网:https://mongoosejs.com/ 75 | 76 | express + mongoose案例:https://github.com/lq782655835/node-elm 77 | 78 | ``` js 79 | // 连接db 80 | import mongoose from 'mongoose'; 81 | import config from 'config-lite'; 82 | mongoose.connect(config.url, {useMongoClient:true}); 83 | 84 | const db = mongoose.connection; 85 | export default db 86 | 87 | // 创建Model 88 | import mongoose from 'mongoose' 89 | 90 | const Schema = mongoose.Schema; 91 | 92 | const adminSchema = new Schema({ 93 | user_name: String, 94 | password: String, 95 | id: Number, 96 | create_time: String, 97 | admin: {type: String, default: '管理员'}, 98 | status: Number, //1:普通管理、 2:超级管理员 99 | avatar: {type: String, default: 'default.jpg'}, 100 | city: String, 101 | }) 102 | 103 | adminSchema.index({id: 1}); 104 | 105 | const Admin = mongoose.model('Admin', adminSchema); 106 | 107 | 108 | export default Admin 109 | 110 | // 使用model 111 | import AdminModel from '../../models/admin/admin' 112 | 113 | const admin = await AdminModel.findOne({user_name}) 114 | 115 | await AdminModel.create({ 116 | user_name, 117 | password: newpassword, 118 | id: admin_id, 119 | create_time: dtime().format('YYYY-MM-DD HH:mm'), 120 | admin: adminTip, 121 | status, 122 | city: cityInfo.city 123 | }) 124 | ``` -------------------------------------------------------------------------------- /docs/vue/vue-code-6.nextTick.md: -------------------------------------------------------------------------------- 1 | # Vue2.x源码分析 - Vue.nextTick 2 | 3 | 先看下该API [Vue官方解释](https://cn.vuejs.org/v2/guide/reactivity.html#%E5%BC%82%E6%AD%A5%E6%9B%B4%E6%96%B0%E9%98%9F%E5%88%97)。 4 | 5 | 主要是利用js Event Loop原理,在执行Vue.nextTick(cb)方法时,把cb推入回调集合中,同时最关键的一步:执行macroTask/microTask(如setTimeout(callbacks, 0))。浏览器底层会在下一个tick(浏览器自己的行为。此时DOM节点操作完成),执行callbacks里的函数。这样这些函数就能拿到已经更新DOM后的节点了。 6 | 再来看下Vue源码是如何实现的,源码在`src/core/util/next-tick.js`文件中,详细解释在代码注释中: 7 | 8 | ``` js 9 | import { noop } from 'shared/util' 10 | import { handleError } from './error' 11 | import { isIOS, isNative } from './env' 12 | 13 | const callbacks = [] 14 | let pending = false 15 | 16 | // 在下一次tick执行时,把缓存的函数集合都执行 17 | function flushCallbacks () { 18 | pending = false 19 | const copies = callbacks.slice(0) 20 | callbacks.length = 0 // 清空callback集合,方便下次tick 21 | for (let i = 0; i < copies.length; i++) { 22 | copies[i]() 23 | } 24 | } 25 | 26 | // micro和macro原理是JS Event Loop 27 | // 这两个函数分别为了存储macro task策略以及micro task 策略 28 | let microTimerFunc 29 | let macroTimerFunc 30 | let useMacroTask = false 31 | 32 | // 以下代码很多,其实最终都是赋值macroTimerFunc 33 | // 优先级:setImmediate->MessageChannel->setTimeout 34 | // macroTimerFunc执行,最终是执行flushCallbacks函数 35 | if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { 36 | macroTimerFunc = () => { 37 | setImmediate(flushCallbacks) 38 | } 39 | } else if (typeof MessageChannel !== 'undefined' && ( 40 | isNative(MessageChannel) || 41 | // PhantomJS 42 | MessageChannel.toString() === '[object MessageChannelConstructor]' 43 | )) { 44 | const channel = new MessageChannel() 45 | const port = channel.port2 46 | channel.port1.onmessage = flushCallbacks 47 | macroTimerFunc = () => { 48 | port.postMessage(1) 49 | } 50 | } else { 51 | /* istanbul ignore next */ 52 | macroTimerFunc = () => { 53 | setTimeout(flushCallbacks, 0) 54 | } 55 | } 56 | 57 | // 设置microTimerFunc函数 58 | if (typeof Promise !== 'undefined' && isNative(Promise)) { 59 | const p = Promise.resolve() 60 | microTimerFunc = () => { 61 | p.then(flushCallbacks) 62 | if (isIOS) setTimeout(noop) 63 | } 64 | } else { 65 | microTimerFunc = macroTimerFunc 66 | } 67 | 68 | // Vue.nextTick or vm.$nextTick API 69 | export function nextTick (cb?: Function, ctx?: Object) { 70 | let _resolve 71 | 72 | // 把cb推入callback集合中 73 | // 执行macroTimerFunc(如:setTimeout)后,在下一个tick中才去执行callback集合里的函数 74 | callbacks.push(() => { 75 | if (cb) { 76 | try { 77 | cb.call(ctx) 78 | } catch (e) { 79 | handleError(e, ctx, 'nextTick') 80 | } 81 | } else if (_resolve) { 82 | _resolve(ctx) 83 | } 84 | }) 85 | if (!pending) { 86 | pending = true 87 | if (useMacroTask) { 88 | macroTimerFunc() 89 | } else { 90 | microTimerFunc() 91 | } 92 | } 93 | 94 | // 支持this.$nextTick().then(...) 95 | if (!cb && typeof Promise !== 'undefined') { 96 | return new Promise(resolve => { 97 | _resolve = resolve 98 | }) 99 | } 100 | } 101 | 102 | ``` -------------------------------------------------------------------------------- /docs/temp/vue3-vite-2.md: -------------------------------------------------------------------------------- 1 | # vite 2 | 3 | 1. vite工具主要还是以koa为基础,所有请求通过koa拦截。(vite提供cli,里面有一系列中间件处理) 4 | 1. 把node_modules下的包(如:vue),改写为 ‘@module/vue’,然后使用koa拦截 5 | 1. 把.vue文件拆分为3个请求,跟vue-loader实现一致 6 | 7 | ## Vue3 API 语法说明 8 | 9 | ### watch和watchEffect区别 10 | 11 | 1. 2.x/3的watch,只能监听特定响应式对象,当对象值变化时,进行逻辑函数绑定。而watchEffect默认对响应式对象依赖收集(意味着不是懒加载)。`简单理解:可以把watchEffect当作是特殊的computed,作用一致而且不用return返回对象`(实际源码中的确两者底层都是effect实现)。 12 | 2. watch可以懒加载(值不变动不执行逻辑函数),而watchEffect初始化加载(因为这里就开始依赖收集了) 13 | 3. watch可以拿到preValue/currentValue,watchEffect只拿到currentValue 14 | 15 | ### ref和reactive实践 16 | 17 | ``` js 18 | // ref 19 | import { ref, onMounted, watchEffect, computed } from 'vue' 20 | export default { 21 | // 1. 组件props依然需要定义 22 | props: { 23 | msg: String 24 | }, 25 | setup(props) { 26 | let count = ref(0) 27 | // 2. 只要computed/watchEffect 里面有ref/reactive,即可被getter依赖收集 28 | let double = computed(() => { 29 | if (count.value > 3) { 30 | return 2 * count.value 31 | } 32 | return count.value 33 | }) 34 | 35 | // 3. computed/watchEffect中可以做任何函数,而不一定只监听固定值 36 | watchEffect(() => { 37 | console.log(count.value) 38 | }) 39 | 40 | // 4. 可以多次onMounted,其实就是this. 41 | onMounted(() => { 42 | console.log(props) 43 | }) 44 | onMounted(() => { 45 | console.log('another') 46 | }) 47 | return { 48 | count, 49 | double, 50 | } 51 | } 52 | } 53 | ``` 54 | 55 | 以上切换为reactive(传统vue2.x)写法时,return必须返回state,而不是解构的{...state},因为解构后就ui就无法再响应式。 56 | 57 | ``` js 58 | // html模板中跟vue2.x的state一致,需要带上 59 | // 60 | import { ref, onMounted, watchEffect, computed, reactive } from 'vue' 61 | export default { 62 | props: { 63 | msg: String 64 | }, 65 | setup(props) { 66 | let state = reactive({ 67 | count: 0, 68 | double: computed(() => { 69 | if (state.count > 3) { 70 | return 2 * state.count 71 | } 72 | return state.count 73 | }) 74 | }) 75 | 76 | watchEffect(() => { 77 | console.log(state.count) 78 | }) 79 | 80 | return { 81 | state 82 | // ...state // 这种方式无法响应式 83 | } 84 | } 85 | } 86 | ``` 87 | 88 | 那如果就想解构导出,同时具有响应式改如何呢?答案是新的api工具: `toRefs(state)` 89 | 90 | ``` js 91 | // 此时html模板代码中,可以省略一层state.xxx 92 | // 93 | import { toRefs } from 'vue' 94 | setup() { 95 | // ... 96 | return { 97 | ...toRefs(state) 98 | } 99 | } 100 | ``` 101 | 102 | ### 生命周期钩子函数 103 | 104 | 只是把以前的`this.$once('hook:mounted', cb)`语法,改为 `onXXX` hooks函数而已 105 | 106 | 与 2.x 版本生命周期相对应的组合式 API 107 | * ~~beforeCreate~~ -> 使用 setup() 108 | * ~~created~~ -> 使用 setup() 109 | * beforeMount -> onBeforeMount 110 | * mounted -> onMounted 111 | * beforeUpdate -> onBeforeUpdate 112 | * updated -> onUpdated 113 | * beforeDestroy -> onBeforeUnmount 114 | * destroyed -> onUnmounted 115 | * errorCaptured -> onErrorCaptured 116 | -------------------------------------------------------------------------------- /docs/react/react-native-2.debug-skill.md: -------------------------------------------------------------------------------- 1 | # ReactNative Mac调试技巧 2 | 3 | 工欲善其事,必先利其器。上篇文章快读搭建并运行一个App应用,但离工程化开发还是远不够的。调试程序是开发过程中必不可少的,高效的调试技能可以提高开发效率,也可以及时发现bug。 4 | 5 | 启动RN应用时,同时会启一个package server的node应用(可以理解为webpack功能),每次修改js代码,不需要重新run xcode,该server服务会把最新的js代码编译并提供给Native代码调用。 6 | 7 | ## 调试技巧清单 8 | 9 | - React Native调试菜单 10 | 11 | - chrome developer tool 12 | 13 | - react-devtools 14 | 15 | - Charles抓包工具 16 | 17 | - 真机调试 18 | 19 | ## React Native调试菜单 20 | 21 | `Command⌘ + D`即可调出调试菜单,里面有我们经常使用的调试功能。 22 | 23 | ![Developer Menu](https://raw.githubusercontent.com/crazycodeboy/RNStudyNotes/master/React%20Native调试技巧与心得/images/Developer%20Menu.jpg) 24 | 25 | >提示:如果`Command⌘ + D`无法打开,是因为模拟器与键盘断开连接了,可以在Xcode中Hardware menu->Keyboard->"Connect Hardware Keyboard" 26 | 27 | ### Reload 28 | 29 | 重新编译App,相当于Web的F5。快捷键`Command⌘ + R` 30 | 31 | ### Hot Reloading 32 | 33 | 热加载,做过Webpack工程化项目的人再熟悉不过。如果每次修改代码后需要手动刷新,效率值大大降低,热加载可以在保存代码后,自动进行增量包编译,实现模拟器的自动刷新。 34 | 35 | ### Enable Live Reload 36 | 37 | ReactNative还给开发者提供了自动刷新的功能,也可以对代码自动刷新。这里和Hot Reloading的区别是Live Reload是全量刷新,每次保存代码都会自动生成bundle包并发送到手机上,使得应用初始化。 38 | 39 | ### Show Inspector 40 | 方便的查看到当前选中元素的位置、样式、层级关系、盒子模型信息等等,类似Chrome Elements Tab 41 | 42 | ## Chrome Developer Tools 43 | 44 | 强大的Chrome可以像调试js那样来调试React Native应用。方式类似webpack的sourcemap,可以对源代码进行断点调试 45 | 46 | #### 第一步:启动远程调试 47 | 48 | 在Developer Menu下单击"Debug JS Remotely" 启动JS远程调试功能。Chrome自动打开Tab页:“http://localhost:8081/debugger-ui” 49 | 50 | #### 第二步:打开开发者工具 51 | 快捷键:`Command⌘ + Option⌥ + I` 52 | 53 | #### 第三部:断点调试 54 | 选中需要调试的代码行。 55 | 56 | ![](./assets/debugger-chrome.jpeg) 57 | 58 | >tips:在控制台(Console)上打印变量,执行脚本等操作。在开发调试中非常有用 59 | 60 | ## react-devtools 61 | 62 | rn调试菜单'Show Inspector'虽然可以让开发者可以看到层级和相关样式,但展现在app中过小,也无法像Chrome elements一样,实时修改。这里推荐使用react-devtools工具,让你轻松完成这一切。配合debugger或show inspector更强大 63 | 64 | 安装 65 | 66 | ``` shell 67 | npm install -g react-devtools 68 | ``` 69 | 70 | 使用 71 | ``` shell 72 | react-devtools 73 | ``` 74 | 75 | ![](./assets/react-devtools.jpeg) 76 | 77 | > tips:该工具同样支持ReactJS哦 78 | 79 | ## Charles抓包工具 80 | 81 | reactnative的网络请求,在chrome中无法捕捉,所以需要使用抓包工具查看网络请求详细信息。Mac推荐[Charles](https://www.charlesproxy.com/)工具 82 | 83 | ## 真机调试 84 | 85 | ### IPhone 86 | 87 | 目前网上大部分真机调试的文章都是基于以前的版本,在笔者最新的版本`v0.52`已经不再需要代码替换IP地址来得到打包的JSBundle。IOS本机测试需要Apple ID账号,如果需要发布到AppStore,则需要Devloper ID。经过测试,流程如下: 88 | 89 | 1. USB连接真机,xcode中设备选择真机 90 | 91 | 2. 单击项目名,Target的ProjectName和ProjectNameTest中**Signing**选项,Team中选择你已经绑定到Apple ID账号。如果没有选择列表,该地方会让你登陆一个账号。 92 | 93 | 3. 单击运行即可在真机中查看App运行。晃动手机也可以调出调试菜单 94 | 95 | ![](./assets/run-iphone.png) 96 | 97 | ### Android 98 | 99 | 1. 根目录下执行`npm start`,确保package server启动,[localhost:8081](http://localhost:8081)可以访问 100 | 101 | 2. 使用adb reverse命令。`adb reverse tcp:8081 tcp:8081` 102 | 103 | 3. 晃动设备,打开开发者菜单,点击`Dev Settings` -> `Debug server host for device` -> 输入本机IP和8081端口 104 | 105 | 4. 完成后晃动设备,选择`Reload JS`即可 106 | 107 | ### 相关链接 108 | 109 | [React中文网](https://reactnative.cn/docs/0.51/debugging.html) 110 | 111 | [react-devtools](https://github.com/facebook/react-devtools) 112 | -------------------------------------------------------------------------------- /docs/js/es6-1.new-feature.md: -------------------------------------------------------------------------------- 1 | # ES6-新增特性一览 2 | 3 | [ecma-263](https://www.ecma-international.org/ecma-262/6.0/)是ES6规范的官网文档,该文档是英文版,而且里面洋洋洒洒写了每个特性的实现步骤,容易看晕。这里推荐@阮一峰老师的[ECMAScript 6 入门](http://es6.ruanyifeng.com)和[es6features](https://github.com/lukehoban/es6features)项目,以下每个特性详细案例描述也是外链该项目内容。 4 | 5 | 以下默认陈述的是ES6标准,部分标注ES7、ES8标准是为了表明其最终发布时间(严谨)。其实大部分在2015年6月(ES6发布时间)都进入了草案阶段(Stage 2),故在babel等转译工具下,都可以使用这些特性在前端工程项目中。 6 | 7 | ## 1. [let/const取代var](https://github.com/lukehoban/es6features#let--const) 8 | 9 | ## 2. [字符串模板](https://github.com/lukehoban/es6features#template-strings) 10 | 11 | ## 3. [对象解构](https://github.com/lukehoban/es6features#destructuring) 12 | * Destructuring 13 | * enhanced object literals({foo} === {foo:foo}) 14 | 15 | ## 4. 新数据类型[Symbol](https://github.com/lukehoban/es6features#symbols) 16 | 17 | ## 5. 新数据结构[Map/Set/WeakMap/WeakSet](https://github.com/lukehoban/es6features#map--set--weakmap--weakset) 18 | 19 | ## 6. [Proxy](https://github.com/lukehoban/es6features#proxies)、Reflect 20 | 21 | ## 7. 扩展 22 | 23 | * Array 24 | * Array.from() 25 | * Array.of() 26 | * Array.copyWithin() 27 | * Array.find() 28 | * Array.findIndex() 29 | * Array.fill() 30 | * Array.includes()`ES7` 31 | * Object 32 | * Object.keys() 33 | * Object.values()`ES8` 34 | * Object.entries()`ES8` 35 | * Object.assign() 36 | * Object. is() 37 | * [Function]() 38 | * default 39 | * [arraw function](https://github.com/lukehoban/es6features#arrows) 40 | * ...rest运算符 41 | * Number 42 | * Number.isNuN() 43 | * Number.isFinite() 44 | * Number.parseInt() 45 | * Number.parseFloat() 46 | * Number.isInteger() 47 | * Number.isSafeInteger() 48 | * Math 49 | * Math.max(x, y) 50 | * Math.trunc(x) 51 | * Math.sign(x) 52 | * Math.acosh(x) 53 | * Math.asinh(x) 54 | * Math.atanh(x); 55 | * Math.cbrt(x) 56 | * Math.clz32(x) 57 | * Math.cosh(x) 58 | * Math.expm1(x) 59 | * Math.fround(x) 60 | * Math.hypot(...values) 61 | * Math.imul(x, y) 62 | * Math.log1p(x) 63 | * Math.log10(x) 64 | * Math.log2(x) 65 | * Math.tanh(x) 66 | 67 | ## 8. 异步 68 | * [Promise](https://github.com/lukehoban/es6features#promises) 69 | * Promise.prototype.then 70 | * Promise.prototype.catch 71 | * Promise.prototype.finally`ES9` 72 | * Promise.all() 73 | * Promise.rece() 74 | * [Iterator](https://github.com/lukehoban/es6features#iterators--forof) 75 | * Iterator接口 76 | * for of 77 | * [Generator](https://github.com/lukehoban/es6features#generators) 78 | * yield* 79 | * async/await`ES8` 80 | 81 | ## 9. [Class类](https://github.com/lukehoban/es6features#classes) 82 | * class 83 | * extends 84 | * decorator`ES7` 85 | 86 | ## 10. [Module](https://github.com/lukehoban/es6features#modules) 87 | * import 88 | * export 89 | 90 | ## 参考文档 91 | 92 | * [es6features](https://github.com/lukehoban/es6features) 93 | 94 | * [Finished Proposals](https://github.com/tc39/proposals/blob/master/finished-proposals.md) 95 | 96 | * [ryf es6 reference](http://es6.ruanyifeng.com/#docs/reference) 97 | -------------------------------------------------------------------------------- /docs/team-standard/recommend-code200.md: -------------------------------------------------------------------------------- 1 | # 推荐-200错误统一处理 2 | 3 | 对于接口层来说,后端经常定义的结构如下: 4 | 5 | ``` js 6 | { 7 | code: [Number], // 状态码 8 | desc: [String], // 详细描述 9 | detail: [Array, Object] // 前端需要的数据 10 | } 11 | ``` 12 | 13 | ### bad 14 | 15 | ``` js 16 | batchAddVariable({ globalParamList: validList }) 17 | .then(res => { 18 | if (res === SERVER_ERROR_CODE.SUCCESS) { // !!!Bad: how many interface, how many judge 200 19 | this.close(true) 20 | this.$toast.show(res.detail.colletion) // !!!Bad: always get detail data 21 | } else { // !!!Bad: too much nest,reading difficulty 22 | this.$toast.show(res.desc) 23 | if (res === SERVER_ERROR_CODE.REPEAT) { 24 | ... 25 | } 26 | } 27 | }) 28 | ``` 29 | 30 | ### good 31 | 32 | ``` js 33 | batchAddVariable({ globalParamList: validList }) 34 | .then(data => { 35 | this.close(true) 36 | this.$toast.show(data.colletion) 37 | }) 38 | .catch(res => { 39 | if (res === SERVER_ERROR_CODE.REPEAT) { 40 | ... 41 | } 42 | }) 43 | ``` 44 | 45 | ### 解决方案 46 | 47 | http层axios拿到数据后进行统一处理 48 | 49 | ``` js 50 | import Vue from 'vue' 51 | import axios from 'axios' 52 | 53 | const service = axios.create({ 54 | baseURL: rootURL, // api的base_url 55 | timeout: 15000, // 请求超时时间 56 | }) 57 | 58 | // request拦截器 59 | service.interceptors.request.use( 60 | config => { 61 | if (config.method === 'post' || config.method === 'put' || config.method === 'delete') { 62 | config.headers['Content-Type'] = 'application/json' 63 | if (config.type === 'form') { 64 | config.headers['Content-Type'] = 'multipart/form-data' 65 | } else { 66 | // 序列化 67 | config.data = JSON.stringify(config.data) 68 | } 69 | } 70 | 71 | return config 72 | }, 73 | error => { 74 | Promise.reject(error) 75 | } 76 | ) 77 | 78 | // respone拦截器 79 | service.interceptors.response.use( 80 | response => { 81 | const res = response.data 82 | if (res.code === SERVER_ERROR_CODE.SUCCESS) { // 统一处理 83 | return res.detail // 直接返回数据 84 | } else { 85 | Vue.prototype.$toast.show(res.desc) // 错误统一报出 86 | return Promise.reject(res) 87 | } 88 | }, 89 | error => { 90 | return Promise.reject(error) 91 | } 92 | ) 93 | 94 | export default service 95 | ``` 96 | 97 | 到此基本就可以很优雅的写逻辑代码了。不过还有个点可以继续优化。通常情况,后台返回非200错误,只需要$toast提示结果就行,catch代码部分可以省略。类似如下: 98 | 99 | ``` js 100 | batchAddVariable({ globalParamList: validList }) 101 | .then(data => this.close(true)) 102 | // .catch(() => {}) // 业务通常这里不需要写 103 | ``` 104 | 105 | 多么简洁的代码,但Promise执行reject代码,浏览器会报`Uncaught (in promise)`错误。这是因为中断了Promise操作但又没有对其进行处理,故由此错误。只需要使用`unhandledrejection`全局处理即可。 106 | 107 | ``` js 108 | // Promise Catch不报错 109 | window.addEventListener('unhandledrejection', event => event.preventDefault()) 110 | ``` 111 | -------------------------------------------------------------------------------- /docs/temp/nei源码阅读.md: -------------------------------------------------------------------------------- 1 | # NEI源码阅读 2 | 3 | ### 源码简析 4 | * bin/nei.js nei工具项目入口,主要提供server、build等命令 5 | * main.js 工具命令对应的函数入口 6 | * lib/fb-modules/util/mock_data_work.js。node vm安全沙箱的包装器。 7 | * lib/fb-modules/util/mock_data.js 定义一些将运行在vm沙箱中的函数。目的是根据nei配置获得对应的mock数据。也就是说,这里是nei配置到最终数据的关键代码。 8 | 9 | ![image](https://user-images.githubusercontent.com/6310131/51990799-c2bfbd80-24e4-11e9-974e-9c056dc72b67.png) 10 | 11 | ### 相关Doc 12 | 13 | * [传给模板的数据说明](https://github.com/NEYouFan/nei-toolkit/blob/master/doc/%E4%BC%A0%E7%BB%99%E6%A8%A1%E6%9D%BF%E7%9A%84%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E.md) 14 | 15 | 16 | 17 | # [AtomValidator](https://github.com/vusion/atom-validator/blob/master/src/AtomValidator.ts) 源码阅读 18 | 19 | ``` js 20 | // 内置规则解析 && 校验,支持string rules/Array rules 21 | const atomValidator = new AtomValidator(); 22 | atomValidator.validate(value, 'required | max(200)') 23 | .then(() => {}).catch((error: string) => {}); 24 | ``` 25 | 26 | ### 1. 正则解析string(仿vue filter): 27 | 28 | https://github.com/vusion/atom-validator/blob/master/src/parseRules.ts 29 | 30 | ### 2. string方法转为真实的方法: 31 | 32 | 过程:split分别拿到方法名key和参数,根据key找到方法,根据Function对参数统一处理拿到真实参数。因为参数名可能是...list,没有上下文不清楚该表达式,所以需要Function中转下,同时可设置context确定在哪个上下文中。 33 | 34 | ``` html 35 | 36 | 37 | 38 | ``` 39 | 40 | 解析后:required | ^azAZ | ^azAZ09-$ | azAZ09$ | minLength(4)的完整格式: 41 | 42 | ``` html 43 | 50 | 51 | 52 | ``` 53 | 54 | ``` js 55 | // parseRules解析规则 56 | // 支持(1,2,3)&& (...list)语法 57 | const resolveArgs = (args: string) => Function(`with (this) { return ${args} }`).bind(this.context); 58 | 59 | const index = ruleName.indexOf('(') // range(1, 4) 60 | if (~index) { 61 | // 拿到内置rule校验方法名 62 | parsedRule.validate = ruleName.slice(0, index); 63 | // 拿到参数(string --> Array) 64 | const args = '[' + ruleName.slice(index + 1, ruleName.length - 1) + ']'; 65 | parsedRule.args = resolveArgs(args); 66 | } 67 | 68 | ``` 69 | 70 | ``` js 71 | // validate校验结果 72 | if (typeof rule.validate === 'string') { 73 | // 根据name,选择指定的内置校验方法 74 | const validator = this.validators[rule.validate]; 75 | validate = async (value: any, rule: Rule, options?: Object) => { 76 | let args: any = rule.args; // 参数 77 | //校验结果 78 | return validator(value, ...args); 79 | } 80 | } 81 | result = validate.call(this.context, value, rule, options); 82 | ``` 83 | 84 | this.$vnode.context: Vue组件上下文 -------------------------------------------------------------------------------- /docs/react/react-redux-actions.md: -------------------------------------------------------------------------------- 1 | # redux-actions 2 | 3 | Redux 是 JavaScript 状态容器,提供可预测化的状态管理。但是同时它也让状态管理变得很冗长,`大量的type、actionCreator、reducer让人不断在写重复的代码`。 4 | 5 | 安装包: 6 | ``` 7 | yarn add redux-actions 8 | ``` 9 | 10 | redux-actions不引入额外的中间件,只是引入了几个API,以方便创建actionCreator和reducer。 11 | 12 | ## createAction 13 | 14 | ``` js 15 | // action.js 16 | import { createAction } from "redux-actions" 17 | 18 | export const INCREMENT = 'INCREMENT' 19 | export const increment = createAction(INCREMENT) 20 | ``` 21 | 22 | createAction是用来创建动作创建器的函数: 23 | 24 | ``` js 25 | createAction( 26 | type, // 动作名 27 | payloadCreator = Identity, // 用来创建动作对象中的payload值,默认使用lodash的Identity 28 | ?metaCreator // 用来创建动作对象中元数据 29 | ) 30 | 31 | const increment = createAction( 32 | 'INCREMENT', 33 | mount => mount, 34 | () => ({ admin: true }) 35 | ); 36 | increment(20); 37 | // { 38 | // type: 'INCREMENT', 39 | // payload: 20, 40 | // meta: { admin: true }, 41 | // } 42 | ``` 43 | 44 | ## handleAction 45 | 46 | ``` js 47 | // reducer.js 48 | import {handleAction} from 'redux-actions' 49 | import {INCREMENT} from './action' 50 | 51 | const defaultState = { count: 1 } 52 | const reducer = handleAction( 53 | INCREMENT, 54 | (state, action) => ({ 55 | count: state.count + action.payload 56 | }), 57 | defaultState 58 | ) 59 | 60 | export default reducer 61 | ``` 62 | 63 | handleAction会返回一个reducer 64 | ``` js 65 | // type为动作类型, reducer为动作处理, defaultState为默认状态 66 | handleAction(type, reducer, defaultState) 67 | 68 | // 上面的createAction效果就等同下面 69 | const reducer = (state = defaultState, action) { 70 | switch(action.type) { 71 | case 'INCREMENT': 72 | return { 73 | count: state.count + action.payload 74 | } 75 | default: 76 | return state 77 | } 78 | } 79 | ``` 80 | 81 | ## 触发redux 82 | 83 | ``` js 84 | store.dispatch(increment(20)) // dispatch带上action的数据 85 | ``` 86 | 87 | ## createActions 88 | 89 | 这个函数用来创建多个动作的,具体用法就是createActions(actionMap) 90 | 91 | actionMap就是一个对象,key值为动作类型,value可以是payloadCreator函数、一个数组[payloadCreator, metaCreator]、嵌套的actionMap。 92 | 93 | ``` js 94 | cosnt {add, remove} = createActions({ 95 | ADD_TODO: todo => ({ todo }), // payload creator 96 | REMOVE_TODO: [ 97 | todo => ({ todo }), // payload creator 98 | (todo, warn) => ({ todo, warn }) // meta 99 | ] 100 | }) 101 | 102 | // {type: 'ADD_TODO', payload: {todo: 'redux-actions'}} 103 | add('redux-actions') 104 | 105 | // {type: 'ADD_TODO', payload: {todo: 'redux-actions'}, meta: {todo: 'redux-actions', warn: 'warn'}} 106 | remove('redux-actions', 'warn') 107 | ``` 108 | 109 | ## handleActions 110 | 111 | handleActions函数是用来处理多个动作的 112 | ``` js 113 | handleActions(reducerMap, defaultState) 114 | 115 | const reducer = handleActions( 116 | { 117 | INCREMENT: (state, action) => ({ 118 | counter: state.counter + action.payload 119 | }), 120 | DECREMENT: (state, action) => ({ 121 | counter: state.counter - action.payload 122 | }) 123 | }, 124 | { counter: 0 } 125 | ); 126 | ``` 127 | 128 | > 增加和减小的逻辑基本一致,所以可以使用combineActions来合并简写。 -------------------------------------------------------------------------------- /docs/temp/rollup.md: -------------------------------------------------------------------------------- 1 | # rollup 2 | 3 | Rollup 提供了多种打包方式,通过 format 属性可以设置你想要打包成的代码类型: 4 | 5 | * `amd` - 输出成AMD模块规则,RequireJS可以用 6 | * `cjs` - CommonJS规则,适合Node,Browserify,Webpack 等 7 | * `es` - 默认值,不改变代码 8 | * `iife` - 输出自执行函数,最适合导入html中的script标签,且代码更小 9 | * `umd` - 通用模式,amd, cjs, iife都能用 10 | 11 | ### Rollup 的好处 12 | * Tree Shaking: 自动移除未使用的代码, 输出更小的文件 13 | * Scope Hoisting: 所有模块构建在一个函数内, 执行效率更高 14 | * Config 文件支持通过 ESM 模块格式书写 15 | * 可以一次输出多种格式:IIFE, AMD, CJS, UMD, ESM 16 | * Development 与 production 版本: .js, .min.js 17 | * 文档精简 18 | 19 | ### 基础插件 20 | * rollup-plugin-alias: 提供 modules 名称的 alias 和 reslove 功能. 21 | * `rollup-plugin-babel`: 提供 Babel 能力, 需要安装和配置 Babel (这部分知识不在本文涉及) 22 | * rollup-plugin-eslint: 提供 ESLint 能力, 需要安装和配置 ESLint (这部分知识不在本文涉及) 23 | * `rollup-plugin-node-resolve`: 解析 node_modules 中的模块 24 | * `rollup-plugin-commonjs`: 转换 CJS -> ESM, 通常配合上面一个插件使用 25 | * rollup-plugin-replace: 类比 Webpack 的 DefinePlugin , 可在源码中通过 process.env.NODE_ENV 用于构建区分 * Development 与 Production 环境. 26 | * rollup-plugin-filesize: 显示 bundle 文件大小 27 | * `rollup-plugin-uglify`: 压缩 bundle 文件 28 | * rollup-plugin-serve: 类比 webpack-dev-server, 提供静态服务器能力 29 | 30 | ``` js 31 | // 典型库应用 32 | import resolve from 'rollup-plugin-node-resolve'; 33 | import commonjs from 'rollup-plugin-commonjs'; 34 | import { eslint } from 'rollup-plugin-eslint'; 35 | import babel from 'rollup-plugin-babel'; 36 | import replace from 'rollup-plugin-replace'; 37 | import { uglify } from 'rollup-plugin-uglify'; 38 | 39 | const packages = require('./package.json'); 40 | 41 | const ENV = process.env.NODE_ENV; 42 | 43 | const paths = { 44 | input: { 45 | root: ENV === 'example' 46 | ? 'example/index.js' 47 | : 'src/index.js', 48 | }, 49 | output: { 50 | root: ENV === 'example' 51 | ? 'example/dist/' 52 | : 'dist/', 53 | }, 54 | }; 55 | 56 | const fileNames = { 57 | development: `${packages.name}.js`, 58 | example: `example.js`, 59 | production: `${packages.name}.min.js` 60 | }; 61 | 62 | const fileName = fileNames[ENV]; 63 | 64 | export default { 65 | input: `${paths.input.root}`, 66 | output: { 67 | file: `${paths.output.root}${fileName}`, 68 | format: 'umd', 69 | name: 'bundle-name' 70 | }, 71 | plugins: [ 72 | resolve(), 73 | commonjs(), 74 | eslint({ 75 | include: ['src/**'], 76 | exclude: ['node_modules/**'] 77 | }), 78 | babel({ 79 | exclude: 'node_modules/**', 80 | runtimeHelpers: true, 81 | }), 82 | replace({ 83 | exclude: 'node_modules/**', 84 | ENV: JSON.stringify(process.env.NODE_ENV), 85 | }), 86 | (ENV === 'production' && uglify()), 87 | ], 88 | }; 89 | 90 | ``` 91 | 92 | 93 | ## Webpack 94 | 95 | Webpack也提供了多种打包方式,通过output.libraryTarget 96 | 97 | * 模块 98 | * `commonjs2` - 入口起点的返回值将分配给 module.exports 对象。这个名称也意味着模块用于 CommonJS 环境 99 | * `amd` - 将你的 library 暴露为 AMD 模块 100 | * umd - 将你的 library 暴露为所有的模块定义下都可运行的方式。它将在 CommonJS, AMD 环境下运行,或将模块导出到 global 下的变量 101 | 102 | ## 参考文章 103 | * [webpack output-librarytarget](https://webpack.docschina.org/configuration/output/#output-librarytarget) 104 | 105 | * [webpack Authoring Libraries](https://webpack.js.org/guides/author-libraries/) -------------------------------------------------------------------------------- /docs/js/http-cross-domain.md: -------------------------------------------------------------------------------- 1 | # HTTP跨域解决方案 2 | 3 | 浏览器同源策略导致不能加载执行其他网站的脚本,这是互联网安全的一部分。**跨域之后ajax请求可以发出,但浏览器在接收前会对其来源进行检查**,如果是不跨域,直接加载数据;如果跨域,会出现著名的`No 'Access-Control-Allow-Origin' header is present on the requested resource`。以下总结一些常用的HTTP跨域解决方案。 4 | 5 | > 常见的跨域都需要后端支持,跨域记得带上http头信息Access-Control-Allow-Origin 6 | 7 | >跨域拦截,并不一定是浏览器限制了发起跨站请求,也可能是跨站请求可以正常发起,但是返回结果被浏览器拦截了。 8 | 9 | ## JSONP 10 | 11 | 利用浏览器script标签不跨域特点,使用get请求到目标api,再通过目标api返回callbackName(data)方式执行js代码。特点: 12 | 1. 只能是get请求 13 | 2. 需要后端配合,因为需要知道前端callbackName命名 14 | 3. 支持老式浏览器 15 | 16 | ``` js 17 | var script = document.createElement('script'); 18 | script.type = 'text/javascript'; 19 | 20 | // 传参并指定回调执行函数为onBack 21 | script.src = 'http://www.domain2.com:8080/login?user=admin&callback=onBack'; 22 | document.head.appendChild(script); 23 | 24 | // 回调执行函数 25 | function onBack(res) { 26 | alert(JSON.stringify(res)); 27 | } 28 | 29 | // 服务端api返回 30 | onBack({"status": true, "user": "admin"}) 31 | ``` 32 | 33 | ## CORS 34 | 35 | 跨域资源共享(Cross-origin resource sharing) 是一种机制,它`使用额外的 HTTP 头`来告诉浏览器 让运行在一个 origin 上的Web应用被准许访问来自不同源服务器上的指定的资源。特点: 36 | 1. W3C标准 37 | 2. 需要浏览器和服务器同时支持,包括增加额外的Http头 38 | 39 | ### CORS简单请求 40 | 41 | * 请求方法:三种方法之一 42 | * HEAD 43 | * GET 44 | * POST 45 | * HTTP头信息:不超出以下几种字段 46 | * Accept 47 | * Accept-Language 48 | * Content-Language 49 | * Last-Event-ID 50 | * Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain 51 | 52 | 在头信息之中,增加一个`Origin字段`。说明本次请求来自哪个域(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求,响应返回`Access-Control-Allow-Origin字段`。 53 | 54 | * 正式请求/响应 55 | * 请求:`Origin`通用 56 | * 响应:`Access-Control-Allow-Origin`通用。如果是需要发送附带身份凭证(如cookies)的请求(前端代码需要设置:xhr.withCredentials=true) 57 | * `Access-Control-Allow-Credentials`:服务器响应需要设置该字段为true,浏览器才会把内容给请求的发送者。 58 | > 为安全性考虑,当响应返回Access-Control-Allow-Credentials:true时,Access-Control-Allow-Origin必须指定具体的Origin,而不是通配符“*” 59 | 60 | ![](https://mdn.mozillademos.org/files/14293/simple_req.png) 61 | 62 | ### CORS非简单请求 63 | 64 | * 请求方法 65 | * PUT 66 | * DELETE 67 | * HTTP头信息: 68 | * Content-Type:application/json 69 | 70 | 规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法,浏览器必须首先使用 `Method: OPTIONS`发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。 71 | 72 | * 预检Options请求/响应 73 | * 请求 74 | * `Origin`简单/非简单CORS通用:预检请求或实际请求的源站 75 | * Access-Control-Request-Headers:将实际请求所携带的首部字段告诉服务器。 76 | * Access-Control-Request-Method:将实际请求所使用的 HTTP 方法告诉服务器。 77 | * 响应 78 | * `Access-Control-Allow-Origin`简单/非简单CORS通用: 指定了允许访问该资源的外域 URI 79 | * Access-Control-Allow-Headers:指明了实际请求中允许携带的首部字段。 80 | * Access-Control-Allow-Methods:用于预检请求的响应,指明了实际请求所允许使用的 HTTP 方法。 81 | * Access-Control-Max-Age:指定了preflight请求的结果能够被缓存多久。在有效时间内,浏览器无须为同一请求再次发起预检请求。 82 | * 正式请求/响应 83 | * 请求:Origin/Access-Control-Request-Headers/Access-Control-Request-Method 84 | * 响应:Access-Control-Allow-Origin 85 | 86 | ![](https://mdn.mozillademos.org/files/14289/prelight.png) 87 | 88 | > 大多数浏览器不支持针对于预检请求的重定向。如果一个预检请求发生了重定向,浏览器将报告错误 89 | 90 | ## 其他 91 | 92 | * 服务端 93 | * nginx代理跨域 94 | * nodejs代理跨域 95 | * websocket 96 | * 浏览器 97 | * html5 postMessage 98 | * iframe 99 | 100 | ## 参考文章 101 | 102 | * MDN [Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) 103 | * [CORS官方规范](https://www.w3.org/TR/cors/) 104 | * [CORS通信](http://javascript.ruanyifeng.com/bom/cors.html) -------------------------------------------------------------------------------- /docs/temp/在线酒店经营.md: -------------------------------------------------------------------------------- 1 | # 在线酒店经营 2 | 3 | ## 1. 背景调研 4 | 5 | 根据网络公开点评大数据分析,**设施、位置、服务、价格、卫生和餐饮**,这六个是酒店用户最为关注的方面。 6 | 7 | ![](https://pic4.zhimg.com/80/4439d6cba53fdd5829e5bfa0f36df7c9_hd.jpg) 8 | 9 | 消费者目前更多地关注酒店的**硬件服务和可进入性**,对酒店设施和位置的点评分别占到了近五分之一;但同时对设施的好评率反而是六个要素中最低的,设施达不到消费者的期望会给他们留下深刻的印象,说明消费者对酒店设施要求越来越高,经营者提高其设施服务的人性化及贴合度是永恒不变的主题。 10 | 11 | 针对服务的评价内容也超过9%,消费者对**酒店服务**的关注紧随设施和位置之后,经营者在注重硬件设施的同时不可忽略服务对消费者体验质量的影响。 12 | 13 | 消费者最不能忍受的是入住酒店有噪音和异味,最基本的休息和卫生得不到保障,会对整体的入住体验有很大影响。 14 | 15 | ## 2. 互联网时代的酒店管理 16 | 17 | **口碑和收益的双赢才是互联网时代酒店的真正目标**。在酒店行业中,在线点评的维护管理是其成败的重要指标。 18 | 19 | 之所以强调**在线声誉管理**,其本质是通过了解住店客人的有效反馈,从而帮助酒店提升管理水平,再而为客人带来更好的入住体验。伴随着用户满意度和认可度的提高,必然引领酒店进入收益提高的良性循环。网评因其高度的普及率,拉近了酒店和客户之间的距离,缩短反馈时效性,为酒店的运营管理提供了宝贵的数据支持。比如布丁酒店,利用建立的微信公众号,可以实现下单和零秒退房,此外还可以与它进行互动。目前已有100万粉丝入驻布丁微信公众号,每天的订单量达数百单,几乎不需要推广费用。 20 | 21 | **服务业的最终走向是塑造自己的品牌**。如果有条件,酒店集团自身需要加强直销渠道的建设,利用互联网技术建立起自己的直销壁垒。比如华住集团的做法,华住通过去哪儿、携程、艺龙微博、飞猪等11个互联网渠道进行导流。但华住为了降低用户成本,增加与用户之间但关联度,再多数情况下使用自由平台渠道(少了中间商,用户对品牌认识更强了)。 22 | 23 | ## 3. 如何做好线上订单 24 | 25 | ### 目前实施 26 | 27 | 1. **较好的酒店硬件设施**。干净整洁的外立面、明亮宽敞的大堂必不可少。 28 | 1. **价格优势**。根据酒店硬件设施以及地理位置,提供一个极具性价比的价格,特别是初期做在线订单阶段。 29 | 1. **开通分销渠道**,增加订单量。通过与各种在线旅游、酒店、餐饮营销网站合作,实施跨界营销整合,打破时空局限。 30 | 1. **做好酒店服务,提高好评率**。消费者线上反馈的内容,需要快速回复并及时作出响应,从而提升顾客消费的满意度,培养顾客的酒店忠诚度,提升酒店竞争力。 31 | 1. **开通酒店在百度地图上的显示**。这对根据地图找房的消费者非常有用。 32 | 33 | ### 长远思考 34 | 35 | 1. 长远规划看,还是要塑造自由品牌。利用微信公众号、酒店官方网站,对已有会员和潜在消费者进行营销信息推广,成本低、推广效果好。 36 | 1. 设立酒店培训,对在职员工进行长期、有计划的培训,有利于提高员工服务质量,培养员工的职业成就感,减少员工流失; 37 | 1. 引进专业酒店成本管理软件,实现酒店运营成本的周期性分析,为管理层的日常酒店运营管理提供科学可靠的数据。通过数据分析,管理者可及时针对过高的不必要成本支出进行及时控制和调整,减少资源浪费,优化酒店财务状况。 38 | 39 | ## 4. 渠道加盟流程 40 | 41 | ### 4.1 携程、去哪儿、艺龙渠道 42 | 43 | 携程是中国最大的在线旅游服务商,通过与携程合作,可以增加线上流量,给酒店更多订单。 44 | 45 | 简单来说,流程是:**提交信息** --> **携程审核通过** --> **发放独有账户名/密码** --> **账户登陆ebooking网站管理(随时修改房价以及接单等)**。如果有不清楚地方,可以拨打携程酒店客服热线:021-52298888。 46 | 47 | 具体操作流程: 48 | 1. 在[携程加盟入口](https://join.easytrip.com/jiameng/#ctm_ref=wx_hp_tool_hsu_n_1)填写联系电话、邮箱,同时补充酒店信息、房型信息、证照信息、合作模版等内容,提交给携程平台进行审核。 49 | 50 | 2. 携程平台会有专门的业务经理进行电话洽谈,无意外携程平台会审核通过,开通携程渠道账户成功(发放独有账户名和密码),同时携程平台也会在当地(南昌)指派区域经理进行对接,后期有任何不明白的地方,都可以与其接洽。 51 | 52 | 3. 使用开通的携程商家账户,登陆上[ebooking](https://ebooking.ctrip.com/ebkassembly/login.aspx)网站,该网站主要用来管理酒店房间信息的修改,以及接收携程渠道的订单,是后期网上处理携程订单、佣金结算的主要工具。 53 | 54 | 4. 携程跟去哪儿、艺龙等渠道本质上是一家公司,所以开通了携程的账号,相当于也开通了去哪儿和艺龙的线上渠道。所以使用ebooking网站可以一键把酒店信息分销给携程、去哪儿、艺龙三个渠道。 55 | 56 | 以下是加盟携程平台需要填写的资料: 57 | 58 | ![image](https://user-images.githubusercontent.com/6310131/66484632-e8707e00-ead9-11e9-8b1c-83a9ef1573c9.png) 59 | 60 | ![image](https://user-images.githubusercontent.com/6310131/66484643-ec9c9b80-ead9-11e9-8c6c-7047cf360c49.png) 61 | 62 | ![image](https://user-images.githubusercontent.com/6310131/66484654-f1f9e600-ead9-11e9-9d19-dedcafc3364e.png) 63 | 64 | ![image](https://user-images.githubusercontent.com/6310131/66484657-f58d6d00-ead9-11e9-80c9-0bd30498077f.png) 65 | 66 | 67 | > 说明:携程ebooking是一个开放、透明的酒店服务平台,以数据为基础,为酒店提供收益管理;在线实时管控房态、房价;处理订单;参加营销活动;点评管理;售卖酒店附加产品等服务。旨在实现酒店方与携程双方共赢,更好地为客人提供服务。 68 | 69 | ### 4.2 飞猪渠道 70 | 71 | 飞猪渠道流程与携程类似,都是先审核,再完善店铺信息,然后开始线上接单。 72 | 73 | ![image](https://user-images.githubusercontent.com/6310131/66484470-9def0180-ead9-11e9-9016-79eaf391ba75.png) 74 | 75 | 76 | ## 参考文章 77 | 78 | * https://www.zhihu.com/question/26491802 79 | * https://www.lunwendata.com/thesis/2019/156866.html 80 | * https://books.google.com.hk/books?id=QPjJDQAAQBAJ&pg=PT30&lpg=PT30&dq=%E5%9C%A8%E7%BA%BF%E9%85%92%E5%BA%97%E7%BB%8F%E8%90%A5&source=bl&ots=FekU6UJTb4&sig=ACfU3U2GPLFHREVhnCPRt0QCHGKXDmxtEg&hl=zh-TW&sa=X&redir_esc=y#v=onepage&q=%E5%9C%A8%E7%BA%BF%E9%85%92%E5%BA%97%E7%BB%8F%E8%90%A5&f=false --------------------------------------------------------------------------------