├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── LICENSE ├── README.md ├── docs ├── .vuepress │ ├── config.js │ └── templates │ │ ├── dev.html │ │ └── ssr.template.html ├── README.md ├── browser.md ├── css.md ├── html.md ├── img │ └── throttle.png ├── js.md ├── network.md ├── node.md ├── other.md ├── safe.md ├── vue.md └── webpack.md ├── package-lock.json └── package.json /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy 2 | on: # 监听 master 分支上的 push 事件 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | build-and-deploy: 8 | runs-on: ubuntu-latest # 构建环境使用 ubuntu 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v2.3.1 12 | with: 13 | persist-credentials: false 14 | 15 | - name: Install and Build # 下载依赖 打包项目 16 | run: | 17 | npm install 18 | npm run build 19 | 20 | - name: Deploy # 将打包内容发布到 github page 21 | uses: JamesIves/github-pages-deploy-action@3.7.1 # 使用别人写好的 actions 22 | with: # 自定义环境变量 23 | ACCESS_TOKEN: ${{ secrets.FRONT_END_BASIC_KNOWLEDGE }} 24 | BRANCH: master 25 | FOLDER: docs/.vuepress/dist 26 | REPOSITORY_NAME: woai3c/woai3c.github.io # 这是我的 github page 地址 27 | TARGET_FOLDER: Front-end-basic-knowledge # 打包的文件将放到静态服务器 introduction-to-front-end-engineering 目录下 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | docs/.vuepress/dist -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 bin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 前端知识点、面试题 2 | 3 | 这是我学习和工作所做的笔记,希望对你有帮助! 4 | 5 | 如果你想看一些更加系统化的知识,可以看一下我的[博客](https://github.com/woai3c/Front-end-articles),里面都是我写的一系列文章。除此之外,还有一些我学习计算机基础知识做的[笔记和实验](https://github.com/woai3c/Notes-and-Labs),有兴趣不妨一看。 6 | 7 | 关注我: 8 | * [知乎](https://www.zhihu.com/people/tan-guang-zhi-19) 9 | * [Github](https://github.com/woai3c) 10 | * [掘金](https://juejin.cn/user/1433418893103645) 11 | * 公众号——前端编程技术分享 12 | 13 | ## [在线阅读](https://woai3c.github.io/Front-end-basic-knowledge/) 14 | * JavaScript 15 | * CSS 16 | * HTML 17 | * Vue 18 | * 浏览器 19 | * 计算机网络 20 | * Node 21 | * Webpack 22 | * 前端安全 23 | * 其他 24 | 25 | ## License 26 | MIT 27 | ## 赞助 28 | ![](https://github.com/woai3c/nand2tetris/blob/master/img/wx.jpg) 29 | ![](https://github.com/woai3c/nand2tetris/blob/master/img/zfb.jpg) 30 | -------------------------------------------------------------------------------- /docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | base: '/Front-end-basic-knowledge/', 3 | title: '前端知识点与面试题', 4 | head: [ 5 | ['meta', { name: 'referrer', content: 'no-referrer' }], 6 | ], 7 | themeConfig: { 8 | sidebar: [ 9 | ['/', '简介'], 10 | ['/js', 'JavaScript'], 11 | ['/css', 'CSS'], 12 | ['/html', 'HTML'], 13 | ['/vue', 'Vue'], 14 | ['/browser', '浏览器'], 15 | ['/network', '计算机网络'], 16 | ['/node', 'Node'], 17 | ['/webpack', 'Webpack'], 18 | ['/safe', '前端安全'], 19 | ['/other', '其他'], 20 | ], 21 | displayAllHeaders: true, 22 | repo: 'https://github.com/woai3c/Front-end-basic-knowledge', 23 | repoLabel: '给作者的 Github 点个 star 吧!', 24 | smoothScroll: true, 25 | } 26 | } -------------------------------------------------------------------------------- /docs/.vuepress/templates/dev.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | -------------------------------------------------------------------------------- /docs/.vuepress/templates/ssr.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | 这是我学习和工作所做的笔记,希望对你有帮助! 2 | 3 | 如果你想看一些更加系统化的知识,可以看一下我的[博客](https://github.com/woai3c/Front-end-articles),里面都是我写的一系列文章。除此之外,还有一些我学习计算机基础知识做的[笔记和实验](https://github.com/woai3c/Notes-and-Labs),有兴趣不妨一看。 4 | 5 | 关注我: 6 | * [知乎](https://www.zhihu.com/people/tan-guang-zhi-19) 7 | * [Github](https://github.com/woai3c) 8 | * [掘金](https://juejin.cn/user/1433418893103645) 9 | * 公众号——前端编程技术分享 10 | 11 | ## [在线阅读](https://woai3c.github.io/Front-end-basic-knowledge/) 12 | * JavaScript 13 | * CSS 14 | * HTML 15 | * Vue 16 | * HTTP 17 | * Node 18 | * Webpack 19 | * 前端安全 20 | * 其他 21 | 22 | ## License 23 | MIT 24 | -------------------------------------------------------------------------------- /docs/browser.md: -------------------------------------------------------------------------------- 1 | ## 关键渲染路径 2 | 关键渲染路径是浏览器将 HTML,CSS 和 JavaScript 转换为屏幕上的像素所经历的步骤序列。优化关键渲染路径可提高渲染性能。关键渲染路径包含了 文档对象模型 (DOM),CSS 对象模型 (CSSOM),渲染树和布局。 3 | 4 | 在解析 HTML 时会创建文档对象模型。HTML 可以请求 JavaScript,而 JavaScript 反过来,又可以更改 DOM。HTML 包含或请求样式,依次来构建 CSS 对象模型。浏览器引擎将两者结合起来以创建渲染树。布局确定页面上所有内容的大小和位置。确定布局后,将像素绘制到屏幕上。 5 | 6 | 优化关键渲染路径可以缩短首次渲染的时间。了解和优化关键渲染路径对于确保重排和重绘可以每秒 60 帧的速度进行,以确保高效的用户交互并避免讨厌是很重要的。 7 | 8 | Web 性能包含了服务器请求和响应、加载、执行脚本、渲染、布局和绘制每个像素到屏幕上。 9 | 10 | 网页请求从 HTML 文件请求开始。服务器返回 HTML -- 响应头和数据。然后浏览器开始解析 HTML,转换收到的数据为 DOM 树。浏览器每次发现外部资源就初始化请求,无论是样式、脚本或者嵌入的图片引用。有时请求会阻塞,这意味着解析剩下的 HTML 会被终止直到重要的资源被处理。浏览器接着解析 HTML,发请求和构造 DOM 直到文件结尾 ,这时开始构造 CSS对象模型。等到 DOM 和 CSSOM 完成之后,浏览器构造渲染树,计算所有可见内容的样式。一旦渲染树完成布局开始,定义所有渲染树元素的位置和大小。完成之后,页面被渲染完成,或者说是绘制到屏幕上。 11 | 12 | * [关键渲染路径](https://developer.mozilla.org/zh-CN/docs/Web/Performance/Critical_rendering_path) 13 | * [css 性能优化](https://developer.mozilla.org/zh-CN/docs/Learn/Performance/CSS) 14 | 15 | ## 浏览器渲染原理 16 | 渲染过程 17 | 1. 解析HTML生成DOM树。 18 | 2. 解析CSS生成CSSOM规则树。 19 | 3. 解析JS,操作 DOM 树和 CSSOM 规则树。 20 | 4. 将DOM树与CSSOM规则树合并在一起生成渲染树。 21 | 5. 遍历渲染树开始布局,计算每个节点的位置大小信息。 22 | 6. 浏览器将所有图层的数据发送给GPU,GPU将图层合成并显示在屏幕上。 23 | 24 | * [浏览器渲染原理](https://developer.mozilla.org/zh-CN/docs/Web/Performance/How_browsers_work) 25 | 26 | ## DOM 树和渲染树区别 27 | DOM 树包含了 HTML 元素,渲染树则是要绘制到页面上的元素。渲染树可以不包含 DOM 树中的元素,像 `` 和它的子节点以及任何具有 `display: none` 样式的结点 28 | 29 | ## 浏览器缓存机制 30 | 当浏览器向服务器发起请求时,服务器会将缓存规则放入HTTP响应报文的HTTP头中和请求结果一起返回给浏览器,控制强制缓存的字段分别是Expires和Cache-Control,其中Cache-Control优先级比Expires高。 31 | 32 | [彻底理解浏览器的缓存机制](https://juejin.cn/post/6844903593275817998) 33 | 34 | ## Reflow和Repaint 35 | #### Reflow 36 | 当涉及到DOM节点的布局属性发生变化时,就会重新计算该属性,浏览器会重新描绘相应的元素,此过程叫Reflow(回流或重排)。 37 | #### Repaint 38 | 当影响DOM元素可见性的属性发生变化 (如 color) 时, 浏览器会重新描绘相应的元素, 此过程称为Repaint(重绘)。因此重排必然会引起重绘。 39 | #### 引起Repaint和Reflow的一些操作 40 | * 调整窗口大小 41 | * 字体大小 42 | * 样式表变动 43 | * 元素内容变化,尤其是输入控件 44 | * CSS伪类激活,在用户交互过程中发生 45 | * DOM操作,DOM元素增删、修改 46 | * width, clientWidth, scrollTop等布局宽高的计算 47 | 48 | #### Repaint和Reflow是不可避免的,只能说对性能的影响减到最小,给出下面几条建议: 49 | * 避免逐条更改样式。建议集中修改样式,例如操作className。 50 | * 避免频繁操作DOM。创建一个documentFragment或div,在它上面应用所有DOM操作,最后添加到文档里。设置display:none的元素上操作,最后显示出来。 51 | * 避免频繁读取元素几何属性(例如scrollTop)。绝对定位具有复杂动画的元素。 52 | * 绝对定位使它脱离文档流,避免引起父元素及后续元素大量的回流 53 | 54 | 参考资料: 55 | * [减少页面重排与重绘(Reflow & Repaint)](https://harttle.land/2015/08/11/reflow-repaint.html) 56 | * [页面重构应注意的repaint和reflow](http://www.blueidea.com/tech/web/2011/8365.asp) 57 | 58 | 59 | ## 同源策略 60 | 同源策略可防止 JavaScript 发起跨域请求。源被定义为协议、主机名和端口号的组合。此策略可防止页面上的恶意脚本通过该页面的文档对象模型,访问另一个网页上的敏感数据。 61 | 62 | 下表给出了与 URL http://store.company.com/dir/page.html 的源进行对比的示例: 63 | |URL| 结果| 原因| 64 | |-|-|-| 65 | |http://store.company.com/dir2/other.html| 同源 |只有路径不同| 66 | |http://store.company.com/dir/inner/another.html |同源 |只有路径不同| 67 | |https://store.company.com/secure.html| 失败 |协议不同| 68 | |http://store.company.com:81/dir/etc.html| 失败 |端口不同 ( http:// 默认端口是80)| 69 | |http://news.company.com/dir/other.html| 失败 |主机不同| 70 | 71 | 参考资料: 72 | * [浏览器的同源策略](https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy) 73 | 74 | 75 | 76 | ## 跨域 77 | * 原因
78 | 浏览器的同源策略导致了跨域 79 | * 作用
80 | 用于隔离潜在恶意文件的重要安全机制 81 | * 解决 82 | 1. jsonp ,允许 script 加载第三方资源 83 | 2. 反向代理(nginx 服务内部配置 Access-Control-Allow-Origin *) 84 | 3. cors 前后端协作设置请求头部,Access-Control-Allow-Origin 等头部信息 85 | 4. iframe 嵌套通讯,postmessage 86 | 87 | 参考资料: 88 | * [新鲜出炉的8月前端面试题](https://zhuanlan.zhihu.com/p/41479807) 89 | * [跨域资源共享 CORS 阮一峰](http://www.ruanyifeng.com/blog/2016/04/cors.html) 90 | 91 | 92 | 93 | ## JSONP 94 | 这是我认为写得比较通俗易懂的一篇文章[jsonp原理详解——终于搞清楚jsonp是啥了](https://blog.csdn.net/hansexploration/article/details/80314948)。 95 | 96 | 97 | 98 | ## 域名收敛 99 | PC 时代为了突破浏览器的域名并发限制,有了域名发散。浏览器有并发限制,是为了防止DDOS攻击。 100 | 101 | **域名收敛**:就是将静态资源放在一个域名下。减少DNS解析的开销。 102 | 103 | **域名发散**:是将静态资源放在多个子域名下,就可以多线程下载,提高并行度,使客户端加载静态资源更加迅速。 104 | 105 | 域名发散是pc端为了利用浏览器的多线程并行下载能力。而域名收敛多用与移动端,提高性能,因为dns解析是是从后向前迭代解析,如果域名过多性能会下降,增加DNS的解析开销。 106 | 107 | ## 为何会出现浏览器兼容问题 108 | * 同一产品,版本越老 bug 越多 109 | * 同一产品,版本越新,功能越多 110 | * 不同产品,不同标准,不同实现方式 111 | ### 处理兼容问题的思路 112 | 1. 要不要做 113 | * 产品的角度(产品的受众、受众的浏览器比例、效果优先还是基本功能优先) 114 | * 成本的角度 (有无必要做某件事) 115 | 116 | 2.做到什么程度 117 | * 让哪些浏览器支持哪些效果 118 | 119 | 3..如何做 120 | * 根据兼容需求选择技术框架/库(jquery) 121 | * 根据兼容需求选择兼容工具(html5shiv.js、respond.js、css reset、normalize.css、Modernizr) 122 | * 条件注释、CSS Hack、js 能力检测做一些修补 123 | 124 | * 渐进增强(progressive enhancement): 针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验 125 | * 优雅降级 (graceful degradation): 一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。 126 | 127 | 参考资料: 128 | * [为何会出现浏览器兼容性问题?如何解决?](https://github.com/jirengu/frontend-interview/issues/35) 129 | 130 | 131 | ## 为什么最好把 CSS 的``标签放在``之间?为什么最好把 JS 的` 968 | ``` 969 | 970 | 参考资料: 971 | * [PerformanceTiming.navigationStart](https://developer.mozilla.org/zh-CN/docs/Web/API/PerformanceTiming/navigationStart) 972 | 973 | ## 当你在浏览器输入一个地址后发生了什么 974 | [当···时发生了什么?](https://github.com/skyline75489/what-happens-when-zh_CN/blob/master/README.rst?utm_medium=social&utm_source=wechat_session&from=timeline&isappinstalled=0) 975 | 976 | 977 | 978 | ## 页面大量图片,如何优化加载,优化用户体验 979 | 1. 图片懒加载。在页面的未可视区域添加一个滚动事件,判断图片位置与浏览器顶端的距离与页面的距离,如果前者小于后者,优先加载。 980 | 2. 如果为幻灯片、相册等,可以使用图片预加载技术,将当前展示图片的前一张和后一张优先下载。 981 | 3. 如果图片为css图片,可以使用CSSsprite,SVGsprite等技术。 982 | 4. 如果图片过大,可以使用特殊编码的图片,加载时会先加载一张压缩的特别厉害的缩略图,以提高用户体验。 983 | 5. 如果图片展示区域小于图片的真实大小,应在服务器端根据业务需要先进行图片压缩,图片压缩后大小与展示一致。 984 | 985 | 986 | 987 | ## 防抖与节流 988 | #### 防抖(debounce) 989 | 在函数需要频繁触发时,只有当有足够空闲的时间时,才执行一次。就好像在百度搜索时,每次输入之后都有联想词弹出,这个控制联想词的方法就不可能是输入框内容一改变就触发的,他一定是当你结束输入一段时间之后才会触发。 990 | 991 | #### 节流(thorttle) 992 | 预定一个函数只有在大于等于执行周期时才执行,周期内调用不执行。就好像你在淘宝抢购某一件限量热卖商品时,你不断点刷新点购买,可是总有一段时间你点上是没有效果,这里就用到了节流,就是怕点的太快导致系统出现bug。 993 | 994 | #### 区别 995 | 在发生持续触发事件时,防抖设置事件延迟并在空闲时间去触发事件,而节流则是隔一定的时间触发一次。 996 | 997 | 一个简单的防抖示例 998 | ```js 999 | let timer 1000 | input.on('input', () => { 1001 | clearTimeout(timer) 1002 | // 停止输入 500 毫秒后开始搜索 1003 | timer = setTimeout(() => { 1004 | // 搜索 1005 | }, 500) 1006 | }) 1007 | ``` 1008 | 一个简单的节流示例 1009 | ```js 1010 | let isClick = false 1011 | button.on('click', () => { 1012 | if (isClick) return 1013 | isClick = true 1014 | // 其他代码。。。 1015 | // 每 10 秒只允许点击一次 1016 | setTimeout(() => { 1017 | isClick = false 1018 | }, 10000) 1019 | }) 1020 | ``` 1021 | 1022 | 来个困难点的,根据图片要求实现节流函数 1023 | 1024 | ![](./img/throttle.png) 1025 | 1026 | 实现 1027 | ```js 1028 | const throttle = (function(delay) { 1029 | let waitForCallFunc 1030 | let canCall = true 1031 | return function throttle(callback) { 1032 | if (!canCall) { 1033 | if (callback) waitForCallFunc = callback 1034 | return 1035 | } 1036 | 1037 | callback() 1038 | canCall = false 1039 | setTimeout(() => { 1040 | canCall = true 1041 | if (waitForCallFunc) { 1042 | throttle(waitForCallFunc) 1043 | waitForCallFunc = null 1044 | } 1045 | }, delay) 1046 | } 1047 | })(1000) 1048 | 1049 | throttle(() => console.log(1)) 1050 | 1051 | setTimeout(() => { 1052 | throttle(() => console.log(2)) 1053 | }, 500) 1054 | 1055 | setTimeout(() => { 1056 | throttle(() => console.log(3)) 1057 | }, 700) 1058 | 1059 | setTimeout(() => { 1060 | throttle(() => console.log(4)) 1061 | }, 1200) 1062 | 1063 | setTimeout(() => { 1064 | throttle(() => console.log(5)) 1065 | }, 1400) 1066 | 1067 | setTimeout(() => { 1068 | throttle(() => console.log(6)) 1069 | }, 1600) 1070 | 1071 | setTimeout(() => { 1072 | throttle(() => console.log(7)) 1073 | }, 2500) 1074 | ``` 1075 | 参考资料: 1076 | * [js网络请求性能优化之防抖与节流](https://blog.csdn.net/jacoox/article/details/80719456) 1077 | 1078 | 1079 | 1080 | ## 如何做到修改url参数页面不刷新 1081 | HTML5引入了 `history.pushState()` 和 `history.replaceState()` 方法,它们分别可以添加和修改历史记录条目。 1082 | ```js 1083 | let stateObj = { 1084 | foo: "bar", 1085 | }; 1086 | 1087 | history.pushState(stateObj, "page 2", "bar.html"); 1088 | ``` 1089 | 假设当前页面为 `foo.html`,执行上述代码后会变为 `bar.html`,点击浏览器后退,会变为 `foo.html`,但浏览器并不会刷新。 1090 | `pushState()` 需要三个参数: 一个状态对象, 一个标题 (目前被忽略), 和 (可选的) 一个 URL. 让我们来解释下这三个参数详细内容: 1091 | 1092 | * 状态对象 — 状态对象 `state` 是一个 JavaScript 对象,通过 `pushState ()` 创建新的历史记录条目。无论什么时候用户导航到新的状态,`popstate` 事件就会被触发,且该事件的 `state` 属性包含该历史记录条目状态对象的副本。 1093 | 状态对象可以是能被序列化的任何东西。原因在于 Firefox 将状态对象保存在用户的磁盘上,以便在用户重启浏览器时使用,我们规定了状态对象在序列化表示后有640k的大小限制。如果你给 `pushState()` 方法传了一个序列化后大于 640k 的状态对象,该方法会抛出异常。如果你需要更大的空间,建议使用 `sessionStorage` 以及 `localStorage`. 1094 | 1095 | * 标题 — Firefox 目前忽略这个参数,但未来可能会用到。传递一个空字符串在这里是安全的,而在将来这是不安全的。二选一的话,你可以为跳转的 `state` 传递一个短标题。 1096 | 1097 | * URL — 该参数定义了新的历史URL记录。注意,调用 `pushState()` 后浏览器并不会立即加载这个 URL,但可能会在稍后某些情况下加载这个 URL,比如在用户重新打开浏览器时。新URL不必须为绝对路径。如果新URL是相对路径,那么它将被作为相对于当前 URL 处理。新 URL 必须与当前URL同源,否则 `pushState()` 会抛出一个异常。该参数是可选的,缺省为当前 URL。 1098 | 1099 | 参考资料: 1100 | * [History API](https://developer.mozilla.org/zh-CN/docs/Web/API/History_API) 1101 | 1102 | 1103 | ## 格式化金钱,每千分位加逗号 1104 | ```js 1105 | function format(str) { 1106 | let s = '' 1107 | let count = 0 1108 | for (let i = str.length - 1; i >= 0; i--) { 1109 | s = str[i] + s 1110 | count++ 1111 | if (count % 3 == 0 && i != 0) { 1112 | s = ',' + s 1113 | } 1114 | } 1115 | return s 1116 | } 1117 | ``` 1118 | ```js 1119 | function format(str) { 1120 | return str.replace(/(\d)(?=(?:\d{3})+$)/g, '$1,') 1121 | } 1122 | ``` 1123 | 1124 | 1125 | ## 请用js去除字符串空格 1126 | ### 去除所有空格 1127 | ``` 1128 | str.replace(/\s/g, '') 1129 | ``` 1130 | ### 去除两边空格 1131 | ``` 1132 | str.replace(/^\s+|\s+$/g, '') 1133 | // 原生方法 1134 | str.trim() 1135 | ``` 1136 | 1137 | 1138 | ## 创建对象有几种方法 1139 | * 字面量 1140 | ```js 1141 | const obj = {a: 1} 1142 | ``` 1143 | * 构造函数 1144 | ```js 1145 | function Obj(val) { 1146 | this.a = val 1147 | } 1148 | 1149 | const obj = new Obj(1) 1150 | ``` 1151 | * Object.create 1152 | ```js 1153 | const obj = Object.create({a: 1}) 1154 | ``` 1155 | 1156 | 1157 | ## null和undefined的区别 1158 | `null` 表示一个对象是“没有值”的值,也就是值为“空” 1159 | 1160 | `undefined` 表示一个变量声明了没有初始化(赋值) 1161 | 1162 | `undefined` 和 `null` 在if语句中,都会被自动转为false 1163 | 1164 | `undefined` 不是一个有效的JSON,而 `null` 是 1165 | 1166 | `undefined` 的类型(typeof)是 `undefined` 1167 | 1168 | `null` 的类型(typeof)是 `object` 1169 | 1170 | Javascript将未赋值的变量默认值设为 `undefined` 1171 | 1172 | Javascript从来不会将变量设为 `null`。 它是用来让程序员表明某个用var声明的变量时没有值的 1173 | 1174 | 1175 | 1176 | ## 反转数组 1177 | ### 要求 1178 | **input**: I am a student
1179 | **output**: student a am I
1180 | 输入是数组 输出也是数组
1181 | 不允许用 `split` `splice` `reverse`
1182 | 1183 | #### 解法一 1184 | ```js 1185 | function reverseArry(arr) { 1186 | const result = [] 1187 | while (arr.length) { 1188 | result.push(arr.pop()) 1189 | } 1190 | 1191 | return result 1192 | } 1193 | 1194 | console.log(reverseArry(['I', 'am', 'a', 'student'])) 1195 | // ["student", "a", "am", "I"] 1196 | ``` 1197 | #### 解法二 1198 | ```js 1199 | function reverseArry(arry) { 1200 | const result = [] 1201 | const distance = arry.length - 1 1202 | for (let i = distance; i >= 0; i--) { 1203 | result[distance - i] = arry[i] 1204 | } 1205 | 1206 | return result 1207 | } 1208 | ``` 1209 | 1210 | 1211 | 1212 | ## 将金额12345转成中文金额表示 1213 | ### 要求 1214 | ```js 1215 | 12345 => 一万两千三百四十五 1216 | 10086 => 一万零八十六 1217 | 100010001 => 一亿零一万零一 1218 | 100000000 => 一亿 1219 | ``` 1220 | 单位支持到亿 1221 | 1222 | ### 实现 1223 | ```js 1224 | function numToString(num) { 1225 | if (num > 999999999) throw '超过金额上限,最大单位为亿' 1226 | const unitMap = ['', '十', '百', '千', '万', '十', '百', '千', '亿'] 1227 | const stringMap = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'] 1228 | const n = num + '' 1229 | const len = n.length 1230 | let lastIndex = len - 1 1231 | let result = '' 1232 | for (let i = 0; i < len; i++) { 1233 | if (i > 0 && n[i] == '0') { 1234 | if (n[i - 1] != '0') result += '零' 1235 | } else { 1236 | result += stringMap[n[i]] + unitMap[lastIndex] 1237 | } 1238 | 1239 | lastIndex-- 1240 | } 1241 | 1242 | lastIndex = result.length - 1 1243 | if (result[lastIndex] == '零') return result.slice(0, lastIndex) 1244 | return result 1245 | } 1246 | 1247 | console.log(numToString(12345)) // 一万二千三百四十五 1248 | console.log(numToString(10086)) // 一万零八十六 1249 | console.log(numToString(100010001)) // 一亿零一万零一 1250 | console.log(numToString(100000000)) // 一亿 1251 | ``` 1252 | 1253 | 1254 | ## 异步求和 1255 | ### 要求 1256 | 提供一个异步 `add` 方法如下,需要实现一个 `await sum(...args)` 函数: 1257 | ```js 1258 | function asyncAdd(a, b, callback) { 1259 | setTimeout(function() { 1260 | callback(null, a + b) 1261 | }, 1000) 1262 | } 1263 | ``` 1264 | ### 实现 1265 | ```js 1266 | function sum(...args) { 1267 | let start = 0 1268 | let result = 0 1269 | let count = 0 // 用于计算开启了多少个 Promise 1270 | 1271 | function _sum(resolve) { 1272 | count++ 1273 | new Promise((r, j) => { 1274 | let a = args[start++] 1275 | let b = args[start++] 1276 | a = a !== undefined? a : 0 1277 | b = b !== undefined? b : 0 // 如果访问的元素超出了数组范围,则转为 0 1278 | asyncAdd(a, b, (context, sum) => { 1279 | r(sum) 1280 | }) 1281 | 1282 | if (start < args.length) { 1283 | _sum(resolve) 1284 | } 1285 | }) 1286 | .then(sum => { 1287 | result += sum 1288 | count-- 1289 | if (count == 0) resolve(result) // 所有的 Promise 执行完毕,返回结果 1290 | }) 1291 | } 1292 | 1293 | return new Promise((resolve, reject) => { 1294 | if (!args || !args.length) return resolve(0) 1295 | if (args.length == 1) return resolve(args[0]) 1296 | _sum(resolve) 1297 | }) 1298 | } 1299 | ``` 1300 | ### 测试 1301 | ```js 1302 | sum(1,2,3,4,5,6,7,8,9,10,11).then(sum => console.log(sum)) // 66 1303 | // or 1304 | async function test() { 1305 | console.log(await sum(1,2,3,4,5,6,7,8,9,10,11)) 1306 | } 1307 | 1308 | test() // 66 1309 | ``` 1310 | 1311 | 1312 | ## 异步求和升级版 1313 | ### 要求 1314 | 假设有一台本地机器,无法做加减乘除运算,因此无法执行 a + b、a+ = 1 这样的 JS 代码,然后我们提供一个服务器端的 HTTP API,可以传两个数字类型的参数,响应结果是这两个参数的和,这个 HTTP API 的 JS SDK(在本地机器上运行)的使用方法如下: 1315 | ```js 1316 | asyncAdd(3, 5, (err, result) => { 1317 | console.log(result); // 8 1318 | }); 1319 | ``` 1320 | 模拟实现: 1321 | ```js 1322 | function asyncAdd(a, b, cb) { 1323 | setTimeout(() => { 1324 | cb(null, a + b); 1325 | }, Math.floor(Math.random()*100)) 1326 | } 1327 | ``` 1328 | 现在要求在本地机器上实现一个 sum 函数,支持以下用法: 1329 | ```js 1330 | (async () => { 1331 | const result1 = await sum(1, 4, 6, 9, 1, 4); 1332 | const result2 = await sum(3, 4, 9, 2, 5, 3, 2, 1, 7); 1333 | const result3 = await sum(1, 6, 0, 5); 1334 | console.log([result1, result2, result3]); // [25, 36, 12] 1335 | })(); 1336 | ``` 1337 | 要求 sum 能在最短的时间里返回以上结果 1338 | 1339 | ### 实现 1340 | ```js 1341 | function asyncAdd(a, b, cb) { 1342 | setTimeout(() => { 1343 | cb(null, a + b); 1344 | }, Math.floor(Math.random()*100)) 1345 | } 1346 | 1347 | function sum(...args) { 1348 | const result = [] 1349 | function _sum(resolve, reject) { 1350 | new Promise((r, j) => { 1351 | let a = args.pop() 1352 | let b = args.pop() 1353 | a = a !== undefined? a : 0 1354 | b = b !== undefined? b : 0 // 如果访问的元素超出了数组范围,则转为 0 1355 | asyncAdd(a, b, (err, res) => { 1356 | if (err) j(err) 1357 | r(res) 1358 | }) 1359 | 1360 | if (args.length) { 1361 | _sum(resolve, reject) 1362 | } 1363 | }) 1364 | .then(val => { 1365 | result.push(val) 1366 | setTimeout(() => { 1367 | if (args.length <= 0) { 1368 | resolve(sum(...result)) 1369 | } 1370 | }, 100) 1371 | }) 1372 | } 1373 | 1374 | return new Promise((resolve, reject) => { 1375 | if (!args || !args.length) resolve(0) 1376 | if (args.length == 1) resolve(args[0]) 1377 | _sum(resolve, reject) 1378 | }) 1379 | } 1380 | 1381 | (async () => { 1382 | const result1 = await sum(1, 4, 6, 9, 1, 4) 1383 | const result2 = await sum(3, 4, 9, 2, 5, 3, 2, 1, 7) 1384 | const result3 = await sum(1, 6, 0, 5) 1385 | console.log([result1, result2, result3]) // [25, 36, 12] 1386 | })() 1387 | ``` 1388 | 1389 | 1390 | 1391 | ## 数字集转换成字母集 1392 | ### 要求 1393 | `a~z` 有 26个字母,按照 `1~26` 编码,现在给定一个数字字符串,输出所有可能的解码结果,如:输入 1234,输出 ['awd', 'abcd', 'lcd'] 1394 | ```js 1395 | const map = [0,'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'] 1396 | function getDecodes(num) { 1397 | if (!num) return [] 1398 | num += '' 1399 | const result = [] 1400 | _getDecodes(num, 0, [], result) 1401 | return result 1402 | } 1403 | 1404 | function _getDecodes(num, start, path, result) { 1405 | if (start == num.length) return result.push([...path]) 1406 | let c = num[start++] 1407 | path.push(map[c]) 1408 | _getDecodes(num, start, path, result) 1409 | path.pop() 1410 | 1411 | if (start == num.length) return 1412 | c += num[start] 1413 | 1414 | if (c > 26) return 1415 | path.push(map[c]) 1416 | _getDecodes(num, start + 1, path, result) 1417 | path.pop() 1418 | } 1419 | ``` 1420 | 1421 | 1422 | ## CommonJS,ES module 是什么,有什么区别? 1423 | 它们都是一种模块规范,例如 Node 使用的就是 CommonJS 规范。ES module 则是语言标准上的模块规范。 1424 | 1425 | 区别: 1426 | 1. CommonJS 模块使用 `require()` 和 `module.exports`,ES6 模块使用 `import`和 `export`。 1427 | 2. CommonJS 模块输出的是一个值的浅拷贝,ES6 模块输出的是值的引用。 1428 | 3. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。 1429 | 4. CommonJS 模块的 `require()` 是同步加载模块,ES6 模块的 `import` 命令是异步加载,有一个独立的模块依赖的解析阶段。 1430 | 5. ES6 模块之中,顶层的 this 指向 undefined;CommonJS 模块的顶层 this 指向当前模块, 1431 | 6. 对于循环加载的处理方法不同 1432 | 1433 | 第 3 个差异是因为 CommonJS 加载的是一个对象(即 `module.exports` 属性),该对象只有在脚本运行完才会生成。 1434 | 1435 | 而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。 1436 | 1437 | 参考资料: 1438 | * [Module 的加载实现](https://es6.ruanyifeng.com/#docs/module-loader) 1439 | 1440 | 1441 | 1442 | ## preload和prefetch 1443 | ### preload 1444 | `preload` 是 `` 标签 `rel` 属性的属性值,同时需要配合 `as` 属性使用。 1445 | 1446 | `as` 指定将要预加载的内容的类型,使得浏览器能够: 1447 | 1. 更精确地优化资源加载优先级。 1448 | 2. 匹配未来的加载需求,在适当的情况下,重复利用同一资源。 1449 | 3. 为资源应用正确的内容安全策略。 1450 | 4. 为资源设置正确的 Accept 请求头。 1451 | 1452 | 看一下这个示例: 1453 | ```html 1454 | 1455 | ``` 1456 | 这种做法将把 `` 标签塞入一个预加载器中。 1457 | 1458 | 这个预加载器在不阻塞页面 onload 事件的情况下,去加载资源。 1459 | 1460 | 我们可以通过以下两个示例来作一个对比: 1461 | ```html 1462 | 1463 | 1464 | 1465 | 1466 | 1467 | Document 1468 | 1471 | 1472 | 1473 | 1474 | 1475 | 1476 | 1477 | 1482 | 1483 | 1484 | ``` 1485 | 上面这个示例从加载到触发 onload 事件需要大概 1400 ms 的时间。再看一下使用 preload 预加载的时间: 1486 | ```html 1487 | 1488 | 1489 | 1490 | 1491 | 1492 | window.onload = () => { 1493 | console.timeEnd('load') // load: 10.8818359375ms 1494 | } 1495 | ``` 1496 | 用 preload 来加载资源,只需要 10 ms 就触发了 onload 事件。 1497 | 1498 | 说明同样是下载文件,使用 preload 不会阻塞 onload 事件。 1499 | 1500 | ### prefetch 1501 | `prefetch` 和 `preload` 不同,使用 `prefetch` 属性指定的资源将在浏览器空闲时间下下载。 1502 | 1503 | 在资源的请求头如果发现有下面这个属性,就代表它是通过 `prefetch` 加载的: 1504 | ```js 1505 | purpose: prefetch 1506 | ``` 1507 | 1508 | 另外,空闲时间是如何确定、如何获取的,目前还没有相关 API。 1509 | 1510 | 1511 | 1512 | ## preload 和 defer 的区别 1513 | preload 和 defer 的相同点是异步下载。那它们的不同点是什么呢? 1514 | 1515 | preload 下载的资源只有在遇到同样的 script 标签时,才会执行对应的脚本。例如下面预加载的 `vue.js`: 1516 | ```html 1517 | 1518 | ``` 1519 | 只有在遇到下面的标签时,才会执行加载的 `vue.js`: 1520 | ```html 1521 | 1522 | ``` 1523 | 1524 | defer 则是异步下载资源,在所有元素解析完成后,触发 DOMContentLoaded 事件前执行。 1525 | 1526 | 1527 | 1528 | ## window.onload 和 DOMContentLoaded 的区别 1529 | 当整个页面及所有依赖资源如样式表和图片都已完成加载时,将触发load事件。 1530 | 1531 | 它与 DOMContentLoaded不同,当纯HTML被完全加载以及解析时,DOMContentLoaded 事件会被触发,而不必等待样式表,图片或者子框架完成加载。 1532 | 1533 | * [load](https://developer.mozilla.org/zh-CN/docs/Web/Events/load) 1534 | * [DOMContentLoaded 事件](https://developer.mozilla.org/zh-CN/docs/Web/API/Document/DOMContentLoaded_event) 1535 | 1536 | 1537 | ## websocket 鉴权、多人连接、心跳机制 1538 | * [WebSocket 的鉴权授权方案](http://www.moye.me/2017/02/10/websocket-authentication-and-authorization/) 1539 | * [WebSocket学习(一)——基于socket.io实现简单多人聊天室](https://segmentfault.com/a/1190000011538416) 1540 | * [理解WebSocket心跳及重连机制(五)](https://www.cnblogs.com/tugenhua0707/p/8648044.html) 1541 | 1542 | 1543 | 1544 | ## Object 与 Map 的区别 1545 | 1. Object 只能选择字符、数值、符号作为 key,Map 则可以使用任何类型的数据作为 key。 1546 | 2. Map 实例会维护键值对的插入顺序,因此可以根据插入顺序执行迭代操作。Chrome Opera 中使用 for-in 语句遍历 Object 属性时会遵循一个规律:它们会先提取所有 key 的 parseFloat 值为非负整数的属性,然后根据数字顺序对属性排序首先遍历出来,然后按照对象定义的顺序遍历余下的所有属性。其它浏览器则完全按照对象定义的顺序遍历属性。 1547 | 1548 | ### 选择 Object 还是 Map 1549 | 对于多数Web开发任务来说,选择 Object 还是 Map 只是个人偏好问题,影响不大。不过,对于在乎内存和性能的开发者来说,对象和映射之间确实存在显著的差别。 1550 | #### 1. 内存占用 1551 | Object 和 Map 的工程级实现在不同浏览器间存在明显差异,但存储单个键/值对所占用的内存数量都会随键的数量线性增加。批量添加或删除键/值对则取决于各浏览器对该类型内存分配的工程实现。 1552 | 1553 | 不同浏览器的情况不同,但给定固定大小的内存, Map 大约可以比 Object 多存储50%的键/值对。 1554 | #### 2. 插入性能 1555 | 向 Object 和 Map 中插入新键/值对的消耗大致相当,不过插入Map 在所有浏览器中一般会稍微快一点儿。对这两个类型来说,插入速度并不会随着键/值对数量而线性增加。 1556 | 1557 | 如果代码涉及大量插入操作,那么显然 Map 的性能更佳。 1558 | #### 3. 查找速度 1559 | 与插入不同,从大型 Object 和 Map 中查找键/值对的性能差异极小,但如果只包含少量键/值对,则 Object 有时候速度更快。在把 Object 当成数组使用的情况下(比如使用连续整数作为属性),浏览器引擎可以进行优化,在内存中使用更高效的布局。 1560 | 1561 | 这对 Map 来说是不可能的。对这两个类型而言,查找速度不会随着键/值对数量增加而线性增加。如果代码涉及大量查找操作,那么某些情况下可能选择 Object 更好一些。 1562 | #### 4. 删除性能 1563 | 使用 delete 删除 Object 属性的性能一直以来饱受诟病,目前在很多浏览器中仍然如此。为此,出现了一些伪删除对象属性的操作,包括把属性值设置为 undefined 或 null 。但很多时候,这都是一 1564 | 种讨厌的或不适宜的折中。 1565 | 1566 | 而对大多数浏览器引擎来说, Map 的 delete() 操作都比插入和查找更快。如果代码涉及大量删除操作,那么毫无疑问应该选择 Map 。 1567 | 1568 | 参考资料: 1569 | * [JavaScript高级程序设计(第4版)](https://book.douban.com/subject/35175321/?from=tag) 1570 | * [js能够保证object属性的输出顺序吗?](http://jartto.wang/2016/10/25/does-js-guarantee-object-property-order/) 1571 | 1572 | 1573 | ## 为什么 WeakMap 和 WeakSet 的键只能使用对象? 1574 | >是为了保证只有通过键对象的引用来取得值。 1575 | ```js 1576 | const m = new WeakMap() 1577 | m.set({}, 100) // 由于 {} 没有在其他地方引用,所以在垃圾回收时,这个值也会被回收。 1578 | 1579 | const a = {} 1580 | m.set(a, 100) // 如果使用这种方式,则不会被回收。因为 {} 有 a 变量在引用它。 1581 | 1582 | a = null // 将 a 置为空后,m 里的值 100 在垃圾回收时将会被回收。 1583 | ``` 1584 | >如果允许原始值,那就没办法区分初始化时使用的字符串字面量和初始化之后使用的一个相等的字符串了。 1585 | 1586 | 所以这句话的意思很明确: 1587 | ```js 1588 | const a = {} // 在创建对象时,分配了一块内存,并把这块内存的地址传给 a 1589 | m.set(a, 100) // 执行 set 操作时,实际上是将 a 指向的内存地址和 100 关联起来 1590 | 1591 | const a = 'abc' // 由于基本数据类型在传递时,传递的是值,而不是引用。 1592 | m.set(a, 100) // 所以执行 set 操作时,实际上是将新的 'abc' 和 100 关联起来,而不是原来 a 变量指向的那个。 1593 | // 那这样就会有问题,m 里存储的永远是没有被引用的键,随时都会被回收。 1594 | ``` 1595 | 参考资料: 1596 | * [JavaScript高级程序设计(第4版)](https://book.douban.com/subject/35175321/?from=tag) 1597 | 1598 | 1599 | ## 实现 async/await 1600 | 利用 `generator()` 实现 `async/await` 主要就是用一个函数(自动执行器)来包装 `generator()`,从而实现自动执行 `generator()`。 1601 | 1602 | 每次执行 `next()` 返回的 `{ value, done }` 中的 value 是一个 Promise,所以要等它执行完毕后再执行下一次 `next()`。 1603 | 1604 | 即在它的后面加一个 `then()` 函数,并且在 `then()` 函数中执行 `next()`。 1605 | ```js 1606 | function t(data) { 1607 | return new Promise(r => setTimeout(() => r(data), 100)) 1608 | } 1609 | 1610 | function *test() { 1611 | const data1 = yield t(1) 1612 | console.log(data1) 1613 | const data2 = yield t(2) 1614 | console.log(data2) 1615 | return 3 1616 | } 1617 | 1618 | function async(generator) { 1619 | return new Promise((resolve, reject) => { 1620 | const gen = generator() 1621 | 1622 | function step(nextFun) { 1623 | // 每一次 next() 都是返回这样的数据 { value: xx, done: false },结束时 done 为 true 1624 | let next 1625 | try { 1626 | // 如果 generator() 执行报错,需要 reject 给外面的 catch 函数 1627 | next = nextFun() 1628 | } catch(e) { 1629 | return reject(e) 1630 | } 1631 | 1632 | // done: true 代表 generator() 结束了 1633 | if (next.done) { 1634 | return resolve(next.value) 1635 | } 1636 | 1637 | Promise.resolve(next.value).then( 1638 | (val) => step(() => gen.next(val)), // 通过 next(val) 将 val 传给 yield 后面的变量 1639 | (err) => step(() => gen.trhow(err)), 1640 | ) 1641 | } 1642 | 1643 | step(() => gen.next()) 1644 | }) 1645 | } 1646 | 1647 | // 1 2 3 1648 | async(test).then(val => console.log(val)) 1649 | ``` 1650 | 1651 | 1652 | ## 实现发布/订阅模式 1653 | ```js 1654 | class Event { 1655 | constructor() { 1656 | this.events = {} 1657 | } 1658 | 1659 | on(event, callback) { 1660 | if (!this.events[event]) { 1661 | this.events[event] = [] 1662 | } 1663 | 1664 | this.events[event].push(callback) 1665 | } 1666 | 1667 | off(event, callback) { 1668 | if (this.events[event]) { 1669 | if (callback) { 1670 | const cbs = this.events[event] 1671 | let l = cbs.length 1672 | while (l--) { 1673 | if (callback == cbs[l]) { 1674 | cbs.splice(l, 1) 1675 | } 1676 | } 1677 | } else { 1678 | this.events[event] = [] 1679 | } 1680 | } 1681 | } 1682 | 1683 | emit(event, ...args) { 1684 | if (this.events[event]) { 1685 | for (const func of this.events[event]) { 1686 | func.call(this, ...args) 1687 | } 1688 | } 1689 | } 1690 | 1691 | once(event, callback) { 1692 | const self = this 1693 | 1694 | function wrap(...args) { 1695 | callback.call(self, ...args) 1696 | self.off(event, wrap) 1697 | } 1698 | 1699 | this.on(event, wrap) 1700 | } 1701 | } 1702 | ``` 1703 | 1704 | 1705 | ## 5个fetch请求,请求完成后要求立即执行,但最终的输出顺序要按照要求输出ABCDE 1706 | ```js 1707 | function run(fetchs = []) { 1708 | return new Promise((resolve, reject) => { 1709 | const result = new Array(fetchs.length).fill(null) 1710 | fetchs.forEach((fetch, i) => { 1711 | fetch 1712 | .then(res => { 1713 | result[i] = res 1714 | if (!result.includes(null)) { 1715 | resolve(result) 1716 | } 1717 | }) 1718 | .catch(err => { 1719 | reject(err) 1720 | }) 1721 | }) 1722 | }) 1723 | } 1724 | 1725 | function delay(str) { 1726 | return new Promise(resolve => { 1727 | setTimeout(() => { 1728 | resolve(str) 1729 | }, Math.random() * 1000) 1730 | }) 1731 | } 1732 | 1733 | run([delay('A'), delay('B'), delay('C'), delay('D'), delay('E')]).then(res => { 1734 | console.log(res) // ["A", "B", "C", "D", "E"] 1735 | }) 1736 | ``` 1737 | 1738 | ## requestIdleCallback 是干什么用的 1739 | [你应该知道的requestIdleCallback](https://juejin.cn/post/6844903592831238157) 1740 | 1741 | ## js 对象循环引用会导致什么问题 1742 | 1. 引用计数无法回收内存 1743 | 2. `JSON.stringify()` 执行会报错。 1744 | 1745 | ## 如何埋点,为什么用1 * 1像素的gif图片做上报 1746 | 1. 能够完成整个 HTTP 请求+响应(尽管不需要响应内容) 1747 | 2. 触发 GET 请求之后不需要获取和处理数据、服务器也不需要发送数据 1748 | 3. 没有跨域问题 1749 | 4. 执行过程无阻塞 1750 | 5. 相比 XMLHttpRequest 对象发送 GET 请求,性能上更好 1751 | 6. GIF的最低合法体积最小(最小的BMP文件需要74个字节,PNG需要67个字节,而合法的GIF,只需要43个字节) 1752 | 1753 | [为什么通常在发送数据埋点请求的时候使用的是 1x1 像素的透明 gif 图片](https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/87) 1754 | 1755 | 1756 | ## Base64图片有什么问题 1757 | 1. 使用base64 编码后比原图大 1758 | 2. 内联到 HTML/CSS 文件,会造成文件尺寸变大,影响首屏加载。如果用外链图片的话,图片可以在页面渲染完成后继续加载,不会造成阻塞。 1759 | 1760 | ## 如何实现一个可设置过期时间的 localStorage 1761 | ```js 1762 | (function () { 1763 | const getItem = localStorage.getItem.bind(localStorage) 1764 | const setItem = localStorage.setItem.bind(localStorage) 1765 | const removeItem = localStorage.removeItem.bind(localStorage) 1766 | 1767 | localStorage.getItem = function (key) { 1768 | const expires = getItem(key + '_expires') 1769 | if (expires && new Date() > new Date(Number(expires))) { 1770 | removeItem(key) 1771 | removeItem(key + '_expires') 1772 | } 1773 | 1774 | return getItem(key) 1775 | } 1776 | 1777 | localStorage.setItem = function (key, value, time) { 1778 | if (typeof time !== 'undefined') { 1779 | setItem(key + '_expires', new Date().getTime() + Number(time)) 1780 | } 1781 | 1782 | return setItem(key, value) 1783 | } 1784 | })() 1785 | ``` 1786 | 1787 | ## JavaScript的sort方法内部使用的什么排序 1788 | sort 使用的是插入排序和快速排序结合的排序算法。 1789 | 1790 | 数组长度不超过10时,使用插入排序。长度超过10使用快速排序。在数组较短时插入排序更有效率。 1791 | 1792 | ## 服务端渲染和预渲染的区别 1793 | [服务端渲染(SSR)和预渲染(Prerendering)有什么区别?](https://www.zhihu.com/question/273930443) 1794 | 1795 | ## canvas 和 svg 区别 1796 | [SVG 与 HTML5 的 canvas 各有什么优点,哪个更有前途?](https://www.zhihu.com/question/19690014) -------------------------------------------------------------------------------- /docs/network.md: -------------------------------------------------------------------------------- 1 | # HTTP 2 | 3 | ## RESTful 4 | REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。 5 | 6 | * GET
7 | get方法在Rest中主要用于获取资源,能够发送参数,不过有限制,且参数都会以?开头的形 式附加在URL尾部。 8 | 规范的get方法处理器应该是幂等的,也就是说对一个资源不论发送多少次get请求都不会更改数据或造成破坏。 9 | * POST
10 | post方法在Rest请求中主要用于添加资源,参数信息存放在请求报文的消息体中相对安全,且可发送较大信息 11 | * PUT
12 | put方法在Rest中主要用于更新资源,因为大多数浏览器不支持put和delete,会自动将put和delete请求转化为get和post. 因此为了使用put和delete方法, 13 | 需要以post发送请求,在表单中使用隐藏域发送真正的请求。 14 | put方法的参数是同post一样是存放在消息中的,同样具有安全性,可发送较大信息。 15 | put方法是幂等的,对同一URL资源做出的同一数据的任意次put请求其对数据的改变都是一致的。 16 | * DELETE
17 | Delete在Rest请求中主要用于删除资源,因为大多数浏览器不支持put和delete,会自动将put和delete请求转化为get和post。 18 | 因此为了使用put和delete方法,需要以post发送请求,在表单中使用隐藏域发送真正的请求。 19 | Delete方法的参数同post一样存放在消息体中,具有安全性,可发送较大信息 Delete方法是幂等的,不论对同一个资源进行多少次delete请求都不会破坏数据 20 | 21 | https://blog.csdn.net/jnshu_it/article/details/80203696 22 | 23 | ## GET和POST的区别 24 | * GET在浏览器回退时是无害的,而POST会再次提交请求。 25 | * GET产生的URL地址可以被Bookmark,而POST不可以。 26 | * GET请求会被浏览器主动cache,而POST不会,除非手动设置。 27 | * GET请求只能进行url编码,而POST支持多种编码方式。 28 | * GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。 29 | * GET请求在URL中传送的参数是有长度限制的,而POST么有。 30 | * 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。 31 | * GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。 32 | 33 | ## Accept和Content-Type 34 | Accept 请求头用来告知客户端可以处理的内容类型,这种内容类型用MIME类型来表示。 35 | 服务器使用 Content-Type 应答头通知客户端它的选择。 36 | ``` 37 | Accept: text/html 38 | Accept: image/* 39 | Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8 40 | ``` 41 | 1.Accept属于请求头, Content-Type属于实体头。
42 | Http报头分为通用报头,请求报头,响应报头和实体报头。
43 | 请求方的http报头结构:通用报头|请求报头|实体报头
44 | 响应方的http报头结构:通用报头|响应报头|实体报头
45 | 46 | 2.Accept代表发送端(客户端)希望接受的数据类型。
47 | 比如:Accept:text/xml;
48 | 代表客户端希望接受的数据类型是xml类型
49 | 50 | Content-Type代表发送端(客户端|服务器)发送的实体数据的数据类型。
51 | 比如:Content-Type:text/html;
52 | 代表发送端发送的数据格式是html。
53 | 54 | 二者合起来,
55 | Accept:text/xml;
56 | Content-Type:text/html
57 | 即代表希望接受的数据类型是xml格式,本次请求发送的数据的数据格式是html。
58 | 59 | ## 状态码 60 | 61 | | 状态码 | 类别 | 描述 | 62 | | -- | -- | -- | 63 | | 1xx | Informational(信息状态码) | 接受请求正在处理 | 64 | | 2xx | Success(成功状态码) | 请求正常处理完毕 | 65 | | 3xx | Redirection(重定向状态码) | 需要附加操作已完成请求 | 66 | | 4xx | Client Error(客户端错误状态码) | 服务器无法处理请求 | 67 | | 5xx | Server Error(服务器错误状态码) | 服务器处理请求出错 | 68 | 69 | [HTTP 响应代码](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status) 70 | 71 | 72 | ## HTTP缓存 73 | https://segmentfault.com/a/1190000010690320 74 | 75 | ## 如何处理不让别人盗用你的图片,访问你的服务器资源 76 | * http header, 对refer做判断看来源是不是自己的网站,如果不是就拒绝 77 | * 通过session校验,如果不通过特定服务生成cookie和session就不能请求得到资源 78 | 79 | 80 | 81 | ## Http与Https的区别 82 | * HTTP 的URL 以http:// 开头,而HTTPS 的URL 以https:// 开头 83 | * HTTP 是不安全的,而 HTTPS 是安全的 84 | * HTTP 标准端口是80 ,而 HTTPS 的标准端口是443 85 | * 在OSI 网络模型中,HTTP工作于应用层,而HTTPS 的安全传输机制工作在传输层 86 | * HTTP 无法加密,而HTTPS 对传输的数据进行加密 87 | * HTTP无需证书,而HTTPS 需要CA机构wosign的颁发的SSL证书 88 | 89 | https://zhuanlan.zhihu.com/p/33778904 90 | 91 | 92 | 93 | ## 什么是Http协议无状态协议?怎么解决Http协议无状态协议? 94 | 无状态协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息也就是说,
95 | 当客户端一次HTTP请求完成以后,客户端再发送一次HTTP请求,HTTP并不知道当前客户端是一个”老用户“。
96 | 97 | 可以使用Cookie来解决无状态的问题,Cookie就相当于一个通行证,第一次访问的时候给客户端发送一个Cookie,
98 | 当客户端再次来的时候,拿着Cookie(通行证),那么服务器就知道这个是”老用户“。
99 | 100 | https://zhuanlan.zhihu.com/p/33778904 101 | 102 | 103 | 104 | ## 常用的HTTP方法有哪些 105 | * GET:用于请求访问已经被URL(统一资源标识符)识别的资源,可以通过URL传参给服务器。 106 | * POST:用于传输信息给服务器,主要功能与Get方法类似,但一般推荐POST方式。 107 | * PUT:传输文件,报文主体包含文件内容,保存到对应URL位置。 108 | * HEAD:获取报文首部,与GET方法类似,只是不返回报文主体,一般用于验证URL是否有效。 109 | * DELET:删除文件,与PUT方法相反,删除对应URL位置的文件。 110 | * OPTIONS:查询相应URL支持的HTTP方法。 111 | 112 | 113 | 114 | ## HTTPS 握手机制 115 | 116 | * [HTTPs入门, 图解SSL从回车到握手](https://zhuanlan.zhihu.com/p/25587986) 117 | * [SSL/TLS协议运行机制的概述](https://www.ruanyifeng.com/blog/2014/02/ssl_tls.html) 118 | * [SSL/TLS 握手过程详解](https://www.jianshu.com/p/7158568e4867) 119 | 120 | 121 | ## options 预检请求 122 | [预检请求](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS#%E9%A2%84%E6%A3%80%E8%AF%B7%E6%B1%82) 123 | 124 | ## dns查询过程,dns用什么协议发起dns查询的 125 | [为什么 DNS 使用 UDP 协议](https://draveness.me/whys-the-design-dns-udp-tcp/) 126 | 127 | ## keep-alive 和多路复用的区别 128 | keep-alive 是指 TCP 处理完一个 HTTP 连接后不关闭,可以继续处理下一个 HTTP 连接。多路复用是 HTTP2 的特性,指多个 HTTP 连接可以同时在一个 TCP 连接上处理。 129 | 130 | ## websocket握手过程 131 | [socket 及 websocket的握手过程](https://blog.csdn.net/yournevermore/article/details/103067079) -------------------------------------------------------------------------------- /docs/node.md: -------------------------------------------------------------------------------- 1 | # Nodejs 2 | 3 | ## package.json 依赖项版本号前面的 ~ ^ 有什么用? 4 | * ~ 会匹配最近的小版本依赖包,比如~1.2.3会匹配所有1.2.x版本,但是不包括1.3.0 5 | * ^ 会匹配最新的大版本依赖包,比如^1.2.3会匹配所有1.x.x的包,包括1.3.0,但是不包括2.0.0 6 | * 不写前缀,只写版本号就不会有更新问题 7 | 8 | 9 | 10 | ## 项目中使用 package-lock.json 锁版本问题 11 | * [项目中使用package-lock.json锁版本问题](https://www.cnblogs.com/yy95/p/10441727.html) 12 | * [npm install 生成的package-lock.json是什么文件?有什么用?](https://www.zhihu.com/question/62331583) 13 | * [npm依赖版本变动引发的惨案](https://segmentfault.com/a/1190000024520174) 14 | 15 | ## peerDependencies 有什么用 16 | 假设一个项目有如下依赖项: 17 | ```json 18 | { 19 | "dependencies": { 20 | "a": "1.0.0", 21 | "b": "1.0.0", 22 | "c": "1.0.0", 23 | } 24 | } 25 | ``` 26 | 并且 b 和 c 也各自有依赖项 a。那么下载依赖后的项目目录是这样的: 27 | ``` 28 | . 29 | ├── project 30 | │ └── node_modules 31 | │ ├── a 32 | │ ├── b 33 | │ │ └── nodule_modules 34 | │ │ └── a 35 | │ └── c 36 | │ │ └── nodule_modules 37 | │ │ └── a 38 | ``` 39 | 这样会有一个问题,依赖 a 会被重复安装 3 次。这时可以使用 `peerDependencies`,它可以避免相同的依赖被重复安装。 40 | 41 | 现在只需要在 b 和 c 的 `package.json` 文件加上以下代码: 42 | ```json 43 | { 44 | "peerDependencies": { 45 | "a": "1.0.0" 46 | } 47 | } 48 | ``` 49 | 这样在安装时就可以避免重复安装依赖了。现在下载依赖后的目录为: 50 | ``` 51 | . 52 | ├── helloWorld 53 | │ └── node_modules 54 | │ ├── a 55 | │ ├── b 56 | │ └── c 57 | ``` 58 | * 如果用户显式依赖了核心库,则可以忽略各插件的 `peerDependency` 声明; 59 | * 如果用户没有显式依赖核心库,则按照插件 `peerDependencies` 中声明的版本将库安装到项目根目录中; 60 | 61 | 注意,如果主项目的 a 依赖项和 b、c 安装的 a 依赖项版本不同,有可能会报错。 62 | 63 | 例如 b 和 c 是一个插件的时候,它们依赖于某个核心依赖才能工作。当 b c 的核心依赖版本和项目的核心依赖版本不同时,就会报错。这时使用 `peerDependencies` 就可以很好的解决这个问题。 64 | 65 | 参考资料: 66 | * [一文搞懂peerDependencies](https://segmentfault.com/a/1190000022435060) 67 | * [浅谈npm 的依赖与版本](https://github.com/SamHwang1990/blog/issues/7) 68 | 69 | ## 垃圾回收机制 70 | [内存回收](https://github.com/woai3c/Notes-and-Labs/blob/master/notes/nodejs-srjc/docs/5.md) -------------------------------------------------------------------------------- /docs/other.md: -------------------------------------------------------------------------------- 1 | # 其他 2 | 3 | ## 前端异常监控 4 | [前端性能和错误监控](https://github.com/woai3c/Front-end-articles/blob/master/monitor.md) 5 | 6 | ## Ascii、GBK、UTF、Unicode 7 | * Ascii(1个字节1个字符) 8 | * GBK是国内的编码标准(汉字2个字节) 9 | * Unicode是国际编码标准(统一2个字节表示一个字符) 10 | * UTF是Unicode实现的另一个标准 11 | > unicode同样也不完美,这里就有两个的问题,一个是,如何才能区别unicode和ascii?
12 | 由于”半角”英文符号只需要用到低8位,所以其高8位永远是0,因此这种大气的方案在保存英文文本时会多浪费一倍的空间
13 | unicode在很长一段时间内无法推广,直到互联网的出现,为解决unicode如何在网络上传输的问题,于是面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位。UTF-8就是在互联网上使用最广的一种unicode的实现方式,这是为传输而设计的编码,并使编码无国界,这样就可以显示全世界上所有文化的字符了。UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度,当字符在ASCII码的范围时,就用一个字节表示,保留了ASCII字符一个字节的编码做为它的一部分,注意的是unicode一个中文字符占2个字节,而UTF-8一个中文字符占3个字节)。从unicode到utf-8并不是直接的对应,而是要过一些算法和规则来转换。 14 | 15 | https://www.zhihu.com/question/23374078/answer/69732605 16 | 17 | ## 前端性能优化 18 | [前端性能优化 24 条建议(2020)](https://github.com/woai3c/Front-end-articles/blob/master/performance.md) 19 | 20 | 21 | ## rpc远程过程调用 22 | [谁能用通俗的语言解释一下什么是 RPC 框架?](https://www.zhihu.com/question/25536695) 23 | 24 | ## 浏览器工作原理 25 | * [浏览器的工作原理:新式网络浏览器幕后揭秘](https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/) 26 | * [图解浏览器的基本工作原理](https://zhuanlan.zhihu.com/p/47407398) 27 | 28 | ## 单点登录 29 | [单点登录(SSO)看这一篇就够了](https://developer.aliyun.com/article/636281) 30 | -------------------------------------------------------------------------------- /docs/safe.md: -------------------------------------------------------------------------------- 1 | # 前端安全 2 | 3 | ## XSS 4 | ### XSS是什么 5 | XSS是一种经常出现在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。
6 | 比如这些代码包括HTML代码和客户端脚本。攻击者利用XSS漏洞旁路掉访问控制——例如同源策略(same origin policy)。
7 | 这种类型的漏洞由于被黑客用来编写危害性更大的网络钓鱼(Phishing)攻击而变得广为人知。
8 | 对于跨站脚本攻击,黑客界共识是:跨站脚本攻击是新型的“缓冲区溢出攻击“,而JavaScript是新型的“ShellCode”。 9 | ``` 10 | 示例: 11 | 12 | ``` 13 | 14 | ### 特点 15 | 能注入恶意的HTML/JavaScript代码到用户浏览的网页上,从而达到Cookie资料窃取、会话劫持、钓鱼欺骗等攻击。 16 | <攻击代码不一定(非要)在 中> 17 | 18 | ### 原因 19 | * Web浏览器本身的设计不安全。浏览器能解析和执行JS等代码,但是不会判断该数据和程序代码是否恶意。 20 | * 输入和输出是Web应用程序最基本的交互,而且网站的交互功能越来越丰富。如果在这过程中没有做好安全防护,很容易会出现XSS漏洞。 21 | * 程序员水平参差不齐,而且大都没有过正规的安全培训,没有相关的安全意识。 22 | * XSS攻击手段灵活多变。 23 | 24 | ### 危害 25 | * 盗取各类用户帐号,如机器登录帐号、用户网银帐号、各类管理员帐号 26 | * 控制企业数据,包括读取、篡改、添加、删除企业敏感数据的能力 27 | * 盗窃企业重要的具有商业价值的资料 28 | * 非法转账 29 | * 强制发送电子邮件 30 | * 网站挂马 31 | * 控制受害者机器向其它网站发起攻击 32 | 33 | ### 如何防范 34 | * 将重要的cookie标记为http only, 这样的话Javascript 中的document.cookie语句就不能获取到cookie了. 35 | * 表单数据规定值的类型,例如:年龄应为只能为int、name只能为字母数字组合。。。。 36 | * 对数据进行Html Encode 处理 37 | * 过滤或移除特殊的Html标签, 例如: ` 202 | 203 | 204 | ``` 205 | 206 | 207 | 208 | 209 | ## vue-router原理 210 | 说简单点,vue-router的原理就是通过对URL地址变化的监听,继而对不同的组件进行渲染。 211 | 212 | 每当URL地址改变时,就对相应的组件进行渲染。原理是很简单,实现方式可能有点复杂,主要有hash模式和history模式。 213 | 214 | 如果想了解得详细点,请查看[面试官: 你了解前端路由吗?](https://juejin.im/post/6844903589123457031)。 215 | 216 | 217 | 218 | ## vuex原理 219 | vuex的原理其实非常简单,它为什么能实现所有的组件共享同一份数据?
220 | 因为vuex生成了一个store实例,并且把这个实例挂在了所有的组件上,所有的组件引用的都是同一个store实例。
221 | store实例上有数据,有方法,方法改变的都是store实例上的数据。由于其他组件引用的是同样的实例,所以一个组件改变了store上的数据, 222 | 导致另一个组件上的数据也会改变,就像是一个对象的引用。
223 | 如果对vuex的实现有兴趣,可以看看我自己造的一个vue轮子对应的[vuex插件](https://github.com/woai3c/mini-vuex/blob/master/mini-vuex.js)。它实现了除vuex模块外的所有功能。 224 | 225 | 226 | 227 | ## v-if和v-show的区别 228 | `v-if` 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。 229 | 230 | `v-if` 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。 231 | 232 | 相比之下,`v-show` 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。 233 | 234 | 一般来说,`v-if` 有更高的切换开销,而 `v-show` 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 `v-show` 较好;如果在运行时条件很少改变,则使用`v-if` 较好。 235 | 236 | https://cn.vuejs.org/v2/guide/conditional.html#v-if-vs-v-show 237 | 238 | 239 | 240 | ## vue怎么实现页面的权限控制 241 | 利用 `vue-router` 的 `beforeEach` 事件,可以在跳转页面前判断用户的权限(利用 cookie 或 token),是否能够进入此页面,如果不能则提示错误或重定向到其他页面,在后台管理系统中这种场景经常能遇到。 242 | 243 | 244 | 245 | ## keep-alive有什么作用 246 | 在 `Vue` 中,每次切换组件时,都会重新渲染。如果有多个组件切换,又想让它们保持原来的状态,避免重新渲染,这个时候就可以使用 `keep-alive`。 247 | `keep-alive` 可以使被包含的组件保留状态,或避免重新渲染。 248 | 249 | 250 | 251 | ## 计算属性有什么作用 252 | 先来看一下计算属性的定义: 253 | 254 | 当其依赖的属性的值发生变化的时,计算属性会重新计算。反之则使用缓存中的属性值。 255 | 256 | 计算属性和vue中的其它数据一样,都是响应式的,只不过它必须依赖某一个数据实现,并且只有它依赖的数据的值改变了,它才会更新。 257 | 258 | 259 | 260 | ## $route和$router的区别 261 | `$route` 是路由信息对象,包括`path`,`params`,`hash`,`query`,`fullPath`,`matched`,`name` 等路由信息参数。 262 | 263 | 而 `$router` 是路由实例对象,包括了路由的跳转方法,钩子函数等 264 | 265 | 266 | ## watch的作用是什么 267 | `watch` 主要作用是监听某个数据值的变化,它和计算属性很相似。 268 | 269 | 借助 `watch` 还可以做一些特别的事情,例如监听页面路由,当页面跳转时,我们可以做相应的权限控制,拒绝没有权限的用户访问页面。 270 | 271 | `watch` 与计算属性的区别有两个:一是计算属性依赖其他属性和有缓存,它没有;二是 `watch` 可以进行异步操作。 272 | 273 | 为什么计算属性不能进行异步操作?因为计算属性必须将计算后的值 `return` 回去,如果在计算属性中使用异步操作,那会返回一个 `undefined`: 274 | ```js 275 | computed: { 276 | test() { 277 | let value 278 | setTimeout(() => value = 10) 279 | return value 280 | } 281 | } 282 | ``` 283 | 就像上述代码,还没等异步操作完成就已经执行 `return value` 了。 284 | 285 | 如果把 `test()` 改成 `async test()` 再结合 `await` 来进行异步操作呢? 286 | ```js 287 | async test() { 288 | let value = 1 289 | value = await new Promise(r => r(10)) 290 | return value 291 | } 292 | ``` 293 | 也不行,因为 `async()` 返回的是一个 `Promise`。 294 | 295 | 296 | ## vue-loader是什么?使用它的用途有哪些? 297 | vue-loader 是解析 .vue 文件的一个加载器,将 template/js/style 转换成 js 模块。 298 | 299 | 用途:js 可以写 es6、style 样式可以 scss 或 less;template 可以加 jade 等。 300 | 301 | 302 | ## 假设定义了一个数组a=[1,2,3],相应的,页面上显示的值为1,2,3,现设a[0]=5,页面上的值会变成5,2,3吗?为什么? 303 | 不会 304 | 305 | 因为 Vue 是使用 `Object.defineProperty` 来监听数值变化的,而直接修改数组的值的这种操作无法监听。 306 | 307 | 例如:`vm.items[indexOfItem] = newValue` 这种操作是无法监听的。 308 | 309 | 如果需要直接修改数组元素的值,可以使用 `Vue.set` 310 | ```js 311 | Vue.set(vm.items, indexOfItem, newValue) 312 | ``` 313 | 314 | [数组更新检测](https://cn.vuejs.org/v2/guide/list.html#%E6%95%B0%E7%BB%84%E6%9B%B4%E6%96%B0%E6%A3%80%E6%B5%8B) 315 | 316 | 317 | ## 父子组件生命周期执行顺序 318 | ```js 319 | // 渲染 320 | parent beforeCreate 321 | parent created 322 | parent beforeMount 323 | sub beforeCreate 324 | sub created 325 | sub beforeMount 326 | sub mounted 327 | parent mounted 328 | 329 | // 数据更新 330 | parent beforeUpdate 331 | sub beforeUpdate 332 | sub updated 333 | parent updated 334 | 335 | // 销毁组件 336 | parent beforeDestroy 337 | sub beforeDestroy 338 | sub destroyed 339 | parent destroyed 340 | ``` 341 | 注意 `mounted` 不会保证所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以在 `mounted` 内部使用 `vm.$nextTick`: 342 | ```js 343 | mounted: function () { 344 | this.$nextTick(function () { 345 | // Code that will run only after the 346 | // entire view has been rendered 347 | }) 348 | } 349 | ``` 350 | 351 | 352 | ## 实现虚拟 DOM 和 diff 算法 353 | * 一个 DOM 具有非常多的属性,DOM 进行增删操作开销大。所以使用虚拟 DOM 能有效降低消耗。 354 | 355 | [如何理解虚拟DOM?](https://www.zhihu.com/question/29504639) -------------------------------------------------------------------------------- /docs/webpack.md: -------------------------------------------------------------------------------- 1 | # Webpack 2 | 3 | ## webpack 和 rollup 的区别 4 | webpack 优点: 5 | 1. 通过loader处理各种各样的资源依赖 6 | 2. HMR模块热替换 7 | 3. 按需加载 8 | 4. 提取公共模块 9 | 10 | rollup 优点: 11 | 1. 编译出来的代码`可读性好` 12 | 2. rollup打包后生成的bundle内容十分`干净`,没有什么多余的代码。相比webpack(webpack打包后会生成__webpack_require__等runtime代码),rollup拥有无可比拟的性能优势,这是由依赖处理方式决定的,`编译时依赖处理(rollup)自然比运行时依赖处理(webpack)性能更好` 13 | 3. 对于ES模块依赖库,rollup会静态分析代码中的 import,并将排除任何未实际使用的代码 14 | 4. 支持程序流分析,能更加正确的判断项目本身的代码是否有副作用(配合tree-shaking) 15 | 5. 支持导出`es`模块文件(webpack不支持导出es模块) 16 | 17 | 参考资料: 18 | * [【第九期】Rollup:下一代ES模块打包工具](https://zhuanlan.zhihu.com/p/75717476) 19 | 20 | 21 | ## webpack 的 runtime 和 manifest 代码有什么用 22 | runtime:根据 manifest 数据来管理模块代码。主要是指模块交互时,连接模块所需的加载和解析逻辑。 23 | 包括:已经加载到浏览器中的连接模块逻辑,以及尚未加载模块的延迟加载逻辑。 24 | 25 | manifest:记录了在打包过程中,各个模块之间的信息及关联关系。 26 | 27 | 参考资料: 28 | * [manifest](https://webpack.docschina.org/concepts/manifest/) 29 | 30 | 31 | 32 | ## 怎么写一个 plugin 和 loader 33 | [实现一个 webpack loader 和 webpack plugin](https://github.com/woai3c/Front-end-articles/issues/6) 34 | 35 | 36 | 37 | ## webpack 能做哪些性能优化 38 | 1. 压缩代码 39 | 2. tree-shaking 40 | 3. 根据文件内容生成 hash 当作文件名,配合 CDN 做文件缓存 41 | 4. 分割代码,按需加载 42 | 5. 将第三方插件或公共代码单独提取出来打包 43 | 44 | 45 | 46 | ## webpack 热更新原理 47 | HMR 即 Hot Module Replacement是指当你对代码修改并保存后,webpack将会对代码进行重新打包,并将改动的模块发送到浏览器端。 48 | 49 | 浏览器用新的模块替换掉旧的模块,去实现局部更新页面而非整体刷新页面。 50 | 51 | ![](https://user-gold-cdn.xitu.io/2019/9/2/16cf203824359397?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 52 | 53 | 如上图所示,右侧Server端使用webpack-dev-server去启动本地服务,内部实现主要使用了webpack、express、websocket。 54 | 55 | - 使用express启动本地服务,当浏览器访问资源时对此做响应。 56 | - 服务端和客户端使用websocket实现长连接 57 | - webpack监听源文件的变化,即当开发者保存文件时触发webpack的重新编译。 58 | - 每次编译都会生成hash值、已改动模块的json文件、已改动模块代码的js文件 59 | - 编译完成后通过socket向客户端推送当前编译的hash戳 60 | - 客户端的websocket监听到有文件改动推送过来的hash戳,会和上一次对比 61 | - 一致则走缓存 62 | - 不一致则通过ajax和jsonp向服务端获取最新资源 63 | - 使用内存文件系统去替换有修改的内容实现局部刷新 64 | 65 | 66 | 参考资料: 67 | * [搞懂webpack热更新原理](https://juejin.im/post/6844903933157048333) 68 | 69 | 70 | 71 | ## webpack 模块加载原理 72 | [深入了解 webpack 模块加载原理](https://github.com/woai3c/Front-end-articles/issues/7) 73 | 74 | 其他参考资料 75 | * [code-splitting 代码切割](https://github.com/youngwind/blog/issues/100) 76 | * [loader 机制](https://github.com/youngwind/blog/issues/101) 77 | * [Webpack 源码解析](https://github.com/lihongxun945/diving-into-webpack) 78 | 79 | ## 如何提高构建速度 80 | 1. HappyPack开启多线程打包受限于 Node 是单线程运行的,所以 Webpack 在打包的过程中也是单线程的,特别是在执行 Loader 的时候,长时间编译的任务很多,这样就会导致等待的情况。HappyPack 可以将 Loader 的同步执行转换为并行的,这样就能充分利用系统资源来加快打包效率了。(或者使用 thread-loader) 81 | 82 | 2. terser-webpack-plugin 开启多线程压缩。 83 | 84 | ## 如何利用webpack来优化前端性能 85 | webpack 做性能优化主要是考虑打包体积和打包速度。 86 | 87 | 打包体积分析用 webpack-bundle-analyzer 插件,速度分析用:speed-measure-webpack-plugin 插件。 88 | 89 | [三十分钟掌握Webpack性能优化](https://juejin.cn/post/6844903651291447309) 90 | ### webpack 模块加载原理 91 | [深入了解 webpack 模块加载原理](https://juejin.cn/post/6872354325553741838) 92 | ### webpack的构建流程是什么 93 | [webpack构建流程分析](https://juejin.cn/post/6844904000169607175) 94 | ### webpack的分包策略 95 | [webpack的分包策略](https://panjiachen.github.io/awesome-bookmarks/blog/webpack/webpack4-b.html) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Front-end-basic-knowledge", 3 | "version": "1.0.0", 4 | "description": "有问题欢迎提 [issues](https://github.com/woai3c/Front-end-basic-knowledge/issues)\r * ### [JavaScript](https://github.com/woai3c/Front-end-basic-knowledge/blob/master/JavaSciprt.md)\r * ### [CSS](https://github.com/woai3c/Front-end-basic-knowledge/blob/master/CSS.md)\r * ### [HTML](https://github.com/woai3c/Front-end-basic-knowledge/blob/master/HTML.md)\r * ### [Vue](https://github.com/woai3c/Front-end-basic-knowledge/blob/master/vue.md)\r * ### [Webpack](https://github.com/woai3c/Front-end-basic-knowledge/blob/master/Webpack.md)\r * ### [Nodejs](Nodejs.md)\r * ### [HTTP](https://github.com/woai3c/Front-end-basic-knowledge/blob/master/HTTP.md)\r * ### [前端安全](https://github.com/woai3c/Front-end-basic-knowledge/blob/master/%E5%89%8D%E7%AB%AF%E5%AE%89%E5%85%A8.md)\r * ### [其他](https://github.com/woai3c/Front-end-basic-knowledge/blob/master/%E5%85%B6%E4%BB%96.md)", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "vuepress dev docs", 8 | "build": "vuepress build docs" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/woai3c/Front-end-basic-knowledge.git" 13 | }, 14 | "keywords": [], 15 | "author": "谭光志", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/woai3c/Front-end-basic-knowledge/issues" 19 | }, 20 | "homepage": "https://github.com/woai3c/Front-end-basic-knowledge#readme", 21 | "devDependencies": { 22 | "vuepress": "^1.8.2" 23 | } 24 | } 25 | --------------------------------------------------------------------------------