├── .gitignore ├── posts ├── Vue源码学习 │ └── mvvm.png ├── babel │ ├── babel.png │ └── babel-parse-1.png ├── 【面试题】http │ ├── 三次握手.png │ ├── 四次挥手.png │ ├── https.webp │ └── osi七层模型.jpeg ├── 【面试题】浏览器 │ └── part1.png ├── 【面试题】设计模式 │ ├── mvc.webp │ ├── mvp.webp │ └── mvvm.webp ├── 算法学习-递归 │ ├── 96_BST.png │ └── merge_ex1.jpg ├── graphql入门 │ └── graphql.png ├── 【面试题】node │ └── libuv.png ├── 【面试题】服务端渲染 │ └── reactSSR.png ├── 算法学习-链表 │ ├── remove_ex1.jpg │ └── single_link.png ├── webpack的树摇功能使用 │ ├── after.png │ └── before.png ├── 【js高级程序】变量、作用域与内存 │ ├── 值复制.png │ └── 引用复制.png ├── 【面试题】js基础 │ └── object-push.png ├── 个人通用js方法库d-js-utils │ └── bg.png ├── 个人网站2-0版本总结个未来规划 │ ├── html1.0.jpg │ └── html2.0.jpg ├── 提升页面性能的n种方法 │ ├── defer&async.jpeg │ └── preconnect.webp ├── hooks的学习和使用 │ └── useLayoutEffect.png ├── javascript事件循环 │ └── event-loop.jpg ├── 【面试题】微前端.md ├── Content-Security-Policy │ ├── meta1.png │ ├── meta_error.png │ ├── style_src.png │ ├── config_result.jpg │ ├── nginx_csp_self.png │ └── res_nginx_conf.png ├── 前端跨域的处理方案.md ├── typescript-泛型.md ├── 【面试题】vue.md ├── IOS快速获取验证信息重复显示的问题.md ├── 一句话调试你的CSS层.md ├── html页面在360极速浏览器兼容模式显示问题解决方法.md ├── webpack4-0-安装-extract-text-webpack-plugin-报错.md ├── 【面试题】web安全.md ├── web安全-SQL注入.md ├── h5新标签学习.md ├── web安全-点击劫持.md ├── code格式校验.md ├── 一个vue搭建的history模式的活动页项目.md ├── 伪数组.md ├── 关于ruby安装-sass-compass报错的问题.md ├── 2020总结.md ├── 【面试题】设计模式.md ├── 2019总结.md ├── 关于移动端按钮点击的交互.md ├── 【面试题】面试题相关文章地址收集.md ├── css3的blur如何消除边缘透明效果.md ├── 对象实例化的时候经历了什么?.md ├── webpack的树摇功能使用.md ├── h5的后台切换到前台的数据更新.md ├── 正则学习.md ├── 递归.md ├── 【面试题】服务端渲染.md ├── TypeScript初识.md ├── web安全-XSS.md ├── web安全-CSRF.md ├── 关于闭包.md ├── babel-plugin-transform-runtime与-babel-plugin-external-helpers.md ├── English learning record.md ├── 个人网站2-0版本总结个未来规划.md ├── 个人通用js方法库d-js-utils.md ├── 【js高级程序】工作者线程-SharedWorker.md ├── typescript发布npm包添加代码提示.md ├── 【面试题】异步.md ├── javascript事件循环.md ├── TypeScript接口,类,函数.md ├── 如何减少webpack4打包时长.md ├── 【面试题】css.md ├── Vue源码学习.md ├── 手写一个简单的发布订阅者模式.md ├── 常用的排序方式有哪些?.md ├── 手写call,apply,bind方法.md ├── docker学习记录.md ├── 关于cookie.md ├── 前端模块化规范.md ├── 【js高级程序】错误处理与调试.md ├── 【面试题】node.md ├── d-video.js视频播放器.md ├── DOM & BOM.md ├── 提升页面性能的n种方法.md ├── 【面试题】typescript.md ├── 对象的继承方式.md ├── 【js高级程序】DOM扩展.md ├── javascript异步编程.md ├── javascript需要注意的this指向问题.md ├── 【js高级程序】模块.md ├── js数据类型转换.md ├── Content-Security-Policy.md ├── 【js高级程序】变量、作用域与内存.md ├── 面试中的那些手写代码题目.md ├── 日志监控.md ├── 算法学习-递归.md ├── 【js高级程序】客户端检测.md ├── css-module.md └── graphql入门.md ├── pnpm-lock.yaml ├── package.json ├── init.js └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ -------------------------------------------------------------------------------- /posts/Vue源码学习/mvvm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/Vue源码学习/mvvm.png -------------------------------------------------------------------------------- /posts/babel/babel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/babel/babel.png -------------------------------------------------------------------------------- /posts/【面试题】http/三次握手.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/【面试题】http/三次握手.png -------------------------------------------------------------------------------- /posts/【面试题】http/四次挥手.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/【面试题】http/四次挥手.png -------------------------------------------------------------------------------- /posts/【面试题】浏览器/part1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/【面试题】浏览器/part1.png -------------------------------------------------------------------------------- /posts/【面试题】设计模式/mvc.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/【面试题】设计模式/mvc.webp -------------------------------------------------------------------------------- /posts/【面试题】设计模式/mvp.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/【面试题】设计模式/mvp.webp -------------------------------------------------------------------------------- /posts/算法学习-递归/96_BST.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/算法学习-递归/96_BST.png -------------------------------------------------------------------------------- /posts/graphql入门/graphql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/graphql入门/graphql.png -------------------------------------------------------------------------------- /posts/【面试题】http/https.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/【面试题】http/https.webp -------------------------------------------------------------------------------- /posts/【面试题】node/libuv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/【面试题】node/libuv.png -------------------------------------------------------------------------------- /posts/【面试题】设计模式/mvvm.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/【面试题】设计模式/mvvm.webp -------------------------------------------------------------------------------- /posts/算法学习-递归/merge_ex1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/算法学习-递归/merge_ex1.jpg -------------------------------------------------------------------------------- /posts/babel/babel-parse-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/babel/babel-parse-1.png -------------------------------------------------------------------------------- /posts/【面试题】http/osi七层模型.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/【面试题】http/osi七层模型.jpeg -------------------------------------------------------------------------------- /posts/【面试题】服务端渲染/reactSSR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/【面试题】服务端渲染/reactSSR.png -------------------------------------------------------------------------------- /posts/算法学习-链表/remove_ex1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/算法学习-链表/remove_ex1.jpg -------------------------------------------------------------------------------- /posts/算法学习-链表/single_link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/算法学习-链表/single_link.png -------------------------------------------------------------------------------- /posts/webpack的树摇功能使用/after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/webpack的树摇功能使用/after.png -------------------------------------------------------------------------------- /posts/webpack的树摇功能使用/before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/webpack的树摇功能使用/before.png -------------------------------------------------------------------------------- /posts/【js高级程序】变量、作用域与内存/值复制.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/【js高级程序】变量、作用域与内存/值复制.png -------------------------------------------------------------------------------- /posts/【js高级程序】变量、作用域与内存/引用复制.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/【js高级程序】变量、作用域与内存/引用复制.png -------------------------------------------------------------------------------- /posts/【面试题】js基础/object-push.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/【面试题】js基础/object-push.png -------------------------------------------------------------------------------- /posts/个人通用js方法库d-js-utils/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/个人通用js方法库d-js-utils/bg.png -------------------------------------------------------------------------------- /posts/个人网站2-0版本总结个未来规划/html1.0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/个人网站2-0版本总结个未来规划/html1.0.jpg -------------------------------------------------------------------------------- /posts/个人网站2-0版本总结个未来规划/html2.0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/个人网站2-0版本总结个未来规划/html2.0.jpg -------------------------------------------------------------------------------- /posts/提升页面性能的n种方法/defer&async.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/提升页面性能的n种方法/defer&async.jpeg -------------------------------------------------------------------------------- /posts/提升页面性能的n种方法/preconnect.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/提升页面性能的n种方法/preconnect.webp -------------------------------------------------------------------------------- /posts/hooks的学习和使用/useLayoutEffect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/hooks的学习和使用/useLayoutEffect.png -------------------------------------------------------------------------------- /posts/javascript事件循环/event-loop.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/javascript事件循环/event-loop.jpg -------------------------------------------------------------------------------- /posts/【面试题】微前端.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 【面试题】微前端 3 | date: 2021-03-29 18:51:32 4 | categories: 微前端 5 | tags: [微前端, 面试] 6 | --- 7 | -------------------------------------------------------------------------------- /posts/Content-Security-Policy/meta1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/Content-Security-Policy/meta1.png -------------------------------------------------------------------------------- /posts/Content-Security-Policy/meta_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/Content-Security-Policy/meta_error.png -------------------------------------------------------------------------------- /posts/Content-Security-Policy/style_src.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/Content-Security-Policy/style_src.png -------------------------------------------------------------------------------- /posts/前端跨域的处理方案.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 前端跨域的处理方案 3 | date: 2019-10-22 17:07:47 4 | categories: JavaScript 5 | tags: ['js'] 6 | --- 7 | 8 | -------------------------------------------------------------------------------- /posts/Content-Security-Policy/config_result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/Content-Security-Policy/config_result.jpg -------------------------------------------------------------------------------- /posts/Content-Security-Policy/nginx_csp_self.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/Content-Security-Policy/nginx_csp_self.png -------------------------------------------------------------------------------- /posts/Content-Security-Policy/res_nginx_conf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IFmiss/blog/master/posts/Content-Security-Policy/res_nginx_conf.png -------------------------------------------------------------------------------- /posts/typescript-泛型.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typescript 泛型 3 | date: 2019-11-20 22:03:39 4 | categories: typescript 5 | tags: [ts] 6 | --- 7 | 8 | ### 关于泛型 9 | 泛型是很多程序语言的一大特点,泛型在强类型程序代码中做的一种数据类型规范,javascript并不支持范型,但是ts支持 10 | -------------------------------------------------------------------------------- /posts/【面试题】vue.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 【面试题】Vue 3 | date: 2021-04-14 20:55:49 4 | categories: typescript 5 | tags: [typescript, 面试] 6 | --- 7 | 8 | ### 双向绑定原理 9 | 10 | ### 模版编译原理 11 | 12 | ### diff 算法 13 | 14 | ### 2.0 数组如何实现监听 15 | 16 | ### computed 原理,与 `wathcher` 的区别 17 | 18 | ### nextTick 原理 19 | 20 | ### keep-alive 原理 21 | -------------------------------------------------------------------------------- /posts/IOS快速获取验证信息重复显示的问题.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: IOS快速获取验证信息重复显示的问题 3 | date: 2019-03-08 22:49:21 4 | categories: javascript 5 | tags: [小技巧, js] 6 | --- 7 | 8 | IOS下在获取手机号短信验证码的时候,ios在键盘上会自动显示验证码内容,以达到快速填写的功能,但是正常的input输入框会重复显示 9 | - 10 | 解决方法 11 | ```html 12 | 13 | ``` 14 | input的maxlength属性设置一个最大值,4 或者 6 等等,验证码多少位值设置为多少 15 | -------------------------------------------------------------------------------- /posts/一句话调试你的CSS层.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 一句话调试你的CSS层 3 | date: 2017-10-12 12:08:46 4 | categories: javascript 5 | tags: [小技巧] 6 | --- 7 | 8 | 这是我在网上看到的一篇文章这段代码是谷歌“名猿”Addy Osmani放出来的 9 | ```js 10 | [].forEach.call($$("*"),function(a){ a.style.outline="1px solid #"+(~~(Math.random()*(1<<24))).toString(16) }); //选取所有的元素,给每个元素加个边框(随机色) 11 | ``` 12 | 执行所有的dom元素添加一个随机的outline 13 | -------------------------------------------------------------------------------- /posts/html页面在360极速浏览器兼容模式显示问题解决方法.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: html页面在360极速浏览器兼容模式显示问题解决方法 3 | date: 2016-9-13 12:51:37 4 | categories: html 5 | tags: [小技巧] 6 | --- 7 | 8 | 自己在写一个页面的时候,涉及到一些css3动画的效果,在360极速兼容模式上显示出现问题: 9 | ```html 10 | 11 | 12 | ``` 13 | 这样就基本上解决了大多在360极速模式上出现的问题! 14 | -------------------------------------------------------------------------------- /posts/webpack4-0-安装-extract-text-webpack-plugin-报错.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: webpack4.0 安装 extract-text-webpack-plugin 报错 3 | date: 2018-3-16 11:59:00 4 | categories: 环境配置 5 | tags: [webpack, npm] 6 | --- 7 | 8 | 最近研究webpack相关配置在搭建通用的webpack项目时候用到extract-text-webpack-plugin,npm install extract-text-webpack-plugin –save-dev之后run 这个项目的时候发现服务启动出错,查找原因后发现,由于webpack升级到4.0以后,extract-text-webpack-plugin默认安装的是3.几的版本,于是执行下面命令行 9 | 10 | ``` npm 11 | npm install extract-text-webpack-plugin@next 12 | ``` 13 | 14 | 再次尝试运行项目就可以成功跑起来了 15 | -------------------------------------------------------------------------------- /posts/【面试题】web安全.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 【面试题】web安全 3 | date: 2021-03-29 18:49:43 4 | categories: web安全 5 | tags: [web安全, 面试] 6 | --- 7 | 8 | ### 你知道哪些攻击方式? 9 | 10 | - [XSS,跨站脚本攻击](https://www.daiwei.site/blog/detail?tid=2f3c39e2-7361-11eb-aac6-00163e0aa4af) 11 | - [CSRF,跨站点请求伪造](https://www.daiwei.site/blog/detail?tid=2f3c3a54-7361-11eb-aac6-00163e0aa4af) 12 | - [XSS,点击劫持](https://www.daiwei.site/blog/detail?tid=2f3c3ac6-7361-11eb-aac6-00163e0aa4af) 13 | - [XSS,SQL 注入](https://www.daiwei.site/blog/detail?tid=2f3c3b3c-7361-11eb-aac6-00163e0aa4af) 14 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: 5.3 2 | 3 | specifiers: 4 | chalk: ^5.0.0 5 | dateformat: ^5.0.2 6 | 7 | devDependencies: 8 | chalk: 5.0.0 9 | dateformat: 5.0.2 10 | 11 | packages: 12 | 13 | /chalk/5.0.0: 14 | resolution: {integrity: sha512-/duVOqst+luxCQRKEo4bNxinsOQtMP80ZYm7mMqzuh5PociNL0PvmHFvREJ9ueYL2TxlHjBcmLCdmocx9Vg+IQ==} 15 | engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} 16 | dev: true 17 | 18 | /dateformat/5.0.2: 19 | resolution: {integrity: sha512-h9vywpuz+ReixnJTwFx5JLtZpS8eLCbRm8shwwKkCKOZA547N6yoMtD3W91Z6+NFZ8wOaZlcaCcK/w+kELhSVg==} 20 | engines: {node: '>=12.20'} 21 | dev: true 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dw-blog", 3 | "version": "1.0.0", 4 | "description": "daiwei's blog", 5 | "main": "init.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/IFmiss/blog.git" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/IFmiss/blog/issues" 18 | }, 19 | "homepage": "https://github.com/IFmiss/blog#readme", 20 | "devDependencies": { 21 | "chalk": "^5.0.0", 22 | "dateformat": "^5.0.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /posts/web安全-SQL注入.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: web安全-SQL注入 3 | date: 2020-10-14 16:07:57 4 | categories: web安全 5 | tags: [web安全] 6 | --- 7 | 8 | ### 举例 9 | 例如登陆请求 10 | 输入用户名和密码登陆用户信息,基本上会执行这个sql语句 11 | ```sql 12 | select * from tb_user_info Where 'username' = 'dw' and 'password' = '123' 13 | ``` 14 | 但是如果输入 15 | username: `'' or 1 = 1 --` 16 | password: `''` 17 | 18 | ```sql 19 | select * from tb_user_info Where 'username' = '' or 1 = 1 and 'password' = '' 20 | ``` 21 | 22 | ### 原理 23 | 主要是攻击者,利用被攻击页面的一些漏洞,改变数据库执行的SQL语句,从而达到获取“非授权信息”的目的。 24 | 25 | ### SQL防御方式 26 | ##### 转义符号 27 | 针对前端在表单提交之前进行数据格式校验 28 | 29 | ##### 服务端sql转译 30 | 后端采用sql语句预编译和绑定变量,是防御sql注入的最佳方法 31 | -------------------------------------------------------------------------------- /posts/h5新标签学习.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: h5新标签学习 3 | date: 2020-10-18 19:51:30 4 | categories: html 5 | tags: [html5] 6 | --- 7 | 8 | 由于平时业务中基本所有元素,感觉h5很多新元素都没有被使用而且也不是特别熟悉,这篇文章则是学习一下html标签 9 | 10 | ### article 11 | 标签规定独立的自包含内容。比如来自一个外部的新闻提供者的一篇新的文章,或者来自 blog 的文本,或者是来自论坛的文本。亦或是来自其他外部源内容。 12 | ```html 13 |
14 |

G2 3 比 0 战胜 GEN.G

15 | G2 也太强了 16 |
17 | ``` 18 | 19 | ### aside 20 | 标签定义其所处内容之外的内容。 21 | - 其中的内容可以是与当前文章有关的相关资料、名次解 22 | - 在article元素之外使用作为页面或站点全局的附属信息部分 23 | > <aside> 的内容可用作文章的侧栏。 24 | 25 | ### audio 26 | 音频播放标签,比较常用 27 | 28 | ### bdi 29 | 标签允许您设置一段文本,使其脱离其父元素的文本方向设置。 30 | 31 | ```html 32 |
  • 你好,html5
  • 33 |
  • 你好,html5
  • 34 | ``` 35 | > 了解下Unicode双向算法 36 | 37 | ### 38 | 39 | 40 | -------------------------------------------------------------------------------- /posts/web安全-点击劫持.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: web安全-点击劫持 3 | date: 2020-10-14 16:04:12 4 | categories: web安全 5 | tags: [web安全] 6 | --- 7 | 8 | ### 点击劫持 9 | 通过覆盖不可见的框架误导受害者点击,比如页面上放置一个frame页面,并设置透明度为0,用户点击按钮的时候(该按钮被攻击者精心准备过,模拟原网页的按钮,诱导用户点击) 10 | 11 | ### 实现原理 12 | `iframe` 透明嵌入到网页中,诱导用户点击 13 | 14 | ### 应对方法 15 | #### 针对`iframe`内嵌可以设置 `X-Frame-Options` 16 | X-Frame-Options HTTP 响应头是用来给浏览器指示允许一个页面可否在 <frame>, <iframe> 或者 <object> 中展现的标记。网站可以使用此功能,来确保自己网站的内容没有被嵌套到别人的网站中去,也从而避免了点击劫持 (clickjacking) 的攻击。 17 | 18 | ```nginx 19 | add_header x-frame-options SAMEORIGIN; 20 | ``` 21 | 请求头设置 `SAMEORIGIN` 22 | - `DENY` 23 | 不允许在frame中展示,即便是在相同域名的页面中嵌套也不允许 24 | - `SAMEORIGIN` 25 | 该页面可以在相同域名页面的frame中展示 26 | - `ALLOW-FROM uri` 27 | 指定来源的frame中展示,逗号分割设置多选域名 28 | ```nginx 29 | add_header X-Frame-Options "ALLOW-FROM http://w1.daiwei.com/,https://www.daiwei.site"; 30 | ``` 31 | -------------------------------------------------------------------------------- /posts/code格式校验.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: code格式校验 3 | date: 2020-12-22 13:32:40 4 | categories: 工程化 5 | tags: [工程化] 6 | --- 7 | 8 | ### 代码美化工具 prettier 9 | 全局或者本地安装 10 | ```code 11 | npm i prettier -g 12 | ``` 13 | or 14 | ```code 15 | npm i prettier --save-dev 16 | ``` 17 | 18 | 目录下配置 `prettier.config.js` 19 | ```js 20 | module.exports = { 21 | // 多行时使用尾后逗号,默认为"es5" "none" | "es5" | "all" 22 | trailingComma: "es5", 23 | // 缩进宽度 24 | tabWidth: 2, 25 | // 是否使用分号,默认为true 26 | semi: true, 27 | // 是否使用单引号包裹字符串,默认为false 28 | singleQuote: true, 29 | // 列宽,默认为80 30 | printWidth: 80, 31 | }; 32 | ``` 33 | 更多配置见: https://prettier.io/docs/en/options.html 34 | 35 | #### prettier vscode 36 | vscode 安装 prettier - code formatter 根据 vscode 配置 `Editor: Format On Save` 为true 支持保存自动格式话代码 37 | > vs code 优先按项目根目录配置进行格式化,没有则使用vs code 安装 prettier 的默认配置 38 | 39 | #### prettier script 配置 40 | 41 | > prettier 专注格式美化,不做代码质量校验,所以eslint 该用还得用 42 | 43 | ### eslint 校验 44 | 45 | -------------------------------------------------------------------------------- /posts/一个vue搭建的history模式的活动页项目.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 一个vue搭建的history模式的活动页项目 3 | date: 2019-03-08 23:06:58 4 | categories: vue 5 | tags: [vue] 6 | --- 7 | 8 | vue单页面应用我一直觉得适合做移动端项目,尤其是微信端的活动页那种活动 9 | 原因是 10 | 11 | - vue有通用的cli脚手架,有vue全家桶, 虽然当下的主流框架都有,但是vue应该是最容易上手的一个了 12 | - vue适合这种小型的项目,页面10个一下,少量的状态管理,稍微复杂的条件显示机制以及状态判断 13 | 14 | 可是我就是在用vue去写微信h5的时候遇到了一些坑。。。 15 | 16 | #### 1.移动端适配 17 | 移动端适配对于前端已经是不能再熟悉的了,比较主流的就是动态设置html的字体大小,以rem的单位来写页面,但是这有个问题,ipad上怎么做兼容,屏幕越大,显示越大,文字越大,图片也达到本身宽度导致显示模糊 18 | 19 | #### 2.路由模式 20 | 这次项目使用的是history模式,运维那边已经配置好了nginx代理,所有指向当前目录下的子目录都会指向index.html,但是前端打包发布一直没法达到真正的效果,页面不显示,最后是打包的配置,以及路由的base属性没有设置导致的 21 | 22 | #### 3.微信授权 23 | 在这之前,我做的微信授权都是后端处理的,做跳转的,所有授权操作后端一个人完成,现在是用微信跳转获取code,code请求接口种植session来验证用户信息 24 | 25 | #### 4.微信分享 26 | vue的微信分享还是很坑的,正常的情况在获取微信jssdk授权之后,分享应该是可以调用的,但是作为但页面应用,微信授权在ios下会出现签名失败的情况,网上也搜索了很多方法,方法就是记录第一次的页面地址,之后每次初始化分享的时候,获取微信jssdk授权地址为第一次的入口地址即可 27 | 28 | #### 5.路由拦截 29 | 涉及到不同页面的状态进入不同页面,虽然主入口都一样,但是需要在进入之前就判断跳转到哪个页面就需要用到路由拦截,配合各种条件执行不同的next() 30 | -------------------------------------------------------------------------------- /posts/伪数组.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 伪数组 3 | date: 2019-10-15 14:58:29 4 | categories: javascript 5 | tags: [js] 6 | --- 7 | 8 | ### 什么是伪数组 9 | - 拥有数组的length属性 10 | - 没有Array的方法 (slice, pop, push...) 11 | - 使用索引存储数据 12 | - `Array.isArray` 返回false 13 | 14 | ### 伪数组存在的场景 15 | - 函数参数 (返回一个 Arguments的伪数组) 16 | ```ts 17 | function TestArray () { 18 | console.log(arguments) 19 | return arguments 20 | } 21 | 22 | TestArray(1, 2, 3, 4) 23 | ``` 24 | 25 | - Dom 元素 (返回一个 HTMLCollection的伪数组) 26 | ```ts 27 | document.getElementsByTagName('div') 28 | ``` 29 | 30 | ### 伪数组转换正常数组的方法 31 | - Array.prototype.slice.call 32 | ```ts 33 | Array.prototype.slice.call(TestArray(1, 2, 3, 4)) 34 | [].slice.call(TestArray(1, 2, 3, 4)) 35 | ``` 36 | 37 | - Array.from 38 | ```ts 39 | Array.from(TestArray(1, 2, 3, 4)) 40 | ``` 41 | 42 | - Array.prototype.push.apply 43 | ```ts 44 | const newArr = Array.prototype.push.apply(TestArray(1, 2, 3, 4)) 45 | const newArr = [].push(TestArray(1, 2, 3, 4)) 46 | ``` 47 | -------------------------------------------------------------------------------- /posts/关于ruby安装-sass-compass报错的问题.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 关于ruby安装 sass compass报错的问题 3 | date: 2016-11-20 12:03:28 4 | categories: css相关 5 | tags: [css] 6 | --- 7 | 8 | 最近硬盘损坏系统被迫重做,之前所有的代码环境,都得重新配置,其中在安装sass compass需要用到ruby的情况下就一直没办法安装成功,真的三个晚上的研究,终于找到真相了! 9 | 之前报错原因如下: 10 | ```js 11 | Error fetching https://ruby.taobao.org/: 12 | SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed (https://gems.ruby-china.org/specs.4.8.gz) 13 | ``` 14 | 15 | 网上查了下,说是被墙了,解决方法就是,把之前的源给删除掉,用淘宝的源: 16 | ```npm 17 | gem sources –remove https://rubygems.org/ // 删除原来的地址 18 | ``` 19 | 20 | ```npm 21 | gem sources -a https://ruby.taobao.org/ 22 | ``` 23 | 24 | 换完了之后我以为这样就可以了, gem install sass还是报错 25 | 26 | ```js 27 | Error fetching http://ruby.taobao.org/: 28 | bad response Not Found 404 (http://ruby.taobao.org/specs.4.8.gz) 29 | ``` 30 | 31 | 找了好久发现: 32 | taobao Gems 源已停止维护,现由 ruby-china 提供镜像服务 33 | 最后就是换了一个源地址 34 | ```code 35 | gem sources -a https://gems.ruby-china.org/ 36 | ``` 37 | 就可以了,就成功了,所以源地址已经更新到 ruby-china 了 -------------------------------------------------------------------------------- /posts/2020总结.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 2020总结 3 | date: 2021-02-04 14:44:54 4 | categories: 总结 5 | tags: [总结] 6 | --- 7 | 8 | 2020年实际已过去一个多月,下个星期也就是大年三十了。总结一下 20年的经历,以及21年的准备 9 | 10 | ### 2020年 11 | 一言难尽,2020年,是工作最辛苦,最累的一年,累的不是技术难度,而是一对多的开发,和工作交流。主要业务开发: 12 | - 虎扑 新版本 PC开发 13 | - app 帖子详情开发 + 优化 14 | - 各个活动页开发 15 | - 组件开发 16 | 17 | ### 个人 18 | - 基于Next.js 的个人网站 19 | - 28 篇学习文档 20 | - github 637 代码提交 21 | - 学习 JavaScript 高级程序 55% 22 | - 尝试搭建简单的[脚手架](https://github.com/IFmiss/tiga-cli) 23 | 24 | ### 去年2020年定的目标 25 | - 坚持写总结,博客,预计 1~2 周一篇博客,不单纯是写,而是总结,加深印象 (100%,28篇文档,平均两周一篇) 26 | - 深入学习 TypeScript (10%,项目会用,单还不够深入) 27 | - 个人网站 UI 重新改版,将原本的 php 接口 使用 node 重写, 网站页面风格改版, 以及 接入 SSR, 接入用户登陆,最好是 github 账号登陆 (70% 登录未实现,Next.js + Koa接口请求) 28 | - 学习 React 源码,深入了解 React 原理 (0% 未完成) 29 | - 实现一个简单的 React (0% 未完成) 30 | - 巩固 JS 基础知识 (40% 学习js 高级程序设计,基础学起) 31 | - 开发一个自己的脚手架 (80%) 32 | - 维护 d-utils工具库 (0% 未完成) 33 | - 学习数据结构,了解常用算法 (10% 未完成) 34 | - 好用的组件抽象,封装,写 5 个组件 (30% 未完成) 35 | - 有可能的话,个人网站后台管理重写 (0% 未完成) 36 | 37 | ### 2021年目标 38 | - [ ] 学习 39 | - [ ] 工作 40 | - [ ] 找个对象 ? 41 | - [ ] 学位考试 42 | 43 | -------------------------------------------------------------------------------- /posts/【面试题】设计模式.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 【面试题】设计模式 3 | date: 2021-04-13 12:27:59 4 | categories: 设计模式 5 | tags: [设计模式, 面试] 6 | --- 7 | 8 | ### 说说 MVC,MVP,MVVM 9 | 10 | **MVC** 11 | 12 | ![MVC](https://www.daiwei.site/static/blog/【面试题】设计模式/mvc.webp) 13 | 14 | - **Model: 数据模型** (用于处理应用程序数据逻辑的部分,如数据库) 15 | - **View: 视图** (用于渲染页面, 通常视图是依据模型数据创建的) 16 | - **Controller: 控制器** (业务逻辑, 通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。) 17 | 18 | **MVP** 19 | 20 | ![MVP](https://www.daiwei.site/static/blog/【面试题】设计模式/mvp.webp) 21 | 22 | - **Model** 负责提供数据 23 | - **View** 负责显示页面视图 24 | - **Presenter** 用于负责 Model 与 View 之间的通信,负责逻辑的处理 25 | 26 | **MVVM** 27 | 28 | ![MVVM](https://www.daiwei.site/static/blog/【面试题】设计模式/mvvm.webp) 29 | 30 | - **Model: 数据模型** 代表数据模型,数据和业务逻辑都在 Model 层中定义 31 | - **ViewModel** 负责监听 Model 中数据的改变并且控制视图的更新 32 | - **View: 视图** 代表 UI 视图,负责数据的展示 33 | 34 | ### 前端常用的设计模式 35 | 36 | 1. 单例模式 37 | 2. 工厂模式 38 | 3. 策略模式 39 | 4. 代理模式 40 | 5. 观察者模式 41 | 6. 模块模式 42 | 7. 构造函数模式 43 | 8. 混合模式 44 | 45 | [前端需要了解的 9 种设计模式](https://www.jianshu.com/p/4f3014fb8b8b) 46 | [前端常用设计模式](https://www.jianshu.com/p/4f3014fb8b8b) 47 | -------------------------------------------------------------------------------- /posts/2019总结.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 2019总结 3 | date: 2020-01-17 16:28:42 4 | categories: 总结 5 | tags: [总结] 6 | --- 7 | 8 | 按照农历来算,2019年就快结束了,总结一下这一年做的事,和来年要做的事 9 | 10 | ### 2019年 11 | #### 公司 12 | 2019年经历两家公司,第一家做教育的公司凉凉,现在在一个做媒体行业的公司 13 | 14 | 上半年算是很忙成狗的时期,但是到了后面,公司基本没啥要做的业务又突然很闲,这种强烈的反差虽然不太适应,但是在刚来的那段时间还是学习到了一些东西 15 | - React 在项目中的应用 16 | - 微信公众号开发,绝大部分场景 17 | - 小程序开发 18 | - 代码风格 19 | 20 | #### 个人 21 | 虽然又在学习,但是和刚做前端那段时间的学习劲,真的没法比,感觉还是太懒散了,2019个人做的事 22 | - 写了 26 篇小总结 23 | - 基于 React + React hooks + mobx 构建个人的第三版本网站 24 | - 学习服务端基本配置 esc主机基于center os 安装不同环境,并使用(mysql, pm2, nginx ...) 25 | - github 639次提交,创建 14 个 repo 26 | - 看了js犀牛书,但是并没有看完 27 | 28 | #### 2020年 29 | 以下是我2020年度要做的事,记录以下,明年再总结 30 | - [ ] 坚持写总结,博客,预计 1~2 周一篇博客,不单纯是写,而是总结,加深印象 31 | - [ ] 深入学习 TypeScript 32 | - [ ] 个人网站 UI 重新改版,将原本的 php 接口 使用 node 重写, 网站页面风格改版, 以及 接入 SSR, 接入用户登陆,最好是 github 账号登陆 33 | - [ ] 学习 React 源码,深入了解 React 原理 34 | - [ ] 实现一个简单的 React 35 | - [ ] 巩固 JS 基础知识 36 | - [x] 开发一个自己的脚手架 37 | - [ ] 维护 d-utils工具库 38 | - [ ] 学习数据结构,了解常用算法 39 | - [ ] 好用的组件抽象,封装,写 5 个组件 40 | - [ ] 有可能的话,个人网站后台管理重写 41 | 42 | ### 总结 43 | 19年虽然有在学习,但是学习的不系统,效率不够高,比较懒散,20年要重拾当初做前端学习的心,好好学习,奥力给!!! 44 | -------------------------------------------------------------------------------- /posts/关于移动端按钮点击的交互.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 关于移动端按钮点击的交互 3 | date: 2019-04-08 22:07:09 4 | categories: css 5 | tags: [css, 移动端, less] 6 | --- 7 | 8 | 在做移动端项目的时候,通常按钮的点击事件 IOS 下默认会有灰色遮罩层,虽然网上很多在找这种遮罩层的去除方法,但是个人觉得这个东西还是很人性化的 9 | 10 | ios下影响到遮罩层的css属性为 11 | ```css 12 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); // 设置颜色 13 | ``` 14 | 就是当点击的时候元素按下的区域背景色设置为透明 15 | 16 | 虽然从样式上看,没有遮罩层看起来舒服,但是,遮罩层显示代表的是用户的一个点击行为事件,当用户点击了(点击事件需要网络请求,这时候在网络较弱的场景下,没有点击之后的反应),也就是没有交互效果,会让用户觉得点击失败而多次点击 17 | 18 | 但是这个css只支持ios Ipad等移动设备 19 | 安卓下并不能实现这种交互效果 20 | 21 | 需求影响,自己写了一个通用的less方法,兼容ios和安卓的点击效果 22 | ```less 23 | // @color 参数 默认 rgba(0,0,0,0.3) 24 | .active(@color: rgba(0,0,0,0.3)) { 25 | -webkit-tap-highlight-color: @color; 26 | // 伪类 27 | &:active { 28 | // 伪元素 29 | &::before { 30 | content: ''; 31 | position: absolute; 32 | left: -0.06rem; 33 | top: -0.06rem; 34 | right: -0.06rem; 35 | bottom: -0.06rem; 36 | background: @color; 37 | border-radius: 0.1rem; 38 | } 39 | } 40 | } 41 | ``` 42 | 由于安卓不兼容`-webkit-tap-highlight-color`,而且ios不兼容`:active`的点击效果(本人测试没有效果),从而兼容IOS和安卓,两者解决方案合并完成这个less方法 43 | -------------------------------------------------------------------------------- /posts/【面试题】面试题相关文章地址收集.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 【面试题】面试题相关文章地址收集 3 | date: 2021-04-13 17:44:44 4 | categories: 5 | tags: 6 | --- 7 | 8 | ### 面试相关文章地址,网上搜集。 9 | 10 | - [完全理解 React Fiber](http://www.ayqy.net/blog/dive-into-react-fiber/) 11 | - [浅谈 instanceof 和 typeof 的实现原理](https://juejin.cn/post/6844903613584654344) 12 | - [字节跳动最爱考的前端面试题:计算机网络基础](https://juejin.cn/post/6939691851746279437) 13 | - [前端年后真题](https://bitable.feishu.cn/app8Ok6k9qafpMkgyRbfgxeEnet?from=logout&table=tblEnSV2PNAajtWE&view=vewJHSwJVd) 14 | - [精读《Webpack5 新特性 - 模块联邦》](https://zhuanlan.zhihu.com/p/115403616) 15 | - [hash 和 history 两种模式的区别](https://www.jianshu.com/p/3fcae6a4968f?open_source=weibo_search) 16 | - [TypeScript 高级特性](https://mp.weixin.qq.com/s/VWggn-5JdbJon6ZzxHPqHw) 17 | - [Chrome Performance 常见名词解释(FP, FCP, LCP, DCL, FMP, TTI, TBT, FID, CLS)](https://blog.csdn.net/c_kite/article/details/104237256) 18 | - [理解 require 原理和实现一个 require](https://zhuanlan.zhihu.com/p/75563307) 19 | - [Http2.0 和 Http3.0](https://www.cnblogs.com/roy1/p/13721842.html) 20 | - [对 babel-transform-runtime,babel-polyfill 的一些理解](https://www.jianshu.com/p/7bc7b0fadfc2) 21 | - [webpack 是如何实现动态导入的 22 | ](https://juejin.cn/post/6844903888319954952) 23 | -------------------------------------------------------------------------------- /posts/css3的blur如何消除边缘透明效果.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: posts 3 | title: css3的blur如何消除边缘透明效果 4 | date: 2018-12-03 10:02:47 5 | categories: css相关 6 | tags: [css] 7 | --- 8 | 9 | 我们都知道css3的功能强大之处,大多数也用过blur的属性去模拟ios的磨砂效果,web页面中,ios有自带磨砂效果的css属性,那就是 10 | ```css 11 | backdrop-filter:blur(10px); 12 | ``` 13 | 但是该css属性并不支持ios以外的设备,所以我们能做的只是通过blur来实现类似的显示效果 14 | ```css 15 | filter: blur(10px); 16 | -webkit-filter: blur(10px); 17 | ``` 18 | 但是 19 | **该属性是有一个问题的,当blur的数值很大的时候,其元素周边会有半透明的效果,底部的背景色会被看到**,前一段时间在做这个效果的时候看到一个解决方法,觉得确实很灵性,代码如下 20 | ```scss 21 | // 添加blur模糊效果 22 | // 如果不是绝对定位,父元素需要设置相对定位 23 | // $blur 为模糊的数值 24 | // $height 区域的高度 25 | // $position 为位置 默认50% 26 | @mixin blur($blur, $height: auto, $position: 50%, $scale: 1.5) { 27 | position: absolute; 28 | top: 0; 29 | left: 0; 30 | right: 0; 31 | background-repeat: no-repeat; 32 | background-size: cover; 33 | background-position: $position; 34 | -webkit-filter: blur($blur); 35 | filter: blur($blur); 36 | -webkit-transform: scale($scale); 37 | transform: scale($scale); 38 | overflow: hidden; 39 | @if ($height == auto) { 40 | bottom: 0; 41 | } 42 | @else { 43 | height: $height; 44 | } 45 | } 46 | ``` 47 | 比如我想让class为test-blur的元素设置背景虚化效果(该元素的夫元素必须要设置超出隐藏) 48 | ```scss 49 | .test-blur { 50 | @include blur(20px); 51 | } 52 | ``` -------------------------------------------------------------------------------- /posts/对象实例化的时候经历了什么?.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 对象实例化的时候经历了什么? 3 | date: 2019-05-13 22:29:44 4 | categories: javascript 5 | tags: [js, "面试"] 6 | --- 7 | 8 | 我们都知道 js 是浏览器端的脚本语言,基本上大多数语言就是面向对象的编程模式,javascript 也是有这类编程模式(OOP),一切皆为对象,而这种场景适用于组件的封装,以及很多场景,比如,如果我想写一个 dog 的构造函数,那我们需要继承于动物的父类,也叫做基类,但是呢今天我们不说对象的继承,单纯的说说对象的实例化 9 | 10 | 11 | 12 | 就拿时间对象 Date 来说,我们在想获取当前的时间信息的时候需要 13 | 14 | ```js 15 | const date = new Date(); 16 | ``` 17 | 18 | 此时我们可以调用 Date.prototype 上的各种属性和方法 19 | 20 | ```js 21 | date.getTime(); 22 | date.getFullYear(); 23 | ``` 24 | 25 | 实例化完成之后,date 就是 Date 对象的实例,判断一个变量是否是某一个对象的实例 26 | 27 | ```js 28 | date instanceof Date; // true 29 | ``` 30 | 31 | 也就是说 date.**proto**属性指向 Date.prototype 32 | 33 | ```js 34 | date.__proto__ === Date.prototype; // true 35 | ``` 36 | 37 | **如果我们还要实例化一个对象** 38 | 39 | ```js 40 | const date1 = new Date(); 41 | date === date1; // false 42 | (date.__proto__ === date1.__proto__) === Date.prototype; // true 43 | ``` 44 | 45 | **由于 this 的指向不同,date 并不等于 date1** 46 | 但是,他们走了同样的实例化步骤 47 | 48 | - 实例化的时候,会创建一个新的对象 {} 49 | - 将构造函数的 this 的值赋给新的对象,也就是 this 的指向指向新的对象 50 | - 在构造函数内执行代码,constructor 的初始化,也就是 new Object() 括号里的参数内容,设置对象的自定义属性和方法 51 | - 设置新对象的**proto**属性指向构造函数的 prototype 对象 52 | - 返回一个新对象 53 | 54 | 所以我们发现两个实例化的对象不相等,但是实例化的对象的**proto**是想等的,这就是原因 55 | 以上就是实例化对象的过程了 56 | -------------------------------------------------------------------------------- /init.js: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { exec } from 'child_process'; 3 | import fs from 'fs'; 4 | import path from 'path'; 5 | import { fileURLToPath } from 'url'; 6 | import dateFormat, { masks } from "dateformat"; 7 | 8 | const __filename = fileURLToPath(import.meta.url); 9 | const __dirname = path.dirname(__filename); 10 | const pathPosts = path.join(__dirname, "posts"); 11 | 12 | const n = (process.argv.slice(2)[0] || "").trim(); 13 | const noDir = process.argv.slice(3)[0] === '-n' 14 | 15 | if (n) { 16 | try { 17 | const now = new Date(); 18 | !noDir && fs.mkdirSync(`${pathPosts}/${n}`) 19 | fs.writeFileSync(`${pathPosts}/${n}.md`, (` 20 | --- 21 | title: '${n}' 22 | date: ${dateFormat(now, "yyyy-mm-dd HH:MM:ss")} 23 | categories: 24 | tags: [] 25 | --- 26 | `) 27 | .replace(/^\n/, '').replace(new RegExp( 28 | `^${' '.repeat(6)}`, 29 | 'gm' 30 | ), '') 31 | .replace(/(\x20*)$/gm, '')); 32 | 33 | console.info(`${chalk.green("Create Success:")} ${chalk.underline(`${pathPosts}/${n}.md`)}`) 34 | exec(`code ${pathPosts}/${n}.md`) 35 | } catch (e) { 36 | if (e.errno === -17) { 37 | console.info(chalk.yellow("File already exists, create failed.")) 38 | } else { 39 | console.error(e); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /posts/webpack的树摇功能使用.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: webpack的树摇功能使用 3 | date: 2019-06-24 16:03:50 4 | categories: javascript 5 | tags: [webpack, js] 6 | --- 7 | 8 | webpack2.0之后就开始支持树摇的操作,树摇的意思就是摇掉不需要或者未使用的代码,之前在写 d-utils 库的时候发现,当方法越来越多,代码越来越多的时候,在项目中引入之后打包,发现打包的js达到300kb,然而对于我实际上用的方法也就5个左右,这会造成打入了很多无用的代码,于是优化开始了 9 | 10 | 11 | tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块语法的 静态结构 特性,例如 import 和 export。这个术语和概念实际上是由 ES2015 模块打包工具 rollup 普及起来的。 12 | 13 | ### 如何让自己的代码支持tree-shaking? 14 | - 使用 ES2015 模块语法(即 import 和 export) 15 | - 通过将 mode 选项设置为 production,启用 minification(代码压缩) 和 tree shaking。 16 | - 无副作用的代码 17 | - class包装方法无法tree-shaking 18 | 19 | ### 优化的方式 20 | ```ts 21 | // a.ts 22 | export default class A { 23 | func1 () {}, 24 | func2 () {} 25 | } 26 | 27 | // main.ts 28 | import A from 'a' 29 | A.func1() 30 | ``` 31 | 此时打包该项目的时候会发现,我并没有使用的func2也被打进包里,如果想移除func2只能这样 32 | 33 | ```ts 34 | // a.ts 35 | export const func1 = () => {} 36 | export function func2 () {} 37 | 38 | // main.ts 39 | import { func1 } from 'a' 40 | func1() 41 | ``` 42 | 这时候是可以成功移除掉func2的代码的,至于为什么class不可以 https://github.com/rollup/rollup/issues/349 43 | 44 | ### 结果 45 | 将部分文件的代码优化成方法的export导出形式,对一个项目的同样代码进行打包查看结果, 46 | 优化之前: 47 | ![优化之前](webpack的树摇功能使用/before.png) 48 | 优化之后: 49 | ![优化之前](webpack的树摇功能使用/after.png) 50 | -------------------------------------------------------------------------------- /posts/h5的后台切换到前台的数据更新.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: h5的后台切换到前台的数据更新 3 | date: 2019-04-03 23:08:52 4 | categories: html 5 | tags: [js, html] 6 | --- 7 | 8 | ### 背景 9 | 10 | 今天遇到了一个需求,严格来说应该是一个 bug,因为页面是一个倒计时,倒计时结束之后页面会进入正在直播的状态,但是有人反馈在锁屏一段时间之后,实际显示的倒计时时长有问题,实际上就相当于从锁屏时候继续执行倒计时,到导致实际数据与显示数据会有误差(以及开始直播了,页面还在倒计时的状态) 11 | 12 | ### 技术分析 13 | 14 | ios 的后台下 js 的代码执行会被挂起,这个是由于 ios 系统的后台机制决定的,从前台切到后台的时候,app 会被挂起假死状态,代码也不会被继续执行,对于网页来说 js 的代码是无法继续执行的,所以需要找到从后台切换到前台的触发条件,再执行重新初始化的操作 15 | 16 | ### visibilitychange 17 | 18 | visibilitychange 是一个网页可见不可见的触发机制,pc 端主要是浏览器标签页 tab 切换的触发条件,假如用户在 A 页面添加该事件 19 | 20 | ```js 21 | // A 22 | document.addEventListener("visibilitychange", function () { 23 | document.title = document.hidden ? "你切到其他页面了" : "终于来了你"; 24 | }); 25 | ``` 26 | 27 | 用户切到其他页面的时候 visibilitychange 事件触发,显示对应文案,切到 A 页面也会执行该事件绑定的方法 28 | 29 | 移动端上也跑了以下发现该事件可以被触发(IOS 下) 30 | 触发条件:微信在打开网页且未关闭的时候 31 | 32 | - 微信网页打开,手机锁屏再解锁,执行该事件 33 | - 按 HOME 键返回桌面,点击进入微信,执行该事件、 34 | - APP 之间的切换,切回微信的时候,执行该事件 35 | 36 | 代码实现 37 | 38 | ```ts 39 | private componentDidmount () { 40 | // 第一次进入页面初始化 41 | this.initTiktok() 42 | document.addEventListener('visibilitychange', function () { 43 | console.log(document.visibilityState); 44 | // 清除定时器再重新初始化定时器 45 | this.initTiktok() 46 | }) 47 | } 48 | 49 | private initTiktok () { 50 | // **** 51 | } 52 | ``` 53 | 54 | 此时再继续看倒计时流程的时候,后台切换到前台之后会重新获取数据之后重新初始化倒计时,这时候时间就可以实时对应的上了 55 | -------------------------------------------------------------------------------- /posts/正则学习.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 正则学习(一) 3 | date: 2019-07-17 22:54:43 4 | categories: javascript 5 | tags: js 6 | --- 7 | 8 | 正则表达是基本在所有的计算机语言中都会用到。因为项目中不太常用,而且基本的正则校验都已经写成通用代码,满足基本的业务场景,但是却没有真正用心去学过,遂开始学习了解 9 | 10 | 11 | ### 常用的正则表达式字符 12 | 13 | #### 元字符 14 | | 名称 | 含义 | 15 | |:---|:---------| 16 | |`.`|匹配除换行符以外的所有字符| 17 | |`\s`|任意空白符(换行符,制表符,空格)| 18 | |`\S`|匹配任意非空字符串| 19 | |`\b`|匹配单词边界,匹配单词的开头和结尾| 20 | |`\B`|匹配非单词边界| 21 | |`\d`|匹配数字0-9,等同于[0-9]| 22 | |`\D`|匹配除换行符以外的所有字符| 23 | |`\w`|匹配数字下划线,字母,且包含大小写,等同于[A-Za-z0-9_]| 24 | |`\W`|匹配数字下划线,字母,且包含大小写以外的字符| 25 | |`^`|匹配输入字符串的开始位置,/^A/ 并不会匹配 "an A" 中的 'A',但是会匹配 "An E" 中的 'A'| 26 | |`$`|匹配输入字符串的结束位置,/A$/ 会匹配 "an A" 中的 'A',但是不会匹配 "An E" 中的 'A'| 27 | 28 | #### 分组符 29 | | 名称 | 含义 | 30 | |:---|:---------| 31 | |`()`|用小括号来指定子表达式| 32 | |`[]`|字符集合,[a-z0-9A-C]| 33 | |`{}`|表示范围,{0, 1}| 34 | 35 | #### 修饰符 36 | | 名称 | 含义 | 37 | |:---|:---------| 38 | |`i`|忽略大小写| 39 | |`g`|全局匹配| 40 | |`m`|多次匹配| 41 | 42 | #### 量词 43 | | 名称 | 含义 | 44 | |:---|:---------| 45 | |`*`|匹配任意次数,等同于{0,}| 46 | |`+`|匹配一次或多次,至少一次,等同于{1,}| 47 | |`?`|匹配0或1次,等同于{0,1}| 48 | |`{m,n}`|匹配m到n次| 49 | 50 | ### 代码实测 51 | ```ts 52 | const Exp1 = /\bhi\b/ 53 | // \b 匹配单词的开始或结束 54 | LogUtils.logInfo(Exp1.test('hi')) // true 55 | LogUtils.logInfo(Exp1.test('hi2')) // false 结尾不是i结尾 56 | 57 | const Exp2 = /\bhi\b.\bLucy\b/ 58 | LogUtils.logInfo(Exp2.test('hi Lucy')) 59 | LogUtils.logInfo(Exp2.test('hi aaa Lucy')) 60 | ``` 61 | -------------------------------------------------------------------------------- /posts/递归.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 递归 3 | date: 2019-03-13 22:24:33 4 | categories: javascript 5 | tags: ['js'] 6 | --- 7 | 8 | 递归 9 | 10 | ### 概念 11 | - 方法自己调用自己 12 | - 有终止条件 13 | 14 | ### 初识 15 | Q:获取1-100的累加总和 16 | 方法为sum(100) 100为累计的数值点 17 | 那么,总和就是 18 | ```js 19 | function sum (n) { 20 | if (n === 1) return 1 21 | return n + sum(n-1) 22 | } 23 | sum(100) // 5050 24 | ``` 25 | 26 | Q: 获取1-100能被5整除的元素的集合 27 | ```js 28 | function sum5 (n) { 29 | console.log(n) 30 | if (n <= 0) return 0 31 | if (n % 5 === 0) return n + sum5(n - 5) 32 | return sum5(n - 1) 33 | } 34 | sum5(100) // 1050 35 | ``` 36 | 37 | ### 规律 38 | 不满足条件直接跳出递归 39 | 总是自身调用自身 40 | ```js 41 | sum(5) = 5 + sum(5 - 1) // 15 42 | = 5 + 4 + sum(4 - 1) 43 | = 5 + 4 + 3 + sum(3 - 1) 44 | = 5 + 4 + 3 + 2 + sum(2 - 1) 45 | = 5 + 4 + 3 + 2 + 1 + sum(1 - 1) 46 | ``` 47 | 其中 sum(0) = 0 结果为5 + 4 + 3 + 2 + 1 = 15 48 | 49 | ### 使用场景 50 | - 多层数据操作 51 | 52 | ### 尾递归 53 | 就拿刚刚的sum方法来说,在浏览器执行sum(100000) 54 | 会发现报错:`Maximum call stack size exceeded` 55 | 这会导致堆栈溢出 56 | 而解决方法就是 **`尾递归`** 57 | ```js 58 | function sum (n, total = 0) { 59 | if (n === 0) return total 60 | return sum(n - 1, n + total) 61 | } 62 | ``` 63 | 而之前的sum5方法也可以使用尾递归计算 64 | ```js 65 | function sum5 (n, total = 0) { 66 | if (n === 0 ) return total 67 | if (n % 5 !== 0) return sum5(n - 1, total) 68 | return sum5(n - 5, n + total) 69 | } 70 | ``` 71 | 72 | ##### 但是我也执行了一下发现好像都会堆栈溢出。。。 73 | > 原因: JS的解释器很少对尾递归的优化实现 -------------------------------------------------------------------------------- /posts/【面试题】服务端渲染.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 【面试题】服务端渲染 3 | date: 2021-03-29 18:50:12 4 | categories: 服务端渲染 5 | tags: [服务端渲染, 面试] 6 | --- 7 | 8 | ### 什么是服务端渲染 9 | 10 | 通俗的说就是 服务端生成 html 资源,在客户端样式渲染,注册挂载事件。 11 | 12 | ### React SSR 实现过程?原理?注意事项? 13 | 14 | ![React SSR 原理](https://www.daiwei.site/static/blog/【面试题】服务端渲染/reactSSR.png) 15 | 16 | - 客户端访问 `/home` 页面 17 | - koa router 引入 react home 组件 18 | - renderToString 将组件转换为 html 字符串。 19 | - 服务端返回 html 字符串,返回类型 html 20 | - 在服务端直出带数据的页面时,将 store 存储在全局变量中,为前端 store 数据获取做准备的过程叫做数据注水。 21 | - 客户端渲染页面,同时加载 js 资源 22 | - 前端获取来自全局变量中的数据并填充自身,用于页面数据渲染的过程叫数据脱水。 23 | - 客户端通过 `hydrate` 重新 render page 组件,生成虚拟 dom,之后操作和客户端渲染一致。 24 | 25 | ### SSR 比 CSR 快吗? 26 | 27 | 首屏渲染 SSR 比 CSR 快,加载其他页面时,SSR 会从头重新加载,而 SPA 则只加载一部分模块,甚至提前与加载组件。 28 | 29 | ### 为什么 SSR 首屏渲染快 30 | 31 | 主要体现在 http 请求数量上,ssr 只需要请求一个 html 文件就能展现出页面,虽然在服务器上会调取接口,但服务器之间的通信要远比客户端快,甚至是同一台服务器上的本地接口调取。 32 | 33 | ### ReactDom.render 与 ReactDom.hydrate 34 | 35 | - React16 的 `hydrate` `时,hydrate` 的策略与 `render` 的策略不一样,其并不会对整个 dom 树做 dom patch,其只会对 text Content 内容做 patch 36 | 37 | - `render` 如果初始 DOM 和当前 DOM 之间存在差异,则可能会更改您的节点。`hydrate`将仅附加事件处理程序。 38 | 39 | ### renderToString 和 renderToStaticMarkup 的区别。 40 | 41 | - `renderToString` 方法渲染的时候带有 `data-reactid` 属性. (render 方法会使用 innerHTML 的方法重写 #react-target 里的内容) 42 | - `renderToStaticMarkup` 则没有 `data-reactid` 属性, 可以节省一点流量。(前端 react.js 会认识之前服务端渲染的内容, 不会重新渲染 DOM 节点, 前端 react.js 会接管页面,执行 componentDidMout 绑定浏览器事件等。) 43 | 44 | `data-reactid` 简单的说就是 react 组件 的一个唯一标示 id。 45 | -------------------------------------------------------------------------------- /posts/TypeScript初识.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: TypeScript初识 3 | date: 2019-03-28 23:22:27 4 | categories: typescript 5 | tags: [js, ts] 6 | --- 7 | 8 | ### 背景 9 | JavaScript是一种弱类型语言,也就是所有的变量命名都是var来定义,es6多了const,和let来定义变量,所有类型的变量都是这几种方式定义,不做类型限制会导致代码在执行的时候会有执行错误如: 10 | ```js 11 | let a = 1 12 | a.toFixed() 13 | 14 | // 此时如果a被赋值了一个字符串 15 | a = '1' 16 | a.toFixed() // 此时则会报错 因为String对象下并没有toFixed方法 17 | ``` 18 | 如果使用typescript定义一个变量为number类型,则不会允许字符串替换之前的number变量,所以`很大程度上避免`了这个问题 19 | ```ts 20 | let a: number = 1; 21 | a.toFixed() 22 | 23 | a = '1' // 此时就会提示错误,用户则会知道字符串无法替换,同样,也不存在toFixed方法 24 | ``` 25 | 很大程度上避免是指在开发时候遇到静态数据替换的时候可以避免,当有动态数据赋值的时候,好像还是没法做到限制 26 | 27 | 以下只是常用的地方的记录 28 | 29 | ### 变量类型定义 30 | ```ts 31 | const ts: number = 1; 32 | const ts: string = 2; 33 | const ts: any = 3; 34 | const ts: boolean = true; 35 | const ts: string[] = ['1', '2', '3']; 36 | const ts: any[] = ['1', 1, undefind]; 37 | const ts: number = [1, 2, 3]; 38 | const ts: Array = [1, 2, 3]; 39 | enum Type { 40 | YES, // 0 41 | NO // 1 42 | } 43 | function test (): void {}; // 没有返回值的方法 44 | function test (): number { 45 | return 1 // 返回值为1的方法 46 | }; 47 | const ts: null = null; 48 | 49 | // 对象的定义 比如定义一个学生,有性别,名字,年龄 50 | interface IUserInfo { 51 | name: string; // 中文或者英文 52 | sex: number; // 0 1 53 | age: number; // 12 54 | } 55 | const user: IUserInfo = { 56 | name: 'dw', 57 | age: 26, 58 | sex: 1 59 | } 60 | ``` 61 | 62 | ---------- 63 | ps: 记录自己在做项目开发中使用到typescript感受到方便实用的地方 64 | 65 | 未完待续 66 | -------------------------------------------------------------------------------- /posts/web安全-XSS.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: web安全-XSS 3 | date: 2020-10-11 16:47:23 4 | categories: web安全 5 | tags: [web安全] 6 | --- 7 | 8 | ### 一起学习了解一下 web安全相关的知识 9 | 10 | ### XSS(Cross Site Scripting)跨站脚本攻击 11 | 12 | 使用注入的js,html代码导致页面样式出入,功能缺失,植入广告,甚至用户信息泄露的问题 13 | 14 | ### XSS 类型 15 | - 基于反射的XSS攻击 16 | 比如访问一个搜索商品的,如果搜索没有内容,页面显示 XXX 不存在 17 | 1. 用户输入带有参数的url => http://a.com?name= 18 | 2. js 处理 url 中 name 的值 字符串 19 | 3. 直接显示商品名称 写入html 进行页面渲染 js被执行 20 | 21 | - 基于存储型的XSS攻击 22 | 1. 用户输入带有参数的url => http://a.com?name= 23 | 2. js 处理 url 中 name 的值 字符串 24 | 3. 数据通过后端存储之后,回显的时候展示返回name值 25 | 4. 直接显示商品名称 写入html 进行页面渲染 js被执行 26 | 27 | - DOM XSS 28 | document.write, eval, img 等等 29 | 30 | ### 举例 31 | 32 | ```html 33 |
    34 | 37 |
    38 | ``` 39 | 40 | 41 | ```html 42 | 43 | 44 | 45 | 46 |
    47 | 48 | ``` 49 | 50 | ### 安全措施 51 | - 输入的内容需要做过滤或者转译(HTMLEncode) 52 | - 避免直接在cookie 中泄露用户隐私 http only 53 | - 采用POST 而非GET 提交表单 54 | - [Content-Security-Policys](https://www.daiwei.site/blog/detail?id=42) 55 | -------------------------------------------------------------------------------- /posts/web安全-CSRF.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: web安全-CSRF 3 | date: 2020-10-14 16:03:01 4 | categories: web安全 5 | tags: [web安全] 6 | --- 7 | 8 | ### CSRF(Cross—Site Request Forgery)跨站点请求伪造 9 | 10 | CSRF 和 XSS 一样,存在巨大危害性,XSS 是利用站点对于资源的信用进行安全攻击,CSRF 则是利用信任网站的用户信息在自己的恶意网站上请求A的服务并携带来自A的用户信息(cookie),执行恶意代码 11 | 12 | ### 攻击方式 13 | - 用户 在 可信用的网站 A 上登陆自己的账号密码,服务端下发cookie作为用户信息识别的方式 14 | - 此时在用户未退出登陆之前,访问网站 B 15 | - B网站加载一个资源请求 如一张带有特殊地址的图片资源 16 | ```html 17 | 18 | ``` 19 | 此时 B网站会发起一个资源请求并携带request 的cookie信息请求网站A的服务,达成自身目的 20 | 21 | ### 应对方法 22 | ##### cookie 的 SameSite 属性 23 | - `Strict` 完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie 24 | - `Lax` 规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。导航到目标网址的 GET 请求,只包括三种情况:链接,预加载请求 25 | - `None` Chrome 计划将Lax变为默认设置。这时,网站可以选择显式关闭SameSite属性,将其设为None。不过,前提是必须同时设置Secure属性(Cookie 只能通过 HTTPS 协议发送),否则无效。 26 | 下面设置无效 27 | ```bash 28 | Set-Cookie: widget_session=abc123; SameSite=None 29 | ``` 30 | 下面的设置有效。 31 | ```bash 32 | Set-Cookie: widget_session=abc123; SameSite=None;Secure; 33 | ``` 34 | ##### 验证 HTTP Referer 35 | 正常的referer来源是自己的网站,如果第三方网站伪造请求,请求的 referer 和自己的网站来源是不一致的 36 | 37 | ##### 使用token 验证 38 | 使用token验证,服务端以某种策略生辰随机字符串,存在 readis 作为key保存,客户端请求头携带token,服务端通过token在readis查询是否匹配,再执行后续的业务操作,相较于cookie在执行http请求时,token 更安全而且不会被站外作为请求头的字段提交(Local / session storage 不会跨域工作) 39 | 40 | 41 | [跨站请求伪造—CSRF](https://segmentfault.com/a/1190000021114673) 42 | 43 | [Cookie 的 SameSite 属性](http://www.ruanyifeng.com/blog/2019/09/cookie-samesite.html) 44 | -------------------------------------------------------------------------------- /posts/关于闭包.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 关于闭包 3 | date: 2019-10-21 18:18:43 4 | categories: javascript 5 | tags: ['js', '面试'] 6 | --- 7 | 8 | 闭包是js经常遇到的一种场景,其含义是指 `能够访问其他函数作用域中变量的函数` 闭包就是一个函数,该函数可以访问其他函数作用域中的变量 9 | 比如: 10 | ```ts 11 | function test () { 12 | var a = 1 13 | function fn () { 14 | console.log(a) 15 | } 16 | fn() 17 | } 18 | var testFn = test() 19 | testFn() 20 | ``` 21 | 其中,fn就是一个闭包函数,因为他可以访问test函数下的a变量 22 | > 正常来说,当test函数执行完毕之后,他的作用域会被销毁,垃圾回收机制会释放其内存空间,但是实际上并没有,应为fn还存在对于test作用域下的引用,而这个引用就是闭包 23 | 24 | 再如: 25 | 很久以前我们在写jquery插件的时候 26 | ```js 27 | // 这是一个立即执行函数,同样它也是一个闭包 28 | (function ($) { 29 | // ... 30 | })(jQuery) 31 | ``` 32 | > 该方法被执行的时候传入 `jQuery` 对象, 而 `$` 则是作为方法的参数接受 `jQuery` 的值, 代表的就是 `jQuery` 对象,为了避免全局污染 33 | 34 | 再比如: 35 | ```ts 36 | function warp (msg, t) { 37 | setTimeout(function () { 38 | console.log(msg) 39 | }, t) 40 | } 41 | warp('hello, 闭包', 3000) 42 | ``` 43 | setTimeout内部的匿名函数,这个函数有来自warp函数作用域的变量,而这个匿名函数实际上就是一个闭包,因此当300毫秒执行之后,函数可以输出信息 44 | 45 | 再来谈谈一个面试常见的问题 46 | ```ts 47 | for (var i = 0; i < 5; i++) { 48 | setTimeout(function () { 49 | console.log(i) 50 | }, 1000) 51 | } 52 | ``` 53 | 这段代码的打印结果是5个5,原因就是i定义在全局对象中,在for循环执行完成之后i的值实际结果为5了,执行setTimeout代码中的i时,全局作用域的i已经是5了,结果就是5个5 54 | 那么实际上这里可已使用闭包,在循环的时候产生一个私有作用域,在私有作用域中访问当前i的值 55 | ```ts 56 | for (var i = 0; i < 5; i++) { 57 | (function (selfI) { 58 | setTimeout(function () { 59 | console.log(selfI) 60 | }, 1000) 61 | })(i) 62 | } 63 | ``` 64 | 此处的setTimeout方法外层包含一个立即执行函数,通过i作为参数传递,selfI接受i的值,匿名函数中的私有变量selfI,被setTimeout中的函数调用,实现了一个闭包,这时候的selfI就是当前i的值,而打印的结果也就是 `0, 1, 2, 3, 4` 65 | 66 | #### 闭包的优缺点 67 | 优点 68 | - 私有作用域,防止变量命名冲突 69 | 70 | 缺点 71 | - 比普通函数更占用内存,解决方法就是将引用的函数对象变量设置为null 72 | -------------------------------------------------------------------------------- /posts/babel-plugin-transform-runtime与-babel-plugin-external-helpers.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: '@babel/plugin-transform-runtime与@babel/plugin-external-helpers' 3 | date: 2020-07-09 22:07:37 4 | categories: 5 | tags: 6 | --- 7 | 8 | ### 先来看看 `@babel/preset-env` 9 | 作为 babel config 的预设工具, @babel/preset-env 可以根据当前的运行环境,确定所需要的plugin和polyfill 10 | ```js 11 | presets: [ 12 | [ 13 | "@babel/preset-env", { 14 | modules: false, 15 | useBuiltIns: false 16 | } 17 | ], 18 | ], 19 | ``` 20 | 主要是这个 `useBuiltIns` 的使用 21 | > 此选项配置如何@babel/preset-env处理polyfills, "usage"| "entry"| false,默认为false。 22 | 23 | `usage` 会参考当前运行环境和代码使用到的特性自动做 polyfill,无需其他配置 24 | `entry` 需要手动配置 polyfills 入口,可以配置单个特性的引入,也可以直接手动引入 polyfill(会有多余转换代码引入) 25 | `false` 不引入polyfill 26 | 27 | ### 再看 `@babel/plugin-transform-runtime` 28 | `@babel/plugin-transform-runtime` 是一个babel插件 29 | 1. 会直接引入`@babel/runtime`下的helper的通用函数, 避免在编译后的输出中出现重复。运行时将被编译到您的构建中的问题,依赖 `@babel/runtime` 30 | 2. 其次是为代码创建一个沙盒环境,提供了诸如内置插件Promise,Set和Map那些会污染全局范围的特性,防止污染全局作用域 31 | 32 | ### `@babel/plugin-external-helpers` 33 | 用来生成一段代码,包含 babel 所有的 helper 函数,这些函数都放在 `@babel/helpers`,如果 babel 编译的时候检测到某个文件需要这些 helpers,在编译成模块的时候,会放到模块的顶部,如果不设置引入通用的 helper.js 则会导致代码重复, 34 | 引入通用js的方式: 35 | 1.生成 helpers.js 文件, 36 | ```code 37 | node_modules/.bin/babel-external-helpers > helpers.js 38 | ``` 39 | 2.配置babel.config.js 40 | ```js 41 | plugins: "plugins": [ 42 | "@babel/plugin-external-helpers" 43 | ] 44 | ``` 45 | 3.主入口引入 script.js 46 | ```js 47 | require('./helpers.js'); 48 | // ... 49 | ``` 50 | 51 | > 如果使用了 transform-runtime,则不需要生成 helpers.js 文件 52 | 53 | ### 参考于 54 | https://babeljs.io/docs/en/babel-runtime 55 | https://babeljs.io/docs/en/babel-plugin-external-helpers 56 | https://babeljs.io/docs/en/babel-plugin-transform-runtime/ 57 | 58 | -------------------------------------------------------------------------------- /posts/English learning record.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: English learning record - 1 3 | date: 2022-06-11 22:48:01 4 | categories: English 5 | tags: [english] 6 | --- 7 | 8 | ```code 9 | 10 | I was a little bit busy 11 | 12 | limited times 13 | 14 | work overtime 15 | 16 | I start work at 10 am 17 | 18 | go though // 经历 19 | 20 | lockdown // 封锁 21 | 22 | It started from March 23 | 24 | I have never heard of this (the TV series This is US) 25 | 26 | co-worker // 同事 27 | 28 | colleague // 同事之间 29 | 30 | definitely. absolutely. exactly // 绝对 确实 31 | 32 | classmate // 同学 33 | 34 | We became roommate 35 | 36 | complex // 建筑群 复杂的 37 | 38 | comfortable 39 | 40 | I'd like to take a walk along the river 41 | 42 | Law 43 | 44 | apartment = flat // 公寓 45 | 46 | kitchen // 厨房 47 | 48 | rice cooker // 电饭煲 49 | 50 | I will find another apartment if I don't relocate to Canada. 51 | 52 | working visa 53 | 54 | Toronto // 多伦多 55 | 56 | atmosphere // 氛围 57 | 58 | We are based in XX 59 | 60 | Another reason is that .... 61 | 62 | You are fluent in English 63 | 64 | first-tier city 65 | 66 | booking apps 67 | 68 | have some thing done 69 | 70 | Could you please tell me where i can ... 71 | 72 | luggage // 行李 73 | 74 | weigh // 称重 75 | 76 | retire 77 | 78 | Personnel // 人员 79 | 80 | make a reservation 81 | 82 | available 可用的 83 | 84 | economy // 经济 节约 85 | 86 | first or economy class 87 | 88 | make an appointment 89 | 90 | 91 | booked a ticket 92 | 93 | Can I confirm my seat? 94 | 95 | boarding pass // 登机证 96 | 97 | luggage // 行李 🧳 98 | 99 | luggage weigh // 行李 称重 100 | 101 | Check-in Counter // 报到柜台 签到柜台 102 | 103 | 104 | ``` 105 | -------------------------------------------------------------------------------- /posts/个人网站2-0版本总结个未来规划.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 个人网站2.0版本总结个未来规划 3 | date: 2018-3-6 11:39:26 4 | categories: 5 | tags: ['总结'] 6 | --- 7 | 8 | 从2016年3月自学前端到现在已经有两年的时间了,自己也喜欢瞎捣鼓一些东西,个人也尝试做自己个人的网站买了相关的配置,个人网站版本迭代到第二个版本,写下这个文档主要是为了记录这两年个人网站的开发历程和未来网站的开发规划 9 | 10 | ## 个人网站2.0版本总结个未来规划 11 | ### 建站初衷: 12 | - 看到别人的作品在网上能够被人看到,觉的网站开发的魅力深深的吸引了我,想自己也做一个 13 | - 希望能了解建站的一体化流畅操作从而了解网页开发 14 | - 工作之余学习方式 15 | - 希望有自己的作品 16 | 17 | ### 建站准备 18 | #### 域名 19 | 当时购买了一个域名,购买域名很便宜第一年通常也就50以内,购买域名之后还需要备案,需要有指定背景(当时是阿里寄过来的一个背景图纸),于是就站在背景前面拍了张照片并上传上去,执行后续的备案流程操作。提交成功之后大概等了十多天才审核通过,当时等的急的哦! 20 | #### 虚拟主机 21 | 当时买的是共享虚拟主机,有流量限制的,之后环境搭建好了之后发现用的太快了之后升级了一个独享虚拟主机不限制流量的那种,就开始我的个人网站建设的准备了 22 | #### 配置 23 | 阿里云个人后台配置的,网站查阅资料,经过一段时间琢磨真的搭建起来了 那个感觉真的是爽啊! 24 | 25 | 26 | ### 初版: 27 | ![v1.0](个人网站2-0版本总结个未来规划/html1.0.jpg) 28 | #### 建设功能: 29 | 首页展示,个人页信息,博客,作品信息,个人中心(后台管理),音乐播放 30 | #### 技术框架与库: 31 | javascript,html5,css3,jquery,ajax,bootstrap,部分页面使用基于AMD规范的模块化开发 32 | #### 进度: 33 | 2016-6 至 2016-8 34 | #### 问题: 35 | 1. 首页由于添加很多图片,以及音频资源请求导致页面加载很慢很慢,当时很了解html5音频的相关操作,代码还是比较不符合规范和逻辑的,之后有优化音频播放器,但是并没有再更新旧版本首页的播放器 36 | 2. 大多数都是静态页面,很多东西修改都不是通过后台操作改动,静态页面改动很频繁也很麻烦 37 | 3. 页面设计不合理,元素嵌套过多,代码混乱 38 | 4. 经过1年的技术学习和沉淀,加上刚开始学习vue框架,就开始想给之前的网站改版并使用vue2.0完成个人网站的开发 39 | 40 | ### 2.0版: 41 | ![v1.0](个人网站2-0版本总结个未来规划/html2.0.jpg) 42 | #### 建设功能: 43 | 首页bing壁纸和个人壁纸,个人状态信息,音乐,资源搜索,等等各种功能 44 | #### 技术框架与库: 45 | javascript,html5,css3,vue2.0,vuex,axios,bootstrap栅格系统css代码等,使用es6语言开发 46 | #### 进度: 47 | 2017-5 至 至今 48 | 相对于v.10优化的地方 49 | 1. 单页面spa应用,用户体验好,按需加载首页加载速度提升了10倍以上 50 | 2. 代码书写相对之前有所提升,网站风格提升 51 | 3. 扩展的功能多资源搜索,每日笑话,新闻头条,以及版本信息等等 52 | 4. 使用当前热门的vue技术,开发更方便,组件化开发深入人心,虚拟dom的渲染机制使得页面性能更加优越 53 | 问题: 54 | 1. 部分地方使用了jqeury的animate实现歌词滚动的效果,作为一个基于vue的项目 使用jquery显得有点格格不入(已解决) 55 | 2. 项目越来越大的时候不方便管理,应该是开始代码的结构没有构思好,不方便维护 56 | 3. 单页面不利于SEO,虽说有相关插件可以将静态页面单独分隔,但是用户体验必定大打折扣 57 | 58 | ### 未来规划 59 | 1. 未来的网站可能还会基于vue,或者重写,但是务必解决的是seo的问题 60 | 2. 加入用户交互。初步的想法是:部分功能需要用户交互,听歌,建议,个人标签点赞等等都需要登录后才能操作(已实现)) 61 | 3. 后台管理页面的搭建,方便个人后台管理操作(计划使用react + ant) 62 | 4. 拓展新功能,优化或重构之前的代码 63 | 5. 开发自己通用的组件库并发布npm包,方便使用(d-utils) 64 | 65 | 直到现在我还是在更新自己的网站,我喜欢这个行业,也会一直继续下去,努力成为一名合格的前端开发工程师! 66 | -------------------------------------------------------------------------------- /posts/个人通用js方法库d-js-utils.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 个人通用js方法库d-js-utils 3 | date: 2018-12-10 12:56:53 4 | categories: 通用组件或工具 5 | tags: [js] 6 | --- 7 | 8 | 9 | 10 | ![d-utils](https://www.daiwei.site/static/blog/个人通用js方法库d-js-utils/bg.png) 11 | 从刚开始做前端开发开始,就有想过收集一些通用的js方法,或者平时自己写的觉得不错的一些方法,然后收集起来,方便以后使用,但是刚开始虽然收集了一些通用方法,但是在使用过程中相对比较麻烦,随着npm项目管理以及webpack工具在现在的项目中必不可少,开始寻思收集一些通用的js方法,并发布到npm,等以后什么项目用到了直接npm install 就可以直接使用了,方便,而且也是一个很好的累积过程 12 | 13 | ## 产生的原因 14 | 从刚开始做前端开发开始,就有想过收集一些通用的js方法,或者平时自己写的觉得不错的一些方法,然后收集起来,方便以后使用,但是刚开始虽然收集了一些通用方法,但是在使用过程中相对比较麻烦,随着npm项目管理以及webpack工具在现在的项目中必不可少,开始寻思收集一些通用的js方法,并发布到npm,等以后什么项目用到了直接npm install 就可以直接使用了,方便,而且也是一个很好的累积过程 15 | 16 | ## 关于d-utils 17 | 该方法一共包含以下属性,每个属性收集了对应的方法内容 18 | 19 | - `DomUtils` 20 | 该属性主要时针对dom元素相关的方法,针对于元素的一些操作 21 | 22 | - `DeviceUtils` 23 | 设备相关的检测与方法 24 | 25 | - `StoreUtils` 26 | 该属性主要是对于数据的操作 27 | 28 | - `HttpRequestUtils` 29 | 基于axios请求的封装 30 | 31 | - `ExpUtils` 32 | 该属性是d-js-utlis里的一个类,此属性包含对于一些字符,或者元素判断是否符合要求 33 | 34 | - `GenericUtils` 35 | 其他相关js工具代码,通用工具类 36 | 37 | - `LogUtils` 38 | 日志相关 39 | 40 | - `PerformanceUtils` 41 | 浏览器性能相关 42 | 43 | - `UrlUtils` 44 | url地址的一系列操作 45 | 46 | - `WeixinUtils` 47 | 微信jssdk相关的方法 48 | 49 | - `ImageUtils` 50 | 图片合成相关 51 | 52 | ## 相关地址 53 | [文档地址: https://ifmiss.github.io/d-js-utils/](https://ifmiss.github.io/d-js-utils/) 54 | 55 | [项目地址: https://github.com/ifmiss/d-js-utils/](https://github.com/ifmiss/d-js-utils/) 56 | 57 | ## 快速使用 58 | #### 安装 (1.1.14及以上的版本) 59 | 使用npm安装 `d-utils` 依赖 60 | ```bash 61 | npm i d-utils 62 | ``` 63 | yarn 64 | ```hash 65 | yarn add d-utils 66 | ``` 67 | #### 使用 68 | 获取所有方法 69 | ```js 70 | import Dutils from 'd-utils' 71 | Dutils.DomUtils.addClass(document.body, 'd-utils') 72 | ``` 73 | 按需引入 74 | ```js 75 | import { DomUtils, LogUtils } from 'd-utils' 76 | DomUtils.addClass(document.body, 'd-utils') 77 | LogUtils.logInfo('d-utils') 78 | ``` 79 | 80 | 直接引用js 81 | ```html 82 | 83 | 86 | ``` 87 | 复制一下代码在控制台打印一下,看会有什么变化 88 | ```js 89 | Dutils.DomUtils.cssFilter(document.body, 'grayscale', 1) 90 | ``` 91 | -------------------------------------------------------------------------------- /posts/【js高级程序】工作者线程-SharedWorker.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 【js高级程序】工作者线程-SharedWorker 3 | date: 2020-11-09 08:18:18 4 | categories: javascript 5 | tags: [js] 6 | --- 7 | 8 | 红宝书学习记录 9 | 10 | ### *原文整理摘抄自 javascript 高级程序开发(第4版) 第27章* 11 | 12 | ### 共享工作者线程 13 | **共享工作者线程或共享线程与专用工作者线程类似,但可以被多个可信任的执行上下文访问。** 例如, 同源的两个标签页可以访问同一个共享工作者线程。 14 | 15 | #### 创建共享工作者线程 16 | 共享工作者线程与专用工作者线程的一个重要区别在于,虽然 Worker()构造函数始终会创建新实 例,而 SharedWorker()则只会在相同的标识不存在的情况下才创建新实例。 17 | 18 | ```js 19 | // 实例化一个共享工作者线程 20 | // - 全部基于同源调用构造函数 21 | // - 所有脚本解析为相同的 URL 22 | // - 所有线程都有相同的名称 23 | new SharedWorker('./sharedWorker.js'); 24 | new SharedWorker('sharedWorker.js'); 25 | new SharedWorker('https://www.example.com/sharedWorker.js'); 26 | ``` 27 | 脚本字符串都解析到相同的 URL,所以也只会创建一个共享工作者线程 28 | 29 | > 共享线程,顾名思义,可以在不同标签页、不同窗口、不同内嵌框架或同源的其他工作者线程之间 共享。 30 | 31 | 初始化共享线程的脚本只会限制 URL,因此下面的代码会创建两个共享工作者线程,尽管加载了相 同的脚本: 32 | ```js 33 | // 实例化一个共享工作者线程 34 | // - 全部基于同源调用构造函数 35 | // - '?'导致了两个不同的 URL 36 | // - 所有线程都有相同的名称 37 | new SharedWorker('./sharedWorker.js'); 38 | new SharedWorker('./sharedWorker.js?'); 39 | ``` 40 | 41 | #### 使用 SharedWorker 对象 42 | SharedWorker()构造函数返回的 SharedWorker 对象被用作与新创建的共享工作者线程通信的连接点。 43 | SharedWorker 实例化对象有以下属性 44 | - `onerror` 错误监听 45 | - `port` 专门用来跟共享线程通信的 MessagePort。 46 | 47 | #### SharedWorkerGlobalScope 48 | 继承 `WorkerGlobalScope` 与专用工作者线程一样,共享工 作者线程也可以通过 self 关键字访问该全局上下文。 49 | - `name` 可选的字符串标识符,可以传给 SharedWorker 构造函数。 50 | - `importScripts()` 用于向工作者线程中导入任意数量的脚本。 51 | - `close()` 与 worker.terminate()对应,用于立即终止工作者线程。没有给工作者线程提供 终止前清理的机会;脚本会突然停止。 52 | - `onconnect` 与共享线程建立新连接时, 应将其设置为处理程序。 connect 事件包括 MessagePort 实例的 ports 数组,可用于把消息发送回父上下文。 53 | - 在通过 worker.port.onmessage 或 worker.port.start()与共享线程建立连接时都会触 发 connect 事件。 54 | 55 | ```js 56 | // sharedWorker.js 57 | self.addEventListener('connect', function ({ ports }) { 58 | console.info('datadatadatadata', data); 59 | const p = ports[0] 60 | 61 | p.onmessage = function (e) { 62 | console.info('e.data', e.data); 63 | } 64 | }); 65 | 66 | // main.js 67 | const sharedWorker = new SharedWorker('./sharedWorker.js') 68 | const worker = sharedWorker.port; 69 | worker.start(); 70 | worker.addEventListener('message', (e) => { 71 | console.log('来自sharedWorker的数据:', e.data) 72 | }, false) 73 | worker.postMessage({test: 123123}) 74 | ``` 75 | 76 | 77 | -------------------------------------------------------------------------------- /posts/typescript发布npm包添加代码提示.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: typescript发布npm包添加代码提示 3 | date: 2019-04-13 22:26:56 4 | categories: typescript 5 | tags: [ts, js] 6 | --- 7 | 8 | 最近在更新d-utils代码的时候将代码语言换成了typescript,目的有两个,一就是本来就打算重新更新一下这个代码,二就是之前的代码在导入代码之后无法支持代码的自动提示,对于我来说是无法接受的,于是使用ts重新更新了一下 9 | 10 | 1.0.96版本是没有提示的 11 | 1.1.15之后是支持代码提示的 12 | 13 | 实现方式: 14 | ```json 15 | { 16 | "name": "@dw/d-utils", 17 | "version": "1.1.19", 18 | "description": "d-utils", 19 | "main": "./lib/index.js", 20 | "typings": "types/index.d.ts", 21 | "scripts": { 22 | "test": "echo \"Error: no test specified\" && exit 1", 23 | "build": "webpack --mode production && tsc", 24 | "build:btp": "webpack --mode production && tsc && npm publish --access=public", 25 | "build:tsc": "tsc", 26 | "dev": "webpack-dev-server --mode development --progress --colors", 27 | "watch": "webpack --progress --colors --watch", 28 | "publish": "npm publish --access=public" 29 | }, 30 | "files": [ 31 | "lib/", 32 | "types/", 33 | "README.md" 34 | ], 35 | // ... 36 | } 37 | ``` 38 | tsconfig.json 39 | ```json 40 | { 41 | "compilerOptions": { 42 | "target": "es6", 43 | "module": "commonjs", 44 | "removeComments": false, 45 | "declaration": true, 46 | "outDir": "./lib", 47 | "declarationDir": "./types", 48 | "baseUrl": "./", 49 | "jsx": "react", 50 | "allowJs": false, 51 | "lib": [ 52 | "dom", 53 | "es2015" 54 | ], 55 | "sourceMap": false, 56 | "noImplicitAny": false 57 | }, 58 | "files": [ 59 | "./src/lib/index.ts" 60 | ], 61 | "include": [ 62 | "src/lib/*.ts" 63 | ], 64 | "exclude": [ 65 | "node_modules" 66 | ] 67 | } 68 | ``` 69 | 70 | 直接使用typescript的编译方式,`npm run build:tsc` 执行tsc命令 71 | `files`的属性设置为 src/lib/index.ts 编译的文件是index.ts 72 | `declaration`设置为true 生成d.ts文件 73 | `declarationDir`生成的d.ts文件地址 74 | `typings`引用d.ts文件声明地址 75 | 这之后会在lib目录下生成ts编译的文件,在types目录下生成d.ts文件 76 | 之后发布只发布 `/lib`, `/types`, `README.md` 文件 77 | 这样在发布之后是下载代码的时候,import导入的代码,是可以支持动态提示的 78 | 79 | 但是tsc编译的代码并没有支持代码压缩 80 | 81 | 所以可以在tsc编译之后再压缩一下,或者压缩玩了之后再获取tsc的types文件就行了 82 | 所以可以执行 83 | ```code 84 | npm run build:tsc 85 | ``` 86 | 或者 87 | ```code 88 | npm run build 89 | ``` 90 | 一键发布 91 | ```code 92 | npm run build:btp 93 | ``` 94 | 95 | over! 96 | -------------------------------------------------------------------------------- /posts/【面试题】异步.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 【面试题】异步 3 | date: 2021-04-07 16:38:01 4 | categories: 异步 5 | tags: [异步, 面试] 6 | --- 7 | 8 | ### Async/Await 如何通过同步的方式实现异步 9 | 10 | Async/Await 就是一个自执行的 generate 函数。利用 generate 函数的特性把异步的代码写成“同步”的形式。 11 | 12 | ### 以下代码执行结果 13 | 14 | ```js 15 | async function async1() { 16 | console.log("async1 start"); 17 | await async2(); 18 | console.log("async1 end"); 19 | } 20 | async function async2() { 21 | console.log("async2"); 22 | } 23 | console.log("script start"); 24 | setTimeout(function () { 25 | console.log("setTimeout"); 26 | }, 0); 27 | async1(); 28 | new Promise(function (resolve) { 29 | console.log("promise1"); 30 | resolve(); 31 | }).then(function () { 32 | console.log("promise2"); 33 | }); 34 | console.log("script end"); 35 | ``` 36 | 37 | 运行结果: 38 | 39 | ```js 40 | // script start 41 | // async1 start 42 | // async2 43 | // promise1 44 | // script end 45 | // async1 end 46 | // promise2 47 | // setTimeout 48 | ``` 49 | 50 | ### 以下代码执行结果 51 | 52 | ```js 53 | const promise = new Promise((resolve, reject) => { 54 | console.log(1); 55 | resolve(5); 56 | console.log(2); 57 | }).then((val) => { 58 | console.log(val); 59 | }); 60 | 61 | promise.then(() => { 62 | console.log(3); 63 | }); 64 | 65 | console.log(4); 66 | 67 | setTimeout(function () { 68 | console.log(6); 69 | }); 70 | ``` 71 | 72 | 结果: 73 | 74 | ```js 75 | // 1 76 | // 2 77 | // 4 78 | // 5 79 | // 3 80 | // 6 81 | ``` 82 | 83 | ### 以下代码执行结果 84 | 85 | ```js 86 | const first = () => 87 | new Promise((resolve, reject) => { 88 | console.log(3); 89 | let p = new Promise((resolve, reject) => { 90 | console.log(7); 91 | setTimeout(() => { 92 | console.log(5); 93 | resolve(6); 94 | }, 0); 95 | resolve(1); 96 | }); 97 | resolve(2); 98 | p.then((arg) => { 99 | console.log(arg); 100 | }); 101 | }); 102 | 103 | first().then((arg) => { 104 | console.log(arg); 105 | }); 106 | console.log(4); 107 | 108 | // 结果: 3 7 4 1 2 5 109 | ``` 110 | 111 | ### 说说 promise 的原理 112 | 113 | - `三种状态` 一旦发生变化,则不可逆转。 114 | - pending 115 | - resolved (fulfilled) 116 | - rejected 117 | - **then 函数返回一个 `promise`** 并接受两个参数(带有参数的函数) 118 | - resolve 返回一个 value, reject 会返回一个 reason 119 | 120 | ### promise 为什么支持 then 链式回调 121 | 122 | Promise.prototype.then()返回的是一个新的 Promise 实例,(注意,不是原来那个 Promise 实例) 123 | -------------------------------------------------------------------------------- /posts/javascript事件循环.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: javascript事件循环 3 | date: 2019-03-20 23:23:56 4 | categories: javascript 5 | tags: ['js'] 6 | --- 7 | 8 | ### 为什么同步,为什么有异步 9 | JavaScript是单线程的,这是源于基于用户行为决定的,以及DOM的操作,这决定了js的单线程的机制,如果js中有多个线程,此时a线程在执行dom节点的添加操作,b线程执行dom的删除操作,浏览器该如何执行操作? 10 | 11 | 拿坐电梯来说,从1楼到18楼,只有一部电梯,人很多很多,第一班电梯上不了,只能等第二班,也就是别人上去了之后,电梯空了,她们才能上去,其他的任务才会继续进入”电梯“直到到达18楼任务完成,后面的任务才会开始执行 12 | 13 | 这会导致如果前面代码出现执行缓慢,后面代码被阻塞,一直等待无法执行 14 | 这些代码都是同步代码,异步代码在js中是如何执行的,js的另一个特点是非阻塞,实现的关键点在于事件队列(Task Queue) 15 | 16 | ### 事件队列 TO 事件循环 17 | js在执行异步代码的时候,不会一直等待异步代码的结果,而是放在一边(事件挂起),继续走自己主流程(栈中的其他任务),当异步的结果返回之后,js会将这些事件加入与当前栈不同的另一个队列,也就是事件队列,而这个队列也不会立刻执行其回调,而是当前栈中所有任务执行完成之后,主线程处于空闲状态,此时查找当前队列中是否有任务,如果有,主线程会从中取出第一个事件,并将该事件对应的回调放到执行栈中,执行当前栈的同步代码,然后是第二个,然后是第三个,如此反复就成了一个事件循环 18 | 19 | ### 代码 20 | ```js 21 | function sum (a, b) { 22 | return console.log(a + b) 23 | } 24 | 25 | sum(0, 1) 26 | sum(1, 1) 27 | setTimeout(function () { 28 | sum(2, 1) 29 | }, 51) 30 | setTimeout(function () { 31 | sum(3, 1) 32 | }, 50) 33 | sum(4, 1) 34 | sum(5, 1) 35 | 36 | // 1 37 | // 2 38 | // 5 39 | // 6 40 | // 3 41 | // 4 42 | ``` 43 | 这段代码就可以验证事件循环的实现方式,首先,会`执行同步代码`,`异步代码会被挂起`,不会执行,自上而下也就是当前栈中的所有任务执行完成之后,也就是`sum(5, 1)`,此时打印出6,这时,`主流程代码执行完毕`,处于闲置状态,这时,之前被`挂起的异步任务队列会被执行`,从第一个开始,setTimeout(function () {}, 50) `优先返回结果`,会被放到第一个队列,同步代码执行完毕之后,`异步队列的回调`会被`放入栈中执行同步代码` 44 | 45 | 46 | ```ts 47 | setTimeout(() => { 48 | console.log('1') 49 | }, 0) 50 | 51 | new Promise((resolve) => { 52 | console.log('2'); 53 | resolve() 54 | }).then(() => { 55 | console.log('3') 56 | new Promise((resolve) => { 57 | console.log('resolve'); 58 | resolve() 59 | }).then(() => { 60 | console.log('end resolve'); 61 | }) 62 | }) 63 | 64 | console.log(4) 65 | ``` 66 | 67 | 这里涉及到宏任务,微任务的概念 68 | **宏任务:setTimeOut、setInverter、JavaScript代码** 69 | **微任务:MutationObserver,Promise的回调函数** 70 | 事件循环的顺序决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。 71 | 上面代码的执行顺序 72 | - 代码为宏任务在主线程,先会遇到 setTimeout 将它的回调函数分发到宏任务的事件队列中 73 | - 走到new Promise的时候代码会直接执行 会先执行`console.log('2')`, 然后将 then 的回调函数执行的方法发放的微任务的事件队列中, 因为promise 的`console.log('2')`是同步执行的,同步任务执行完后会去清空microtasks queues, 最后清空完微任务再去宏任务队列取值。 74 | - 之后执行同步代码的 `console.log(4)` 75 | - 主线程代码执行完成之后,先执行微任务中的代码,也就是then之后的回调代码 `console.log('3')` 76 | - 此时new Promise也会被执行,会执行`console.log('resolve')`, then作为微任务再次被放单事件队列中 77 | - 第一个pormise的回调执行完成之后,当前的任务队列中没有宏任务要执行,则开始微任务代码的执行也就是 then 的回调 `console.log('end resolve');` 78 | - 该队列下的所有宏任务,微任务执行完成之后结束此次任务,执行后续的任务 79 | - 微任务代码执行完成之后,会再次查找宏任务中的代码,也就是settimeout的回调函数 `console.log('1')` 80 | 81 | **结果** 82 | ```ts 83 | // 2 84 | // 4 85 | // 3 86 | // resolve 87 | // end resolve 88 | // 1 89 | ``` 90 | 91 | ### 事件循环示意图 92 | ![event-loop](javascript事件循环/event-loop.jpg) 93 | 94 | 95 | 以上是浏览器中js的事件循环机制 96 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | #### [博客地址](www.daiwei.site/blog) 2 | 3 | ### 面试题 4 | 5 | [【面试题】服务端渲染](https://www.daiwei.site/blog/detail?tid=0a0de458-a642-11eb-ae4f-00163e0aa4af) 6 | 7 | [【面试题】工程化](https://www.daiwei.site/blog/detail?tid=29d5896c-a642-11eb-ae4f-00163e0aa4af) 8 | 9 | [【面试题】web 安全](https://www.daiwei.site/blog/detail?tid=8919b71a-a644-11eb-ae4f-00163e0aa4af) 10 | 11 | [【面试题】js 基础](https://www.daiwei.site/blog/detail?tid=04dcba1e-a644-11eb-ae4f-00163e0aa4af) 12 | 13 | [【面试题】react](https://www.daiwei.site/blog/detail?tid=3c2e8f12-a644-11eb-ae4f-00163e0aa4af) 14 | 15 | [【面试题】http](https://www.daiwei.site/blog/detail?tid=d71e17d3-a643-11eb-ae4f-00163e0aa4af) 16 | 17 | [【面试题】浏览器](https://www.daiwei.site/blog/detail?tid=5ef4f0ab-a642-11eb-ae4f-00163e0aa4af) 18 | 19 | [【面试题】设计模式](https://www.daiwei.site/blog/detail?tid=abfdff63-a642-11eb-ae4f-00163e0aa4af) 20 | 21 | [【面试题】手写代码](https://www.daiwei.site/blog/detail?tid=c8018661-a642-11eb-ae4f-00163e0aa4af) 22 | 23 | [【面试题】算法](https://www.daiwei.site/blog/detail?tid=ebd274df-a642-11eb-ae4f-00163e0aa4af) 24 | 25 | [【面试题】vue](https://www.daiwei.site/blog/detail?tid=702bff53-a644-11eb-ae4f-00163e0aa4af) 26 | 27 | [【面试题】异步](https://www.daiwei.site/blog/detail?tid=2d45800a-a643-11eb-ae4f-00163e0aa4af) 28 | 29 | [【面试题】css](https://www.daiwei.site/blog/detail?tid=92569ee9-a643-11eb-ae4f-00163e0aa4af) 30 | 31 | [【面试题】node](https://www.daiwei.site/blog/detail?tid=24083a95-a644-11eb-ae4f-00163e0aa4af) 32 | 33 | [【面试题】typescript](https://www.daiwei.site/blog/detail?tid=54d39b29-a644-11eb-ae4f-00163e0aa4af) 34 | 35 | #### 页面性能 36 | 37 | [提升页面性能的 n 种方法](https://www.daiwei.site/blog/detail?tid=2f3c471d-7361-11eb-aac6-00163e0aa4af) 38 | 39 | #### js 高级程序设计学习 40 | 41 | [【js 高级程序】迭代器与生成器](https://www.daiwei.site/blog/detail?tid=2f3c4687-7361-11eb-aac6-00163e0aa4af) 42 | 43 | [【js 高级程序】变量、作用域与内存](https://www.daiwei.site/blog/detail?tid=2f3c419e-7361-11eb-aac6-00163e0aa4af) 44 | 45 | [【js 高级程序】BOM](https://www.daiwei.site/blog/detail?tid=2f3c40f2-7361-11eb-aac6-00163e0aa4af) 46 | 47 | [【js 高级程序】DOM 扩展](https://www.daiwei.site/blog/detail?tid=2f3c3f8c-7361-11eb-aac6-00163e0aa4af) 48 | 49 | [【js 高级程序】DOM](https://www.daiwei.site/blog/detail?tid=2f3c4044-7361-11eb-aac6-00163e0aa4af) 50 | 51 | [【js 高级程序】工作者线程-ServiceWorker](https://www.daiwei.site/blog/detail?tid=2f3c3f05-7361-11eb-aac6-00163e0aa4af) 52 | 53 | [【js 高级程序】工作者线程-SharedWorker](https://www.daiwei.site/blog/detail?tid=2f3c3e7e-7361-11eb-aac6-00163e0aa4af) 54 | 55 | #### web 安全 56 | 57 | [web 安全-SQL 注入](https://www.daiwei.site/blog/detail?tid=2f3c3bd9-7361-11eb-aac6-00163e0aa4af) 58 | 59 | [web 安全-点击劫持](https://www.daiwei.site/blog/detail?tid=2f3c3ac6-7361-11eb-aac6-00163e0aa4af) 60 | 61 | [web 安全-CSRF](https://www.daiwei.site/blog/detail?tid=2f3c3a54-7361-11eb-aac6-00163e0aa4af) 62 | 63 | [web 安全-XSS](https://www.daiwei.site/blog/detail?tid=2f3c39e2-7361-11eb-aac6-00163e0aa4af) 64 | 65 | [Content-Security-Policy](https://www.daiwei.site/blog/detail?tid=2f3c3965-7361-11eb-aac6-00163e0aa4af) 66 | 67 | #### webpack 68 | 69 | [webpack 配置-splitChunks](https://www.daiwei.site/blog/detail?tid=2f3c3793-7361-11eb-aac6-00163e0aa4af) 70 | -------------------------------------------------------------------------------- /posts/TypeScript接口,类,函数.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: TypeScript接口,类,函数 3 | date: 2019-03-31 11:19:38 4 | categories: typescript 5 | tags: [js, ts] 6 | --- 7 | 8 | ### 接口(interface) 9 | 接口从类型定义来说和type很像,但是两者也有区别和差异 10 | 11 | #### interface 和 type的区别 12 | - 都可以描述一个对象或者函数(共同点) 13 | ```ts 14 | type User = { 15 | name: string; 16 | age: number; 17 | } 18 | 19 | interface User { 20 | name: string; 21 | age: number; 22 | } 23 | ``` 24 | - type可以声明基本类型别名,联合类型,元组等类型,而interface则不可以 25 | ```ts 26 | type Name = string; 27 | 28 | interface InfoA { 29 | name: string; 30 | reset(): void; 31 | } 32 | 33 | interface InfoB { 34 | name: string; 35 | reset(): void; 36 | } 37 | 38 | type InfoAll = InfoA | InfoB 39 | type InfoList = [InfoA, InfoB] 40 | ``` 41 | - interface的声明可以合并,type不可以 42 | ```ts 43 | interface Info { 44 | name: string; 45 | age: number; 46 | } 47 | interface Info { 48 | sex: number 49 | } 50 | 51 | // Info 会有三个属性类型定义 相当于属性合并 52 | ``` 53 | 反正按照tslint规范来说,最好是用interface 54 | 55 | #### 基本用法 56 | ```ts 57 | interface User { 58 | name: string; 59 | age: number; 60 | readonly brithday: string; // brithday 属性不可以被重新赋值 61 | sex?: number; // 可选属性 62 | } 63 | 64 | function setUser (user: User): void { 65 | // .... 66 | } 67 | ``` 68 | 69 | ### 类(class) 70 | 类的定义和es6的class定义相同,不过typescript支持各种类型修饰符以及静态属性等操作 71 | #### 基本定义 72 | ```ts 73 | class UserInfo { 74 | name: string; 75 | age: number; 76 | constructor: (name: string; age: number) { 77 | this.name = name; 78 | this.age = age; 79 | } 80 | disc () { 81 | return `${this.name}已经${this.age}岁了` 82 | } 83 | } 84 | const user = new UserInfo ('Dw', 26) 85 | user.name // Dw 86 | user.disc() // Dw已经26岁了 87 | ``` 88 | #### 继承 89 | ```ts 90 | class Student extends UserInfo { 91 | grade: string; 92 | constructor: (grade: string; name: string; age: number) { 93 | this.grade = grade 94 | super() 95 | } 96 | } 97 | const student = new Student('四年级', 'Dw', 26) 98 | ``` 99 | 100 | #### 公共,私有与受保护的修饰符 101 | ```ts 102 | class User { 103 | private name: string 104 | constructor(name: string) { 105 | this.name = name 106 | } 107 | } 108 | new User('dw').name // 错误: 'name' 是私有的 109 | ``` 110 | protected修饰符与private修饰符的行为很相似,但protected成员在派生类中(继承的类中)仍然可以访问 111 | 112 | #### 静态属性 113 | ```ts 114 | class User { 115 | static name: string 116 | getName function (): string { 117 | return User.name 118 | } 119 | } 120 | ``` 121 | static定义的属性不能通过this.name访问,只能通过User.name访问到 122 | 123 | ### 函数 124 | 和JavaScript一样,TypeScript函数可以创建有名字的函数和匿名函数 125 | #### 函数类型 126 | ```ts 127 | function add (x: number, y: number): number { 128 | return x + y 129 | } 130 | ``` 131 | 参数里定义的是参数的类型,括号后面的定义number是这个方法的返回值 132 | 133 | #### 可选参数和默认参数 134 | ```ts 135 | function bindName (firstName: string, lastName?: string): string { 136 | if (lastName) { 137 | return firstName 138 | } 139 | return `${firstName} - ${lastName}` 140 | } 141 | ``` 142 | 注意:可选参数必须跟在必须参数后面 143 | 144 | #### this参数 145 | ```ts 146 | function fn(this: void) { 147 | // ... 148 | } 149 | ``` 150 | -------------------------------------------------------------------------------- /posts/如何减少webpack4打包时长.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 如何减少webpack4打包时长 3 | date: 2019-11-19 16:34:39 4 | categories: 环境配置 5 | tags: [webpack] 6 | --- 7 | 8 | 最近在面试过程中被问到这个题目一种方法都说不出,刚好入职的公司有一个这个类型的分享,自己在这个基础上进行本地尝试,记录一下做个总结 9 | 10 | ### webpack基本配置 11 | #### test 的 include & exclude 12 | ```ts 13 | module.exports = { 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.js$/, 18 | use: ['babel-loader'], 19 | exclude: /(node_modules)/, 20 | } 21 | ] 22 | } 23 | } 24 | ``` 25 | `include` 表示精确某个文件或某个目录下的test 26 | `exclude` 表示除某一个文件或者某一个目录不走 babel-loader 27 | 28 | ### AutoDllPlugin 29 | 安装: 30 | ```code 31 | npm install --save-dev autodll-webpack-plugin 32 | ``` 33 | 使用: 34 | ```ts 35 | module.exports = { 36 | plugins: [ 37 | new HtmlWebpackPlugin({ 38 | inject: true, 39 | template: './src/index.html', 40 | }), 41 | new AutoDllPlugin({ 42 | inject: true, // will inject the DLL bundles to index.html 43 | filename: '[name].js', 44 | entry: { 45 | vendor: [ 46 | 'react', 47 | 'react-dom' 48 | ] 49 | } 50 | }) 51 | ] 52 | } 53 | ``` 54 | Will Result in: 55 | ```html 56 | 57 | 58 | 59 | 60 | Test 61 | 62 | 63 | 64 | ... 65 | 66 | 67 | 68 | 69 | 70 | ``` 71 | 地址:https://github.com/asfktz/autodll-webpack-plugin 72 | 73 | ### 使用 happypack 插件 74 | happypack 插件是分配系统多进程执行代码构建,来加速代码构建 75 | 76 | ```code 77 | npm install --save-dev happypack 78 | ``` 79 | 80 | 使用方式 81 | ```ts 82 | // @file: webpack.config.js 83 | const HappyPack = require('happypack'); 84 | 85 | exports.module = { 86 | rules: [ 87 | { 88 | test: /.js$/, 89 | // 1) replace your original list of loaders with "happypack/loader": 90 | // loaders: [ 'babel-loader?presets[]=es2015' ], 91 | use: 'happypack/loader', 92 | include: [ /* ... */ ], 93 | exclude: [ /* ... */ ] 94 | } 95 | ] 96 | }; 97 | 98 | exports.plugins = [ 99 | // 2) create the plugin: 100 | new HappyPack({ 101 | // 3) re-add the loaders you replaced above in #1: 102 | loaders: [ 'babel-loader?presets[]=es2015' ] 103 | }) 104 | ]; 105 | ``` 106 | 地址:https://github.com/amireh/happypack 107 | 108 | ### 使用 cache-loader 109 | 在一些性能开销较大的 loader 之前添加此 loader,以将结果缓存到磁盘里 110 | ```code 111 | npm install --save-dev cache-loader 112 | ``` 113 | ```ts 114 | module.exports = { 115 | module: { 116 | rules: [ 117 | { 118 | test: /\.js$/, 119 | use: ['cache-loader', 'babel-loader'], 120 | include: path.resolve('src'), 121 | }, 122 | ], 123 | }, 124 | }; 125 | ``` 126 | 请注意,保存和读取这些缓存文件会有一些时间开销,所以请只对性能开销较大的 loader 使用此 loader,可以在test的loaders之前添加cache-loader 127 | 文档地址:https://www.webpackjs.com/loaders/cache-loader/ 128 | 插件地址:https://github.com/webpack-contrib/cache-loader 129 | 130 | ### 打包文件资源分析插件 webpack-bundle-analyzer 131 | 132 | 未完 待续... -------------------------------------------------------------------------------- /posts/【面试题】css.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 【面试题】css 3 | date: 2021-03-30 15:42:03 4 | categories: css 5 | tags: [css, 面试] 6 | --- 7 | 8 | ### BFC 9 | 10 | - 定义 11 | BFC 即 Block Formatting Contexts (块级格式化上下文) 12 | 可理解为单个的独立容器,其样式不会影响到外部元素。 13 | 14 | - 如何触发 15 | - **float 的值不为 none** 16 | - **overflow 的值不为 visible** 17 | - **display 为 inline-block, table-cell, table-caption, flex, inline-flex** 18 | - **position 为 fixed,absolute** 19 | 20 | ### div 水平垂直居中 21 | 22 | - absolute + margin-left | top 23 | - flex 24 | - grid 25 | - absolute + translate 26 | - table-cell 27 | - line-height 28 | 29 | ### CSS 盒模型 30 | 31 | - 标准盒模型 width = content (`box-sizing: content-box`) 32 | - IE 盒模型 width = padding + border + content (`box-sizing: border-box`) 33 | 34 | ### flex: 0 0 100px 是什么意思 35 | 36 | - `flex-grow` 37 | 取值:默认 0,用于决定**项目在有剩余空间的情况下是否放大**,默认不放大;注意,即便设置了固定宽度,也会放大。 38 | - `flex-shrink` 39 | 默认 1,用于决定**项目在空间不足时是否缩小**,默认项目都是 1,**即空间不足时大家一起等比缩小**;注意,即便设置了固定宽度,也会缩小。设置为 0,则即便空间不够,自身也不缩小。 40 | - `flex-basis` 41 | 取值:默认 auto,用于设置项目宽度,默认 auto 时,项目会保持默认宽度,或者以 width 为自身的宽度,**但如果设置了 flex-basis,权重会 width 属性高,因此会覆盖 widtn 属性。** 42 | 43 | `flex: 0 0 100px` 则表示项目无论有多大空间,不放大,不缩小,始终保持 100px 44 | 45 | ### 为什么要使用 transform 而不是 margin-left,right 46 | 47 | `transform` 是独立的层,margin 则会导致重绘回流。 48 | 49 | **transform 原理** 50 | transform 是通过创建一个 RenderLayers(渲染) 合成层,拥有独立的 `GraphicsLayers`(绘图层)。每一个`GraphicsLayers`都有一个一个 `Graphics Context`, 其对应的 `GraphicsLayers` 会 paint 进 `Graphics Context` 中,合成器会最终负责将由 `Graphics Context` 输出的位图合并成最终的屏幕展示图案。 51 | 52 | `transform` 发生在 `Composite Layer` 这一步,他所引起的 paint 也只是发生在单独的 `GraphicsLayer` 中,不会引起整个页面的回流重绘。 53 | 54 | ### 清除浮动的方式 55 | 56 | ```html 57 |
    58 |
    59 |
    60 |
    61 | ``` 62 | 63 | - 添加一个空 div,利用 css 提高的 clear:both 清除浮动,让父级 div 能自动获取到高度。(也可以基于 parent 添加伪元素设置) 64 | - 父级 div 定义 overflow:hidden 65 | - 手动设置父元素高度 66 | - 利用 BFC,BFC 计算高度时,浮动元素也可以参与计算,因此清除浮动,只需触发一个 BFC 即可。(上面的设置 overflow: hidden 就是) 67 | 68 | ### 通过 link 引入的 css 会阻塞页面渲染吗? 69 | 70 | link 标签不会阻塞 DOM 解析,但会阻塞 DOM 渲染。 71 | 72 | 浏览器会并行解析 DOM Tree,和 CSSOM Tree,DOM Tree 解析不依赖于 link,但是 render Tree 依赖于前两者,等 Dom Tree, CSSOM Tree 解析完毕之后才能合成 render Tree。 73 | 74 | ### CSS 预处理带来的好处 75 | 76 | - 语法强大,可执行执行嵌套,或循环的一些操作。 77 | - 常用代码使用代码块,节省大量代码。 78 | - 变量,混入提升代码复用性。 79 | - 额外的颜色函数。 80 | 81 | > 问题则是编译 css 需要时间。 82 | 83 | ### inline 的元素能设置宽高、margin 属性吗 84 | 85 | inline 元素不能设置宽高,外边距只能设置左- 右,不能设置上下 86 | 87 | ### CSS 选择器 88 | 89 | **`div p`** : 所有`div` 下的所有的 `p` 标签 90 | 91 | **`div > p`** : 所有 `div` 下第一层所有的 `p` 标签 92 | 93 | **`div + p`** : 所有 `div` 的兄弟 `p` 标签 (必须位于 div 同级别的后方) 94 | 95 | **`div ~ p`**: : 所有 `div` 的兄弟 `p` 标签 (必须位于 div 同级别的前方) 96 | 97 | **`[title~=flower]`** : 选择 title 属性包含单词 "flower" 的所有元素。**必须是字符快** 98 | 99 | - `title="tulip flower ab"` 里面的三个都行,但是 `lower` 就不行 100 | 101 | **`[lang|=en]`** : 选择 lang 属性值以 "en" 开头的所有元素。 102 | 103 | **`a[href^="https"]`** : 选择 href 属性值以 "https" 开头的所有 a 元素。 104 | 105 | **`a[href$=".pdf"]`** : 选择 href 属性值以 "pdf" 结尾的所有 a 元素。 106 | 107 | **`a[href*="abc"]`**: 选择其 href 属性值中包含 "abc" 子串的每个 a 元素。 108 | 109 | ### postion relative 设置 top 100px 110 | 111 | 则表示会相对于自身,高度向下移动 100px,且会覆盖到其他元素 112 | 113 | ### CSS 权重 114 | 115 | - 行内样式 +1000 116 | - id 选择器 +100 117 | - 属性选择器、class 或者伪类 +10 118 | - 元素选择器,或者伪元素 +1 119 | - 通配符 +0 120 | -------------------------------------------------------------------------------- /posts/Vue源码学习.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Vue源码学习 3 | date: 2018-12-10 15:38:43 4 | categories: vue 5 | tags: [vue, 源码] 6 | --- 7 | 8 | vue是当下最流行的前端框架,简单,易上手,使用场景丰富的特点,从刚学习vue到现在也有一年多了,期间从vue播放器,到个人网站,再到公司的一些vue项目,感觉对于vue的学习和了解也就是一些基本的操作。为了更深入了解vue的实现机制,开始学习和了解vue的源码。这也是基于 `[Vue.js 技术揭秘](https://ustbhuangyi.github.io/vue-analysis/)`的学习 9 | 10 | 11 | ### 关于vue 12 | vue: 一款MVVM框架 13 | 核心思想: 数据驱动 14 | 特点:低耦合,易上手,环境搭建方便 15 | 16 | 以下是边看 Vue.js 技术揭秘 边总结的一点对于vue认识,只是为了加深对于vue的认识和了解 17 | 18 | ### New Vue 19 | 1. 执行 init 方法 (如果没有new 实例化,会报错) 20 | 2. 合并配置 21 | 3. 初始化生命周期 22 | 4. 初始化events 23 | 5. 初始化渲染 24 | 6. 初始化data, props, computed, watcher等 25 | 在初始化的最后,检测到如果有 el 属性,则调用 vm.$mount 方法挂载 vm,挂载的目标就是把模板渲染成最终的 DOM 26 | 27 | ### Vue的$mount挂载 28 | 1. 判断挂载的元素是否是`body`, `html` 29 | vue 不允许挂载到body,html上 (提供的元素只能作为挂载点。不同于 Vue 1.x,所有的挂载元素会被 Vue 生成的 DOM 替换。因此不推荐挂载root实例到 或者 上。这是官方的解释) 30 | 2. 缓存了原型上的 $mount 方法,最后再重新定义该方法 31 | ```js 32 | const mount = Vue.prototype.$mount 33 | ``` 34 | 3. 生成render的方法 35 | 代码来自: [Vue实例属性之el,template,render](https://blog.csdn.net/hxy19971101/article/details/79948074) 36 | ```js 37 | new Vue({ 38 | el: '.app', 39 | data: { 40 | info: '这是通过el属性获取挂载元素的outerHTML方式渲染。' 41 | }, 42 | template: '
    这是template属性模板渲染。
    ', 43 | render: function(h){ 44 | return h('div', {}, '这是render属性方式渲染。') 45 | } 46 | }) 47 | ``` 48 | - 如果存在render函数,会优先渲染`render`方法里的内容 49 | - 如果没有render函数,会先渲染`template`中的内容,且通过 `compileToFunctions` 来生成render方法 50 | - 如果上面两个都没有,则通过 template = `getOuterHTML(el)` 来设置模版,且通过 `compileToFunctions` 来生成render方法 51 | 52 | 最后都会调用原先原型上的 $mount 方法挂载。 53 | ```js 54 | return mount.call(this, el, hydrating) 55 | ``` 56 | $mount 方法是定于在Vue.prototype上的方法 57 | $mount 方法实际上会去调用 `mountComponent` 58 | 而 `mountComponent` 的核心作用就是 59 | - 先调用 vm._render 方法先生成虚拟 vNode 60 | - 再例化一个渲染Watcher 61 | ```js 62 | new Watcher(vm, updateComponent, noop, { 63 | before () { 64 | if (vm._isMounted) { 65 | callHook(vm, 'beforeUpdate') // 如果已经是Mounted的状态, 则执行 beforeUpdate 的钩子函数 66 | } 67 | } 68 | }, true /* isRenderWatcher */) 69 | ``` 70 | - 在它的回调函数中会调用 updateComponent 方法,最终调用 vm._update 更新 DOM。 71 | ```js 72 | if (vm.$vnode == null) { 73 | vm._isMounted = true 74 | callHook(vm, 'mounted') 75 | } 76 | return vm 77 | ``` 78 | 函数最后判断为根节点的时候设置 vm._isMounted 为 true, 表示这个实例已经挂载了,同时执行 mounted 钩子函数。 这里注意 vm.$vnode 表示 Vue 实例的父虚拟 Node,所以它为 Null 则表示当前是根 Vue 的实例。 79 | 80 | ### _render 81 | Vue 的 _render 方法是实例的一个私有方法,它用来把实例渲染成一个虚拟 Node 82 | ```js 83 | vnode = render.call(vm._renderProxy, vm.$createElement) 84 | ``` 85 | 86 | ### Virtual DOM 87 | 虚拟dom其实是一个VNode对象,是用JS对象记录一个dom节点的副本,当dom发生更改时候,先用虚拟dom进行diff,算出最小差异,然后再修改真实dom,通过递归的方式进行同级vnode的diff,最终实现整个DOM树的更新 88 | Vue.js 中 Virtual DOM 是借鉴了一个开源库 [snabbdom](https://github.com/snabbdom/snabbdom) 的实现,然后加入了一些 Vue.js 特色的东西 89 | 90 | ### 双向绑定原理 91 | 通过Object.defineProperty()来劫持vue中各个属性的setter、getter 92 | 代码来源: [vue面试常见问题小结](https://blog.csdn.net/connie_0217/article/details/79271508) 93 | ```js 94 | Object.defineProperty(obj, "newKey", { 95 | get:function (){return ...} | undefined,//读取属性返回的值,即类似上面的value 96 | set:function (value){ return ...} | undefined//设置属性的值 97 | configurable: true | false 98 | enumerable: true | false 99 | }) 100 | ``` 101 | 结合发布订阅者模式 102 | 通过监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者 103 | 通过订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图 104 | 通过解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器 105 | ![](Vue源码学习/mvvm.png) 106 | 107 | -- 未完待续 -- -------------------------------------------------------------------------------- /posts/手写一个简单的发布订阅者模式.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 手写一个简单的发布订阅者模式 3 | date: 2019-04-20 21:57:26 4 | categories: javascript 5 | tags: [js] 6 | --- 7 | 8 | 发布订阅者模式是JavaScript的一种设计模式,比较常用的场景就是 document的dom元素监听事件,于是就尝试手写了一个简单的发布订阅者模式 9 | 10 | 11 | 定义一个EventUtils类 12 | 13 | ```ts 14 | export default class EventUtils { 15 | /** 16 | * 键值对 对应事件名称以及数组的值 17 | */ 18 | static handler: {}; 19 | /** 20 | * on 方法 添加监听事件 21 | */ 22 | static on(name: string, handler: Function): typeof EventUtils; 23 | /** 24 | * off 方法 移除监听事件 25 | */ 26 | static off(name: string, handler: Function): typeof EventUtils; 27 | /** 28 | * emit 方法 触发监听的事件 29 | */ 30 | static emit(name: string, ...args: any): typeof EventUtils; 31 | /** 32 | * once 方法 添加事件 只会被执行一次 33 | */ 34 | static once(name: string, handler: Function): void; 35 | } 36 | ``` 37 | 38 | ### handler 存放订阅事件数据信息 39 | ```ts 40 | // 订阅者注册事件信息 41 | // click 举例 42 | { 43 | 'click': [{ 44 | funciton () { 45 | console.log('click event') 46 | } 47 | }, { 48 | function (t) { 49 | console.log(`click event t: ${t}`) 50 | } 51 | }], { 52 | // ... 53 | } 54 | } 55 | 56 | // 数据结构 57 | interface IHandler { 58 | // 回调方法 59 | fn: Function, 60 | // 类型 on once 区分 61 | type: string, 62 | // 事件名称 63 | name: string 64 | } 65 | ``` 66 | 67 | ### EventUtils.on 方法添加订阅事件信息 68 | ```ts 69 | static on (name: string, handler: Function) { 70 | const i:IHandler = { 71 | fn: handler, 72 | type: 'on', 73 | name: name 74 | } 75 | // 判断是否已经有这个事件订阅 76 | // 有就直接push 77 | if (Object.keys(EventUtils.handler).includes(name)) { 78 | EventUtils.handler[name].push(i) 79 | return EventUtils 80 | } 81 | // 没有就创建一个且添加到数组中 82 | EventUtils.handler[name] = [].concat(i) 83 | return EventUtils 84 | } 85 | ``` 86 | 87 | ### EventUtils.once 方法添加一次事件订阅,执行之后取消事件订阅 88 | ```ts 89 | // once 方法 添加事件 只会被执行一次 90 | static once (name: string, handler: Function) { 91 | EventUtils.on(name, handler) 92 | EventUtils.handler[name][0]['type'] = 'once' 93 | } 94 | ``` 95 | 96 | ### EventUtils.off 取消事件订阅 97 | ```ts 98 | static off (name: string, handler: Function) { 99 | const event: any[] = EventUtils.handler[name] 100 | if (event) { 101 | for (let i = event.length - 1; i >= 0; i--) { 102 | if (event[i].fn === handler) { 103 | event.splice(i, 1) 104 | } 105 | } 106 | } 107 | return EventUtils 108 | } 109 | ``` 110 | 111 | ### EventUtils.emit 112 | ```ts 113 | static emit (name: string, ...args: any) { 114 | const event = EventUtils.handler[name] 115 | let newEvent = [] 116 | event && event.length && event.forEach((item: IHandler, index: number) => { 117 | item.fn.call(this, ...args) 118 | 119 | // 如果有只监听一次的事件 120 | if (item.type !== 'once') { 121 | newEvent.push(event.slice(index, index + 1)) 122 | } 123 | }) 124 | 125 | const hasOnce = event && event.length && event.some((item: IHandler) => { 126 | return item.type === 'once' 127 | }) 128 | 129 | if (hasOnce) { 130 | EventUtils.handler[name] = newEvent 131 | } 132 | 133 | // 这里做一个执行完成之后的 once代码 off 的操作 134 | return EventUtils 135 | } 136 | ``` 137 | 138 | ### 代码执行 139 | ```ts 140 | const handle = function () { 141 | console.log('test 触发') 142 | } 143 | 144 | const handle1 = function (i) { 145 | console.log('test1 触发,参数:', i) 146 | } 147 | 148 | EventUtils.on('test', handle) 149 | EventUtils.once('test1', handle1) 150 | 151 | EventUtils.emit('test') 152 | EventUtils.emit('test1', 'hahaha') 153 | ``` 154 | -------------------------------------------------------------------------------- /posts/常用的排序方式有哪些?.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 常用的排序方式有哪些? 3 | date: 2019-10-22 21:29:46 4 | categories: 数据结构 5 | tags: [javascript] 6 | --- 7 | 8 | 在写排序之前我们需要先搞懂两个概念 `排序的稳定性` 和 `时间复杂度` 9 | 10 | ### 排序的稳定性 11 | 12 | 排序分为稳定排序和不稳定排序,稳定排序在排序过程中不会影响到想等数据之间的相对位置,如 13 | 14 | ```ts 15 | // 我们为了方便标示 将2做区分为a2 和 b2 16 | // const a = [2, 3, 2, 1] 17 | const a = [a2, 3, b2, 1]; 18 | ``` 19 | 20 | 冒泡排序的步骤是这样的 21 | 22 | - [a2, 3, b2, 1] 23 | - [a2, b2, 3, 1] 24 | - [a2, b2, 1, 3] 25 | - [a2, 1, b2, 3] 26 | - [1, a2, b2, 3] 27 | 28 | 整个过程 a2 和 b2 的位置相对没有发生变化,因此冒泡排序属于稳定排序 29 | 30 | 选择排序是这样的 31 | 32 | - [a2, 3, b2, 1] 33 | - [1, 3, b2, a2] 34 | - [1, b2, 3, a2] 35 | - [1, b2, a2, 3] 36 | 37 | 选择排序在排序过程中 a2 和 b2 的相对位置已经发生变化,因此选择排序属于不稳定排序 38 | 39 | ### 时间复杂度 40 | 41 | 这个就直接看这篇文章:[(数据结构)十分钟搞定算法的时间复杂度](https://www.jianshu.com/p/f4cca5ce055a) 42 | 43 | ```ts 44 | // 数组为arr,将arr升序排列 45 | const arr = [1, 3, 0, 88, 33, 22, 120, 4, 2, 2, 3, 52]; 46 | ``` 47 | 48 | ### 冒泡排序(稳定排序) 49 | 50 | 冒泡排序是最常见的排序方式 51 | 52 | ```ts 53 | const arr = [1, 3, 0, 88, 33, 22, 120, 4, 2, 2, 3, 52]; 54 | 55 | for (let i = 0; i < arr.length; i++) { 56 | for (let j = 0; j < arr.length - 1; j++) { 57 | if (arr[j] > arr[j + 1]) { 58 | [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; 59 | } 60 | } 61 | } 62 | console.log(arr); // [0, 1, 2, 2, 3, 3, 4, 22, 33, 52, 88, 120] 63 | ``` 64 | 65 | 实现方式: 66 | 67 | - 从第一个元素开始,比较相邻的元素,如果前一个比后一个大,则交换两个元素位置,继续比较第二个(原本的第一个)和第三个的大小以此类推 68 | - 第一轮比较完了之后会找到一个最大数值,在数组的最后一位 69 | - 开始第二轮,从第二个开始,执行第一步,得到的是第二大元素,放在倒数第二个位置 70 | - 第三个、第四个、第五个... 以此类推 71 | - 最后得到一个从小到大的排序 72 | 73 | 时间复杂度: O(n²) 两次 for 循环 74 | 75 | ### 选择排序(不稳定排序) 76 | 77 | ```ts 78 | const arr = [1, 3, 0, 88, 33, 22, 120, 4, 2, 2, 3, 52]; 79 | 80 | for (let i = 0; i < arr.length - 1; i++) { 81 | for (let j = i + 1; j < arr.length; j++) { 82 | if (arr[i] > arr[j]) { 83 | [arr[i], arr[j]] = [arr[j], arr[i]]; 84 | } 85 | } 86 | } 87 | console.log(arr); // [0, 1, 2, 2, 3, 3, 4, 22, 33, 52, 88, 120] 88 | ``` 89 | 90 | 实现方式: 91 | 92 | - 从第一个元素开始到倒数第二个元素,因为执行到这个时候已经知道最大值了,而且已经在最后一位了 93 | - 子循环查询从第一个元素之后(第二个)到最后一个,有没有比地一个元素小的,有的话交换位置,继续往后查找有没有比当前第一个元素小的,如果小的话以此类推(此时可以得到第一次内部轮训最小的一个元素,且已经放在数组的第一位了) 94 | - 第二个、第三个、第四个... 以此类推 95 | - 最后得到一个从小到大的排序 96 | 97 | 时间复杂度: O(n²) 两次 for 循环 98 | 99 | ### 插入排序(稳定排序) 100 | 101 | ```ts 102 | const arr = [1, 3, 0, 88, 33, 22, 120, 4, 2, 2, 3, 52]; 103 | 104 | for (let i = 1; i < arr.length; i++) { 105 | for (let j = i; j > 0; j--) { 106 | if (arr[j] < arr[j - 1]) { 107 | [arr[j - 1], arr[j]] = [arr[j], arr[j - 1]]; 108 | } 109 | } 110 | } 111 | console.log(arr); 112 | ``` 113 | 114 | 实现方式 115 | 116 | - 第一次从第二个元素开始,内层循环从第二个和第一个元素进行比对大小,如果第一个比第二个大则调换位置 117 | - 第二次从第三个元素开始,内层循环从第三个看是否比第二个大,如果大的话则不做任何操作,如果小则第三个和第二个调换,然后再和第一个比较大小,重复此操作 118 | - 第三次、第四次、第五次... 119 | - 最后得到一个从小到大的排序 120 | 121 | 时间复杂度: O(n²) 两次 for 循环 122 | 123 | ### 快速排序(不稳定排序) 124 | 125 | ```ts 126 | const arr = [1, 3, 0, 88, 33, 22, 120, 4, 2, 2, 3, 52]; 127 | 128 | function sort(lists) { 129 | if (lists.length <= 1) { 130 | return lists; 131 | } 132 | const tmp = lists.splice(0, 1)[0]; 133 | let left = []; 134 | let right = []; 135 | lists.forEach((item) => { 136 | if (tmp > item) { 137 | left.push(item); 138 | } else { 139 | right.push(item); 140 | } 141 | }); 142 | 143 | return sort(left).concat(tmp, sort(right)); 144 | } 145 | 146 | console.log(sort(arr)); 147 | ``` 148 | 149 | 实现方式 150 | 151 | - 递归的方式,第一次在数组中随机取一个元素,注意是取出,arr 的 length 被取出会少一个长度 152 | - 取出的一个元素作为分界线,小于这个元素的放在左边,否则放在右边 153 | - 第一轮结束之后,针对两边的 arr 执行相同的操作,知道执行方法传入的数组的长度为 1,返回这个数组 154 | - 将每次执行的方法 由 `左` - `tmp` - `右` 连接完成快速排序操作 155 | 时间复杂度: O (nlogn) 156 | 157 | ### 归并排序 158 | 159 | 未完 待续 160 | -------------------------------------------------------------------------------- /posts/手写call,apply,bind方法.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 手写call,apply,bind方法 3 | date: 2019-06-03 15:54:53 4 | categories: javascript 5 | tags: [js] 6 | --- 7 | 8 | 之前在面试一个前端工作的时候,一个面试官给我一张纸,一支笔让我手写一个bind方法要求能实现和bind一样的效果,当时就一脸懵逼,慢慢熬出大概的思路。之后网上搜了一下对应的方式,自己照着写了一遍理解了一下,很有收获,记录下 9 | 10 | 11 | bind方法是挂载在Function.prototype的一个方法,这里使用 selfBind 避免冲突 12 | - bind方法返回的是一个可执行的方法 13 | - bind的第一个参数 使用会让this指向对应的对象之中,没有回默认指向 window 14 | - bind的第二个以及以后的参数 则作为调用的方法的参数传入 15 | ```ts 16 | // 申明 不然在Function.prototype设置对应方法会报错 17 | interface Function { 18 | selfCall(...args: any): any; 19 | selfApply(...args: any): any; 20 | selfBind(...args: any): any; 21 | } 22 | ``` 23 | 24 | ```ts 25 | // call 26 | Function.prototype.selfCall = function (context) { 27 | // 设置context上下文,如果没有则默认为 window 28 | const selfContext = context || window 29 | try { 30 | // __Self__Call_Fn设置为调用的方法 31 | selfContext.__Self__Call_Fn = this 32 | // 获取第二个以及之后的参数 33 | const arg = [...arguments].slice(1) 34 | // 执行该方法赋值给 result 35 | const result = selfContext.__Self__Call_Fn(...arg) 36 | // 移除 selfContext 上的 __Self__Call_Fn 属性 37 | delete selfContext.__Self__Call_Fn 38 | // 返回 result 39 | return result 40 | } catch (e) { 41 | console.log(e) 42 | delete selfContext.__Self__Call_Fn 43 | } 44 | } 45 | ``` 46 | 47 | ```ts 48 | // apply 49 | Function.prototype.selfApply = function (context) { 50 | // 设置context上下文,如果没有则默认为 window 51 | const selfContext = context || window 52 | try { 53 | // __Self_Apply_Fn设置为调用的方法 54 | selfContext.__Self_Apply_Fn = this 55 | // 获取第二个参数 56 | const arg = [...arguments][1] 57 | // 判断第二个参数是否为数组 58 | if (!Array.isArray(arg)) { 59 | throw new TypeError('apply 的第二个参数被要求是数组') 60 | } 61 | // 执行该方法赋值给 result 62 | const result = selfContext.__Self_Apply_Fn(...arg) 63 | // 移除 selfContext 上的 __Self_Apply_Fn 属性 64 | delete selfContext.__Self_Apply_Fn 65 | // 返回 result 66 | return result 67 | } catch (e) { 68 | console.log(e) 69 | delete selfContext.__Self_Apply_Fn 70 | } 71 | } 72 | ``` 73 | 74 | ```ts 75 | // bind 返回一个方法 76 | Function.prototype.selfBind = function (context) { 77 | // 设置context上下文,如果没有则默认为 window 78 | const selfContext = context || window 79 | try { 80 | // __Self_Bind_Fn 设置为调用的方法 81 | selfContext.__Self_Bind_Fn = this 82 | // 获取第二个以及之后的参数 83 | const arg = [...arguments].slice(1) 84 | // 返回一个方法 85 | return function () { 86 | selfContext.__Self_Bind_Fn(...arg) 87 | } 88 | } catch (e) { 89 | console.log(e) 90 | delete selfContext.__Self_Bind_Fn 91 | } 92 | } 93 | ``` 94 | 在使用的时候: 95 | ```ts 96 | const Person = { 97 | name: 'Tom', 98 | say (self = '1111', a) { 99 | console.log('Person.say') 100 | console.log(`我叫${this.name}---${self}---${a}`) 101 | } 102 | } 103 | 104 | const Person1 = { 105 | name: 'Tom 1', 106 | firstName: 'd', 107 | lastName: 'w', 108 | } 109 | 110 | const Student = { 111 | firstName: 'dai', 112 | lastName: 'wei', 113 | getName () { 114 | console.log(`FullName: ${this.firstName} -- ${this.lastName}`) 115 | } 116 | } 117 | 118 | Person.say.selfCall(Person1, 222, '111') // 我叫Tom 1---222---111 119 | Person.say.selfCall(Person1, 999) // 我叫Tom 1---999---undefined 120 | Student.getName.selfCall(Person1) // FullName: d -- w 121 | Person.say.selfApply(Person1, [333, 111, 222]) // 我叫Tom 1---333---111 122 | Person.say.selfApply(Person1, 333, 111, 222) // 报错 TypeError: apply 的第二个参数被要求是数组 123 | Person.say.selfBind(Person1, 'dwdwdwdwdwdwdw', '111')() // 我叫Tom 1---dwdwdwdwdwdwdw---111 124 | ``` 125 | 以上! 126 | -------------------------------------------------------------------------------- /posts/docker学习记录.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: docker学习记录 3 | date: 2021-06-01 17:46:25 4 | categories: docker 5 | tags: [工程化] 6 | --- 7 | 8 | Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。 9 | 10 | Docker 可以让自己的项目在服务器上的docker容器内部执行。 11 | 12 | ### 容器 13 | 14 | #### docker ps 15 | 显示所有在运行中的容器 16 | 17 | #### docker ps -a 18 | 显示所有的容器,包括未运行的。 19 | 20 | #### docker run [OPTIONS] IMAGE [COMMAND] [ARG...] 21 | 创建一个新的容器并运行一个命令 22 | - OPTIONS 23 | - -d: 后台运行容器,并返回容器ID; 24 | - -p: 指定端口映射,格式为:**主机(宿主)端口**:**容器端口** 25 | - -P: 随机端口映射,容器内部端口随机映射到主机的端口 26 | - --name="nginx-lb": 为容器指定一个名称; 或者 --name hi_container 27 | - --dns 8.8.8.8: 指定容器使用的DNS服务器,默认和宿主一致; 28 | - -h "mars": 指定容器的hostname; 29 | - -m :设置容器使用内存最大值; 30 | 31 | ```code 32 | docker run -dp 3001:3000 app-stared 33 | docker run -dp 3000:3000 --name hi app-stared // 指定name hi 34 | docker run --name mynginx -d nginx:latest // 以后台模式启动 nginx last版本 35 | ``` 36 | 启动 `app-stared` 镜像浏览器3001映射到容器内的3000端口 37 | 38 | #### docker stop [containerId] 39 | 停止一个运行中的容器 40 | ```code 41 | docker stop d1 // 停止运行中的容器d1 42 | ``` 43 | 44 | #### docker rm [OPTIONS] CONTAINER [CONTAINER...] 45 | 删除一个容器 46 | - OPTIONS 47 | - -f :通过 SIGKILL 信号强制删除一个运行中的容器。 48 | - -l :移除容器间的网络连接,而非容器本身。 49 | - -v :删除与容器关联的卷。 50 | ```code 51 | docker rm -f db01 db02 // 强制删除容器 db01、db02 52 | docker rm $(docker ps -a -q) // 删除所有已经停止的容器 53 | ``` 54 | #### docker logs [OPTIONS] CONTAINER 55 | 获取容器的日志 56 | - OPTIONS 57 | - -f : 跟踪日志输出 58 | - --since :显示某个开始时间的所有日志 59 | - -t : 显示时间戳 60 | - --tail :仅列出最新N条容器日志 61 | ```code 62 | docker logs -f mynginx 63 | docker logs --tail 10 mynginx 64 | ``` 65 | 66 | #### docker port 67 | 列出指定的容器的端口映射,或者查找将PRIVATE_PORT NAT到面向公众的端口。 68 | 69 | ```code 70 | docker port [OPTIONS] CONTAINER [PRIVATE_PORT[/PROTO]] 71 | 72 | docker port mymysql 73 | 3306/tcp -> 0.0.0.0:3306 74 | ``` 75 | 76 | ### 镜像 77 | 镜像可以看成是由多个镜像层叠加起来的一个文件系统,镜像层也可以简单理解为一个基本的镜像,而每个镜像层之间通过指针的形式进行叠加。 78 | 79 | #### docker images 80 | 查看所有镜像 81 | ```code 82 | docker images // 查看所有镜像 83 | ``` 84 | 85 | #### docker pull 86 | 从镜像仓库中拉取或者更新指定镜像 87 | ```code 88 | docker pull [OPTIONS] NAME[:TAG|@DIGEST] 89 | 90 | docker pull [image name]:version // 拉取镜像 支持版本号 docker pull ubuntu:12.04 91 | ``` 92 | 93 | #### push 94 | 将本地的镜像上传到镜像仓库,要先登陆到镜像仓库 95 | - OPTIONS 96 | - --disable-content-trust :忽略镜像的校验,默认开启 97 | ```code 98 | docker push [OPTIONS] NAME[:TAG] 99 | 100 | docker push myapache:v1 // 上传本地镜像myapache:v1到镜像仓库中。 101 | ``` 102 | 103 | #### docker image rm [image name] 104 | 删除某一个镜像 105 | #### docker build 106 | 用于使用 Dockerfile 创建镜像。 107 | 108 | ### Dockerfile 配置 109 | Dockerfile 一般分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令,’#’ 为 Dockerfile 中的注释。 110 | 111 | #### FROM:指定基础镜像,必须为第一个命令 112 | ```code 113 | FROM 114 | FROM : 115 | FROM @ 116 | 117 | FROM mysql:5.6 118 | ``` 119 | 120 | #### MAINTAINER: 维护者信息 121 | ```code 122 | MAINTAINER 123 | 124 | MAINTAINER dw 125 | MAINTAINER dw 126 | ``` 127 | 128 | #### RUN:构建镜像时执行的命令 129 | **两种格式**: 130 | - shell 执行 131 | ```code 132 | RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME' 133 | ``` 134 | - exec执行 135 | ```code 136 | RUN ["/bin/bash", "-c", "echo hello"] 137 | ``` 138 | #### COPY: 复制指令,从上下文目录中复制文件或者目录到容器里指定路径。 139 | ```code 140 | COPY [--chown=:] <源路径1>... <目标路径> 141 | ``` 142 | 143 | #### CMD 类似于 RUN 指令,用于运行程序,但二者运行的时间点不同: 144 | - CMD 在docker run 时运行。 145 | - RUN 是在 docker build。 146 | > 如果 Dockerfile 中**如果存在多个 CMD 指令,仅最后一个生效**。 147 | 148 | ```code 149 | CMD echo 1 150 | CMD ["npm", "run", "test"] // 必须是双引号 151 | ``` 152 | #### ENV 设置环境变量, 那么在后续的指令中,就可以使用这个环境变量。 153 | ```code 154 | ENV 155 | ENV = =... 156 | ``` 157 | 158 | ```code 159 | ENV NODE_VERSION 7.2.0 160 | 161 | RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \ 162 | && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" 163 | ``` 164 | -------------------------------------------------------------------------------- /posts/关于cookie.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 关于cookie 3 | date: 2019-03-06 00:27:40 4 | categories: javascript 5 | tags: js 6 | --- 7 | 8 | **`http请求的时候cookie的数据会携带在请求头中传递给后端`**,我一直以为只是简单的数据存储,这应该算是http的基本常识了,还是得好好看,好好敲 9 | 10 | ### 对比h5的存储机制(localStorage和sessionStorage) 11 | | 名称 | 存储大小 | 过期时间 | 12 | |:-----|:---|:----------| 13 | |**cookie**|4kb|可自定义过期时间| 14 | |**localStorage**|4-8mb|没有过期时间,不清楚永久有效| 15 | |**sessionStorage**|4-8mb|关闭页面或浏览器后被清除| 16 | 17 | ### 其他区别 18 | #### 代码执行方式 19 | localStorage和sessionStorage都有html5的API,添加,删除,移除所有变得很简单 20 | ```js 21 | // 如果需要判断浏览器是否支持 可使用 window.localStorage判断 22 | // 添加 23 | window.localStorage.setItem('name', value) 24 | // 删除 25 | window.localStorage.removeItem('name') 26 | // 获取 27 | window.localStorage.getItem('name') 28 | // 删除所有 29 | window.localStorage.clear() 30 | ``` 31 | 同样的,sessionStorage也是这种操作方式 32 | ```js 33 | // 如果需要判断浏览器是否支持 可使用 window.sessionStorage判断 34 | // 添加 35 | window.sessionStorage.setItem('name', value) 36 | // 删除 37 | window.sessionStorage.removeItem('name') 38 | // 获取 39 | window.sessionStorage.getItem('name') 40 | // 删除所有 41 | window.sessionStorage.clear() 42 | ``` 43 | localStorage和sessionStorage都是全局的`Storage对象`,所以是可以不加windows的 44 | 45 | 然而cookie就没有那么简单明了的api了 46 | ```js 47 | // 获取 48 | document.cookie 49 | // "a=1; b=2; c=3" 50 | ``` 51 | `document.cookie`直接可以拿到浏览器的数据信息,是一个键值对的字符串,用于获取cookie的信息 52 | `document.cookie=...`同样可以被赋值,用于设置cookie的信息 53 | ##### 基本方法 54 | - 添加cookie: 55 | ```js 56 | /** 57 | * @description 设置Cookie 58 | * @param { String } name cookie名称 59 | * @param { String } value cooke的值 60 | * @param { Number } exp 过期时间 默认2小时 单位毫秒 61 | * @link https://ifmiss.github.io/d-js-utils/#/lib/_store?id=setcookie 62 | * @example 63 | * // 设置name为test的值为12345,设置过期时间为1小时 64 | * Dutils.store.setCookie('test', '12345', 60 * 60 * 1000) 65 | */ 66 | setCookie (name, value, exp = 60 * 60 * 2 * 1000) { 67 | let date = new Date() 68 | date.setTime(date.getTime() + exp) 69 | document.cookie = `${name}=${escape(value)};expires=${date.toGMTString()}` 70 | } 71 | ``` 72 | - 获取cookie: 73 | ```js 74 | /** 75 | * @description 获取Cookie 76 | * @param { String } name cookie名称 77 | * @returns { (Array | Null) } 返回数据 78 | * @link https://ifmiss.github.io/d-js-utils/#/lib/_store?id=getcookie 79 | * @example 80 | * Dutils.store.getCookie('test') 81 | */ 82 | getCookie (name) { 83 | if (name) { 84 | let reg = new RegExp(`(^| )${name}=([^;]*)(;|$)`) 85 | let arr = document.cookie.match(reg) 86 | return arr&&arr[2] ? arr[2] : null 87 | } 88 | let getAllCookies = [] 89 | if (document.cookie !== '') { 90 | let arrCookie = document.cookie.split('; ') 91 | for (let k in arrCookie) { 92 | getAllCookies.push({ 93 | name: `${unescape(arrCookie[k].split('=')[0])}`, 94 | value: `${unescape(arrCookie[k].split('=')[1])}` 95 | }) 96 | } 97 | return getAllCookies 98 | } else { 99 | return null 100 | } 101 | } 102 | ``` 103 | - 删除Cookie 104 | ```js 105 | /** 106 | * @description 删除Cookie 107 | * @param { String } name cookie名称 如果不传参数则设置所有cookie过期 108 | * @link https://ifmiss.github.io/d-js-utils/#/lib/_store?id=rmcookie 109 | * @example 110 | * Dutils.store.rmCookie('test') 111 | */ 112 | rmCookie (name) { 113 | let date = new Date() 114 | date.setTime(date.getTime() - 1) 115 | if (name) { 116 | let cookieInfo = store.getCookie(name) 117 | if (cookieInfo !== null) { 118 | document.cookie = `${name}=${cookieInfo};expires=${date.toGMTString()}` 119 | } 120 | return 121 | } 122 | let getAllCookies = store.getCookie() 123 | for (let k in getAllCookies) { 124 | document.cookie = `${getAllCookies[k].name}=${getAllCookies[k].value};expires=${date.toGMTString()}` 125 | } 126 | } 127 | ``` 128 | 129 | ##### 在http请求的时候 130 | - `cookie会作为请求内容存放在httpRequest的header里面`,此时后端是可以拿到cookie里面的数据信息的,可以通过cookie的方式和后端建立数据通信,但也会有安全性问题 131 | - 而localStorage和sessionStorage 只是单纯的数据存储的概念 132 | 133 | -------------------------------------------------------------------------------- /posts/前端模块化规范.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 前端模块化 3 | date: 2019-12-08 17:36:48 4 | categories: JavaScript 5 | tags: ['js'] 6 | --- 7 | 8 | ### CommonJs 同步加载模块 9 | - 使用 `require` 方法导入所需要的模块,可定义变量赋值,此变量即可执行模块内部的方法(**同步引入**) 10 | ```ts 11 | require('module_name') 12 | const cmd = require('module_name') 13 | cmd.method() 14 | cmd.prop 15 | .... 16 | ``` 17 | - 使用 `module.exports` 导出所需要的模块 18 | ```ts 19 | const a = 1 20 | const addA = () => { 21 | a++ 22 | } 23 | module.exports { 24 | a, 25 | addA 26 | } 27 | ``` 28 | 需要注意的是,一旦我们拿到了a的值,不管我们如何执行addA,暴露出得a的值还是为1,引用类型除外 29 | 看代码 30 | ```ts 31 | // cmd.js 32 | let count = 1 33 | let obj = { 34 | size: 1 35 | } 36 | const addCount = () => { 37 | count ++ 38 | console.log('addCount', count) 39 | } 40 | 41 | const addSize = () => { 42 | obj.size ++ 43 | console.log('addSize', obj.size) 44 | } 45 | 46 | module.exports = { 47 | count, 48 | obj, 49 | addSize, 50 | addCount 51 | } 52 | ``` 53 | ```ts 54 | // index.js 55 | LogUtils.logInfo(cmd.count, 'CMD.count') // CMD.count 1 56 | cmd.addCount() // addCount 2 57 | LogUtils.logInfo(cmd.count, 'CMD.count') // CMD.count 1 58 | 59 | 60 | LogUtils.logInfo(cmd.obj.size, 'CMD.obj.size') // CMD.obj.size 1 61 | cmd.addSize() // addSize 2 62 | LogUtils.logInfo(cmd.obj.size, 'CMD.obj.size') // CMD.obj.size 2 63 | ``` 64 | #### 特点 65 | - 简单易用 66 | - 模块复用(服务器端) 67 | 实际上浏览器端也可以用,通过模拟`require`, `exports`实现 68 | - 同步加载的特点让他不适于在浏览器端使用,会阻塞页面加载和代码执行 69 | 70 | #### 加载原理 71 | CommonJS的一个模块,就是一个脚本文件。require命令第一次加载该脚本,就会执行整个脚本,然后在内存生成一个对象 72 | ```ts 73 | { 74 | id: '...', 75 | exports: { ... }, 76 | loaded: true, 77 | ... 78 | } 79 | ``` 80 | id属性是模块名,exports属性是模块输出的各个接口,loaded属性是一个布尔值,表示该模块的脚本是否执行完毕。其他还有很多属性,这里都省略了。 81 | 以后需要用到这个模块的时候,就会到exports属性上面取值。即使再次执行require命令,也不会再次执行该模块,而是到缓存之中取值。 82 | 83 | #### 循环加载 84 | http://www.ruanyifeng.com/blog/2015/11/circular-dependency.html 85 | 86 | ### AMD 异步加载模块 87 | 通过回调函数的执行方式,执行异步操作,等到引入的模块加载完成之后才会执行回调函数的代码 88 | - 通过 `define` 定义函数模块 89 | - 通过 `require` 引入函数模块 90 | 91 | 在定义模块的过程中,如果定义的模块有需要引入的依赖,需要在 define 内部的回调函数之前添加模块路径地址,可以引入多个模块,使用过程中就是在回调中传入对应顺序的依赖,并为其命名,之后在回调函数内部即可访问到该模块的属性方法 92 | 格式如下 93 | ```ts 94 | define( 95 | '模块id', 96 | ['依赖数组'], 97 | function([依赖数组]){ //工厂函数 98 | ... 99 | } 100 | ); 101 | ``` 102 | 103 | ```ts 104 | // amd.js 105 | define('add', ['./../node_modules/d-utils/lib/index.js'], function(Dutils) { 106 | let a = 1 107 | function getA () { 108 | Dutils.LogUtils.logInfo(a, 'getA') 109 | } 110 | 111 | function addA () { 112 | a ++ 113 | } 114 | 115 | return { 116 | a, 117 | addA, 118 | getA 119 | } 120 | }) 121 | ``` 122 | 123 | 同样在使用模块的时候,和define类似 124 | ```ts 125 | // index.js 126 | require(['./amd'], function(AMD) { 127 | console.log(AMD.a) 128 | AMD.addA() 129 | console.log(AMD.a) 130 | 131 | // getA 132 | AMD.getA() 133 | }) 134 | ``` 135 | #### 特点 136 | - require会执行代码请求,引入需要的模块 137 | - 异步加载,回调函数内部可以拿到引入的所有模块信息 138 | 139 | ### UMD 通用模块规范 140 | 产生的原因是为了整合 CMD 和 AMD 的规范 使用工厂模式定义不同规范 141 | ```ts 142 | (function (global, factory) { 143 | // 判断是否支持cmd 144 | if (typeof exports === 'object' && typeof module !== undefined) { 145 | module.exports = factory() 146 | } else if (typeof define === 'function' && define.amd) { // 设置amd 147 | define('add', [], factory) 148 | } else { 149 | global.add = factory() // 添加至全局环境 150 | } 151 | })(this, function() { 152 | let a = 1 153 | function addA () { 154 | a++ 155 | } 156 | return { 157 | a, 158 | addA 159 | } 160 | }) 161 | ``` 162 | #### 特点 163 | - 兼容cmd amd 模块 164 | 165 | ### ES6模块 166 | - 通过 `import` 导入模块 167 | - 通过 `export` 导出模块 168 | 详细语法可以看阮老师es6的 169 | 170 | ```ts 171 | // add.js 172 | export let a = 1 173 | export const addA = function () { 174 | a ++ 175 | } 176 | export default { 177 | a, 178 | addA 179 | } 180 | ``` 181 | ```ts 182 | import ADD, { 183 | a, 184 | addA as changeAddName 185 | } from './module_name' 186 | 187 | LogUtils.logInfo(a, 'Es6') // 1 188 | ADD.addA()/ 189 | LogUtils.logInfo(a, 'Es6') // 2 190 | changeAddName() 191 | LogUtils.logInfo(a, 'Es6') // 3 192 | ``` 193 | 这个和之前的cmd 的例子不一样,es6 在引入之后,修改模块的值,会使模块自身的值也发生变化 194 | 看阮老师说的 195 | JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的import有点像 Unix 系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。 196 | 197 | #### 循环加载 198 | http://www.ruanyifeng.com/blog/2015/11/circular-dependency.html 199 | 200 | ### 参考于: 201 | 谈谈Js前端模块化规范: https://segmentfault.com/a/1190000015991869 202 | 203 | commmjs规范: https://www.jianshu.com/p/dd08f4095a49 204 | 205 | JavaScript 模块的循环加载: http://www.ruanyifeng.com/blog/2015/11/circular-dependency.html 206 | 207 | ECMAScript 6 入门: https://es6.ruanyifeng.com/ 208 | 209 | 210 | -------------------------------------------------------------------------------- /posts/【js高级程序】错误处理与调试.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 【js高级程序】错误处理与调试 3 | date: 2020-10-23 07:19:29 4 | categories: javascript 5 | tags: [js] 6 | --- 7 | 8 | 红宝书学习记录 9 | 10 | ### *原文整理摘抄自 javascript 高级程序开发(第4版) 第21章* 11 | 12 | ### 错误处理 13 | #### try/catch 语句 14 | ECMA-262 第 3 版新增了 try/catch 语句,作为在 JavaScript 中处理异常的一种方式。 15 | ```js 16 | try { 17 | // 可能出错的代码 18 | } catch (error) { 19 | // 出错时要做什么 20 | } 21 | ``` 22 | > 因此无法捕获异步代码 23 | 24 | ##### finally 子句 25 | try/catch 语句中可选的 finally 子句始终运行。 26 | > 只要代码中包含了 finally 子句,try 块或 catch 块中的 return 语句就会被忽略,理解这一点很重要。在使用 finally 时一定要仔细确认代码的行为。 27 | 28 | ##### 错误类型 29 | 代码执行过程中会发生各种类型的错误。 每种类型都会对应一个错误发生时抛出的错误对象。 ECMA-262 定义了以下 8 种错误类型: 30 | - `Error` Error 是基类型,其他错误类型继承该类型。浏览器很少会抛出 Error 类型的错误,该类型主要用于开 发者抛出自定义错误。 31 | - `InternalError` InternalError 类型的错误会在底层 JavaScript 引擎抛出异常时由浏览器抛出。例如,递归过多导 致了栈溢出。 32 | - `EvalError` EvalError 类型的错误会在使用 eval()函数发生异常时抛出。 33 | - `RangeError` 错误会在数值越界时抛出。例如,定义数组时如果设置了并不支持的长度 34 | - `SyntaxError` JavaScript 语法错误时发生 35 | - `TypeError` 主要发生在变量不是预期类型,或者访问不存在的方法时。很 多原因可能导致这种错误,尤其是在使用类型特定的操作而变量类型不对时。 36 | - `URIError` 只会在使用 encodeURI()或 decodeURI()但传入了格式错误的 URI 时发生。 37 | 38 | #### 抛出错误 39 | 与 try/catch 语句对应的一个机制是 throw 操作符,用于在任何时候抛出自定义错误。throw 操 作符必须有一个值,但值的类型不限。下面这些代码都是有效的: 40 | ```js 41 | throw 12345; 42 | throw "Hello world!"; 43 | throw true; 44 | throw { name: "JavaScript" }; 45 | ``` 46 | 可以通过内置的错误类型来模拟浏览器错误。每种错误类型的构造函数都只接收一个参数,就是错 误消息。 47 | ```js 48 | throw new Error("Something bad happened."); 49 | throw new SyntaxError("I don't like your syntax."); throw new InternalError("I can't do that, Dave."); throw new TypeError("What type of variable do you take me for?"); 50 | ``` 51 | 52 | 继承 Error也可以创建自定义的错误类型, 创建自定义错误类型 时,需要提供 name 属性和 message 属性 53 | ```js 54 | class CustomError extends Error { 55 | constructor(message) { 56 | super(message); 57 | this.name = "CustomError"; 58 | this.message = message; 59 | } 60 | } 61 | throw new CustomError("My message"); 62 | ``` 63 | 64 | #### error 事件 65 | 任何没有被 try/catch 语句处理的错误都会在 window 对象上触发 error 事件。 66 | ```js 67 | window.onerror = (message, url, line) => { 68 | console.log(message); 69 | return false; 70 | }; 71 | ``` 72 | 在任何错误发生时,无论是否是浏览器生成的,都会触发 error 事件并执行这个事件处理程序。 然后,浏览器的默认行为就会生效,像往常一样显示这条错误消息。可以返回 false 来阻止浏览器默 认报告错误的行为 73 | > 图片也支持 error 事件。 74 | 75 | #### 错误处理策略 76 | - **静态代码分析器** (JSHint、JSLint、Google Closure 和 TypeScript) 77 | - **类型转换错误** 主要原因是使用了会自动改变某个值的数据类型的操作符或语言构造。 78 | > 使用等于 (==)或不等于(!=)操作符,以及在 if、for 或 while 等流控制语句中使用非布尔值,经常会导致 类型转换错误。 79 | - **数据类型错误** 因为 JavaScript 是松散类型的,所以变量和函数参数都不能保证会使用正确的数据类型。开发者需 要自己检查数据类型,确保不会发生错误。数据类型错误常发生在将意外值传给函数的时候。 80 | - **通信错误** 81 | - URL 格式或发送数据的格式不正确。 (encodeURIComponent编码) 82 | - 服务器响应非预期值,导致访问数据字段无效直接报错 83 | 84 | #### 把错误记录到服务器中 85 | 使用 Image 对象发送请求主要是从灵活性方面考虑的。 86 | - 所有浏览器都支持 Image 对象,即使不支持 XMLHttpRequest 对象也一样。 87 | - 不受跨域规则限制。通常,接收错误消息的应该是多个服务器中的一个,而 XMLHttpRequest 此时就比较麻烦。 88 | - 记录错误的过程很少出错。大多数 Ajax 通信借助 JavaScript 库的包装来处理。如果这个库本身 出错,而你又要利用它记录错误,那么显然错误消息永远不会发给服务器。 89 | 90 | ```js 91 | function logError(sev, msg) { 92 | let img = new Image(), 93 | encodedSev = encodeURIComponent(sev), 94 | encodedMsg = encodeURIComponent(msg); 95 | img.src = 'log.php?sev=${encodedSev}&msg=${encodedMsg}'; 96 | } 97 | 98 | for (let mod of mods){ 99 | try { 100 | mod.init(); 101 | } catch (ex) { 102 | logError("nonfatal", 'Module init failed: ${ex.message}'); 103 | } 104 | } 105 | ``` 106 | 在这个例子中,模块初始化失败就会调用 logError()函数。第一个参数是表示错误严重程度的 "nonfatal",第二个参数在上下文信息后面追加了 JavaScript 错误消息。记录到服务器的错误消息应 该包含尽量多的上下文信息,以便找出错误的确切原因。 107 | 108 | ### 调试技术 109 | #### 把消息记录到控制台 110 | 所有主流浏览器都有 JavaScript 控制台,该控制台可用于查询 JavaScript 错误。 111 | 112 | #### 理解控制台运行时 113 | 浏览器控制台是个读取-求值-打印-循环(REPL,read-eval-print-loop),与页面的 JavaScript 运行 时并发。这个运行时就像浏览器对新出现在 DOM 中的 `script` 标签求值一样。在控制台中执行的命 令可以像页面级 JavaScript 一样访问全局和各种 API。控制台中可以执行任意数量的代码,与它可能会 阻塞的任何页面级代码一样。修改、对象和回调都会保留在 DOM 和运行时中。 114 | JavaScript 运行时会限制不同窗口可以访问哪些内容 115 | 控制台运行时也会集成开发者工具,提供常规 JavaScript 开发中所没有的上下文调试工具。 116 | 117 | #### 使用 JavaScript 调试器 118 | debugger 119 | 120 | #### 在页面中打印消息 121 | 另一种常见的打印调试消息的方式是把消息写到页面中指定的区域。这个区域可以是所有页面中都 包含的元素,但仅用于调试目的;也可以是在需要时临时创建的元素。 122 | ```js 123 | function log(message) { 124 | const console = document.getElementById("debuginfo"); 125 | if (console === null) { 126 | console = document.createElement("div"); 127 | console.id = "debuginfo"; 128 | console.style.background = "#dedede"; 129 | console.style.border = "1px solid silver"; console.style.padding = "5px"; 130 | console.style.width = "400px"; 131 | console.style.position = "absolute"; 132 | console.style.right = "0px"; 133 | console.style.top = "0px"; 134 | document.body.appendChild(console); 135 | } 136 | console.innerHTML += '

    ${message}

    '; 137 | } 138 | ``` 139 | 140 | #### 抛出错误 141 | 抛出错误是调试代码的很好方式。如果错误消息足够具体,只要看一眼错误就可以确定 原因。好的错误消息包含关于错误原因的确切信息,因此可以减少额外调试的工作量。 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /posts/【面试题】node.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 【面试题】node 3 | date: 2021-03-29 18:50:57 4 | categories: node 5 | tags: [node, 面试] 6 | --- 7 | 8 | ### node 事件循环 9 | 10 | Node 10 及以前: 11 | 12 | ![Node 事件循环](https://www.daiwei.site/static/blog/【面试题】node/libuv.png) 13 | 14 | Libuv 是一个高性能的,事件驱动的异步 I/O 库,它本身是由 C 语言编写的,具有很高的可移植性。 15 | **事件循环的 7 个主要阶段** 16 | **times:** setTimeout setInterval 回调 17 | **I/O callbacks:** 处理异步事件的回调,比如网络 I/O,比如文件读取 I/O。当这些 I/O 动作都**结束**的时候,在这个阶段会触发它们的回调。(上一轮循环的回调) 18 | **idle, prepare:** 这个阶段内部做一些动作,仅 node 内部使用 19 | **I/O poll 阶段:** 获取新的 I/O 事件,适当的条件下 node 将阻塞在这里 20 | **check:** 执行 setImmediate() 回调 21 | **close callback:** 关闭 I/O 的动作,比如文件描述符的关闭,socket 断开,等等等 22 | 23 | > process.nextTick 的操作,会在每一轮事件循环的最后执行 24 | 25 | - 执行全局 Script 的同步代码 26 | - 执行 microtask 微任务,先执行所有 Next Tick Queue 中的所有任务 27 | - 在执行 Other Microtask Queue 中的所有任务 28 | - 执行完成之后开始执行 macrotask 宏任务,共 6 个阶段,每个阶段红任务执行完成之后,再执行 步骤 2 ,然后继续执行下一个阶段的宏任务,再执行 步骤 2。 29 | - 如:Times Queue -> 步骤 2 -> I/O Queue -> 步骤 2 -> Check Queue -> 步骤 2 -> Close Callback -> 步骤 2 -> Times Queue -> ... 30 | - 这就是 Node 的 Event Loop 31 | 32 | Node 11 以后: 33 | 类似浏览器事件循环 34 | 35 | ### 介绍以下 nodejs 中间件 36 | 37 | 中间件主要是指封装 http 请求细节处理的方法。引入了 Node 中间件来简化和封装这些基础逻辑处理细节。 38 | 39 | ### node 性能优化 40 | 41 | - 使用最新版本的 Node.js 42 | - 避免使用同步代码 43 | - 缓存静态文件 44 | - 使用 gzip 45 | - 实现 SSL/TLS 和 HTTP/2 46 | - 读写分离 47 | - 多进程架构 48 | 49 | ### node 错误监控 50 | 51 | - 语法错误,运行时错误触发 JavaScript error 52 | - 访问不存在的文件触发系统错误。 53 | - 自定义 error 54 | 55 | **日志** 56 | 57 | - nginx log 58 | - log4js 59 | - pm2 log 60 | 61 | ### koa2 中间件原理。 62 | 63 | 🧅 洋葱模型 64 | 通过 use() 注册多个中间件放入数组中,从外层开始往内执行,遇到 next()执行下一个中间件,所有中间件执行完成之后,开始返回,一次执行中间件未执行的部分。 65 | 66 | ### node 如何进行跨域通信 67 | 68 | Node 设置允许跨域 69 | 70 | ```js 71 | res.header("Access-Control-Allow-Origin", "https://www.daiwei.site"); 72 | ``` 73 | 74 | ### require 原理 75 | 76 | ```js 77 | let name = require("./a"); 78 | console.log(name); 79 | ``` 80 | 81 | 上述代码引入 `a.js` 文件时候,内部大致发生的流程如下: 82 | 83 | - 将 `./a` 转化为绝对路径,并且补充后缀名(`c:\Users\chenying\Desktop\code\a.js`) 84 | - 根据绝对路径判断缓存中是否存在缓存的文件,如果存在则取缓存,不存在则继续 85 | - 创建 `Module` 实例 `module`,将绝对路径传入 86 | - 取得绝对路径的后缀名,根据后缀名(`.js`)调用对应的处理函数 87 | - 读 `.js` 和 `.json` 文件思路大同小异,通过 fs.readFileSync()读取文件内容 88 | - 对读到的 `.js` 文件的内容外层包裹一个函数,并且将字符串转成函数执行 89 | - 对读到的 `.json` 文件的内容,转为对象,并且赋值给`module.exports` 90 | 91 | ### node 异常处理 92 | 93 | - 使用 `try catch` 方式来处理异常 94 | > `try catch` 无法处理异步代码块内出现的异常 95 | - 使用 `event` 方式来处理异常 96 | 97 | ```js 98 | const events = require("events"); 99 | // 创建一个事件监听对象 100 | const emitter = new events.EventEmitter(); 101 | // 监听error事件 102 | emitter.addListener("error", (e) => { 103 | // 处理异常信息 104 | console.log(11122222); // 能打印 1112222 说明异常捕获到了 105 | console.log(e); 106 | }); 107 | // 触发 error事件 108 | emitter.emit("error", new Error("你代码出错了")); 109 | ``` 110 | 111 | - 自带的 `error first callback` 的方式 112 | - `Promise` `catch` 方式 113 | - `process` 方式 `uncaughtException` 114 | 115 | ```js 116 | process.on("uncaughtException", (e) => { 117 | console.log("我能进来,说明可以处理异常"); 118 | console.log(e); 119 | }); 120 | 121 | function testFunc() { 122 | throw new Error("error"); 123 | } 124 | 125 | testFunc(); 126 | ``` 127 | 128 | ### `node` 如何创建子进程 129 | 130 | 通过 `child_process` 对象中的以下几个方法创建 131 | 132 | - **spawn 方法** child_process.spawn(command[, args][, options]) 133 | 子进程中执行的是非 node 程序,提供一组参数后,执行的结果以流的形式返回。 134 | - **exec 方法** child_process.exec(command[, options][, callback]) 135 | 子进程执行的是非 node 程序,提供一串 shell 命令,执行结果后以回调的形式返回,它与 execFile 不同的是,exec 可以直接执行一串 shell 命令。 136 | > 不是基于 stream 的 存在 命令注入 的安全风险 137 | - **execFile 方法** child_process.execFile(file[, args][, options][, callback]) 138 | 子进程中执行的是非 node 程序, 提供一组参数后,执行的结果以回调的形式返回。 139 | > 不是基于 stream 的 存在 命令注入 的安全风险 140 | - **fork 方法** child_process.fork(modulePath[, args][, options]) 141 | 子进程执行的是 node 程序,提供一组参数后,执行的结果以流的形式返回,它与 spawn 不同的是,fork 生成的子进程只能执行 node 应用。 142 | 143 | ### `node` 中 `cluster` 是怎样开启多进程的,并且一个端口可以被多个进程监听吗 144 | 145 | ### 你了解 node 多进程吗 146 | 147 | ### node 进程中怎么通信 148 | 149 | ### node 可以开启多线程吗 150 | 151 | ### 进程和线程是什么 152 | 153 | Master-Worker 模式, 也就是说进程分为 Master(主)进程 和 worker(工作)进程。master 进程负责调度或管理 worker 进程,那么 worker 进程负责具体的业务处理。 154 | 155 | **1. Node 原生 IPC** (Inter-Process Communication,进程间通信)支持 156 | 157 | **父元素** 158 | 159 | ```js 160 | const { fork } = require("child_process"); 161 | 162 | const child = fork("./child.js"); 163 | 164 | child.on("message", (m) => { 165 | console.log("父进程收到消息", m); 166 | }); 167 | 168 | child.send("this is data from parent"); 169 | ``` 170 | 171 | **子元素** 172 | 173 | ```js 174 | process.on("message", (m) => { 175 | console.log("子进程收到消息", m); 176 | }); 177 | 178 | // 使父进程输出: 父进程收到消息 { foo: 'bar', baz: null } 179 | process.send({ foo: "bar", baz: NaN }); 180 | ``` 181 | 182 | 结果 183 | 184 | ```code 185 | 子进程收到消息 this is data from parent 186 | 父进程收到消息 { foo: 'bar', baz: null } 187 | ``` 188 | 189 | **2. 通过 sockets** 190 | **2. 通过 message queue** 191 | -------------------------------------------------------------------------------- /posts/d-video.js视频播放器.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: posts 3 | title: d-video.js 视频播放器 4 | date: 2018-12-05 15:30:06 5 | categories: javascript 6 | tags: [js, video, 插件] 7 | --- 8 | 9 | ### 关于d-video.js 10 | d-video.js是之前的公司中业务需求所拓展的一个通用的pc端插件,兼容ie9及目前主流浏览器,基于video元素来封装的视频的一体化操作 11 | 该插件包涵以下功能 12 | - 语速调整 13 | - 清晰度调整 14 | - 音量调整,菜单的显示与隐藏 15 | - 全屏设置,动态调整视频大小 16 | - 播放下一个视频的动态显示 17 | 18 | ### 代码引入 19 | es6环境下可以基于npm 安装, import 导入的方式 20 | 安装: 21 | ```code 22 | npm install d-video 23 | ``` 24 | 导入: 25 | ```js 26 | import Dvideo from 'd-video' 27 | ``` 28 | 29 | es5的代码可以使用script标签直接引用 30 | ```html 31 | 32 | ``` 33 | 34 | ### 使用 35 | 导入完之后,就可以开始使用了 36 | #### 初始化 37 | ```js 38 | var video = new Dvideo ({ 39 | ele: '#testVideo', 40 | // 名字 41 | title: 'Pneumatic Tokyo - EnV', 42 | nextVideoExtend: function () { 43 | // 点击下一页的回调函数, 此时通常会执行获取下一段视频的信息,然后执行视频切换 44 | video.setVideoInfo(title, url, currentT) 45 | }, 46 | // 是否显示下一页按钮,配合nextVideoExtend 47 | showNext: true, 48 | width: '580px', 49 | height: '292px', 50 | src: 'http://www.daiwei.org/index/video/EnV%20-%20PneumaticTokyo.mp4', 51 | // 是否自动播放 52 | autoplay: true, 53 | setVideoDefinition: function (type, e, current) { 54 | if (type === '0') { 55 | alert('你点击了标清') 56 | // video.setVideoInfo('這是標清','这里填写视频的标清地址',current) 57 | } 58 | if (type === '1') { 59 | alert('你点击了标清') 60 | // video.setVideoInfo('這是標清','这里填写视频的高清地址',current) 61 | } 62 | if (type === '2') { 63 | alert('你点击了标清') 64 | // video.setVideoInfo('這是標清','这里填写视频的超清地址',current) 65 | } 66 | video.showLoading(false) 67 | }, 68 | }) 69 | ``` 70 | 71 | #### 属性 72 | ##### ele 73 | ```code 74 | dom 元素, 元素id需要带 # , 比如 #video 或者 .video 75 | ``` 76 | ##### src 77 | ```code 78 | 视频地址 79 | ``` 80 | ##### isShowPoster 81 | ```code 82 | 是否显示封面,默认为true bool 83 | ``` 84 | ##### title 85 | ```code 86 | 视频的名称 string 87 | ``` 88 | ##### width 89 | ```code 90 | 视频显示宽度 string 默认 '300px' 91 | ``` 92 | ##### height 93 | ```code 94 | 视频显示高度 string 默认 '160px' 95 | ``` 96 | ##### showNext 97 | ```code 98 | 是否显示下一集按钮 bool 默认true 99 | ``` 100 | ##### autoplay 101 | ```code 102 | 是否自动播放 bool 默认 true 103 | ``` 104 | ##### ctrSpeedDuration 105 | ```code 106 | 控制条 关闭的时间 number (ms) 107 | ``` 108 | ##### loop 109 | ```code 110 | 视频是否循环播放 bool 默认false 111 | ``` 112 | ##### showVolume 113 | ```code 114 | 是否显示音量设置 bool 默认true 115 | ``` 116 | ##### volume 117 | ```code 118 | 音量大小 number 0.8 119 | ``` 120 | ##### showVolumeUnFull 121 | ```code 122 | 在非全屏幕下是否显示音量调整条 bool 默认false 123 | ``` 124 | ##### showPlayBackRate 125 | ```code 126 | 是否显示设置语速菜单列表 bool 默认true 127 | ``` 128 | ##### showPlayBackRateUnFull 129 | ```code 130 | 是否在未全屏的情况下 显示语速 bool 默认true 131 | ``` 132 | ##### playbackRate 语速的设置 object 133 | ```code 134 | activeIndex: 索引 number 135 | rateList: 语速 array [0.8, 1, 1.2, 2] 136 | ``` 137 | ##### showVideoDefinition 138 | ```code 139 | 是否显示清晰度 bool 默认true 140 | ``` 141 | ##### showVideoDefinitionUnFull 142 | ```code 143 | 非全屏的状态下是否显示 bool 默认true 144 | ``` 145 | ##### videoDefinition: 清晰度的设置 object 146 | ```code 147 | activeIndex: 索引 number 148 | definitionList: 清晰度选项 object 149 | definitionList.type: 类型 150 | definitionList.name: 名称 151 | ``` 152 | ##### nextVideoExtend 153 | ```code 154 | 可让用户自定义扩展 点击下一个视频的操作 function 155 | ``` 156 | ##### setVideoDefinition 157 | ```code 158 | 设置清晰度的回调 参数 (type, event, currentT) function 159 | ``` 160 | ##### onTimeupdate 161 | ```code 162 | 进度更新事件 参数(currentT) function 163 | ``` 164 | ##### onPlaying 165 | ```code 166 | 视频播放事件 参数(currentT) function 167 | ``` 168 | ##### onPause 169 | ```code 170 | 视频暂停事件 function 171 | ``` 172 | ##### onEnded 173 | ```code 174 | 视频播放结束事件 function 175 | ``` 176 | ##### onLoadedMetaData 177 | ```code 178 | 元数据加载成功事件 function 179 | ``` 180 | 181 | #### 方法 182 | ##### 更新视频宽度高度 183 | ```js 184 | video.updateVideoSize() 185 | @param { number } width 宽度 186 | @param { number } height 高度 187 | ``` 188 | ##### 显示上下菜单 189 | ```js 190 | video.showTopBottomCtrl() 191 | @param { bool } disappearance 是否自动消失 192 | ``` 193 | ##### 关闭上下菜单 194 | ```js 195 | video.hideTopBottomCtrl() 196 | @param { bool } immediately 是否立刻关闭 197 | ``` 198 | ##### 更新音量 199 | ```js 200 | video.updateVolume(0.5) 201 | @param { number } vol 音量大小 0 - 1 之间 202 | ``` 203 | ##### 快进 204 | ```js 205 | video.videoForward(seconds) 206 | @param { number } seconds 快进时长 207 | ``` 208 | 209 | ##### 快退 210 | ```js 211 | video.videoRewind(seconds) 212 | @param { number } seconds 快退时长 213 | ``` 214 | 215 | ##### 跳转到具体位置 216 | ```js 217 | video.videoSeek(seconds) 218 | @param { number } seconds 跳转的位置 219 | ``` 220 | 221 | ##### 切换视频地址 222 | ```js 223 | video.setVideoInfo() 224 | @param { sting } title 视频的名称 225 | @param { string } url 视频的地址 226 | @param { number } currentT 视频开始播放的时间,默认为0 227 | ``` 228 | -------------------------------------------------------------------------------- /posts/DOM & BOM.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: DOM & BOM 3 | date: 2019-03-24 11:58:13 4 | categories: javascript 5 | tags: [js, html] 6 | --- 7 | 8 | ### JavaScript组成部分 9 | - ECMAScript 基本语法 10 | - DOM (文档对象模型) 11 | - BOM (浏览器对象模型) 12 | 13 | ### DOM:文档对象模型 14 | DOM 对于前端来说应该是再熟悉不过了,早期jquery的时代对于DOM的接触已经非常频繁了,文档对象模型是指:页面渲染的标签对象信息(如document,body,p标签等等),包含他们的属性,挂载事件等信息 15 | 16 | #### DOM的顶层对象:document 17 | 18 | ##### document 对象集合 19 | document是文档对象模型的顶层对象,之后则是html 再到 body...,document对象可以使我们对于页面所有的元素进行访问 20 | ```js 21 | document.all // 提供对文档中所有 HTML 元素的访问。 22 | ``` 23 | 以上返回值都是一个虚拟数组 24 | 25 | | 集合 | 描述 | 26 | |:-----|:---| 27 | |`all`|提供对文档中所有 HTML 元素的访问。| 28 | |`anchors`|返回对文档中所有 Anchor 对象的引用。| 29 | |`forms`|返回对文档中所有 Form 对象引用。| 30 | |`images`|返回对文档中所有 Image 对象引用。| 31 | |`links`|返回对文档中所有 Area 和 Link 对象引用。| 32 | 33 | ##### document 对象属性 34 | | 属性 | 描述 | 35 | |:-----|:---| 36 | |`body`|提供对 元素的直接访问。对于定义了框架集的文档,该属性引用最外层的 。| 37 | |[`cookie`](/hexo-blog/关于cookie)|设置或返回与当前文档有关的所有 cookie。| 38 | |`domain`|返回当前文档的域名。| 39 | |`lastModified`|返回文档被最后修改的日期和时间。| 40 | |`links`|返回对文档中所有 Area 和 Link 对象引用。| 41 | |`referrer`| 返回载入当前文档的文档的 URL。| 42 | |`title`|返回当前文档的标题。| 43 | |`URL`|返回当前文档的 URL。| 44 | 45 | ##### document 对象方法 46 | | 方法 | 描述 | 47 | |:-----|:---| 48 | |`close()`|关闭用 document.open() 方法打开的输出流,并显示选定的数据。| 49 | |`getElementById()`|返回对拥有指定 id 的第一个对象的引用。| 50 | |`getElementsByName()`|返回带有指定名称的对象集合。| 51 | |`write()`|向文档写 HTML 表达式 或 JavaScript 代码。| 52 | |`document.querySelector('p')`|返回所有p标签元素的集合| 53 | 54 | #### HTML元素: element 对象 55 | 通常我们针对元素操作的时候需要拿到当前元素的节点信息,再给元素执行appendChild,getAttribute,等待增删改查的操作 56 | ##### element比较常用的方法和属性 57 | | 方法/属性 | 描述 | 58 | |:-----|:---| 59 | |`element.appendChild()`|向元素添加新的子节点,作为最后一个子节点。| 60 | |`element.childNodes`|返回元素子节点的 NodeList。| 61 | |`element.className`|设置或返回元素的 class 属性。| 62 | |`element.clientHeight`| 返回元素的可见高度。| 63 | |`element.clientWidth`|返回元素的可见宽度。| 64 | |`element.element.getElementsByTagName()`|返回拥有指定标签名的所有子元素的集合。| 65 | |`element.insertBefore()`|在指定的已有的子节点之前插入新节点。| 66 | |...|...| 67 | 68 | #### 元素事件: dom event 69 | Event 对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态。 70 | 71 | 事件通常与函数结合使用,函数不会在事件发生前被执行! 72 | ##### 事件句柄 (Event Handlers) 73 | | 事件名称 | 何时发生该事件 | 74 | |:-----|:---| 75 | |`onblur`|元素失去焦点。| 76 | |`onchange`|域的内容被改变。| 77 | |`onclick`|当用户点击某个对象时调用的事件句柄。| 78 | |`ondblclick`|当用户双击某个对象时调用的事件句柄。 79 | | 80 | |`onerror`|在加载文档或图像时发生错误。| 81 | |`onload`|一张页面或一幅图像完成加载。| 82 | |`onmousedown`|鼠标按钮被按下。| 83 | |...|...| 84 | 85 | ### BOM: 浏览器对象模型 86 | - BOM主要用于管理窗口与窗口之间的通讯,因此其核心对象是window; 87 | - BOM由一系列相关的对象构成,并且每个对象都提供了很多方法与属性; 88 | #### BOM顶层的对象: window 89 | window对象是js中的顶级对象,所有定义在全局作用域中的变量、函数都会变成window对象的属性和方法,在调用的时候可以省略window。 90 | ```js 91 | localStorage 92 | window.localStorage 93 | 94 | location.href 95 | window.location.href 96 | ``` 97 | ##### window对象属性或者方法 98 | | 属性/方法 | 描述 | 99 | |:-----|:---| 100 | |`history`|对 History 对象的只读引用。请参数 History 对象。| 101 | |`innerheight`|返回窗口的文档显示区的高度。| 102 | |`innerwidth`|返回窗口的文档显示区的宽度。| 103 | |`location`|用于窗口或框架的 Location 对象。| 104 | |`alert()`| 显示带有一段消息和一个确认按钮的警告框。| 105 | |`setTimeout()`|在指定的毫秒数后调用函数或计算表达式。| 106 | |`clearInterval() `|取消由 setInterval() 设置的 timeout。| 107 | |`resizeTo()`|把窗口的大小调整到指定的宽度和高度。| 108 | |`scrollTo()`|把内容滚动到指定的坐标。| 109 | |...|...| 110 | 111 | #### Navigator 对象 112 | Navigator 对象包含有关浏览器的信息。 113 | ##### Navigator 对象属性或者方法 114 | | 属性/方法 | 描述 | 115 | |:-----|:---| 116 | |`appCodeName`|返回浏览器的代码名。| 117 | |`appName`|返回浏览器的名称。| 118 | |`appVersion`|返回浏览器的平台和版本信息。| 119 | |`onLine`|返回指明系统是否处于脱机模式的布尔值。| 120 | |`platform`|返回运行浏览器的操作系统平台。| 121 | |`userAgent `|返回由客户机发送服务器的 user-agent 头部的值。| 122 | |`javaEnabled()`|规定浏览器是否启用 Java。| 123 | |`taintEnabled()`|规定浏览器是否启用数据污点 (data tainting)。| 124 | |...|...| 125 | 126 | #### Screen 对象 127 | Screen 对象包含有关客户端显示屏幕的信息。 128 | ##### Screen 对象属性 129 | | 属性 | 描述 | 130 | |:-----|:---| 131 | |`availHeight`|返回显示屏幕的高度 (除 Windows 任务栏之外)。| 132 | |`availWidth`|返回显示屏幕的宽度 (除 Windows 任务栏之外)。| 133 | |`height`|返回显示屏幕的高度。| 134 | |`width`|返回显示器屏幕的宽度。| 135 | |`updateInterval`|设置或返回屏幕的刷新率。| 136 | |...|...| 137 | 138 | #### History 对象 139 | History 对象包含用户(在浏览器窗口中)访问过的 URL。History 对象是 window 对象的一部分,可通过 window.history 属性对其进行访问。 140 | ##### History 对象属性 141 | | 属性/方法 | 描述 | 142 | |:-----|:---| 143 | |`length`|返回浏览器历史列表中的 URL 数量。| 144 | |`back()`|加载 history 列表中的前一个 URL。| 145 | |`forward()`|加载 history 列表中的下一个 URL。| 146 | |`go()`|加载 history 列表中的某个具体页面。| 147 | 148 | #### Location 对象 149 | Location 对象包含有关当前 URL 的信息。可通过 window.location 属性来访问。 150 | ##### Location 对象属性 151 | | 属性/方法 | 描述 | 152 | |:-----|:---| 153 | |`hash`|设置或返回从井号 (#) 开始的 URL(锚)。| 154 | |`host`|设置或返端口号| 155 | |`hostname`|设置或返回当前 URL 的主机名。| 156 | |`href`|设置或返回完整的 URL。| 157 | |`pathname`|设置或返回当前 URL 的路径部分。| 158 | |`port`|设置或返回当前 URL 的端口号。| 159 | |`protocol`|设置或返回当前 URL 的协议。| 160 | |`search`|设置或返回从问号 (?) 开始的 URL(查询部分)。| 161 | |`assign()`|加载新的文档。| 162 | |`reload()`|重新加载当前文档。| 163 | |`replace()`|用新的文档替换当前文档。| 164 | 165 | ### 总结 166 | - BOM是浏览器对象模型,用来获取或设置浏览器的属性、行为,例如:新建窗口、获取屏幕分辨率、浏览器版本号等。 167 | - DOM是文档对象模型,用来获取或设置文档中标签的属性,例如获取或者设置input表单的value值。 168 | -------------------------------------------------------------------------------- /posts/提升页面性能的n种方法.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 提升页面性能的n种方法 3 | date: 2020-12-18 08:29:38 4 | categories: 总结 5 | tags: [总结] 6 | --- 7 | 8 | ### 减少资源请求 9 | 10 | - #### `css spirites` 11 | 图片资源合并一张,background-position 显示 12 | - #### 图片懒加载 13 | 图片懒加载实现视口的资源请求,避免在没有曝光的情况下,加载不必要的资源 14 | - #### 图片 webp 格式 15 | - #### http 接口合并请求 16 | 后端接口聚合 或者 中间层接口聚合(`graphQL`) 17 | 18 | ### 控制资源优先级 19 | 20 | - #### js css 排放位置 21 | css 位于 body 顶部 22 | js 位于 body 底部 23 | - #### 避免阻塞主线程(long Task) 24 | 超过 50ms 的 js 任务可理解为 long Task, 25 | - #### 控制阻塞资源 26 | - ###### 控制 script type **`defer`** or **`async`** 标签达到低优先级, 避免阻塞主线程 27 | ![defer and async](https://www.daiwei.site/static/blog/提升页面性能的n种方法/defer&async.jpeg) 28 | - ###### 避免 `js` 存放于 head 中 29 | - #### css 避免阻塞 30 | 当一个媒体查询的结果值计算出来是 false 的时候 (`media` 设置非 `all`),浏览器仍然会下载样式表,但是不会在渲染页面之前等待样式表的资源可用 31 | ```jsx 32 | // jsx 33 | { 38 | if (rel.currentTarget.media != "all") { 39 | rel.currentTarget.media = "all"; 40 | } 41 | }} 42 | > 43 | ``` 44 | 45 | ### CSS 优化 46 | 47 | - #### content-visibility 48 | 提高渲染性能,可以跳过在屏幕外的内容渲染初始化页面的加载时间 49 | - #### CSS3 触发 GPU 加速 50 | 使用 opcatiy or css 3d 触发 GPU 加速,配合 will-change 通过告知浏览器该元素会有哪些变化,使浏览器提前做好优化准备,增强页面渲染性能 51 | - #### 避免动态请求 52 | 53 | ### 预加载,预请求 54 | 55 | - #### preconnect 56 | ![preconnect](https://www.daiwei.site/static/blog/提升页面性能的n种方法/preconnect.webp) 57 | 允许浏览器在将 HTTP 请求实际发送到服务器之前建立早期连接。可以预先启动诸如`DNS查找`,`TCP握手`和`TLS协商`之类的连接,从而消除了这些连接的往返延迟,并为用户节省了时间。 58 | ##### 执行代码 59 | ```html 60 | // crossOrigin 设置 use-credentials 表示允许携带cookie信息 61 | 66 | 67 | 72 | 77 | ``` 78 | - #### dns-prefetch 79 | DNS Prefetch 是一种 DNS 预解析技术。 80 | 浏览网页时,浏览器会在加载网页时对网页中的域名进行解析缓存,这样在你单击当前网页中的连接时就无需进行`DNS`的解析,减少用户等待时间,提高用户体验。 81 | ```html 82 | 83 | 84 | ``` 85 | 86 | ### 缓存 87 | 88 | - #### Http 缓存 89 | 90 | - **强缓存**(按照优先级排序) 91 | 92 | - **cache-control**(HTTP/1.1 中定义) 93 | 94 | ```code 95 | add_header Cache-Control max-age=3600; 96 | ``` 97 | 98 | > 设置 Cache-Control 则会覆盖 `expires` 设置 99 | 100 | - **expires**(HTTP/1.0 中定义) 101 | expires 设置资源缓存的时间 102 | ```nginx 103 | # 设置一天后过期 s: 秒, m: 分, h: 时, d: 天 104 | expires 1d; 105 | ``` 106 | 107 | - **协商缓存**(按照优先级排序) 108 | 第一次请求数据时,服务器会返回缓存标识和数据资源,客户端会将缓存标识与数据保存在本地 109 | 第二次请求数据时,会将缓存标识发送给服务器,服务器根据标识判断,如果可以使用缓存,则返回 304 状态码,浏览器使用缓存的数据内容 110 | - **Etag / If-None-Match** 111 | Etag:服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器决定) 112 | If-None-Match:再次请求服务器时,会将标识发送给服务器,服务器发现 `If-None-Match` 与被请求资源的标识一致时,则返回 304,并从本地缓存获取资源,否则重新获取新数据 113 | - **Last-Modified / If-Modified-Since** 114 | Last-Modified:服务器在响应请求时,告诉浏览器资源的最后修改时间。 115 | If-Modified-Since:下次请求时会带上此前的资源修改时间, 116 | 117 | - #### 模版数据缓存 118 | 模板 JS + 数据填充,减少访问量大的页面频繁的资源请求导致性能损耗 119 | 120 | #### ServiceWork 缓存 + 离线缓存 121 | 122 | 使用 ServiceWork 实现静态资源,网络请求,甚至在离线环境下的数据展示,提升用户体验 123 | 124 | ### 体积优化 125 | 126 | - #### js, css minify,文件压缩 127 | 128 | 通过 webpack, rollup 等打包工具实现 js, css 代码的压缩 129 | 130 | - #### tree shaking 131 | 132 | tree shaking(树摇),摇掉不使用的代码,减少包体积(只支持 ES module) 133 | 134 | - #### 优化 CSS 体积 135 | - css 代码重用 136 | - 元素样式公共代码合并编写(复用) 137 | - css 代码缩写 138 | ```css 139 | margin: 15px 20px 12px 24px; 140 | border-top: thin solid #000000; 141 | ``` 142 | - 删除不必要的零和单位 143 | - #### GZIP 144 | 基于文本(js,css...)的资源应该使用压缩(gzip)来最小化网络总字节 145 | ```nginx 146 | # 开启gzip压缩 147 | gzip on; 148 | # 压缩类型 149 | gzip_types text/css text/javascript application/javascript image/jpeg image/png image/gif; 150 | # 超过1k以上压缩 151 | gzip_min_length 1k; 152 | # 等级1-9 9最小的压缩,传输最快 但是消耗cpu 153 | gzip_comp_level 6; 154 | ``` 155 | 156 | ### 外部优化 157 | 158 | - #### 静态资源 CDN 159 | CDN (Content Delivery Network) 从技术上全面解决由于网络带宽小、用户访问量大、网点分布不均等原因,提高用户访问网站的响应速度 160 | - #### WebWorker 161 | 处理密集型运算时可使用 WebWorker 单独开辟线程, 让脚本单独创建一个 JavaScript 线程,以执行委托的任务 162 | 163 | ### webview 优化 164 | 165 | - #### 使用离线包而非服务器承载静态资源 166 | 167 | 离线包的目的是为了客户端本地快速加载网页,达到页面秒开效果,但是这种资源请求协议不是 http 协议,在接口请求的时候可能会有跨域问题(IOS 中常会遇到) 168 | 169 | - #### 接口数据预加载 170 | 接口数据在客户端做预加载请求,如客户端列表页点击访问详情页,详情页是 H5 ,通常情况下是整个 APP 的重要页面 171 | **如:** 172 | - 今日头条 的文章详情页面,会在列表页获取加载好的数据通过 webview 植入到 `localStorge` 中 173 | - 点击列表打开 文章列表,通过 文章 id 优先从缓存中获取数据,如果不存在,则走接口请求下发 174 | - 渲染页面 175 | - #### JS 预加载 176 | 客户端在加载页面之前,后台打开 webview 预先加载代码内容 177 | - #### 同层渲染 178 | H5 中渲染原生的组件,并做降级处理(主要是提升用户体验) 179 | -------------------------------------------------------------------------------- /posts/【面试题】typescript.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 【面试题】typescript 3 | date: 2021-03-29 18:50:49 4 | categories: typescript 5 | tags: [typescript, 面试] 6 | --- 7 | 8 | ### 访问修饰符 9 | 10 | - `public` 都可以在任何地方进行访问。 11 | - `private` 只能在类定义内部进行访问。 12 | - `protected` 的属性和方法可以从类定义内部访问,也可以从子类中访问。 13 | - `readonly` 属性设置为只读的 14 | 15 | ### interface 与 type 的区别 16 | 17 | - `type` 可以声明基本类型别名,使用交叉类型、联合类型等,`interface` 不行 18 | - `type` 语句中还可以使用 `typeof` 获取实例的 类型进行赋值 19 | ```js 20 | type B = typeof div; 21 | ``` 22 | - `interface` 同类型声明可以被合并,而 `type` 不行。 23 | 24 | ### 范型 pick/Partial/record ... 25 | 26 | - [TypeScript 高级特性](https://mp.weixin.qq.com/s/VWggn-5JdbJon6ZzxHPqHw) 27 | 28 | ### implements 与 extends 的区别 29 | 30 | - `implements` 必须要实现定义在父类的所有方法,且不需要定义 super 方法 31 | - `extends` 不需要实现父类的所有方法,且需要 super 做 this 指向的修改 32 | 33 | ### 范型如何设置默认值 34 | ```ts 35 | interface Component { 36 | state: S; 37 | } 38 | ``` 39 | 40 | ### 枚举和 object 的区别 41 | - 写法不一致 42 | - 枚举解析的是 key,value; value,key都有 43 | ```js 44 | var itemCategory; 45 | (function (itemCategory) { 46 | itemCategory[itemCategory["healing"] = 0] = "healing"; 47 | itemCategory[itemCategory["crafting"] = 1] = "crafting"; 48 | itemCategory[itemCategory["armor"] = 2] = "armor"; 49 | itemCategory[itemCategory["weapon"] = 3] = "weapon"; 50 | //... 51 | })(itemCategory || (itemCategory = {})); 52 | ``` 53 | 54 | ### window 扩展类型 55 | ```ts 56 | declare global { 57 | interface Window { 58 | module_name: any; 59 | } 60 | } 61 | ``` 62 | 63 | ### never 与 void 64 | 当一个函数返回空值时,它的返回值为 void 类型,但是,当一个函数永不返回时(或者总是抛出错误),它的返回值为 never 类型。 65 | 66 | ### typescript中,class, abstract class, interface 在tsc 之后会产生代码吗? 67 | `abstract class`: 会产生 68 | `interface`: 不会 69 | `class`: 会,会被打成es5 的构造函数 70 | 71 | ### `implement Partial` 72 | `Partial` 返回一个包含所有T的子集的type。 73 | 请自行实现MyPartial。 74 | 75 | ```ts 76 | type MyPartial = { 77 | [P in keyof T]?: T[P] 78 | } 79 | ``` 80 | 81 | ### `implement Required` 82 | ```ts 83 | type MyRequired = { 84 | [K in keyof T]-?: T[K]; 85 | } 86 | ``` 87 | 88 | ### `implement Readonly` 89 | ``` ts 90 | type MyReadonly = { 91 | readonly [K in keyof T]: T[K] 92 | } 93 | 94 | type Foo = { 95 | a: string 96 | } 97 | 98 | const a:Foo = { 99 | a: 'BFE.dev', 100 | } 101 | a.a = 'bigfrontend.dev' 102 | // OK 103 | 104 | const b:MyReadonly = { 105 | a: 'BFE.dev' 106 | } 107 | b.a = 'bigfrontend.dev' 108 | // Error 109 | ``` 110 | 111 | ### implement Record 112 | ```ts 113 | type MyRecord = { 114 | [P in K]: V; 115 | } 116 | 117 | type Key = 'a' | 'b' | 'c' 118 | 119 | const a: Record = { 120 | a: 'BFE.dev', 121 | b: 'BFE.dev', 122 | c: 'BFE.dev' 123 | } 124 | a.a = 'bigfrontend.dev' // OK 125 | a.b = 123 // Error 126 | a.d = 'BFE.dev' // Error 127 | ``` 128 | 129 | ### implement Pick 130 | ```ts 131 | type MyPick = { 132 | [P in K]: T[P]; 133 | } 134 | 135 | type A = MyPick // {a: string, b: number} 136 | ``` 137 | 138 | ### implement Omit 139 | ```ts 140 | type MyOmit = { 141 | [P in Exclude]: T[P] 142 | } 143 | 144 | type B = MyOmit // {a: string, b: number} 145 | ``` 146 | 147 | ### implement Exclude 148 | ```ts 149 | type MyExclude = T extends E ? never : T; 150 | 151 | type C = MyExclude // 'a' | 'b' 152 | ``` 153 | 154 | ### implement Extract 155 | ```ts 156 | type MyExtract = T extends U ? T : never; 157 | 158 | type C = MyExtract // 'b' | 'c' 159 | ``` 160 | 161 | ### implement NonNullable 162 | ```ts 163 | type MyNonNullable = Exclude 164 | 165 | type Foo = 'a' | 'b' | null | undefined 166 | 167 | type A = MyNonNullable // 'a' | 'b' 168 | ``` 169 | 170 | ### implement Parameters 171 | ```ts 172 | type MyParameters = T extends (...args: infer P) => any ? [...P] : unknown[] 173 | ``` 174 | 175 | ### implement ConstructorParameters 176 | ```ts 177 | type MyConstructorParameters any> 178 | = T extends new (...args: infer U) => any ? U : never; 179 | ``` 180 | 181 | ### implement ReturnType 182 | ```ts 183 | type MyReturnType 184 | = T extends (...args: any[]) => infer R ? R : never; 185 | ``` 186 | 187 | ### implement InstanceType 188 | ```ts 189 | type MyInstanceType any> 190 | = T extends new (...args: any) => infer R ? R : never; 191 | ``` 192 | 193 | ### implement ThisParameterType 194 | ```ts 195 | type MyThisParameterType 196 | = T extends (this: infer P, ...args: any[]) => any ? P : unknown 197 | ``` 198 | 199 | ### implement OmitThisParameter 200 | `Function.prototype.bind()` 返回一个this已经bind过后的`function`。 对于这种情况,可以用 `OmitThisParameter` 来增加 `type` 信息。 201 | 请自行实现 `MyOmitThisParameter`。 202 | ```ts 203 | function foo(this: {a: string}) {} 204 | foo() // Error 205 | 206 | const bar = foo.bind({a: 'BFE.dev'}) 207 | bar() // OK 208 | 209 | type Foo = (this: {a: string}) => string 210 | type Bar = MyOmitThisParameter // () => string 211 | ``` 212 | 答案: 213 | ```ts 214 | type MyOmitThisParameter 215 | = T extends (this: any, ...args: infer P) => infer R ? (...args: P) => R : T; 216 | ``` -------------------------------------------------------------------------------- /posts/对象的继承方式.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 对象的继承方式 3 | date: 2019-05-27 10:46:26 4 | categories: javascript 5 | tags: [js] 6 | --- 7 | 8 | 关于对象的继承方式的学习和总结 9 | 10 | ### 原型链的继承方式 11 | 12 | **概念:** 父元素作为子类的原型 13 | **缺点:** 父元素的原型属性方法的变化会影响到子类 14 | ```ts 15 | function User (name: string) { 16 | this.name = name 17 | this.getName = function (): string { 18 | return this.name 19 | } 20 | this.setName = function (name: string): void { 21 | this.name = name 22 | } 23 | } 24 | 25 | User.prototype.test = function () { 26 | console.log('test user') 27 | } 28 | 29 | function Student (age: number) { 30 | this.age = age 31 | } 32 | 33 | Student.prototype = new User('dw') 34 | 35 | const s = new Student(21) 36 | s.test() // 'test user' 37 | 38 | // 此时 s 的值为 { age: 21 }, 但是由于继承了User对象,拥有User对象的属性和方法 39 | ``` 40 | 对象在获取一个属性或者方法的时候,会先访问自身是否有这个属性,如果有,则直接返回,如果没有,则寻找父级对象,继续执行属性和方法的查找,如果有则返回,没有则重复执行这类操作,直至最顶层,如果没有则返回undefined 41 | 42 | ```ts 43 | // 因此,s 为 { age: 21 } 44 | s.name === 'dw' // true 此时访问的是父元素的name属性, 因为s 自身并没有这个属性, 他是继承 User,因此能拿到User实例的属性和方法 45 | 46 | s.name = 'dwdwdwdw' // 此时 s 的值为 { age: 21, name: 'dwdwdwdw' } 47 | s.setName('newDw') // 此时 s 的值为 { age: 21, name: 'newDw' } 48 | Student.prototype // name的值还是 dw 49 | ``` 50 | s.name 和 s.setName 只会作用自身的对象属性,但是在访问的时候,如果自身没有该属性和方法,则会往上层层查找 51 | 52 | ### 构造继承方式 53 | 54 | **概念:** 使用call,apply的方式把父类的this制定的属性方法创建在子级的对象,不涉及对象继承,原型链继承 55 | **缺点:** 56 | - 只能继承(Student)的实例属性和方法,父类(User)原型扩展不会影响到子类(子类无法访问其原型上扩展的属性或者方法) 57 | - 每个子类都会拿到父类的所有属性和方法并存在子类的实例中,这样会增加不必要的内存问题 58 | 59 | ```ts 60 | function User (name: string) { 61 | this.name = name 62 | this.getName = function (): string { 63 | return this.name 64 | } 65 | this.setName = function (name: string): void { 66 | this.name = name 67 | } 68 | } 69 | 70 | User.prototype.test = function () { 71 | console.log('test user') 72 | } 73 | 74 | function Student (name: string, age: number) { 75 | User.call(this, name) 76 | this.age = age 77 | } 78 | 79 | const s = new Student('dw', 26) 80 | s.test() // VM424:1 Uncaught TypeError: s.test is not a function 81 | 82 | // s 并不像原型链继承的方式只有age属性,而是所有的属性和方法都存在s对象上 83 | // { 84 | // age: 26, 85 | // getName: function () {}, 86 | // name: 'dw', 87 | // setName: function (name) {} 88 | // } 89 | ``` 90 | 这种继承方式并不存在对象继承,而仅仅是将 User 的方法和属性设置到 Student 构造函数之上, 实现方式就是利用call apply 指定this的指向执行调用机制 91 | 92 | ### 原型链 + 构造继承组合的方式 93 | **概念:** 使用call方法调用父类构造函数,继承父类的属性,然后再通过父类实例作为子类的原型 94 | **缺点:** 95 | - 无论什么情况下,都会调用两次父类构造函数 96 | - 父类中(User)的属性和方法既存在于子类(Student)的实例化对象中,又存在于子类的原型中(Student.prototype)说是会内存占用 97 | - 一组在实例上,一组在 Student 原型中,这是不合理的 98 | 99 | ```ts 100 | function User (name, sex) { 101 | this.name = name 102 | this.sex = sex 103 | console.log('name-sex', name, sex) 104 | this.setSex = function () { 105 | console.log('this is User setSex') 106 | } 107 | } 108 | 109 | User.prototype.test = function () { 110 | console.log('this is User test') 111 | } 112 | 113 | function Student (name, sex, grade) { 114 | console.log(name, sex) 115 | // 继承 User的属性 116 | User.call(this, name, sex) 117 | this.grade = grade 118 | this.setGrade = function () { 119 | console.log('this is Student setGrade') 120 | } 121 | } 122 | 123 | // 设置原型 124 | Student.prototype = new User() 125 | console.log(Student.prototype.constructor) 126 | console.log(Student.prototype) 127 | // 设置构造函数由Student初始化 128 | Student.prototype.constructor = Student 129 | console.log(Student.prototype.constructor) 130 | console.log(Student.prototype) 131 | Student.prototype.setStudent = function () { 132 | console.log('this is Student setStudent') 133 | } 134 | // 用于测试 Student.prototype.constructor = Student 135 | // 可查看 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor 136 | Student.prototype.test = function () { 137 | console.log('this is Student test') 138 | return new this.constructor() 139 | } 140 | const c1 = new Student('张三', '男', '三年级') 141 | const c2 = new Student('王洁', '女', '四年级') 142 | 143 | console.log(c1); 144 | console.log(c1.constructor); 145 | 146 | console.log(c2); 147 | console.log(c2.constructor); 148 | ``` 149 | Student类通过设置原型链继承的方式定义prototype为User的实例,这样在实例化Student的时候,c1是可以访问到User的属性方法了,其次在实例化Student的时候,执行User.call,保证实例的对象有User的副本 150 | 151 | ### 寄生组合式继承 152 | **概念:** 使用call方法调用父类构造函数,继承父类的属性,通过设置中间函数,创建父类原型的副本,存放在子类的原型上,避免两次调用父类构造函数 153 | **缺点:** 暂无,可能就稍微麻烦一点吧 154 | ```js 155 | function User (name, sex) { 156 | this.name = name 157 | this.sex = sex 158 | this.sayHello = function () { 159 | console.log(`Hello ${this.name}`) 160 | } 161 | } 162 | User.prototype.test = function () { 163 | console.log('this is User test') 164 | } 165 | 166 | User.prototype.saySex = function () { 167 | console.log(`my Sex is ${this.sex}`) 168 | } 169 | 170 | function warp (Child, Parent) { 171 | const prototype = Object.create(Parent.prototype) 172 | prototype.constructor = Child 173 | console.log(Parent.prototype) 174 | console.log(Object.create(Parent.prototype)) 175 | console.log(prototype) 176 | Child.prototype = prototype // 这里的目的是为了拿到原型属性,以及定义constructor指向自身 177 | } 178 | 179 | function Student (name, sex, age) { 180 | User.call(this, name, sex) // 这里初始化构造函数属性继承与User 181 | this.age = age 182 | } 183 | 184 | Student.prototype.sayAge = function () { 185 | console.log(`${name}的年龄是:${this.age}`) 186 | } 187 | 188 | warp(Student, User) 189 | const v1 = new Student('dw', '男', 22) 190 | const v2 = new Student('zg', '女', 12) 191 | ``` 192 | 寄生组合式继承是组合式继承的简化版本 193 | -------------------------------------------------------------------------------- /posts/【js高级程序】DOM扩展.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 【js高级程序】DOM扩展 3 | date: 2020-11-20 08:02:52 4 | categories: javascript 5 | tags: [js] 6 | --- 7 | 8 | 红宝书学习记录 9 | 10 | ### *原文整理摘抄自 javascript 高级程序开发(第4版) 第15章* 11 | 12 | ### DOM 扩展 13 | 14 | #### Selectors API 15 | ##### querySelector() 16 | querySelector()方法接收 CSS 选择符参数,返回匹配该模式的第一个后代元素,如果没有匹配 项则返回 null。 17 | ```js 18 | document.querySelector("body"); 19 | document.querySelector("#myDiv"); 20 | ``` 21 | 22 | ##### querySelectorAll() 23 | 这个方法返回的是一个 NodeList 的静态实例, querySelector的集合 24 | 25 | ##### matches() 26 | matches()方法(在规范草案中称为 matchesSelector())接收一个 CSS 选择符参数,如果元素 匹配则该选择符返回 true,否则返回 false。 27 | ```js 28 | if (document.body.matches("body.page1")) { 29 | // true 30 | } 31 | ``` 32 | 33 | #### 元素遍历 34 | 背景: IE9 之前的版本不会把元素间的空格当成空白节点,而其他浏览器则会。这样就导致了 childNodes 和 firstChild 等属性上的差异。为了弥补这个差异,同时不影响 DOM 规范,W3C 通过新的 Element Traversal 规范定义了一组新属性。 35 | - `childElementCount` 返回子元素数量(不包含文本节点和注释) 36 | - `firstElementChild` 指向第一个 Element 类型的子元素(Element 版 firstChild) 37 | - `lastElementChild` 指向最后一个 Element 类型的子元素(Element 版 lastChild); 38 | - `previousElementSibling` 指向前一个Element类型的同胞元素( Element 版 previousSibling); 39 | - `nextElementSibling` 指向后一个 Element 类型的同胞元素(Element 版 nextSibling)。 40 | 41 | #### HTML5 42 | ##### CSS 类扩展 43 | ###### getElementsByClassName 44 | getElementsByClassName()是 HTML5 新增的最受欢迎的一个方法,暴露在 document 对象和 所有 HTML 元素上。 45 | 46 | ###### classList属性 47 | 要操作类名,可以通过 className 属性实现添加、删除和替换。但 className 是一个字符串, 所以每次操作之后都需要重新设置这个值才能生效,即使只改动了部分字符串也一样。 48 | 49 | 新增方法: 50 | - `add(value)` 向类名列表中添加指定的字符串值 value。如果这个值已经存在,则什么也不做。 51 | - `contains(value)` 返回布尔值,表示给定的 value 是否存在。 52 | - `remove(value)` 从类名列表中删除指定的字符串值 value。 53 | - `toggle(value)` 如果类名列表中已经存在指定的 value,则删除;如果不存在,则添加。 54 | 55 | ```js 56 | // 删除"disabled"类 57 | div.classList.remove("disabled"); 58 | 59 | // 添加"current"类 60 | div.classList.add("current"); 61 | 62 | // 切换 63 | div.classList.add("on"); 64 | ``` 65 | 66 | ##### 焦点管理 67 | ###### document.activeElement 68 | HTML5 增加了辅助 DOM 焦点管理的功能。首先是 `document.activeElement`,始终包含当前拥有焦点的 DOM 元素。 69 | > 默认情况下,document.activeElement 在页面刚加载完之后会设置为 document.body。而在 页面完全加载之前,document.activeElement 的值为 null。 70 | 71 | ###### document.hasFocus() 72 | 确定文档是否获得了焦点,就可以帮助确定用户是否在操作页面。 73 | 74 | ##### HTMLDocument 扩展 75 | ###### readyState 属性 76 | HTML5 将这个属性写进了标准。document.readyState 属性有两个可能的值 77 | - `loading`,表示文档正在加载; 78 | - `complete`,表示文档加载完成。 79 | 80 | ###### compatMode 属性 81 | 自从 IE6 提供了以标准或混杂模式渲染页面的能力之后,检测页面渲染模式成为一个必要的需求。 IE 为 document 添加了 compatMode 属性,这个属性唯一的任务是指示浏览器当前处于什么渲染模式。 82 | - 标准模式下 document.compatMode 的值是"CSS1Compat" 83 | - 混杂模式下, document.compatMode 的值是"BackCompat" 84 | ```js 85 | if (document.compatMode == "CSS1Compat"){ 86 | console.log("Standards mode"); 87 | } else { 88 | console.log("Quirks mode"); 89 | } 90 | ``` 91 | 92 | ###### head 属性 93 | 作为对 document.body(指向文档的 `body` 元素)的补充,HTML5 增加了 `document.head` 属性,指向文档的 `head` 元素。 94 | 95 | ##### 字符集属性 96 | HTML5 增加了几个与文档字符集有关的新属性。其中,characterSet 属性表示文档实际使用的 字符集,也可以用来指定新字符集。 97 | ```js 98 | console.log(document.characterSet); // "UTF-16" 99 | document.characterSet = "UTF-8"; 100 | ``` 101 | 102 | ##### 自定义数据属性 103 | HTML5 允许给元素指定非标准的属性,但要使用前缀 data-以便告诉浏览器,这些属性既不包含 与渲染有关的信息,也不包含元素的语义信息。 104 | ```html 105 |
    106 | ``` 107 | 定义了自定义数据属性后, 可以通过元素的 dataset 属性来访问。 dataset 属性是一个 DOMStringMap 的实例,包含一组键/值对映射。 108 | ```js 109 | let div = document.getElementById("myDiv"); 110 | // 取得自定义数据属性的值 111 | let appId = div.dataset.appId; // 12345 112 | div.dataset.appId = 23456 113 | console.log(div.dataset.appId) // 23456 114 | ``` 115 | 116 | ##### 插入标记 117 | ###### innerHTML 属性 118 | - 在读取 innerHTML 属性时,会返回元素所有后代的 HTML 字符串,包括元素、注释和文本节点。 119 | - 而在写入 innerHTML 时,则会根据提供的字符串值以新的 DOM 子树替代元素中原来包含的所有节点。 120 | 121 | ###### outerHTML 属性 122 | - 读取 outerHTML 属性时, 会返回调用它的元素(及所有后代元素)的 HTML 字符串。 123 | - 在写入 outerHTML 属性时,调用它的元素会被传入的 HTML 字符串经解释之后生成的 DOM 子树取代。 124 | 125 | ###### insertAdjacentHTML()与 insertAdjacentText() 126 | 关于插入标签的最后两个新增方法是 insertAdjacentHTML()和 insertAdjacentText()。 127 | 第一个参数 必须是下列值中的一个: 128 | - `beforebegin` 插入当前元素前面,作为前一个同胞节点; 129 | - `afterbegin` 插入当前元素内部,作为新的子节点或放在第一个子节点前面; 130 | - `beforeend` 插入当前元素内部,作为新的子节点或放在最后一个子节点后面; 131 | - `afterend` 插入当前元素后面,作为下一个同胞节点。 132 | 133 | ```html 134 | 135 |

    136 | 137 | foo 138 | 139 |

    140 | 141 | ``` 142 | > 在使用 innerHTML、 outerHTML 和 insertAdjacentHTML()之前,最好手动删除要被替换的元素上关联的事件处理程序和 JavaScript 对象。 143 | 144 | ##### scrollIntoView 145 | DOM 规范中没有涉及的一个问题是如何滚动页面中的某个区域。HTML5 选择了标准化 scrollIntoView()。 146 | - `alignToTop`: bool值 147 | - true: 窗口滚动后元素的顶部与视口顶部对齐。 148 | - false: 窗口滚动后元素的底部与视口底部对齐。 149 | - `scrollIntoViewOptions` 是一个选项对象。 150 | - `behavior` 定义过渡动画,可取的值为"smooth"和"auto",默认为"auto"。 151 | - `block` 定义垂直方向的对齐,可取的值为"start"、"center"、"end"和"nearest",默 认为 "start"。 152 | - `inline` 定义水平方向的对齐,可取的值为"start"、"center"、"end"和"nearest",默 认为 "nearest"。 153 | 154 | #### 专有扩展 155 | ##### children 属性 156 | ```js 157 | let childCount = element.children.length; 158 | let firstChild = element.children[0]; 159 | ``` 160 | 161 | ##### contains()方法 162 | contains()方法应该在要搜索的祖先元素上调 用,参数是待确定的目标节点。 163 | ```js 164 | console.log(document.documentElement.contains(document.body); 165 | ``` 166 | 167 | ##### 插入标记 168 | ###### innerText 属性 169 | - innerText 属性对应元素中包含的所有文本内容,无论文本在子树中哪个层级。在用于读取值时, innerText 会按照深度优先的顺序将子树中所有文本节点的值拼接起来。 170 | - 在用于写入值时,innerText 会移除元素的所有后代并插入一个包含该值的文本节点。 171 | 172 | ###### outerText属性 173 | - outerText 与 innerText 是类似的, 只不过作用范围包含调用它的节点。 174 | - 要读取文本值时, outerText 与 innerText 实际上会返回同样的内容。 175 | -------------------------------------------------------------------------------- /posts/javascript异步编程.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: javascript异步编程 3 | date: 2019-03-17 19:37:11 4 | categories: javascript 5 | tags: js 6 | --- 7 | 8 | ### 背景 9 | js是单线程语言,浏览器只分配给js一个主线程,用来执行任务,异步编程是JavaScript的一种编程方式,用于在不影响主程序去执行其他的操作,如网络请求,文件读写,等等,如果这些操作都是同步执行的话,那么对于用户来说就是一个灾难,毕竟在执行其他操作的时候,后续的代码没法同步执行 10 | 11 | ### 异步编程的方式 12 | 使用不同方式实现 ·红绿灯· 的操作模式 13 | - 执行红灯操作3秒 14 | - 之后执行绿灯操作2秒 15 | - 最后执行黄灯1秒 16 | - // 程序结束 // 17 | 18 | #### 1. callback 回调 19 | ```js 20 | function red (ms) { 21 | console.log('----- red start -----') 22 | setTimeout(function () { 23 | green(2000) 24 | }, ms) 25 | } 26 | 27 | function green (ms) { 28 | console.log('----- green start -----') 29 | setTimeout(function () { 30 | yellow(1000) 31 | }, ms) 32 | } 33 | 34 | function yellow (ms) { 35 | console.log('----- yellow start -----') 36 | setTimeout(function () { 37 | console.log('----- ALL DOWN -----') 38 | }, ms) 39 | } 40 | 41 | red(3000) 42 | ``` 43 | 定义三种方法,第一个方法执行结束执行第二个,再执行第三个,所有的后续操作都是在setTimeout方法的回调中实现的 44 | 45 | 但是如果我要放在我本身方法的回调里呢? 46 | ```js 47 | function red (ms, callback) { 48 | console.log('----- red start -----') 49 | setTimeout(function () { 50 | callback && callback() 51 | }, ms) 52 | } 53 | 54 | function green (ms, callback) { 55 | console.log('----- green start -----') 56 | setTimeout(function () { 57 | callback && callback() 58 | }, ms) 59 | } 60 | 61 | function yellow (ms, callback) { 62 | console.log('----- yellow start -----') 63 | setTimeout(function () { 64 | callback() 65 | }, ms) 66 | } 67 | 68 | red(3000, function () { 69 | green(2000, function () { 70 | yellow(1000, function () { 71 | console.log('----- ALL DOWN -----') 72 | }) 73 | }) 74 | }) 75 | ``` 76 | 这种方式表现出回调方法的特性,层层嵌套,然而这也会存在我们都知道的问题,回调地狱 77 | 78 | #### 2. 事件监听 79 | #### 3. 发布/订阅 80 | 简单的发布订阅者模式 81 | ```js 82 | function TestObserver () { 83 | this.handler = {} 84 | } 85 | 86 | TestObserver.prototype.on = function (eventType, handler) { 87 | const list = Object.keys(this.handler) 88 | if (!list.includes(eventType)) { 89 | this.handler[eventType] = [] 90 | } 91 | this.handler[eventType].push(handler) 92 | return this 93 | } 94 | 95 | TestObserver.prototype.off = function (eventType, handler) { 96 | const offType = this.handler[eventType] 97 | if (offType) { 98 | for (let i = offType.length; i >= 0; i--) { 99 | if (offType[i] === handler) { 100 | offType.splice(i, 1) 101 | } 102 | } 103 | } 104 | return this 105 | } 106 | 107 | TestObserver.prototype.emit = function (eventType) { 108 | const handlerArgs = Array.prototype.slice.call(arguments, 1) 109 | const handlerType = this.handler[eventType] 110 | handlerType.forEach(item => { 111 | item.apply(this, handlerArgs) 112 | }) 113 | return this 114 | } 115 | ``` 116 | 执行的时候如同第一个方式,代码量大,不以管理 117 | ```js 118 | const obs = new TestObserver() 119 | obs.on('red', function (ms) { 120 | console.log('----- red start -----') 121 | setTimeout(function () { 122 | obs.emit('green', 2000) 123 | }, ms) 124 | }) 125 | 126 | obs.on('green', function (ms) { 127 | console.log('----- green start -----') 128 | setTimeout(function () { 129 | obs.emit('yellow', 1000) 130 | }, ms) 131 | }) 132 | 133 | obs.on('yellow', function (ms) { 134 | console.log('----- yellow start -----') 135 | setTimeout(function () { 136 | console.log('----- ALL DOWN -----') 137 | }, ms) 138 | }) 139 | 140 | obs.emit('red', 3000) 141 | ``` 142 | 143 | #### 4. 协程 144 | Thunk + Generator 145 | 146 | ```js 147 | function *toDo () { 148 | yield console.log('----- red start -----') 149 | yield console.log('----- green start -----') 150 | yield console.log('----- yellow start -----') 151 | yield console.log('----- ALL DOWN -----') 152 | } 153 | 154 | function Thunk (fn) { 155 | return function () { 156 | const args = Array.prototype.slice.call(arguments) 157 | return function (callback) { 158 | args.push(callback) 159 | return fn.apply(this, args) 160 | } 161 | } 162 | } 163 | 164 | const timer = function (time, callback) { 165 | setTimeout(function () { 166 | callback && callback () 167 | }, time) 168 | } 169 | 170 | // const light = toDo() 171 | // const timeout = Thunk(timer) 172 | // light.next() 173 | // timeout(3000)(function () { 174 | // light.next() 175 | // }) 176 | // timeout(3000)(function () { 177 | // light.next() 178 | // }) 179 | // timeout(3000)(function () { 180 | // light.next() 181 | // }) 182 | ``` 183 | 184 | #### 5. Promise 185 | es6的到来Promise是最常用的一个新功能 186 | ```js 187 | const red = function (ms) { 188 | console.log('----- red start -----') 189 | return new Promise (resolve => { 190 | setTimeout(() => { 191 | resolve() 192 | }, ms) 193 | }) 194 | } 195 | 196 | const green = function (ms) { 197 | console.log('----- green start -----') 198 | return new Promise (resolve => { 199 | setTimeout(() => { 200 | resolve() 201 | }, ms) 202 | }) 203 | } 204 | 205 | const yellow = function (ms) { 206 | console.log('----- yellow start -----') 207 | return new Promise (resolve => { 208 | setTimeout(() => { 209 | resolve() 210 | }, ms) 211 | }) 212 | } 213 | 214 | red(3000) 215 | .then(() => green(2000)) 216 | .then(() => yellow(1000)) 217 | .then(() => { 218 | console.log('----- ALL DOWN -----') 219 | }) 220 | ``` 221 | 看起来舒服多了 222 | 223 | #### 6. async/await 224 | es7语法 225 | 三个灯的方法定义和上面一样,返回一个promise即可 226 | ```js 227 | async function toDo () { 228 | await red(3000) 229 | await green(2000) 230 | await yellow(1000) 231 | console.log('----- ALL DOWN -----') 232 | } 233 | toDo() 234 | ``` 235 | 236 | ### 总结 237 | 从callback 到 事件/发布订阅 到 协程 到 Promise 再到 async/await 238 | 方法越来越简单 -------------------------------------------------------------------------------- /posts/javascript需要注意的this指向问题.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: javascript需要注意的this指向问题 3 | date: 2019-10-15 17:13:35 4 | categories: javascript 5 | tags: ["js"] 6 | --- 7 | 8 | this 指向是 JavaScript 常见的一个知识点,也是平时经常遇到的问题,包括面试时候也经常会问到 9 | 10 | ### 全局环境中 11 | 12 | 在全局环境中,this 指向 windows 13 | 14 | ```ts 15 | console.log(this); // window 16 | var a = 1; 17 | console.log(window.a); // 1 18 | this.b = 3; 19 | console.log(b); // 3 20 | console.log(window.b); // 3 21 | ``` 22 | 23 | ### 对象中方法 24 | 25 | 此时方法运行的时候绑定的是 obj 对象,this 指向 obj 26 | 27 | ```ts 28 | function fn() { 29 | console.log(this.a); 30 | } 31 | 32 | var obj = { 33 | a: 1, 34 | b: fn, 35 | }; 36 | 37 | obj.b(); // 1 38 | ``` 39 | 40 | 虽然 foo 是 obj 对象的方法,但是在函数 foo 执行的时候,this 是在函数执行的时候绑定的,也就是 foo 函数的作用域的对象也就是 window 41 | 42 | ```ts 43 | var a = "window.a"; 44 | function fn() { 45 | console.log(this.a); 46 | } 47 | 48 | var obj = { 49 | a: 1, 50 | b: fn, 51 | c: () => { 52 | console.log(this.a); 53 | }, 54 | }; 55 | var foo = obj.b; 56 | obj.b(); // 1 57 | foo(); // window.a 58 | obj.c(); // window.a 59 | ``` 60 | 61 | ### 构造函数中 62 | 63 | 构造函数中的 this 指向的是实例化构造函数返回的新的对象 64 | 65 | ```ts 66 | function Fn() { 67 | this.a = 1; 68 | } 69 | var foo = new Fn(); 70 | foo.a; // 1 71 | ``` 72 | 73 | ### call, apply 方式调用函数 74 | 75 | call, apply 可以修改调用函数的 this 指向 76 | 77 | ```ts 78 | function fn() { 79 | console.log(this); 80 | } 81 | 82 | fn.call("test"); // string对象 test 83 | fn.call(null); // window 84 | fn.call({ 85 | a: 1, 86 | }); // { a: 1 } 87 | ``` 88 | 89 | ### 定时器 90 | 91 | 定时器中的 this,指向的是 window 92 | 93 | ```ts 94 | var a = "window.a"; 95 | setTimeout(function () { 96 | console.log(this); // window 97 | }); 98 | 99 | const obj = { 100 | a: 1, 101 | b: function () { 102 | setTimeout(function () { 103 | console.log(this.a); 104 | }, 400); 105 | }, 106 | }; 107 | obj.b(); // window.a 108 | ``` 109 | 110 | ### 元素绑定事件 111 | 112 | 回调函数执行的 this 指向的是当前的元素 113 | 114 | ```ts 115 | const ele = document.getElementById('btn') 116 | ele.onclick = funciton () { 117 | console.log(this) // ele 元素 118 | } 119 | ele.addEventListener('click', function () { 120 | console.log(this)} // ele 元素 121 | ) 122 | ``` 123 | 124 | ### 函数调用的时候绑定 bind 125 | 126 | ```ts 127 | const ele = document.getElementById('btn') 128 | ele.onclick = funciton () { 129 | console.log(this) // window 130 | }.bind(window) 131 | ``` 132 | 133 | 以上是 this 指向的出现场景,下面再做一些 this 相关的面试题巩固了解一下 134 | 135 | #### 题目一 136 | 137 | ```ts 138 | this.x = 9; // this refers to global "window" object here in the browser 139 | var module = { 140 | x: 81, 141 | getX: function () { 142 | return this.x; 143 | }, 144 | }; 145 | 146 | module.getX(); // 81 147 | 148 | var retrieveX = module.getX; 149 | retrieveX(); // 9 150 | ``` 151 | 152 | 因为 this 的指向是在函数执行的时候绑定的,而非函数创建时绑定,所以 retrieveX() === this.retrieveX() === 9, module.getX() === 81 153 | 154 | #### 题目二 155 | 156 | ```ts 157 | function foo() { 158 | console.log(this.a); 159 | } 160 | var a = 2; 161 | var b = { a: "b.a", foo: foo }; 162 | var c = { a: "c.a" }; 163 | 164 | foo(); // 2 165 | b.foo(); // 'b.a' 166 | console.log(b.foo); 167 | (c.foo = b.foo)(); // 2 168 | c.foo(); // 'c.a' 169 | ``` 170 | 171 | 至于为什么`(c.foo = b.foo)() === 2`,我们来解释一下 172 | (c.foo = b.foo)()我们可以这么写 173 | 174 | ```ts 175 | function foo() { 176 | console.log(this.a); 177 | } 178 | var a = 2; 179 | var b = { a: "b.a", foo: foo }; 180 | var c = { a: "c.a" }; 181 | 182 | function fn() { 183 | c.foo = b.foo; 184 | return c.foo; 185 | } 186 | fn()(); // 2 187 | ``` 188 | 189 | 而 fn 执行的方法指向的 this 是 window,结果也就是 2 了 190 | 191 | #### 题目三 192 | 193 | ```ts 194 | var len = 10; 195 | function fn() { 196 | console.log(this.len); 197 | } 198 | 199 | var obj = { 200 | len: 5, 201 | getLen: function (fn) { 202 | console.log(this.len); 203 | fn(); 204 | arguments[0](); 205 | }, 206 | }; 207 | obj.getLen(fn, 1); 208 | // 5 209 | // 10 210 | ``` 211 | 212 | this 永远指向调用他的对象,没错 `getLen`方法里的 this 指向的是 obj 对象,所以拿到的第一个打印信息是 5,但是在该方法内部执行的 fn 有单独的 this 绑定,也就是 window,所以第二个结果为 10 213 | 214 | #### 题目四 215 | 216 | ```ts 217 | var foo = 1; 218 | function fn() { 219 | this.foo = 2; 220 | console.log(foo); 221 | } 222 | fn(); // 2 223 | ``` 224 | 225 | fn 执行方法 this 指向的 window,导致 foo 被赋值成 2,打印的就是 2 226 | 227 | ```ts 228 | var foo = 1; 229 | function fn() { 230 | this.foo = 2; 231 | console.log(foo); 232 | } 233 | new fn(); // 1 234 | new fn().foo; // 2 实例化的this指向的不是window 而是 实例化生成的新对象 235 | ``` 236 | 237 | #### 题目五 238 | 239 | ```ts 240 | var a = { 241 | name: "zh", 242 | sayName: function () { 243 | console.log(`hello, ${this.name}`); 244 | }, 245 | }; 246 | var name = "ls"; 247 | function sayName() { 248 | var s = a.sayName; 249 | s(); // 'hello, ls' 250 | a.sayName(); // 'hello, zh' 251 | a.sayName(); // 'hello, zh' 252 | (b = a.sayName)(); // 'hello, ls' 253 | } 254 | 255 | sayName(); 256 | ``` 257 | 258 | - 分析 259 | - `s()` this 指向的是全局对象也就是 window.name 也就是 'hello, ls' 260 | - `a.sayName()` 此时执行的是 a 对象下的方法,a.sayName 绑定的对象是 a,a.name 为'zh',因此结果为'hello, zh' 261 | - `(a.sayName)()` 等价于 a.sayName() 结果和上面一样为'hello, zh' 262 | - `(b = a.sayName)()` 赋值操作再执行 b 方法等价于第一个结果为 'hello, ls' 263 | 264 | #### 题目六 265 | 266 | ```ts 267 | var name = "zh"; 268 | var obj = { 269 | name: "ls", 270 | sayName: function () { 271 | console.log(`this.name: ${this.name}`); 272 | }, 273 | callback: function () { 274 | var _this = this; 275 | return function () { 276 | var sayName = _this.sayName; 277 | _this.sayName(); // this.name: ls 278 | sayName(); // this.name: zh 279 | }; 280 | }, 281 | }; 282 | obj.callback()(); 283 | ``` 284 | 285 | - 分析 286 | - `obj.callback()` 返回一个函数,此时已经绑定了\_this 的指向,为`obj`对象,然后再执行这个函数 287 | - `_this.sayName()` 就相当于`obj.sayName()`,所以执行的结果为 'this.name: ls' 288 | - `sayName()` 在执行的时候当前函数的时候 this 的指向为 window,此时的 sayName 方法中的 this.name 相当于 window.name 也就是'zh',最后的结果为'this.name: zh' 289 | -------------------------------------------------------------------------------- /posts/【js高级程序】模块.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 【js高级程序】模块 3 | date: 2020-10-29 08:30:28 4 | categories: javascript 5 | tags: [js] 6 | --- 7 | 8 | 红宝书学习记录 9 | 10 | ### *原文整理摘抄自 javascript 高级程序开发(第4版) 第26章* 11 | 12 | JavaScript 是异步加载的解释型语言,所以得到广泛应用的各种模块实现也表现出不同的形态。 这些不同的形态决定了不同的结果,但最终它们都实现了经典的模块模式。 13 | 14 | 15 | ### ES6 之前的模块加载器 16 | 在 ES6 原生支持模块之前,使用模块的 JavaScript 代码本质上是希望使用默认没有的语言特性。因 此, 必须按照符合某种规范的模块语法来编写代码, 另外还需要单独的模块工具把这些模块语法与 JavaScript 运行时连接起来。这里的模块语法和连接方式有不同的表现形式,通常需要在浏览器中额外 加载库或者在构建时完成预处理。 17 | 18 | #### CommonJS 19 | CommonJS 规范概述了**同步声明依赖的模块定义**。这个规范主要用于在服务器端实现模块化代码组织,但也可用于定义在浏览器中使用的模块依赖。CommonJS 模块语法不能在浏览器中直接运行。 20 | 21 | > 一般认为,Node.js的模块系统使用了 CommonJS规范,实际上并不完全正确。Node.js使用了轻微修改版本的 CommonJS,**因为 Node.js 主要在服务器环境下使用,所以不需要考虑网络延迟问题。** 22 | 23 | CommonJS 模块定义需要使用 require()指定依赖,而使用 exports 对象定义自己的公共 API 24 | ```js 25 | // moduleA 26 | 27 | var moduleB = require('./moduleB'); 28 | 29 | module.exports = { 30 | stuff: moduleB.doStuff(); 31 | }; 32 | ``` 33 | `moduleA` 通过使用模块定义的相对路径来指定自己对 `moduleB` 的依赖。什么是“模块定义”,以及 如何将字符串解析为模块,完全取决于模块系统的实现。 34 | 35 | 请求模块会加载相应模块, 而把模块赋值给变量也非常常见, 但赋值给变量不是必需的。 调用 require()意味着模块会原封不动地加载进来: 36 | ```js 37 | console.log('moduleA'); 38 | require('./moduleA'); // "moduleA" 39 | ``` 40 | 41 | 无论一个模块在 require()中被引用多少次,**模块永远是单例**。在下面的例子中,moduleA 只会 被打印一次。这是因为无论请求多少次,moduleA 只会被加载一次。 42 | ```js 43 | console.log('moduleA'); 44 | var a1 = require('./moduleA'); 45 | var a2 = require('./moduleA'); 46 | 47 | console.log(a1 === a2); // true 48 | ``` 49 | **在 CommonJS 中,模块加载是模块系统执行的同步操作**因此 require()可以像下面这样以编程 方式嵌入在模块中: 50 | ```js 51 | if (loadCondition) { 52 | require('./moduleA'); 53 | } 54 | ``` 55 | 56 | #### 异步模块定义(AMD) #### 57 | CommonJS 以服务器端为目标环境,能够一次性把所有模块都加载到内存,而异步模块定义(AMD, Asynchronous Module Definition)的模块定义系统则以浏览器为目标执行环境,这需要考虑网络延迟的 问题。 58 | AMD 的一般策略是让模块声明自己的依赖,而运行在浏览器中的模块系统会按需获取依赖,并 在依赖加载完成后立即执行依赖它们的模块。 59 | **AMD模块实现的核心是用函数包装模块定义。** 60 | ```js 61 | // ID 为'moduleA'的模块定义。moduleA 依赖 moduleB, 62 | // moduleB 会异步加载 63 | define('moduleA', ['moduleB'], function(moduleB) { 64 | return { 65 | stuff: moduleB.doStuff(); 66 | } 67 | } 68 | ``` 69 | 70 | #### 通用模块定义 (UMD) 71 | 为了统一 CommonJS 和 AMD 生态系统,通用模块定义(UMD,Universal Module Definition)规范 应运而生。 72 | 73 | 本质上,UMD 定义的模块会在启动时 检测要使用哪个模块系统,然后进行适当配置,并把所有逻辑包装在一个立即调用的函数表达式(IIFE) 中。 74 | 75 | ```js 76 | (function (root, factory) { 77 | if (typeof define === 'function' && define.amd) { 78 | // AMD。注册为匿名模块 79 | define(['moduleB'], factory); 80 | } else if (typeof module === 'object' && module.exports) { 81 | // Node。不支持严格 CommonJS 82 | // 但可以在 Node 这样支持 module.exports 的 83 | // 类 CommonJS 环境下使用 84 | module.exports = factory(require(' moduleB ')); 85 | } else { 86 | // 浏览器全局上下文(root 是 window) 87 | root.returnExports = factory(root. moduleB); 88 | } 89 | }(this, function(moduleB) { 90 | // 以某种方式使用 moduleB 91 | 92 | // 将返回值作为模块的导出 93 | // 这个例子返回了一个对象 94 | // 但是模块也可以返回函数作为导出值 95 | return {}; 96 | }) 97 | ``` 98 | 99 | ### 使用 ES6 模块 100 | #### 模块标签及定义 101 | ```html 102 | 103 | ``` 104 | 105 | #### 执行顺序 106 | ```html 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | ``` 116 | > 与传统脚本不同,所有模块都会像<script defer>加载的脚本一样按顺序执行。解析到<script type="module">标签后会立即下载模块文件,但执行会延迟到文档解析完成。无论对嵌入的模块代码, 还是引入的外部模块文件,都是这样。 117 | > <script type="module">在页面中出现的顺序就是它们执行 的顺序。与<script defer>一样,修改模块标签的位置,无论是在<head>还是在<body>中,只会影 响文件什么时候加载,而不会影响模块什么时候加载。 118 | 119 | #### 模块加载 120 | ECMAScript 6 模块的独特之处在于,既可以通过浏览器原生加载,也可以与第三方加载器和构建工 具一起加载。有些浏览器还没有原生支持 ES6 模块,因此可能还需要第三方工具。事实上,很多时候使 用第三方工具可能会更方便。 121 | 122 | - 顶级模块加载整个依赖图,且是异步完成 123 | - 浏览器 会解析入口模块,确定依赖,并发送对依赖模块的请求 124 | - 文件通过网络返回后,浏览器就会解析它 们的内容,确定它们的依赖 125 | - 如果这些二级依赖还没有加载,则会发送更多请求。 126 | 127 | 这个异步递归加载过程会持续到整个应用程序的依赖图都解析完成。解析完依赖图,应用程序就可以正式加载模块了。 128 | 129 | #### 模块行为 130 | ECMAScript 6 模块借用了 CommonJS 和 AMD 的很多优秀特性。下面简单列举一些。 131 | - 模块代码只在加载后执行。 132 | - 模块只能加载一次。 133 | - 模块是单例。 134 | - 模块可以定义公共接口,其他模块可以基于这个公共接口观察和交互。 135 | - 模块可以请求加载其他模块。 136 | - 支持循环依赖。 137 | 138 | ES6 模块系统也增加了一些新行为。 139 | - ES6 模块默认在严格模式下执行。 140 | - ES6 模块不共享全局命名空间。 141 | - 模块顶级 this 的值是 undefined(常规脚本中是 window)。 142 | - 模块中的 var 声明不会添加到 window 对象。 143 | - ES6 模块是异步加载和执行的。 144 | > ES6根本不会关心是否发生了"循环加载",只是生成一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。 145 | 146 | #### 模块导出 147 | ES6 模块支持两种导出: 148 | - 命名导出 149 | ```js 150 | const foo = 'foo'; 151 | export { foo }; 152 | export const bar = 'bar'; 153 | export { foo as myFoo }; 154 | ``` 155 | 156 | - 默认导出。 157 | ```js 158 | const foo = 'foo'; 159 | const bar = 'bar'; 160 | export default foo; 161 | export { foo as default }; 162 | export { foo as default, bar }; 163 | ``` 164 | > ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。在import 代码引入之前,是不会执行js代码块的 165 | > export 关键字用于声明一个值为命名导出。导出语句必须在模块顶级,不能嵌套在某个块中 166 | 167 | #### 模块导入 168 | 模块可以通过使用 import 关键字使用其他模块导出的值。与 export 类似,import 必须出现在 模块的顶级 169 | ```js 170 | import { foo } from './fooModule.js'; 171 | import foo from './fooModule.js'; 172 | ``` 173 | > js 代码被 JavaScript 引擎编译时 foo 只是作为变量存储了 const foo = ...., fooModule.js 中导出的方法引用,当执行foo方法的时候,就会找到引用方法的代码并执行 174 | 175 | #### 模块转移导出 176 | 模块导入的值可以直接通过管道转移到导出。此时,也可以将默认导出转换为命名导出,或者相反。 如果想把一个模块的所有命名导出集中在一块,可以像下面这样在 bar.js 中使用*导出: 177 | ```js 178 | export * from './foo.js'; 179 | export { foo, bar as myBar } from './foo.js'; 180 | export { default } from './foo.js'; 181 | export { foo as default } from './foo.js'; 182 | ``` 183 | 184 | #### 工作者模块 185 | ECMAScript 6 模块与 Worker 实例完全兼容。在实例化时,可以给工作者传入一个指向模块文件的 路径,与传入常规脚本文件一样。Worker 构造函数接收第二个参数,用于说明传入的是模块文件。 186 | ```js 187 | // 第二个参数默认为{ type: 'classic' } 188 | const scriptWorker = new Worker('scriptWorker.js'); 189 | 190 | const moduleWorker = new Worker('moduleWorker.js', { type: 'module' }); 191 | ``` 192 | 193 | #### 向后兼容 194 | ```html 195 | // 支持模块的浏览器会执行这段脚本 196 | // 不支持模块的浏览器不会执行这段脚本 197 | 198 | 199 | // 支持模块的浏览器不会执行这段脚本 // 200 | 不支持模块的浏览器会执行这段脚本 201 | 202 | ``` 203 | 204 | -------------------------------------------------------------------------------- /posts/js数据类型转换.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: js数据类型转换 3 | date: 2019-10-21 23:40:30 4 | categories: javascript 5 | tags: ['js'] 6 | --- 7 | 8 | JavaScript 数据类型分为基本数据类型和引用数据类型 9 | 10 | ### 基本数据类型 11 | - `string` 12 | - `boolean` 13 | - `undefined` 14 | - `null` 15 | - `number` 16 | - `symbol` (es6新类型) 17 | - `bigint` (现在处在 ECMAScript 标准化过程中的 第三阶段) 18 | 19 | ### 引用数据类型 20 | - `function` 21 | - `object` 22 | - `array` 23 | 24 | ### 数据类型转换 25 | 以下是一张来自 `javacript权威指南`中的一张数据转换的图片 26 | ![js数据转换](https://www.daiwei.site/static/blog/js数据类型转换/1.png) 27 | 28 | 我们需要知道数据类型转换有 29 | - 数字 30 | - Bool值 31 | - 字符串 32 | 33 | ### 值转为数字 34 | Number() 函数, Number函数会将里面的num值转化为number类型,如果无法转化为数字,就会成NaN 35 | 36 | #### 原始类型值 37 | ```ts 38 | // 字符串:如果不可以被解析为数值, 39 | Number('1as2') // NaN 40 | Number('1') // 1 41 | 42 | // 布尔值:true 转成 1,false 转成 0 43 | Number(true) // 1 44 | Number(false) // 0 45 | 46 | Number(null) // 0 47 | Number(undefined) // NaN 48 | ``` 49 | 其他转换的方法 parseInt, parseFloat 50 | Number相对于parseInt更为严格(parseInt逐个解析字符,而Number函数整体转换字符串的类型。)比如 51 | ```ts 52 | Number('1as2') // NaN 53 | parseInt('1as2') // 1 54 | ``` 55 | #### 对象 56 | 如果Number方法的参数是对象时,将返回NaN,除非是包含单个数值的数组。 57 | ```ts 58 | Number({}) // NaN 59 | Number({a: 1, b: 2}) // NaN 60 | Number([5]) // 5 61 | Number(['a']) // NaN 62 | Number(['1']) // 1 63 | ``` 64 | Number的参数为对象的时候会走以下的类型转换 65 | - 此时会先调用对象自身的valueOf方法,如果返回的是原始值,则直接作为参数执行Number方法,然后break 66 | - 如果valueOf返回的还是对象的话,则调用对象的toString方法,如果返回原始值类型,则执行Number方法,然后break 67 | - 如果toSting返回的还是对象,则报错 68 | 69 | 看下面的例子 70 | ```ts 71 | const bar = { a : 1 } 72 | const foo = [1] 73 | const stra = ['a1'] 74 | 75 | // 我们按上面步骤一步一步走 76 | bar.valueOf() // { a: 1 } 还是对象。所以需要走toString方法 77 | foo.valueOf() // [1] 同样是对象,也需要再走toString方法 78 | stra.valueOf() // ['a1'] 同上 79 | 80 | // valueOf执行之后如果还不是原始值开始走toString 81 | bar.toString() // '[object Object]' 是原始值,开始执行Number('[object Object]') 82 | foo.toString() // '1' 是原始值,开始执行Number('1') 83 | stra.toString() // 'a1' 是原始值,开始执行Number('a1') 84 | 85 | // 最后得到的结果分别为 86 | Number('[object Object]') // NaN 87 | Number('1') // 1 88 | Number('a1') // NaN 89 | ``` 90 | 91 | ### 值转为字符 92 | String() 函数,将参数转换成原始的字符串,并返回转换后的值。 93 | #### 原始类型值 94 | ```ts 95 | String(1) // '1' 96 | String(null) // 'null' 97 | String(false) // 'false' 98 | String('1') // '1' 99 | String('undefined') // 'undefined' 100 | ``` 101 | #### 对象 102 | String方法的参数如果是对象,返回一个类型字符串;如果是数组,返回该数组的字符串形式。 103 | ```ts 104 | String([]) // '' 105 | String([1, 2]) // '1, 2' 106 | String({}) // '[object Object]' 107 | ``` 108 | String方法的转换规则,与Number方法基本相同,只是互换了valueOf方法和toString方法的执行顺序 109 | - 对象先调用自身的toString方法,如果返回的是原始类型值,直接作为参数执行String方法,然后break 110 | - 如果toString返回的不是原始类型则调用对象的valueOf,如果返回的是原始值,则执行String方法,然后break 111 | - 如果valueOf返回的还是对象,则报错 112 | 113 | 继续看例子 114 | ```ts 115 | const bar = { a : 1 } 116 | const foo = [1] 117 | const stra = ['a1'] 118 | 119 | // 先执行对象的toString方法 120 | bar.toString() // '[object Object]' String('[object Object]') 121 | foo.toString() // '1' 是原始值,开始执行String('1') 122 | stra.toString() // 'a1' 是原始值,开始执行String('a1') 123 | 124 | // 直接执行结果了 125 | String('[object Object]') // '[object Object]' 126 | String('1') // '1' 127 | String('a1') // 'a1' 128 | ``` 129 | 130 | 我们再来试试这个对象,看返回的结果是啥 131 | ```ts 132 | var obj = { 133 | valueOf: function () { 134 | return 'this is valueOf'; 135 | }, 136 | toString: function () { 137 | return {}; 138 | } 139 | }; 140 | 141 | String(obj) // "this is valueOf" 142 | ``` 143 | 为啥? obj.toString()被重写返回对象,所以会继续看obj.valueOf,返回的是`'this is valueOf'`,最后就相当于 144 | ```ts 145 | String(obj.valueOf()) 146 | // 等同于 147 | String('this is valueOf') // 'this is valueOf' 148 | ``` 149 | 150 | ### 值转为布尔值 151 | Boolean函数可以将任意类型转化成布尔值 152 | 以下5个值转化结果为false,其他的全部为true 153 | - undefined 154 | - null 155 | - ''(空字符串) 156 | - 0 (包括-0, +0) 157 | -NaN 158 | 159 | ```ts 160 | Boolean(undefined) // false 161 | Boolean(null) // false 162 | Boolean('') // false 163 | Boolean(0) // false 164 | Boolean(NaN) // false 165 | Boolean([]) // true 166 | Boolean({}) // true 167 | Boolean(1) // true 168 | ``` 169 | 170 | ### 隐式类型转换 171 | 在某些情况下,即使我们不提供显示转换,Javascript也会进行自动类型转换,主要情况有: 172 | - 自动转换为布尔值 173 | - 自动转换为字符串 174 | - 自动转换为数值 175 | 176 | ### 自动转换为布尔值 177 | JavaScript遇到预期为布尔值的时候会将非布尔值自动转换成布尔值,系统会自定调用 Boolean 函数 178 | 除了以下五个值以外,其他的自动转为true 179 | - `undefined` 180 | - `null` 181 | - `-0 , +0` 182 | - `NaN` 183 | - `''` 184 | 185 | 189 | ```ts 190 | !undefined // true 191 | !null // true 192 | !0 // true 193 | !NaN // true 194 | !'' // true 195 | ``` 196 | 如果判断元素获取布尔返回值的话 197 | ```ts 198 | const a = '1' 199 | a ? true : false // true 200 | !!a // true 201 | ``` 202 | 203 | ### 自动转换为字符串 204 | JavaScript 遇到预期为字符串的地方,会将非字符串的值自动转换为原始类型值在转为字符串 205 | ```ts 206 | '1' + 1 // '11' 207 | '1' + true // '1true' 208 | '1' + false // '1false' 209 | '1' + {} // '1[object Object]' 210 | '1' + [] // '1' 211 | '1' + function () {} // '1function () {}' 212 | '1' + undefined // '1undefined' 213 | '1' + null // '1null' 214 | ``` 215 | 216 | ### 自动转换为数值 217 | JavaScript 遇到预期为数值的地方,会自动将参数转化成数值,系统内部会自动调用Number函数 218 | 除了加法运算符(+)有可能把运算子转为字符串,其他运算符都会把运算子自动转成数值。 219 | ```ts 220 | '5' - '2' // 3 221 | '5' * '2' // 10 222 | true - 1 // 0 223 | false - 1 // -1 224 | '1' - 1 // 0 225 | '5' * [] // 0 226 | false / '5' // 0 227 | 'abc' - 1 // NaN 228 | null + 1 // 1 229 | undefined + 1 // NaN 230 | ``` 231 | 232 | ### 常见面试题 233 | #### Question 1 234 | ```ts 235 | [] == false // true 236 | ![] == false // true 237 | ``` 238 | 第一个结果为`true` 原因是 [] 被转换成 '' 之后被转化成数值 0 返回true 239 | 第二个结果同样`true` 前边多了个!,直接被转成bool值取反,[]转bool为true,取反为false,所以结果也相等 240 | 241 | #### Qusetion 2 242 | ```ts 243 | [] + {} // '[object Object]' 244 | {} + [] // 0 245 | ``` 246 | 第一个按照正常的js隐式转换逻辑[] 转换为''(空字符串),{} 转换为 '[object Object]', 结果为 '[object Object]' 247 | 第二个的{}被视为一个代码块,而不是一个javascript对象,所以相当于 +[],将 []转化为数值,所以结果为 0 248 | 249 | 参考于:[阮一峰,JavaScript标准教程](http://javascript.ruanyifeng.com/grammar/conversion.html#toc0) 250 | 251 | -------------------------------------------------------------------------------- /posts/Content-Security-Policy.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Content-Security-Policy 3 | date: 2020-10-14 00:00:57 4 | categories: web安全 5 | tags: [javascript, html] 6 | --- 7 | 8 | 基于 XSS 的解决方案,Content-Security-Policy(CSP 内容安全策略) 可以根本性的解决跨站脚本攻击,由开发者自己定义网页中的(script,style,iframe,font,img 等)资源请求的配置信息,可理解为资源请求的白名单配置 9 | 10 | CSP 的主要目标是减少和报告 XSS 攻击 ,XSS 攻击利用了浏览器对于从服务器所获取的内容的信任。恶意脚本在受害者的浏览器中得以运行,因为浏览器信任其内容来源,即使有的时候这些脚本并非来自于它本该来的地方。 11 | 12 | ### 设置方式 13 | - meta标签设置 14 | - 服务端配置 Content-Security-Policy 15 | 16 | ### meta 设置 17 | ```html 18 | 19 | ``` 20 | 这里表示:当前页面只允许加载同域的script 脚本, 不允许加载object 嵌入地址,style样式的引入只允许 daiwei.site 域名下加载 21 | 22 | 如图 23 | ![style资源](https://www.daiwei.site/static/blog/Content-Security-Policy/style_src.png) 24 | css 本地临时地址 http://localhost:1996/css/app-af63c014edc89c09025e.css 直接被视为具有安全隐患的资源地址从而会被拒绝,而 https://www.daiwei.site/web_next/_next/static/css/styles.33733be0.chunk.css 则可以正常加载 25 | 26 | 再看这两个iframe 27 | ![iframe](https://www.daiwei.site/static/blog/Content-Security-Policy/meta1.png) 28 | 上面的加载失败(http://www.daiwei.site) 29 | 下面的加载成功 (http://www.daiwei.site) 30 | 原因就是 child-src 设置只允许 https 的协议访问 31 | 32 | 具体报错截图 33 | ![iframe_error](https://www.daiwei.site/static/blog/Content-Security-Policy/meta_error.png) 34 | 35 | 至于 script-src 'self' 表示 指向与要保护的文件所在的源,包括相同的 URL scheme 与端口号。必须有单引号。一些浏览器会特意排除 blob 与 filesystem 。需要设定这两种内容类型的站点可以在 Data 属性中进行设定。 36 | 37 | ### 服务端配置 38 | ```nginx 39 | add_header Content-Security-Policy "default-src 'self';"; 40 | ``` 41 | 这表示视,音频,图片,css,js,等必须是同域名且同协议且同端口号 42 | 此时我访问 https://www.daiwei.site; (忽略serviceworker 报错) 43 | ![设置default-src 'self'](https://www.daiwei.site/static/blog/Content-Security-Policy/nginx_csp_self.png) 44 | 引入google 的字体直接被拒绝,以及从二级域名引入的图片地址也会被拒绝,此时设置; 45 | 46 | ```nginx 47 | add_header Content-Security-Policy "default-src 'self' https://*.daiwei.site; 48 | img-src 'self' https://www.bing.com https://*.daiwei.site; 49 | script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.daiwei.site; 50 | font-src 'self' https://*.daiwei.site https://fonts.gstatic.com; 51 | style-src 'self' https://*.daiwei.site 'unsafe-inline' https://fonts.googleapis.com;"; 52 | ``` 53 | 配置之后 54 | ![设置csp](https://www.daiwei.site/static/blog/Content-Security-Policy/config_result.jpg) 55 | nginx_conf_result 56 | 57 | 资源请求头可以看到 `Content-Security-Policy` 的配置信息 58 | res_header_csp 59 | ![设置csp](https://www.daiwei.site/static/blog/Content-Security-Policy/res_nginx_conf.png) 60 | 61 | ### 资源加载配置(具体可查看mdn详解) 62 | - [default-src](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Security-Policy/default-src): 指令可以为其他 CSP 拉取指令(fetch directives)提供备选项。对于以下列出的指令,假如不存在的话,那么用户代理会查找并应用 default-src 指令的值。 63 | - [child-src](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Security-Policy/child-src): 定义了Web worker的有效源以及使用诸如和元素加载的嵌套浏览上下文 64 | - frame 65 | - iframe 66 | - [connect-src](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Security-Policy/connect-src): 用于控制允许通过脚本接口加载的链接地址。其中受到影响的API如下: 67 | - a标签, ping 68 | - Fetch 请求 69 | - XMLHttpRequest 70 | - WebSocket 71 | - EventSource 72 | - [font-src](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Security-Policy/font-src): 指令定义了 @font-face 加载字体的有效源规则。 73 | - [frame-src](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Security-Policy/frame-src): 嵌套浏览上下文使用元素如加载指令指定有效来源 (child-src 也可使用) 74 | - frame 75 | - iframe 76 | - [img-src](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Security-Policy/img-src): 图片以及 favicons 加载指令指定有效来源 77 | - img 78 | - favicons 79 | - [manifest-src](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Security-Policy/manifest-src): HTTP指令指定可以将哪些清单应用于资源 80 | - [media-src](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Security-Policy/media-src): 用于加载媒体的有效源 81 | - video 82 | - audio 83 | - [object-src](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Security-Policy/object-src): 该HTTP指令指定的有效来源,和元素。 84 | - object 85 | - embed 86 | - applet 87 | - [script-src](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Security-Policy/script-src): js执行脚本指定有效来源 88 | - script 89 | - [style-src](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Security-Policy/style-src): stylesheet样式有效来源 90 | - link stylesheet 91 | - [worker-src](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Security-Policy/worker-src): 指令指定的有效来源Worker,SharedWorker或ServiceWorker脚本 92 | 93 | ### 资源配置选项值 94 | ##### 'self' 95 | 指提供受保护文档的来源,包括相同的URL方案和端口号。您必须包括单引号。一些浏览器特别排除blob和filesystem从源代码指令。需要允许这些内容类型的站点可以使用Data属性指定它们。 96 | 97 | ##### 'unsafe-eval' 98 | 允许使用eval()和类似的方法从字符串创建代码。您必须包括单引号。 99 | 100 | ##### 'unsafe-hashes' 101 | 允许启用特定的内联事件处理程序。如果您只需要允许内联事件处理程序而不是内联 `script` 元素或javascript:URL,则这是比使用unsafe-inline表达式更安全的方法。 102 | 103 | ##### 'unsafe-inline' 104 | 允许使用内联资源,例如内联 `script` 元素,javascript:URL,内联事件处理程序和内联 `style` 元素。单引号是必需的。 105 | 106 | ##### 'none' 107 | 指空集;也就是说,没有URL匹配。单引号是必需的。 108 | 109 | ##### 'nonce- <base64-value>' 110 | 使用加密随机数(使用一次的数字)的特定内联脚本的允许列表。服务器每次发送策略时都必须生成一个唯一的随机数值。提供难以猜测的随机数至关重要,因为绕开资源策略是微不足道的。有关示例,请参见不安全的内联脚本。指定随机数会使现代浏览器忽略'unsafe-inline',但仍可以为没有随机数支持的旧版浏览器设置该随机数。 111 | 112 | ##### '<hash-algorithm>-<base64-value>' 113 | 脚本或样式的sha256,sha384或sha512哈希。此源的使用由两部分组成,并用破折号隔开:用于创建哈希的加密算法和脚本或样式的base64编码哈希。生成哈希值时,请勿包含 `script` 或 `style` 标记,并注意大小写和空格很重要,包括前导或尾随空格。有关示例,请参见不安全的内联脚本。在CSP 2.0中,这仅适用于内联脚本。script-src对于外部脚本,CSP 3.0允许它。 114 | ... 115 | 116 | ### 注意的点 117 | - 多个值也可以并列,用空格分隔。 118 | - 如果同一个限制选项使用多次,只有第一次会生效。 119 | - `script-src` 和 `object-src` 是必设的,除非设置了 `default-src`。因为攻击者只要能注入脚本,其他限制都可以规避。而object-src必设是因为 Flash 里面可以执行外部脚本。 120 | - `script-src` 不能使用 `unsafe-inline` 关键字(除非伴随一个nonce值),也不能允许设置data:URL。 121 | ```html 122 | 123 | 124 | ``` 125 | - 特别注意 JSONP 的回调函数。 126 | ```html 127 | 128 | ``` 129 | 130 | ### 参考于 131 | 132 | [Locking Down Your Website Scripts with CSP, Hashes, Nonces and Report URI](https://www.troyhunt.com/locking-down-your-website-scripts-with-csp-hashes-nonces-and-report-uri/) 133 | 134 | [Content-Security-Policy - HTTP | MDN](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP) 135 | 136 | [Content Security Policy 入门教程](http://www.ruanyifeng.com/blog/2016/09/csp.html) 137 | -------------------------------------------------------------------------------- /posts/【js高级程序】变量、作用域与内存.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 【js高级程序】变量、作用域与内存 3 | date: 2020-11-26 22:47:48 4 | categories: javascript 5 | tags: [js] 6 | --- 7 | 8 | 红宝书学习记录 9 | 10 | ### _原文整理摘抄自 javascript 高级程序开发(第 4 版) 第 4 章_ 11 | 12 | ### 原始值与引用值 13 | 14 | **原始值** 就是最简单的数据 15 | 16 | > 目前有六种原始值 Undefined、Null、Boolean、Number、String 和 Symbol、BigInt 17 | 18 | **引用值** 由多个值构成的对象 19 | 引用值是保存在内存中的对象。 20 | JavaScript 不允许直接访问内存位置,因此也就 不能直接操作对象所在的内存空间。在操作对象时,实际上操作的是对该对象的引用(reference)而非 实际的对象本身。为此,保存引用值的变量是按引用(by reference)访问的。 21 | 22 | #### 动态属性 23 | 24 | 原始值和引用值的定义方式很类似,都是创建一个变量,然后给它赋一个值。 25 | 对于引用值而言,可以随时添加、修改和删除其属性和方法。 26 | 27 | #### 复制值 28 | 29 | 除了存储方式不同,原始值和引用值在通过变量复制时也有所不同。 30 | 31 | - 原始值的存储复制完全独立,互不干扰 32 | ![值复制](https://www.daiwei.site/static/blog/【js高级程序】变量、作用域与内存/值复制.png) 33 | - 引用值存储在变量中的值也会被复制到新变量所在的位置。区 别在于,**这里复制的值实际上是一个指针,它指向存储在堆内存中的对象**。 34 | ![引用复制](https://www.daiwei.site/static/blog/【js高级程序】变量、作用域与内存/引用复制.png) 35 | 36 | #### 传递参数 37 | 38 | ECMAScript 中所有函数的参数都是按值传递的。 39 | 这意味着函数外的值会被复制到函数内部的参数中,就像从一个变量复制到另一个变量一样。 40 | 41 | - 如果是原始值,那么就跟原始值变量的复制一样 42 | - 如果是 引用值,那么就跟引用值变量的复制一样。 43 | 44 | #### 确定类型 45 | 46 | `typeof` 确定值是不是对象 47 | `instanceof` 反对元素是否是某个类型的实例 48 | 49 | ```js 50 | result = variable instanceof constructor; 51 | ``` 52 | 53 | ```js 54 | console.log(person instanceof Object); // 变量 person 是 Object 吗? 55 | console.log(colors instanceof Array); // 变量 colors 是 Array 吗? 56 | console.log(pattern instanceof RegExp); // 变量 pattern 是 RegExp 吗? 57 | ``` 58 | 59 | ### 执行上下文与作用域 60 | 61 | 变量或函数的上下文决定 了它们可以访问哪些数据,以及它们的行为。 62 | 63 | 全局上下文是最外层的上下文。 64 | 65 | 上下文在其所有代码都执行完毕后会被销毁,包括定义 在它上面的所有变量和函数(全局上下文在应用程序退出前才会被销毁,比如关闭网页或退出浏览器)。 66 | 67 | 每个函数调用都有自己的上下文。当代码执行流进入函数时,函数的上下文被推到一个上下文栈上。 在函数执行完之后,上下文栈会弹出该函数上下文,将控制权返还给之前的执行上下文。 类似洋葱模型 68 | 69 | 上下文中的代码在执行的时候,会创建变量对象的一个作用域链(scope chain)。这个作用域链决定了各级上下文中的代码在访问变量和函数时的顺序。 70 | 71 | 代码执行时的标识符解析是通过沿作用域链逐级搜索标识符名称完成的。 72 | 73 | ```js 74 | var color = "blue"; 75 | 76 | function changeColor() { 77 | if (color === "blue") { 78 | color = "red"; 79 | } else { 80 | color = "blue"; 81 | } 82 | } 83 | changeColor(); 84 | ``` 85 | 86 | 函数 changeColor()的作用域链包含两个对象 87 | 88 | - 它自己的变量对象(就 是定义 `arguments` 对象的那个) 89 | - 全局上下文的变量对象 90 | 这个函数内部之所以能够访问变量 color,就是因为**可以在作用域链中找到它**。 91 | 92 | 此外,局部作用域中定义的变量可用于在局部上下文中替换全局变量。 93 | 94 | #### 作用域链增强 95 | 96 | 虽然执行上下文主要有全局上下文和函数上下文两种(eval()调用内部存在第三种上下文),但有其他方式来增强作用域链。 97 | 某些语句会导致在作用域链前端临时添加一个上下文,这个上下文在代码执行后会被删除 98 | 99 | - try/catch 语句的 catch 块 (创建一个新的变量对象,这个变量对象会包含要抛出的错误对象的声明) 100 | - with 语句 (会向作用域链前端添加指定的对象) 101 | 这两种情况下,都会在作用域链前端添加一个变量对象。 102 | 103 | ```js 104 | function buildUrl() { 105 | let qs = "?debug=true"; 106 | 107 | with (location) { 108 | let url = href + qs; 109 | return url; 110 | } 111 | } 112 | ``` 113 | 114 | 这里,with 语句将 location 对象作为上下文,因此 location 会被添加到作用域链前端。当 with 语句中的代码引用变量 href 时,实际上引用的是 location.href,也就是自己变量对象的属性。 115 | 116 | #### 变量声明 117 | 118 | var, let, const 119 | 120 | 1. 使用 var 的函数作用域声明 121 | 在使用 var 声明变量时,变量会被自动添加到最接近的上下文。 122 | 123 | - 在函数中,最接近的上下文就是函数的局部上下文。 124 | - 在 with 语句中,最接近的上下文也是函数上下文。 125 | - 如果变量未经声明就被初始化了, 那么它就会自动被添加到全局上下文 126 | > var 声明会被拿到函数或全局作用域的顶部,位于作用域中所有代码之前。这个现象叫作“提升” (hoisting)。 127 | 128 | 2. 使用 let 的**块级作用域声明** 129 | ES6 新增的 let 关键字跟 var 很相似,但它的作用域是块级的,这也是 JavaScript 中的新概念。 130 | 块级作用域由最近的一对包含花括号{}界定。 131 | 换句话说,if 块、while 块、function 块,甚至连单独 的块也是 let 声明变量的作用域。 132 | 133 | > 严格来讲,let 在 JavaScript 运行时中也会被提升,但由于“暂时性死区”(temporal dead zone)的 缘故,实际上不能在声明之前使用 let 变量。 134 | 135 | ```js 136 | for (let i = 0; i < 10; i++) { 137 | setTimeout(() => { 138 | console.info(i); 139 | }); 140 | } 141 | ``` 142 | 143 | 每次循环所生成的块级作用域包含着当时 i 的值, 块级作用域在定时器回调中被使用到,函数执行每次都有一个单独的作用域 i,可正常输出 0,2,3..9 144 | 145 | 3. 使用 const 的**常量声明** 146 | 使用 const 声明的变量必须同时初始化为某个值。 147 | 148 | ```js 149 | const INIT_SIZE = 3; 150 | ``` 151 | 152 | ### 垃圾回收 153 | 154 | JavaScript 是使用垃圾回收的语言,也就是说执行环境负责在代码执行时管理内存。 155 | 156 | 基本思路:确定哪个变量不会再 使用,然后释放它占用的内存。这个过程是周期性的,即垃圾回收程序每隔一定时间(或者说在代码执 行过程中某个预定的收集时间)就会自动运行。 157 | 158 | #### 回收策略: 标记清理 159 | 160 | JavaScript 最常用的垃圾回收策略是标记清理 161 | 162 | 当变量进入上下文,比如在函数内部声明一个变量时,这个变量会被加上存在于上下文中的标记。当变量离开上下文时,也会被加上离开上下文的标记。 163 | 164 | 垃圾回收程序运行的时候,会标记内存中存储的所有变量(记住,标记方法有很多种)。 165 | 166 | 然后,它会将所有在上下文中的变量,以及被在上下文中的变量引用的变量的标记去掉。 167 | 168 | 在此之后再被加上标记 的变量就是待删除的了,原因是任何在上下文中的变量都访问不到它们了。 169 | 170 | 随后垃圾回收程序做一次**内存清理**,销毁带标记的所有值并收回它们的内存。 171 | 172 | #### 回收策略: 引用计数 173 | 174 | 另一种没那么常用的垃圾回收策略是引用计数 175 | 176 | 其思路是对每个值都记录它被引用的次数。声明变量并给它赋一个引用值时,这个值的引用数为 1。如果同一个值又被赋给另一个变量,那么引用数加 1。 177 | 178 | 类似地,如果保存对该值引用的变量被其他值给覆盖了,那么引用数减 1。当一个值的引用数为 0 时,就说明没办法再访问到这个值了,因此可以安全地收回其内存了。 179 | 180 | 垃圾回收程序下次运行的时候就会释放引用数为 0 的值的内存。 181 | 182 | > 会存在循环引用问题 183 | 184 | #### 性能 185 | 186 | 垃圾回收程序会周期性运行,如果内存中分配了很多变量,则可能造成性能损失,因此垃圾回收的 时间调度很重要。 187 | 188 | 在内存有限的移动设备上,垃圾回收有可能会明显拖慢渲染的速度和帧速率。 189 | 190 | #### 内存管理 191 | 192 | - **解除引用:** 如果数据不需要,把它设置 null 193 | 194 | > 解除对一个值的引用并不会自动导致相关内存被回收。解除引用的关键在于确保相关的值已经不在上下文里了,因此它在下次垃圾回收时会被回收。 195 | 196 | - **通过 const 和 let 声明提升性能** 197 | `const` `let` 都是以块(非函数)为作用域 198 | 199 | - **隐藏类和删除操作** 200 | 运行期间,V8 会将创建的对象与隐藏类关联起来,以跟踪它们的属性特征。能够共享相同隐藏类 的对象性能会更好,V8 会针对这种情况进行优化,但不一定总能够做到。 201 | 202 | ```js 203 | function Article() { 204 | this.title = "Inauguration Ceremony Features Kazoo Band"; 205 | } 206 | 207 | let a1 = new Article(); 208 | let a2 = new Article(); 209 | ``` 210 | 211 | V8 会在后台配置,让这两个类实例共享相同的隐藏类,因为这两个实例共享同一个构造函数和原 型。假设之后又添加了下面这行代码: 212 | 213 | ```js 214 | a2.author = "Jake"; 215 | ``` 216 | 217 | 此时两个 Article 实例就会对应两个不同的隐藏类。 218 | 219 | ```js 220 | function Article(opt_author) { 221 | this.title = "Inauguration Ceremony Features Kazoo Band"; 222 | this.author = opt_author; 223 | } 224 | 225 | let a1 = new Article(); 226 | let a2 = new Article("Jake"); 227 | ``` 228 | 229 | 这样,两个实例基本上就一样了(不考虑 hasOwnProperty 的返回值),因此可以共享一个隐藏类, 从而带来潜在的性能提升。 230 | 不过要记住,使用 delete 关键字会导致生成相同的隐藏类片段。**解决方式就是不删除该属性而是设置属性为 null** 231 | 232 | - **静态分配与对象池** (间接控制触发垃圾回收的条件) 233 | 234 | ### 小结 235 | 236 | - JavaScript 变量可以保存两种类型的值:原始值和引用值。 237 | - 原始值: `Undefined`、`Null`、`Boolean`、`Number`、`String` 和 `Symbol`。 238 | - **原始值大小固定,因此保存在栈内存上**。 239 | - 从一个变量到另一个变量复制原始值会创建该值的第二个副本。 240 | - **引用值是对象,存储在堆内存上**。 241 | - 包含引用值的变量实际上只包含指向相应对象的一个**指针**,而**不是对象本身**。 242 | - 从一个变量到另一个变量复制引用值只会复制指针,因此结果是两个变量都指向同一个对象。 243 | - `typeof` 操作符可以确定值的原始类型,而 `instanceof` 操作符用于确保值的引用类型。 244 | - 执行上下文分 245 | - 全局上下文 246 | - 函数上下文 247 | - 块级上下文。 248 | - 代码执行流每进入一个新上下文,都会创建一个作用域链,用于搜索变量和函数。 249 | - 函数或块的局部上下文不仅可以访问自己作用域内的变量,而且也可以访问任何包含上下文乃 至全局上下文中的变量。 250 | - 全局上下文只能访问全局上下文中的变量和函数,不能直接访问局部上下文中的任何数据。 251 | - **变量的执行上下文用于确定什么时候释放内存**。 252 | - 离开作用域的值会被自动标记为可回收,然后在垃圾回收期间被删除。 253 | - 主流的垃圾回收算法是**标记清理**,即先给当前不使用的值加上标记,再回来回收它们的内存。 254 | - 引用计数是另一种垃圾回收策略,需要记录值被引用了多少次。JavaScript 引擎不再使用这种算 法,但某些旧版本的 IE 仍然会受这种算法的影响,原因是 JavaScript 会访问非原生 JavaScript 对 象(如 DOM 元素)。 255 | - 引用计数在代码中存在`循环引用`时会出现问题。 256 | - 解除变量的引用不仅可以消除循环引用,而且对垃圾回收也有帮助。为促进内存回收,全局对象、全局对象的属性和循环引用都应该在不需要时解除引用。 257 | -------------------------------------------------------------------------------- /posts/面试中的那些手写代码题目.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 面试中的那些手写代码题目 3 | date: 2019-10-28 10:22:32 4 | categories: javascript 5 | tags: ["js", "面试"] 6 | --- 7 | 8 | 话不多说,直接进入正题 9 | 10 | ### JS 防抖函数 11 | 12 | #### 基本的防抖函数 13 | 14 | ```ts 15 | export function debounce(fn: Function, wait: number = 1000) { 16 | let t: any; 17 | return function () { 18 | const _this = this; 19 | t && clearTimeout(t); 20 | 21 | t = setTimeout(function () { 22 | fn.apply(_this, arguments); 23 | }, wait); 24 | }; 25 | } 26 | ``` 27 | 28 | #### 添加 immediate 参数 29 | 30 | ```ts 31 | export function debounce( 32 | fn: Function, 33 | wait: number = 1000, 34 | immediate: boolean = true 35 | ) { 36 | let t: any; 37 | return function () { 38 | const _this = this; 39 | const _arg = arguments; 40 | t && clearTimeout(t); 41 | 42 | if (immediate) { 43 | let isDo = !t; 44 | t = setTimeout(function () { 45 | t = null; 46 | }, wait); 47 | if (isDo) { 48 | fn.apply(_this, _arg); 49 | } 50 | } else { 51 | t = setTimeout(function () { 52 | fn.apply(_this, _arg); 53 | }, wait); 54 | } 55 | }; 56 | } 57 | ``` 58 | 59 | ### JS 节流函数 60 | 61 | 节流的概念为多次点击的过程中只会在每次大于 wait 等待时长执行方法 62 | 63 | ```ts 64 | export function throttle(fn: Function, wait: number = 1000) { 65 | let prev: number = +new Date(); 66 | 67 | return function () { 68 | const now: number = +new Date(); 69 | if (now - prev > wait) { 70 | fn.apply(this, arguments); 71 | prev = +new Date(); 72 | } 73 | }; 74 | } 75 | ``` 76 | 77 | ### 手写 call 78 | 79 | call 返回一个执行结果,第二个及以后的参数为执行函数的参数 80 | 81 | ```ts 82 | Function.prototype.selfCall = function (context: Window = window) { 83 | const ctx = context || {}; 84 | ctx.fn = this; 85 | const args = Array.from(arguments).slice(1); 86 | const result = ctx.fn(args); 87 | delete ctx.fn; 88 | return result; 89 | }; 90 | 91 | const bar = { 92 | a: "this is bar", 93 | }; 94 | const foo = { 95 | a: 1, 96 | getA: function (name, age = 0) { 97 | console.log(`prop: ${this.a}`); 98 | console.log(`name: ${name}`); 99 | console.log(`age: ${age}`); 100 | console.log("-------------------"); 101 | }, 102 | }; 103 | 104 | foo.getA.selfCall(bar, "selfCall"); 105 | 106 | // prop: this is bar 107 | // name: selfCall 108 | // age: 0 109 | // ------------------- 110 | ``` 111 | 112 | ### 手写 apply 113 | 114 | apply 同样也是返回一个执行结果,只是第二个参数为数组 115 | 116 | ```ts 117 | Function.prototype.selfApply = function (context: Window = window) { 118 | const ctx = context || {}; 119 | ctx.fn = this; 120 | const arg = Array.from(arguments).slice(1)[0]; 121 | const result = ctx.fn(...arg); 122 | delete ctx.fn; 123 | return result; 124 | }; 125 | 126 | // 接着call的对象信息执行代码 127 | foo.getA.selfApply(bar, ["selfApply", 22]); 128 | 129 | // prop: this is bar 130 | // name: selfApply 131 | // age: 22 132 | // ------------------- 133 | ``` 134 | 135 | ### 手写 bind 136 | 137 | > 注意:bind 方法只是返回一个可执行函数,执行需要由用户自己执行返回的函数 138 | 139 | ```ts 140 | Function.prototype.selfBind = function (context: Window = window) { 141 | const fn = this; 142 | const ctx = context || {}; 143 | const arg = Array.from(arguments).slice(1); 144 | return function () { 145 | fn.call(ctx, ...arg); 146 | }; 147 | }; 148 | 149 | foo.getA.selfBind(null, "selfBind", 22)(); 150 | 151 | // prop: undefined 152 | // name: selfBind 153 | // age: 22 154 | // ------------------- 155 | ``` 156 | 157 | ### 手写 Object.create 方法 158 | 159 | ### 科里化函数 160 | 161 | 通过判断参数是否符合实际方法所需参数的数量进行执行函数或者继续递归判断 162 | 163 | ```ts 164 | export function curry(fn: Function) { 165 | const _this = this; 166 | const args = Array.from(arguments).slice(1); 167 | // fn.length 属性指明函数的形参个数。 168 | const len = fn.length; 169 | 170 | return function () { 171 | const _args = Array.from(arguments); 172 | args.push(..._args); 173 | if (args.length < len) { 174 | return curry.call(_this, fn, ...args); 175 | } 176 | 177 | return fn.apply(_this, args); 178 | }; 179 | } 180 | 181 | // test 182 | const addCur = function (a, b, c) { 183 | console.log("a + b + c", a + b + c); 184 | }; 185 | 186 | const reduceCur = function (a, b, c) { 187 | console.log("a - b - c", a - b - c); 188 | }; 189 | 190 | const add = curry(addCur, 2); 191 | s(1)(2); // a + b + c 6 192 | s(1, 3); // a + b + c 6 193 | 194 | const reduce = curry(reduceCur); 195 | reduce(1)(2)(3); // a - b - c -1 196 | reduce(1, 2, 3); // a - b - c -3 197 | ``` 198 | 199 | ### compose 200 | 201 | 类似 react 中组件, compose(fn1, fn2, fn3) (...args) = > fn1(fn2(fn3(...args))) 202 | 203 | ```ts 204 | export function compose(...fn: Function[]) { 205 | return function (...args: any) { 206 | return fn.reduceRight((prevResult, currentFn) => { 207 | return currentFn.call(this, ...prevResult); 208 | }, args); 209 | }; 210 | } 211 | ``` 212 | 213 | ### 实现一个 `instanceof` 214 | 215 | instanceof 的原理就是判断这个变量是否来自于某一个构造函数 216 | 217 | ```ts 218 | export function selfInstanceOf(left, right) { 219 | while (true) { 220 | if (left === null) return false; 221 | if (left === right) return true; 222 | if (left.__proto__ === right.prototype) return true; 223 | left = left.__proto__; 224 | } 225 | } 226 | 227 | // test 228 | selfInstanceOf({}, Object); // true 229 | selfInstanceOf({}, Array); // false 230 | selfInstanceOf([], Array); // true 231 | selfInstanceOf([], Object); // true 232 | 233 | const Fn = function () {}; 234 | const a = new Fn(); 235 | selfInstanceOf(a, Fn); // true 236 | selfInstanceOf(a, Object); // true 237 | ``` 238 | 239 | ### 数组去重 240 | 241 | 就不使用 Set 去实现去重了,使用 filter + indexOf 实现数组去重 242 | 243 | #### 方法一 244 | 245 | ```ts 246 | export function uniqueArray(arr: any[]) { 247 | return arr.filter((item, index) => arr.indexOf(item) === index); 248 | } 249 | 250 | console.log(uniqueArray([1, 2, 3, 2, 1, 3, 4, 5, 6, 3, 8])); 251 | // [1, 2, 3, 4, 5, 6, 8] 252 | ``` 253 | 254 | #### 方法二 255 | 256 | ```ts 257 | export function uniqueArray(arr: any[]) { 258 | const newArr = arr.sort(); 259 | let result = [newArr[0]]; 260 | for (let i = 1; i < newArr.length; i++) { 261 | newArr[i] !== newArr[i - 1] && result.push(newArr[i]); 262 | } 263 | return result; 264 | } 265 | console.log(uniqueArray([1, 2, 3, 2, 1, 3, 4, 5, 6, 3, 8])); 266 | // [1, 2, 3, 4, 5, 6, 8] 267 | ``` 268 | 269 | ### JS 深拷贝 270 | 271 | `JSON.stringify` 的问题导致我们需要自己手写基本的深拷贝方法 272 | 以下只做了 object array 的判断 273 | 274 | ```ts 275 | export function deepClone(obj: Object | any) { 276 | let result: any = {}; 277 | for (let k in obj) { 278 | let typeStr = Object.prototype.toString 279 | .call(obj[k]) 280 | .match(/\[object (.*?)\]/)[1] 281 | .toLowerCase(); 282 | switch (typeStr) { 283 | case "object": 284 | result[k] = deepClone(obj[k]); 285 | break; 286 | case "array": 287 | result[k] = obj[k].slice(); 288 | break; 289 | default: 290 | result[k] = obj[k]; 291 | } 292 | } 293 | return result; 294 | } 295 | ``` 296 | 297 | > 这里需要考虑到一个对象重复引用的问题, 如果存在对象引用,会导致代码重复执行,可以参考 lodash 的深拷贝代码, WeakMap 可以解决这个问题,后面会针对这个代码进行优化 298 | -------------------------------------------------------------------------------- /posts/日志监控.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 日志监控 3 | date: 2019-01-22 23:13:23 4 | categories: javascript 5 | tags: js 6 | --- 7 | 8 | 最近项目上有对于网站监控的需求,主要功能如下 9 | 1. 白屏时间 10 | 2. ajax请求响应时间 11 | 3. js报错信息 12 | 这些都需要通过request请求将对应的数据提交到后端,虽然从功能上来说,后端运维的log日志完全可以看到大多数内容,但是既然要求做了那就写呗 13 | 14 | ### 白屏时间 15 | 这个就是使用浏览器自带的 `performance` 对象来实现了 16 | performance.timing则是包含浏览器在各个状态下的时间戳以及其他属性值 17 | 部分js时间计算的代码 18 | 19 | ``` js 20 | Logger.prototype.getTiming = function () { 21 | var _return = { 22 | // DNS查询耗时 23 | dnsT: this.timing.domainLookupEnd - this.timing.domainLookupStart, 24 | // 白屏时间 25 | loadT: this.timing.domLoading - this.timing.navigationStart, 26 | // request请求耗时 27 | requestT: this.timing.responseEnd - this.timing.responseStart, 28 | // TCP链接耗时 29 | tcpT: this.timing.connectEnd - this.timing.connectStart, 30 | // 解析dom树耗时 31 | renderDomT: this.timing.domComplete - this.timing.domInteractive, 32 | // domready时间(用户可操作时间节点) 33 | readyDomT: this.timing.domContentLoadedEventEnd - this.timing.navigationStart, 34 | // onload时间(总下载时间) 35 | onLoadT: this.timing.loadEventEnd - this.timing.navigationStart 36 | }; 37 | return _return; 38 | }; 39 | ``` 40 | 41 | ### ajax请求响应时间 42 | 由于公司网址都是用的$.ajax的请求方法,新老官网以及落地页,关于ajax请求的代码就有好几百个,不可能一个个在ajax里写starttime然后成功或者失败定义endtime拿到差值再减,看起来很难维护 43 | 想了很久,最后决定重写$.ajax,也就是包装这个方法,专业术语叫 AOP 装饰者模式 44 | ```js 45 | (function ($, w) { 46 | // window上初始一个方法 47 | w._ajax = $.ajax; 48 | $.ajax = function (arg) { 49 | // 请求之前的时间 50 | var start_t = new Date().getTime() 51 | // 拿到$.ajax的执行的成功回调,记录下来 52 | var success = arg.success 53 | // 重写succes 54 | arg.success = function () { 55 | // 拿到$.ajax传入的参数 56 | var data = arguments 57 | // 成功执行后定义的方法 58 | var time = new Date().getTime() - start_t 59 | // 此时执行默认的ajax,因为默认的ajax不会捕获success之后的回调函数 60 | _ajax({ 61 | url: 'url', 62 | data: { 63 | time: time 64 | } 65 | success: success.call(this, data) 66 | }); 67 | } 68 | return _ajax.call($, arg) 69 | }; 70 | })(jQuery, window); 71 | ``` 72 | 73 | ### js报错信息 74 | js报错主要就是用window的onerror事件去监听js的报错信息 75 | 这里需要注意一下几点 76 | 77 | #### 1.不能使用 window.onerror = function () {...} ,而是使用 window.addEventListener ('error', funciton (e) {}) 78 | 原因就是 window.onerror 会覆盖或者被覆盖,因为他是表达式,所以在其他地方执行这个方法的时候,会被覆盖,addEventListener则是添加一个error的监听事件,不会影响之前error的监听代码 79 | 80 | #### 2.执行顺序 81 | error的监听代码尽量写在js最前面,因为涉及到调用 $.ajax , 因此放在jquery引入之后,其余的代码则放在后面, 先初始化监听, 后执行后续代码 82 | 83 | #### 3.crossorigin 84 | 在script引入日志监控代码标签没有用到crossorigin的时候, 由于跨域调用的windows脚本, onerror的事件不会打印详细的报错信息, 为了打印详细信息, 需配置crossorigin属性, 这需要后端的 Access-Control-Allow-Origin 支持 85 | 86 | #### 4.不是所有js报错代码都可以监听 87 | - console.error 不会被执行 88 | - try {} catch () {} 不会被监听 89 | - 静态资源加载错误不会被监听 90 | 91 | 代码如下 92 | ```js 93 | Logger.prototype.initErrorEvent = function () { 94 | /** 95 | * @param {String} errorMessage 错误信息 96 | * @param {String} scriptURL 出错文件的URL 97 | * @param {Number} lineNumber 出错代码的行号 98 | * @param {Number} columnNumber 出错代码的列号 99 | * @param {Object} errorObj 错误信息Object 100 | */ 101 | var _this = this; 102 | window.addEventListener('error', function (e) { 103 | setTimeout(function () { 104 | _this.staticError({ 105 | errorMessage: e.message, 106 | scriptURL: e.filename, 107 | lineNumber: e.lineno, 108 | columnNumber: e.colno, 109 | errorObj: e.error 110 | }); 111 | }, 0); 112 | }); 113 | }; 114 | 115 | Logger.prototype.staticError = function (data) { 116 | $.ajax({ 117 | url: 'url...', 118 | type: 'POST', 119 | dataType: 'json', 120 | data: { 121 | url: window.location.href, 122 | current_time: new Date().getTime(), 123 | js_url: data.scriptURL, 124 | error_info: data.errorMessage, 125 | error_line: data.lineNumber, 126 | error_column: data.columnNumber 127 | } 128 | }); 129 | }; 130 | ``` 131 | 132 | 完整的logger代码,不包含包装的ajax方法 133 | ```js 134 | var Logger = /** @class */ (function () { 135 | function Logger() { 136 | this.timing = window.performance.timing; 137 | this.initErrorEvent(); 138 | } 139 | Logger.prototype.getTiming = function () { 140 | var _return = { 141 | // DNS查询耗时 142 | dnsT: this.timing.domainLookupEnd - this.timing.domainLookupStart, 143 | // 白屏时间 144 | loadT: this.timing.domLoading - this.timing.navigationStart, 145 | // request请求耗时 146 | requestT: this.timing.responseEnd - this.timing.responseStart, 147 | // TCP链接耗时 148 | tcpT: this.timing.connectEnd - this.timing.connectStart, 149 | // 解析dom树耗时 150 | renderDomT: this.timing.domComplete - this.timing.domInteractive, 151 | // domready时间(用户可操作时间节点) 152 | readyDomT: this.timing.domContentLoadedEventEnd - this.timing.navigationStart, 153 | // onload时间(总下载时间) 154 | onLoadT: this.timing.loadEventEnd - this.timing.navigationStart 155 | }; 156 | return _return; 157 | }; 158 | /** 159 | * 初始化error监听事件 160 | */ 161 | Logger.prototype.initErrorEvent = function () { 162 | /** 163 | * @param {String} errorMessage 错误信息 164 | * @param {String} scriptURL 出错文件的URL 165 | * @param {Number} lineNumber 出错代码的行号 166 | * @param {Number} columnNumber 出错代码的列号 167 | * @param {Object} errorObj 错误信息Object 168 | */ 169 | var _this = this; 170 | window.addEventListener('error', function (e) { 171 | setTimeout(function () { 172 | _this.staticError({ 173 | errorMessage: e.message, 174 | scriptURL: e.filename, 175 | lineNumber: e.lineno, 176 | columnNumber: e.colno, 177 | errorObj: e.error 178 | }); 179 | }, 0); 180 | }); 181 | }; 182 | /** 183 | * 事件错误的回调事件 184 | */ 185 | Logger.prototype.staticError = function (data) { 186 | $.ajax({ 187 | url: '', 188 | type: 'POST', 189 | dataType: 'json', 190 | data: { 191 | url: window.location.href, 192 | current_time: new Date().getTime(), 193 | js_url: data.scriptURL, 194 | error_info: data.errorMessage, 195 | error_line: data.lineNumber, 196 | error_column: data.columnNumber 197 | } 198 | }); 199 | }; 200 | /** 201 | * 页面加载时长的数据信息 202 | */ 203 | Logger.prototype.fetchPageLoadInfo = function () { 204 | var timingInfo = this.getTiming(); 205 | $.ajax({ 206 | url: '', 207 | type: 'POST', 208 | dataType: 'json', 209 | data: { 210 | url: window.location.href, 211 | current_time: new Date().getTime(), 212 | response_time: timingInfo.loadT 213 | } 214 | }); 215 | }; 216 | return Logger; 217 | }()); 218 | window.logger = new Logger(); 219 | window.addEventListener('load', function () { 220 | setTimeout(function () { 221 | logger.fetchPageLoadInfo(); 222 | }, 1000); 223 | }, false); 224 | ``` 225 | -------------------------------------------------------------------------------- /posts/算法学习-递归.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 算法学习-递归 3 | date: 2021-01-07 07:39:55 4 | categories: 算法 5 | tags: ['算法'] 6 | --- 7 | 8 | ### leet_code 的递归算法的学习 9 | 10 | #### 344.反转字符串 11 | 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。 12 | 13 | 不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 14 | 15 | 你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符 16 | 17 | 示例 1: 18 | ```code 19 | 输入:["h","e","l","l","o"] 20 | 输出:["o","l","l","e","h"] 21 | ``` 22 | 示例 2: 23 | ```code 24 | 输入:["H","a","n","n","a","h"] 25 | 输出:["h","a","n","n","a","H"] 26 | ``` 27 | 28 | ##### 代码: 29 | ```ts 30 | function reverseString(s: string[]): void { 31 | const fn = (start: number, end: number, s: string[]) => { 32 | if (start > end) { 33 | return; 34 | } 35 | let tmp = s[start]; 36 | s[start] = s[end]; 37 | s[end] = tmp; 38 | fn(start + 1, end - 1, s); 39 | } 40 | 41 | fn(0, s.length - 1, s); 42 | }; 43 | ``` 44 | > 通过首尾替换的方式修改数组的值,左边的值 ++ ,右边的值--, 直到 left > right (索引)跳出递归 45 | 46 | #### 509.斐波那契数 47 | 斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。 48 | 49 | ##### 代码 50 | - **递归** 51 | ```ts 52 | let cache = new Map(); 53 | function fib(N: number): number { 54 | if (N == 0) return 0 55 | if (N == 1) return 1 56 | if (cache.has(N)) { 57 | return cache.get(N) 58 | } 59 | let ret = fib(N - 1) + fib(N - 2) 60 | cache.set(N, ret); 61 | return ret; 62 | } 63 | ``` 64 | - **数组** 65 | ```ts 66 | function fibArr(n: number): number { 67 | let arr = [0, 1]; 68 | for (let i = 2; i <= n; i++) { 69 | arr[i] = arr[i - 1] + arr[i - 2]; 70 | } 71 | return arr[n]; 72 | } 73 | ``` 74 | 75 | #### 206.反转链表 76 | 反转一个单链表。 77 | 78 | 示例: 79 | 80 | ```code 81 | 输入: 1->2->3->4->5->NULL 82 | 输出: 5->4->3->2->1->NULL 83 | ``` 84 | ```ts 85 | function reverseList(head: ListNode | null): ListNode | null { 86 | if (head === null || head.next === null) { 87 | return head; 88 | } 89 | const node = reverseList(head.next); 90 | head.next.next = head; 91 | head.next = null; 92 | return node; 93 | } 94 | ``` 95 | 96 | #### 104.二叉树的最大深度 97 | 给定一个二叉树,找出其最大深度。 98 | 99 | 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 100 | 101 | **说明**: 叶子节点是指没有子节点的节点。 102 | **示例**: 103 | 给定二叉树 [3,9,20,null,null,15,7], 104 | ```code 105 | 3 106 | / \ 107 | 9 20 108 | / \ 109 | 15 7 110 | ``` 111 | 返回它的最大深度 3 。 112 | ```ts 113 | function maxDepth(root: TreeNode | null): number { 114 | return root 115 | ? Math.max(maxDepth(root.left), maxDepth(root.right)) + 1 116 | : 0 117 | }; 118 | ``` 119 | 120 | #### 50. Pow(x, n) 121 | 实现 `pow(x, n)` ,即计算 x 的 n 次幂函数。 122 | **示例 1**: 123 | ```code 124 | 输入: 2.00000, 10 125 | 输出: 1024.00000 126 | ``` 127 | **示例 2**: 128 | ```code 129 | 输入: 2.10000, 3 130 | 输出: 9.26100 131 | ``` 132 | **示例 3**: 133 | ```code 134 | 输入: 2.00000, -2 135 | 输出: 0.25000 136 | 解释: 2-2 = 1/22 = 1/4 = 0.25 137 | ``` 138 | **说明**: 139 | - -100.0 < x < 100.0 140 | - n 是 32 位有符号整数,其数值范围是 [−231, 231 − 1] 。 141 | 142 | **1. for循环累乘** 143 | ```ts 144 | function myPow(x: number, n: number): number { 145 | let newN = n; 146 | let newX = x; 147 | if (newN < 0) { 148 | newX = 1 / x; 149 | newN = -n 150 | } 151 | let aws = 1; 152 | console.info(newX); 153 | for (let i = 0; i < newN; i++) { 154 | aws *= newX; 155 | } 156 | return aws; 157 | } 158 | ``` 159 | 160 | **2. 递归** 161 | ```code 162 | pow(2, 10) === pow(2, 5) * pow(2, 5) === pow(2, pow(2, 5)) 163 | 164 | pow(2, 9) === pow(2, 4) * pow(2, 4) * 2 === pow(2, pow(2, 4)) * 2 165 | ``` 166 | 我们可以知道 167 | **在次方为偶数的情况下:** 一个值(2)的 10次方相当于 是,两个该值的一半(10 / 2) 的乘积 168 | **在次方为奇数的情况下:** 一个值(2)的 9次方相当于 是,两个该值的一半【向下取整的次方】(`Math.floor(9 / 2)`) * 2 的乘积 169 | ```ts 170 | function myPow(x: number, n: number): number { 171 | const fastPow = (x: number, n: number): number => { 172 | if (n === 0) { 173 | return 1; 174 | } 175 | 176 | const half = fastPow(x, Math.floor(n / 2)); 177 | if (n % 2 === 1) { 178 | // 基数 179 | return half * half * x; 180 | } else { 181 | return half * half; 182 | } 183 | } 184 | 185 | const ret = fastPow(x, Math.abs(n)); 186 | // n < 0 负次方则将总的乘积求导数 187 | return n > 0 ? ret : 1 / ret; 188 | }; 189 | ``` 190 | 191 | #### 21.合并两个有序链表 192 | 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 193 | 194 | 示例 1: 195 | 196 | ![合并两个有序链表](https://www.daiwei.site/static/blog/算法学习-递归/merge_ex1.jpg) 197 | 198 | ```code 199 | 输入:l1 = [1,2,4], l2 = [1,3,4] 200 | 输出:[1,1,2,3,4,4] 201 | ``` 202 | 203 | 示例 2: 204 | ```code 205 | 输入:l1 = [], l2 = [] 206 | 输出:[] 207 | ``` 208 | 209 | 示例 3: 210 | ```code 211 | 输入:l1 = [], l2 = [0] 212 | 输出:[0] 213 | ``` 214 | ```ts 215 | function mergeTwoLists(l1: ListNode | null, l2: ListNode | null): ListNode | null { 216 | // 如果 l1 到头了,直接拼l2的值 217 | if (l1 === null) { 218 | return l2; 219 | } 220 | 221 | // 如果 l2 到头了,拼接l1的值 222 | if (l2 === null) { 223 | return l1; 224 | } 225 | 226 | // l1.val > l2.val 则将l1 和 l2 的下一个做比较,l2的next值就是比较之后的值 227 | if (l1.val > l2.val) { 228 | l2.next = mergeTwoLists(l1, l2.next); 229 | return l2; 230 | } else { 231 | // 同理 232 | l1.next = mergeTwoLists(l1.next, l2); 233 | return l1; 234 | } 235 | }; 236 | ``` 237 | 238 | #### 779.第K个语法符号 239 | 在第一行我们写上一个 0。接下来的每一行,将前一行中的0替换为01,1替换为10。 240 | 241 | 给定行数 N 和序数 K,返回第 N 行中第 K个字符。(K从1开始) 242 | **例子:** 243 | ```code 244 | 输入: N = 1, K = 1 245 | 输出: 0 246 | 247 | 输入: N = 2, K = 1 248 | 输出: 0 249 | 250 | 输入: N = 2, K = 2 251 | 输出: 1 252 | 253 | 输入: N = 4, K = 5 254 | 输出: 1 255 | 256 | 解释: 257 | 第一行: 0 258 | 第二行: 01 259 | 第三行: 0110 260 | 第四行: 01101001 261 | ``` 262 | **注意:** 263 | 1. N 的范围 [1, 30]. 264 | 2. K 的范围 [1, 2^(N-1)]. 265 | 266 | ```ts 267 | function kthGrammar(N: number, K: number): number { 268 | if (N === 1) return 0; 269 | // 如果 K 是奇数,则 N,K 对应的值为父级(N-1,((K + 1)) / 2)的位置的值 270 | if (K % 2) { 271 | return kthGrammar(N-1, (K + 1) / 2); 272 | } else { 273 | // 偶数 274 | // 上一行为0 下一行为1 275 | // 上一行为1 下一行为0 276 | return kthGrammar(N-1, K / 2) === 0 ? 1 : 0; 277 | } 278 | }; 279 | ``` 280 | 281 | #### 95.不同的二叉搜索树 II 282 | 给定一个整数 n,生成所有由 1 ... n 为节点所组成的 二叉搜索树 。 283 | 284 | **示例:** 285 | ```code 286 | 输入:3 287 | 输出: 288 | [ 289 |   [1,null,3,2], 290 |   [3,2,null,1], 291 |   [3,1,null,null,2], 292 |   [2,1,3], 293 |   [1,null,2,null,3] 294 | ] 295 | 解释: 296 | 以上的输出对应以下 5 种不同结构的二叉搜索树: 297 | 298 | 1 3 3 2 1 299 | \ / / / \ \ 300 | 3 2 1 1 3 2 301 | / / \ \ 302 | 2 1 2 3 303 | ``` 304 | **提示:** 305 | - 0 <= n <= 8 306 | 307 | ![二叉搜索树](https://www.daiwei.site/static/blog/算法学习-递归/96_BST.png) 308 | 309 | ```ts 310 | function generateTrees(n: number): Array { 311 | // 0 直接返回 312 | if (n === 0) return []; 313 | 314 | // 递归函数 初始值 以及最大值 315 | const getBSTnum = (start: number, n: number) => { 316 | if (start > n) return [null]; 317 | if (start === n) return [new TreeNode(start)]; 318 | 319 | const res = []; 320 | for (let i = start; i <= n; i++) { 321 | let leftBST = getBSTnum(start, i - 1); 322 | let rightBST = getBSTnum(i + 1, n); 323 | 324 | for (let leftV of leftBST) { 325 | for (let rightV of rightBST) { 326 | const root = new TreeNode(i); 327 | root.left = leftV; 328 | root.right = rightV; 329 | res.push(root); 330 | } 331 | } 332 | } 333 | return res; 334 | }; 335 | return getBSTnum(1, n); 336 | }; 337 | ``` 338 | 339 | #### 总结 340 | - 递归算法的时间复杂度通常是递归调用的数量和计算的时间复杂度的乘积 341 | - 递归算法的空间复杂度, 虑造成空间消耗的两个部分: 342 | - 递归相关空间 (不断执行递归,堆栈数据得不到释放,规模过大可能会导致堆栈溢出) 343 | - 非递归相关空间(与递归过程没有直接关系的内存空间,通常包括为全局变量分配的空间(通常在堆中)) 344 | 345 | > 记忆化技术优化递归性能 346 | 347 | https://leetcode-cn.com/leetbook/read/recursion/xk5vw2/ 348 | -------------------------------------------------------------------------------- /posts/【js高级程序】客户端检测.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 【js高级程序】客户端检测 3 | date: 2020-11-25 07:36:34 4 | categories: javascript 5 | tags: [js] 6 | --- 7 | 8 | 红宝书学习记录 9 | 10 | ### *原文整理摘抄自 javascript 高级程序开发(第4版) 第13章* 11 | 12 | ### 能力检测 13 | 能力检测(又称特性检测)即在 JavaScript **运行时中使用一套简单的检测逻辑,测试浏览器是否支 持某种特性**。 14 | ```js 15 | if (object.propertyInQuestion) { 16 | // 使用 object.propertyInQuestion 17 | } 18 | ``` 19 | 能力检测的关键是理解两个重要概念。 20 | - 应该先检测最常用的方式。 21 | - 必须检测切实需要的特性。某个能力存在并不代表别的能力也存在。 22 | 23 | #### 基于能力检测进行浏览器分析 24 | ##### 检测特性 25 | 如果你的应用程序需要使用特定的浏览器能力,那么最好集中检测所 有能力,而不是等到用的时候再重复检测。 26 | ```js 27 | // 检测浏览器是否支持 Netscape 式的插件 28 | let hasNSPlugins = !!(navigator.plugins && navigator.plugins.length); 29 | 30 | // 检测浏览器是否具有 DOM Level 1 能力 31 | let hasDOM1 = !!(document.getElementById && document.createElement && document.getElementsByTagName); 32 | ``` 33 | > 能力检测最适合用于决定下一步该怎么做,而不一定能够作为辨识浏览器的标志。 34 | 35 | ### 用户代理检测 36 | 用户代理检测通过浏览器的用户代理字符串确定使用的是什么浏览器。用户代理字符串包含在每个 HTTP 请求的头部,在 JavaScript 中可以通过 navigator.userAgent 访问。 37 | - 在服务器端,常见的做法 是根据接收到的用户代理字符串确定浏览器并执行相应操作。 38 | - 而在客户端,用户代理检测被认为是不可 靠的,只应该在没有其他选项时再考虑。 39 | 40 | #### Gecko 41 | Gecko渲染引擎是 Firefox 的核心。 42 | ```code 43 | Mozilla/MozillaVersion (Platform; Encryption; OS-or-CPU; Language; PrereleaseVersion)Gecko/GeckoVersion ApplicationProduct/ApplicationProductVersion 44 | ``` 45 | 46 | #### WebKit 47 | 2003年,苹果宣布将发布自己的浏览器 Safari。Safari 的渲染引擎叫 WebKit,是基于 Linux 平台浏 览器 Konqueror 使用的渲染引擎 KHTML 开发的。 48 | ```code 49 | Mozilla/5.0 (Platform; Encryption; OS-or-CPU; Language) AppleWebKit/AppleWebKitVersion (KHTML, like Gecko) Safari/SafariVersion 50 | 51 | Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/522.15.5 (KHTML, like Gecko) Version/3.0.3 Safari/522.15.5 52 | ``` 53 | 54 | #### Blink 55 | 谷歌的 Chrome 浏览器使用 Blink 作为渲染引擎,使用 V8 作为 JavaScript 引擎。Chrome 的用户代理 字符串包含所有 WebKit 的信息,另外又加上了 Chrome 及其版本的信息。 56 | ```code 57 | Mozilla/5.0 (Platform; Encryption; OS-or-CPU; Language) AppleWebKit/AppleWebKitVersion (KHTML, like Gecko) Chrome/ChromeVersion Safari/SafariVersion 58 | ``` 59 | 60 | #### Opera 61 | ```code 62 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36 OPR/52.0.2871.64 63 | ``` 64 | 65 | #### iOS 与 Android 66 | iOS 和 Android 移动操作系统上默认的浏览器都是基于 WebKit 的,因此具有与相应桌面浏览器一样 的用户代理字符串。 67 | 68 | 69 | #### 浏览器分析 70 | 通过检测用户代理来识别浏览器并不是完美的方式,毕竟这个字符串是可以造假的。 71 | 只不过实现 window.navigator 对象的浏览器(即所有现代浏览器)都会提供 userAgent 这个只读属性。因此, 简单地给这个属性设置其他值不会有效: 72 | 73 | 不过,有些浏览器提供伪私有的__defineGetter__方法, 利用它可以篡改用户代理字符串: 74 | ```js 75 | // Chrome 浏览器测试 76 | window.navigator.__defineGetter__('userAgent', () => 'foobar') 77 | console.info(window.navigator.userAgent); // "foobar" 78 | ``` 79 | 80 | ### 软件与硬件检测 81 | #### 识别浏览器与操作系统 (测试于mac pc端) 82 | 1. `navigator.oscpu` 通常对应用户代理字符串中操作系统/系统架构相关信息。 83 | ```js 84 | // Chrome Safari 显示为 undefind 85 | console.info(navigator.oscpu) // undefind 86 | ``` 87 | 88 | 2. `navigator.vendor` 通常包含浏览器开发商信息。 89 | ```js 90 | // Chrome 91 | console.info(navigator.vendor) // Google Inc. 92 | 93 | // Safari 94 | console.info(navigator.vendor) // Apple Computer, Inc. 95 | ``` 96 | 97 | 3. `navigator.platform` 通常表示浏览器所在的操作系统。 98 | ```js 99 | // Chrome 100 | console.info(navigator.vendor) // MacIntel 101 | 102 | // Safari 103 | console.info(navigator.vendor) // MacIntel 104 | ``` 105 | 106 | 4. `screen.colorDepth` 和 `screen.pixelDepth` 107 | 返回一样的值,即显示器每像素颜色的位深。 108 | ```js 109 | // Chrome 110 | console.info(screen.colorDepth, screen.pixelDepth) // 30 30 111 | 112 | // Safari 113 | console.info(screen.colorDepth, screen.pixelDepth) // 24 24 114 | ``` 115 | 116 | 5. `screen.orientation` 返回一个 ScreenOrientation 对象 117 | ```js 118 | // Chrome 119 | console.info(screen.orientation) 120 | // { 121 | // angle: 0 122 | // onchange: null 123 | // type: "landscape-primary" 124 | // } 125 | 126 | // Safari 127 | console.info(screen.orientation) // undefind 128 | ``` 129 | 130 | #### 浏览器元数据 131 | navigator 对象暴露出一些 API,可以提供浏览器和操作系统的状态信息。 132 | 133 | ##### Geolocation API 134 | 浏览器脚本感知当前设备的地理位 置。这个 API 只在安全执行环境(通过 HTTPS 获取的脚本)中可用。 135 | 136 | ```js 137 | navigator.geolocation.getCurrentPosition((position) => console.info(position)); 138 | 139 | // 如果同意返回如下信息 140 | // { 141 | // coords: { 142 | // accuracy: 55 143 | // altitude: null 144 | // altitudeAccuracy: null 145 | // heading: null 146 | // latitude: 31.273845500000004 147 | // longitude: 121.471465 148 | // speed: null 149 | // }, 150 | // timestamp: 1606297728920 151 | // } 152 | ``` 153 | - `coords` 154 | - `accuracy` 以米为单位的精度(纬度相关) 155 | - `altitude` 海拔高度 (精度值:米) 设备必须具备相应的能力(比 如 高度计),否则为null 156 | - `altitudeAccuracy` 海拔精度(米) 157 | - `heading` 表示相对于正北方向移动的角度(0 ≤ heading < 360) 158 | - `latitude` 纬度 159 | - `longitude` 经度 160 | - `speed` 表示设备每秒移动的速度 161 | - `timestamp` 表示查询时间的时间戳 162 | 163 | `getCurrentPosition` 第二个参数是error 错误的回调函数,参数e是一个对象,包含一下参数 164 | - `code` 属性是一个整数,表示以下 3 种错误 165 | - `PERMISSION_DENIED` 地理位置信息的获取失败,因为该页面没有获取地理位置信息的权限。 166 | - `POSITION_UNAVAILABLE` 地理位置获取失败,因为至少有一个内部位置源返回一个内部错误。 167 | - `TIMEOUT` 获取地理位置超时,通过定义 `PositionOptions.timeout` 来设置获取地理位置的超时时长。 168 | - `message` string 169 | 170 | `getCurrentPosition` 第三个参数 `PositionOptions`对象,可设置如下属性 171 | - `enableHighAccuracy` true 表示返回的值应该尽量精确,默认值为 false。 172 | - `timeout` 毫秒 表示在以 TIMEOUT 状态调用错误回调函数之前等待的最长时间 173 | - `maximumAge` 毫秒 表示返回坐标的最长有效期,默认值为 0。0 表示强 制系统忽略缓存的值,每次都重新查询。 174 | 175 | ##### Connection State 和 NetworkInformation API 176 | 1. Connection State: online & offline 177 | ```js 178 | const connectionStateChange = () => console.log(navigator.onLine); 179 | 180 | // 网络连接时触发 181 | window.addEventListener('online', connectionStateChange); 182 | 183 | // 网络断开时触发 184 | window.addEventListener('offline', connectionStateChange); 185 | ``` 186 | 187 | 2. NetworkInformation API 188 | navigator 对象还暴露了 NetworkInformation API,可以通过 navigator.connection 属性使用 189 | ```js 190 | navigator.connection // NetworkInformation {onchange: null, effectiveType: "4g", rtt: 100, downlink: 10, saveData: false} 191 | ``` 192 | 以下是 NetworkInformation API 暴露的属性。 193 | - `downlink` 整数,表示当前设备的带宽(以 Mbit/s 为单位),舍入到最接近的 25kbit/s。这个值可能会根据历史网络吞吐量计算,也可能根据连接技术的能力来计算。 194 | - `downlinkMax` 整数,表示当前设备最大的下行带宽(以 Mbit/s 为单位),根据网络的第一跳来确定。因为第一跳不一定反映端到端的网络速度,所以这个值只能用作粗略的上限值。 195 | - `effectiveType` 字符串枚举值,表示连接速度和质量。`slow-2g` | `2g` | `3g` | `4g` 196 | - `rtt` 表示当前网络实际的往返时间,舍入为最接近的 25 毫秒。 197 | - `type` 字符串枚举值,表示网络连接技术。这个值可能为下列值之一。 198 | - `bluetooth` 蓝牙 199 | - `cellular` 蜂窝 200 | - `ethernet` 以太网 201 | - `none` 无网络连接。相当于 navigator.onLine === false。 202 | - `mixed` 多种网络混合。 203 | - `wifi` wifi状态 204 | - ... 205 | - `saveData` 表示用户设备是否启用了“节流”(reduced data)模式。 206 | - `onchange` 会在任何连接状态变化时激发一个 change 事件。 207 | ```js 208 | navigator.connection.addEventListener('change',changeHandler) 209 | ``` 210 | 211 | 3. Battery Status API 212 | ```js 213 | (async() => { 214 | const res = await navigator.getBattery(); 215 | alert(JSON.stringify(res)) // BatteryManager 对象。 216 | })() 217 | ``` 218 | BatteryManager 包含 4 个只读属性,提供了设备电池的相关信息 219 | - `charging` 布尔值,表示设备当前是否正接入电源充电。如果设备没有电池,则返回 true。 220 | - `chargingTime` 整数,表示预计离电池充满还有多少秒。 221 | - `dischargingTime` 整数,表示预计离电量耗尽还有多少秒。 222 | - `level` 浮点数,表示电量百分比。电量完全耗尽返回 0.0,电池充满返回 1.0。如果设备没有电 223 | 池,则返回 1.0。 224 | 225 | 这个 API 还提供了 4 个事件属性,可用于设置在相应的电池事件发生时调用的回调函数。 226 | - `onchargingchange` 充电状态变化时的处理程序 227 | - `onchargingtimechange` 充电时间变化时的处理程序 228 | - `ondischargingtimechange` 放电时间变化时的处理程序 229 | - `onlevelchange` 电量百分比变化时的处理程序 230 | 231 | #### 硬件 232 | 1. 处理器核心数 `navigator.hardwareConcurrency` 233 | 返回浏览器支持的逻辑处理器核心数量,包含表示核心 234 | 数的一个整数值(如果核心数无法确定,这个值就是 1)。关键在于,这个值表示浏览器可以并行执行的 最大工作线程数量,不一定是实际的 CPU 核心数。 235 | 236 | 2. 设备内存大小 `navigator.deviceMemory` 237 | 返回设备大致的系统内存大小,包含单位为 GB 的浮点数(舍入 238 | 为最接近的 2 的幂:512MB 返回 0.5,4GB 返回 4)。 239 | 240 | 3. 最大触点数 `navigator.maxTouchPoints` 241 | 返回触摸屏支持的最大关联触点数量,包含一个整数值。 242 | 243 | -------------------------------------------------------------------------------- /posts/css-module.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: css-module 3 | date: 2020-03-30 00:05:24 4 | categories: css 5 | tags: css 6 | --- 7 | 8 | ### 产生的背景 9 | 一个好看的网页 css 样式密不可分,网页中标签的颜色,字体,背景,动画,等等都是用css控制实际的展示效果,通过标签,类名称等不同选择器定义css样式,但久而久之,项目越来越大的时候,不同组件,不同标签之间样式可能会被污染,影响实际展示效果 10 | 11 | ### 实际项目中出现的问题 12 | - 样式被覆盖 13 | - class 名称过长 14 | 15 | ### 配置 16 | css module 目前有四种方式集成到项目,具体请看 https://github.com/css-modules/css-modules/blob/master/docs/get-started.md , 我们主要学习 webpack 中在css-loader的使用 17 | 18 | #### 通过 css-loader 配置支持 css module实现 19 | ```ts 20 | [ 21 | { 22 | test: /\.less$/, 23 | use: [ 24 | 'style-loader', 25 | { 26 | loader: 'css-loader', 27 | options: { 28 | modules: true 29 | } 30 | }, 31 | "less-loader", 32 | { 33 | loader: 'style-resources-loader', 34 | options: { 35 | patterns: path.resolve(__dirname, './../src/styles/val.less') 36 | } 37 | } 38 | ], 39 | }, 40 | ] 41 | ``` 42 | 设置 css-loader 的 `options.modules` 为true 配置css module支持 43 | 44 | 由于我是在 less中使用,且环境是ts的开发环境,所以在不做设置的情况下,通过css-module 的方式引入less文件会报错 45 | 46 | ```less 47 | // module.less 48 | .main { 49 | color: #fff; 50 | padding: 4px 15px; 51 | background: rgb(163, 25, 25); 52 | border-radius: 4px; 53 | } 54 | ``` 55 | 56 | ```tsx 57 | // module.tsx 58 | import React from 'react' 59 | import styles from './index.less' 60 | 61 | console.log(styles) // {main: "zEvk6qCVDzWwH0LgzL_3-"} 62 | 63 | export default () => { 64 | return ( 65 | hello 66 | ) 67 | } 68 | ``` 69 | css-module给 span 元素生成一个独一无二的全局 class 名称 `zEvk6qCVDzWwH0LgzL_3-` ,这样就不会受全局样式命名冲突的影响,使用`styles.main` 即可拿到class 名称, 实际的效果如下 70 | ![css-module显示效果](https://www.daiwei.site/static/blog/css-module/css-module显示效果.png) 71 | 编辑结果如下 72 | ![css 编译结果](https://www.daiwei.site/static/blog/css-module/css编译结果.png) 73 | 74 | > 注意: 由于我使用的是 ts 开发环境,在以es6 引入的方式引入 less文件的时候,会被提示,index.less不是模块,网上看了一下,顶一下 *.less 模块就可以了,[来自这里](https://stackoverflow.com/questions/57635943/how-do-i-make-import-as-style-from-from-x-less-work),代码如下 75 | 76 | ```ts 77 | // global.d.ts 78 | declare module '*.less' { 79 | const classes: {[key: string]: string}; 80 | export default classes; 81 | } 82 | ``` 83 | 84 | #### localIdentName 生成的class标识符 85 | 继续之前的操作,在默认不配置 `localIdentName` 的时候,返回的随机class 名称为: `zEvk6qCVDzWwH0LgzL_3-`, 为了统一风格和可读性,我们定义一种class命名格式 86 | ```ts 87 | { 88 | loader: 'css-loader', 89 | options: { 90 | modules: { 91 | localIdentName: '[path][name]__[local]--[hash:base64:5]', 92 | } 93 | } 94 | }, 95 | ``` 96 | 生成的结果 97 | ![localIdentName 生成的结果](https://www.daiwei.site/static/blog/css-module/localIdentName生成的结果.png) 98 | 结果为: `src-components-Module-index__text--3iAYn` 99 | [path]: src/components/Module 100 | [name]: index 101 | [local]: text 102 | [hash:base64:5]: 3iAYn 103 | 104 | - [name] 资源的基本名称 105 | - [path] 资源相对于context查询参数或选项的路径。 106 | - [local] 当前定义的类名称 107 | - [hash:base64:5] base64前5个字符转hash 108 | 至于其他的 标识 名,见 https://github.com/webpack/loader-utils#interpolatename 109 | 110 | > Recommendations: 111 | > - use '[path][name]__[local]' for development 112 | > - use '[hash:base64]' for production 113 | 114 | #### context 设置上下文 115 | ```ts 116 | { 117 | loader: 'css-loader', 118 | options: { 119 | modules: { 120 | localIdentName: '[path][name]__[local]--[hash:base64:5]', 121 | context: path.resolve(__dirname, './../src') 122 | } 123 | } 124 | }, 125 | ``` 126 | 添加context之后,path的目录就是给予src开始,其结果为 `components-Module-index__text--Tnlj5` 127 | [path]: src/components/Module 128 | [name]: index 129 | [local]: text 130 | [hash:base64:5]: 3iAYn 131 | 除去最后的 hash 不管, name,local 没发生变化,变化的只是 path , path 的 context 发生了变化 132 | 133 | #### mode ("local" | "global" | "pure") 134 | ```ts 135 | { 136 | loader: 'css-loader', 137 | options: { 138 | modules: { 139 | localIdentName: '[path][name]__[local]--[hash:base64:5]', 140 | context: path.resolve(__dirname, './../src'), 141 | // Using `local` value has same effect like using `modules: true` 142 | mode: 'local' 143 | } 144 | } 145 | }, 146 | ``` 147 | 还有一些其他的属性,暂时不研究其具体的意义了(主要是不清楚...) 148 | #### hashPrefix `string` 149 | #### getLocalIdent 150 | #### localIdentRegExp 151 | 152 | ### 使用 153 | 基本的使用方式前面已经展示了,需要在具体研究一下 `css-module` 的使用方法 154 | - 选择器作为变量引入到class名 155 | - 全局作用域 156 | ```less 157 | :global { 158 | .main { 159 | color: #fff; 160 | padding: 4px 15px; 161 | background: rgb(163, 25, 25); 162 | border-radius: 4px; 163 | } 164 | .text { 165 | color: gray; 166 | font-size: 16px; 167 | font-weight: bold; 168 | } 169 | } 170 | ``` 171 | 这段代码在webpack编译之后和之前没什么区别,会在所有 class 为 `main`, `text` 的元素上生效,此时对于这种类型, `styles.className` 的这种写法是不生效的,因为 styles 对象,是一个空对象 172 | ![global的class为空对象](https://www.daiwei.site/static/blog/css-module/global的class为空对象.png) 173 | 如果需要引入对象只需要引入对应的 class 字符串即可 174 | 175 | - 局部作用域 176 | ```less 177 | :local { 178 | .main { 179 | color: #fff; 180 | padding: 4px 15px; 181 | background: rgb(163, 25, 25); 182 | border-radius: 4px; 183 | } 184 | .text { 185 | color: gray; 186 | font-size: 16px; 187 | font-weight: bold; 188 | } 189 | } 190 | ``` 191 | `:local .main` 的使用方式和直接使用 `.main` 实际上是一致的效果,都会生成 `option.localIdentName` 定义的class格式 192 | 193 | - class 继承 194 | ```less 195 | .main { 196 | color: #fff; 197 | padding: 4px 15px; 198 | background: rgb(163, 25, 25); 199 | border-radius: 4px; 200 | &:hover { 201 | background: blue; 202 | } 203 | &::before { 204 | content: 'hi css module'; 205 | background: green; 206 | } 207 | } 208 | .text { 209 | composes: main; 210 | color: gray; 211 | font-size: 16px; 212 | font-weight: bold; 213 | } 214 | ``` 215 | 同样的代码,text 类下通过 `composes: main;` 引入想要继承的类名称,就可以实现属性,以及伪类,伪元素的继承 216 | ![className继承](https://www.daiwei.site/static/blog/css-module/className继承.png) 217 | 而实际上对应的 class 类的对象信息如下 218 | ![class继承对应的对象名称](https://www.daiwei.site/static/blog/css-module/class继承对应的对象名称.png) 219 | `text`属性的第二个 class名称 对应 `main` 的class名称,因为有共同的样式效果 220 | 221 | - class 继承(来自外部文件的继承方式) 222 | ```less 223 | // auto.less 224 | .auto { 225 | background: #14f3b0; 226 | font-size: 14px; 227 | } 228 | ``` 229 | ```less 230 | // index.less 231 | .main { 232 | color: #fff; 233 | padding: 4px 15px; 234 | background: rgb(163, 25, 25); 235 | border-radius: 4px; 236 | &:hover { 237 | background: blue; 238 | } 239 | &::before { 240 | content: 'hi css module'; 241 | background: green; 242 | } 243 | } 244 | .text { 245 | composes: auto from './auto.less'; 246 | color: gray; 247 | font-size: 16px; 248 | font-weight: bold; 249 | } 250 | ``` 251 | `text` 元素会引入 auto.less 的 auto类的样式,设置背景色为翠绿色,如下图: 252 | ![class继承来自外部文件](https://www.daiwei.site/static/blog/css-module/class继承来自外部文件.png) 253 | 此时 this is text 元素的class名称可以看到 `components-Module-auto__auto` 一个来自 components 的 Module文件下的 auto.less 样式文件的 auto 类 254 | 255 | - 变量的引入 256 | 尝试在 auto.less 中使用变量达到方便维护和开发的效果 257 | ```less 258 | @color: #14f3b0; 259 | .auto { 260 | background: @color; 261 | font-size: 14px; 262 | } 263 | ``` 264 | 引入的时候发现并没有效果,样式被编译成了 265 | ```css 266 | .components-Module-auto__auto--KJeL5 { 267 | background: @color; 268 | font-size: 14px; 269 | } 270 | ``` 271 | `@color` 并没有被转换成对应的色值, 不过css-loader 支持变量定义, 来看代码 272 | ```less 273 | @value v-auto-color #14f3b0; 274 | @value s-auto-select auto; 275 | @value m-lg (min-width: 960px); 276 | .s-auto-select { 277 | background: v-auto-color; 278 | font-size: 14px; 279 | } 280 | @media m-lg { 281 | body { 282 | background: #000; 283 | } 284 | } 285 | ``` 286 | 官方推荐命名: 287 | v-: 变量 288 | s-: 选择器 289 | m-: 媒体查询 290 | 291 | [CSS Modules 用法教程](http://www.ruanyifeng.com/blog/2016/06/css_modules.html) 292 | [webpack/css-loader](https://www.webpackjs.com/loaders/css-loader/) 293 | [css-loader](https://github.com/webpack-contrib/css-loader#modules) 294 | [CSS module 入门](https://segmentfault.com/a/1190000014722978) 295 | -------------------------------------------------------------------------------- /posts/graphql入门.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: graphql入门 3 | date: 2020-01-23 19:26:08 4 | categories: javascript 5 | tags: [graphql, js] 6 | --- 7 | 8 | ### GraphQL 是什么 9 | #### 一种用于 API 的查询语言 10 | GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。 11 | 12 | ### 背景 13 | - 请求多个接口,页面请求借口过多,多次资源请求,难维护 14 | - 数据过度获取,或者数据字段缺失 15 | - 参数类型校验 16 | - 前端对于接口的处理被动,字段无法变更 17 | - 如何实现代码即文档 18 | 19 | ### GraphQL 特点 20 | - API 聚合 21 | - 请求合并, 字段组合 22 | - 类型系统描述 23 | - 辅助工具,提升开发效率 24 | 25 | ### 浏览器查询语言 26 | 在客户端获取gql聚合数据的时候,需要用到gql提供的查询操作 27 | 28 | #### query 查询 29 | 查询字段: 获取数据,比如查找, 获取所有文章的 id 和 title 字段 30 | ```graphql 31 | query { 32 | article { 33 | result { 34 | id 35 | title 36 | } 37 | } 38 | } 39 | ``` 40 | 41 | 查询结果 42 | ```json 43 | { 44 | "data": { 45 | "article": { 46 | "result": [ 47 | { 48 | "id": "1", 49 | "title": "JavaScript" 50 | }, 51 | { 52 | "id": "2", 53 | "title": "html5新特性" 54 | }, 55 | { 56 | "id": "3", 57 | "title": "css3相关特性" 58 | } 59 | ] 60 | } 61 | } 62 | } 63 | ``` 64 | 65 | #### mutation 变更 66 | 变更:对数据进行变更,比如增加、删除、修改 67 | 添加tip的count信息 会返回对应tip的详情 68 | ```graphql 69 | mutation { 70 | tipIncrease (id: 1) { 71 | result { 72 | count 73 | } 74 | } 75 | } 76 | ``` 77 | 变更的结果 78 | ```json 79 | { 80 | "data": { 81 | "tipIncrease": { 82 | "result": { 83 | "count": 30 84 | } 85 | } 86 | } 87 | } 88 | ``` 89 | 90 | #### substription 订阅 91 | 当数据发生更改,进行消息推送 92 | 93 | #### 事例代码 94 | ```js 95 | import Http from './../utils/http' 96 | 97 | /** 98 | * 合并 gql 请求的方法 99 | */ 100 | export const gql = async (type, name, ...gqlStr) => { 101 | console.log(`【${type.toUpperCase()}】===【${name}】:`) 102 | console.log([...gqlStr].join('\n')) 103 | try { 104 | const { data } = await Http.post('/graphql', { 105 | query: ` 106 | ${type} ${name} { 107 | ${[...gqlStr].join('\n')} 108 | } 109 | ` 110 | }) 111 | return data 112 | } catch (e) { 113 | throw e 114 | } 115 | } 116 | 117 | /** 118 | * 获取tips 的query数据 119 | */ 120 | export const tipsQuery = (page = 0, size = 20) => { 121 | return ` 122 | tips(page: ${page}, size: ${size}) { 123 | result { 124 | id 125 | name 126 | count 127 | } 128 | } 129 | ` 130 | } 131 | 132 | /** 133 | * 获取banner 信息 134 | */ 135 | export const bannerQuery = () => { 136 | return ` 137 | banner { 138 | result { 139 | id 140 | desc 141 | url 142 | } 143 | } 144 | ` 145 | } 146 | 147 | /** 148 | * 获取文章类型信息 149 | */ 150 | export const articleTypeQuery = () => { 151 | return ` 152 | articleType { 153 | result { 154 | id 155 | name 156 | } 157 | } 158 | ` 159 | } 160 | 161 | /** 162 | * 获取文章信息 163 | */ 164 | export const articleQuery = (page = 0, size = 20, type = 0) => { 165 | return ` 166 | article(page: ${page}, size: ${size}, type: ${type}) { 167 | result { 168 | id 169 | title 170 | tid 171 | createDate 172 | desc 173 | content 174 | type 175 | editDate 176 | user 177 | } 178 | } 179 | ` 180 | } 181 | 182 | /** 183 | * 获取 tips info 184 | */ 185 | export const tips = async () => { 186 | const { data } = await gql('query', 'getTips', tipsQuery()) 187 | return data 188 | } 189 | 190 | /** 191 | * 获取页面的所有数据信息 192 | */ 193 | export const pageAInfo = async () => { 194 | return await gql('query', 'getPageAInfo', 195 | tipsQuery(), 196 | bannerQuery(), 197 | articleTypeQuery(), 198 | articleQuery() 199 | ) 200 | } 201 | 202 | /** 203 | * tip increase 204 | */ 205 | export const tipIncrease = async (id) => { 206 | return await gql('mutation', 'tipIncrease', ` 207 | tipIncrease(id: ${id}) { 208 | result { 209 | count 210 | } 211 | } 212 | `) 213 | } 214 | ``` 215 | 216 | ### Schema 和类型 217 | 全称Schema Definition Language。GraphQL实现了一种可读的模式语法,SDL和JavaScript类似,这种语法必须存储为String格式。GraphQL Schema声明了返回的数据和结构。 218 | ```js 219 | const typeDefs = ` 220 | type Query { 221 | article(page: Int, size: Int, type: Int): ArticleResponse 222 | articleType: ArticleTypeResponse 223 | } 224 | 225 | type Article { 226 | id: ID! 227 | tid: Int 228 | title: String 229 | createDate: Date 230 | desc: String 231 | content: String 232 | editDate: Date 233 | type: String 234 | user: String 235 | } 236 | 237 | type ArticleType { 238 | id: ID! 239 | name: String 240 | } 241 | 242 | type ArticleResponse implements Response { 243 | code: Int 244 | msg: String 245 | result: [Article] 246 | } 247 | 248 | type ArticleTypeResponse implements Response { 249 | code: Int 250 | msg: String 251 | result: [ArticleType] 252 | } 253 | ` 254 | module.exports = typeDefs 255 | ``` 256 | 257 | #### 默认量标类型 258 | GraphQL 自带一组默认标量类型 259 | - `Int`:有符号 32 位整数。 260 | - `Float`:有符号双精度浮点值。 261 | - `String`:UTF‐8 字符序列。 262 | - `Boolean`:true 或者 false。 263 | - `ID`:ID 标量类型表示一个唯一标识符,通常用以重新获取对象或者作为缓存中的键。ID 类型使用和 String 一样的方式序列化;然而将其定义为 ID 意味着并不需要人类可读型。 264 | 265 | #### 自定义量标类型 266 | 自定义量标类型用于定义除 默认量标类型 的其他类型,如 Date 格式 267 | - `resolve`中 类型定义是通过 `GraphQLScalarType` 事例化对象,在其对应的回调函数中执行类型格式化操作 268 | - 通过 `scalar` 申明一个新类型 269 | ```js 270 | const resolve = { 271 | Date: new GraphQLScalarType({ 272 | name: 'Date', 273 | description: '自定义Date标量类型', 274 | 275 | // 对来自客户端的值进行处理, 对变量的处理 276 | parseValue(value) { 277 | console.log('parseValue', value) 278 | return moment(value, 'YYYY-MM-DD hh:mm:ss') 279 | }, 280 | 281 | // 返回给客户端的值进行处理 282 | serialize(value) { 283 | console.log('serialize', value) 284 | return moment(new Date(value)) 285 | }, 286 | 287 | // parseLiteral则会对Graphql的参数进行处理,参数会被解析转换为AST抽象语法树 288 | parseLiteral(ast) { 289 | if (ast.kind === Kind.INT) { 290 | return moment(new Date(ast.value)) 291 | } 292 | return null 293 | } 294 | }) 295 | } 296 | // 在 defs 中注册新类型 297 | // commmon.defs.js 298 | const typeDefs = ` 299 | # 新增 Date 数据结构 300 | scalar Date 301 | 302 | interface Response { 303 | code: Int 304 | msg: String 305 | } 306 | ` 307 | module.exports = typeDefs 308 | ``` 309 | 310 | 311 | ### resolver 解析器 312 | 解析器提供了将gql的操作(查询,突变或订阅)转换为数据的行为,在这个过程中,resolver可能会进行数据库的操作,resolver函数包含四个参数 313 | - obj 上一个resolve的解析结果 (数据层级多的时候可用) 最顶层为 undefined 314 | - args 传递用于查询的参数,id 或者 page,size… 315 | - context 解析器上下文,包含请求状态 316 | - Info 包含与当前查询有关的特定于字段的信息 317 | 318 | #### 解析客户端查询语句 319 | ```gql 320 | query { 321 | article { 322 | code 323 | msg 324 | result { 325 | id, 326 | author { 327 | name 328 | } 329 | desc 330 | } 331 | } 332 | } 333 | ``` 334 | 这段 query 查询,在resolve中是这样被解析的 335 | - 进行第一次解析,当前的类型是query 类型,resolver 名为 article 336 | - 使用article的 resolver 获取解析数据,第一层解析结束 337 | - 之后对第一层解析的返回值,进行第二层解析,article类型为 `ArticleResponse` , 执行 code 解析 Int, msg 解析String,result 解析 自定义对象类型 `Article` 338 | - id在 `Article` 类型中为标量类型,解析结束 339 | - author 在 `Article` 类型中为自定义类型 `Author` ,执行 `Author` 的 resolver解析 340 | - 解析name String 结束 `Author` 的 解析 341 | - 解析desc String 至此完成 `Article` 解析 342 | - `ArticleResponse` 中 result 自定义类型解析完毕,至此,`ArticleResponse` 字段全部解析完成 343 | 344 | > 遇到一个Query之后,尝试使用它的Resolver取值,之后再其Resolver对返回值进行解析,这个过程是递归的,直到所解析Field的类型是 Scalar Type (标量类型) 为止 345 | 346 | ### GraphQL 工作流程 347 | 如图 348 | ![GraphQL 工作流程](https://www.daiwei.site/static/blog/graphql入门/graphql.png) 349 | 350 | ### 演示地址 351 | gql 请求页面: [地址访问](http://www.daiwei.site/graphql/){:target="_blank"} 352 | 353 | 非gql请求页面: [地址访问](http://106.14.207.56:8080/b){:target="_blank"} 354 | 355 | ### 参考于 356 | [graphql从入门到实战](https://juejin.im/post/5cd68a9b51882568047fa6eb) 357 | 358 | [apollo.vue](https://apollo.vuejs.org/) 359 | 360 | [30分钟理解GraphQL核心概念](https://segmentfault.com/a/1190000014131950) 361 | 362 | --------------------------------------------------------------------------------