From e4953e00c1c5a5dff1be65009972a6bd1687f12d Mon Sep 17 00:00:00 2001 From: fuzhiqiang Date: Sun, 16 Jun 2024 15:42:17 +0800 Subject: [PATCH] deploy --- 404.html | 2 +- ...42\347\264\242\346\234\200\344\274\230\350\247\243.html" | 2 +- article/cms.html | 2 +- assets/front-end-engineering_node.md.dc95de0f.js | 1 - assets/front-end-engineering_node.md.dc95de0f.lean.js | 1 - assets/front-end-engineering_node.md.e4a62891.js | 1 + assets/front-end-engineering_node.md.e4a62891.lean.js | 1 + fe-utils/git.html | 2 +- "fe-utils/js\345\267\245\345\205\267\345\272\223.html" | 2 +- fe-utils/tool.html | 2 +- fragment/Monorepo.html | 2 +- fragment/api-no-repeat.html | 2 +- fragment/auto-try-catch.html | 2 +- fragment/babel-console.html | 2 +- fragment/const.html | 2 +- fragment/disable-debugger.html | 2 +- fragment/fetch-pause.html | 2 +- fragment/fetch.html | 2 +- fragment/forEach.html | 2 +- fragment/nextTick.html | 2 +- fragment/npm-scripts.html | 2 +- fragment/promise-cancel.html | 2 +- fragment/react-duplicate.html | 2 +- fragment/react-hooks-timer.html | 2 +- fragment/react-useState.html | 2 +- fragment/return-await.html | 2 +- fragment/setTimeout.html | 2 +- fragment/tree-shaking.html | 2 +- fragment/useRequest.html | 2 +- fragment/var-array.html | 2 +- fragment/video.html | 2 +- ...56\345\206\205\346\240\270\346\236\266\346\236\204.html" | 2 +- .../\346\216\245\345\217\243\350\256\276\350\256\241.html" | 2 +- "fragment/\346\262\231\347\233\222.html" | 2 +- "fragment/\351\273\221\347\231\275.html" | 2 +- ...45\244\204\347\220\206\345\231\250\344\271\213SCSS.html" | 2 +- .../CSS\345\267\245\347\250\213\345\214\226.html" | 2 +- front-end-engineering/PackageManager.html | 2 +- front-end-engineering/engineering-onepage.html | 2 +- front-end-engineering/jscompatibility.html | 2 +- front-end-engineering/modularization.html | 2 +- front-end-engineering/node.html | 6 +++--- front-end-engineering/performance.html | 2 +- "front-end-engineering/pnpm\345\216\237\347\220\206.html" | 2 +- front-end-engineering/theme.html | 2 +- front-end-engineering/webpack5-mf.html | 2 +- ...ck\345\270\270\347\224\250\346\213\223\345\261\225.html" | 2 +- ...15\347\253\257\345\267\245\347\250\213\345\214\226.html" | 2 +- .../\345\220\216\345\244\204\347\220\206\345\231\250.html" | 2 +- getting-started.html | 2 +- hashmap.json | 2 +- html-css/CSS.html | 2 +- html-css/HTML.html | 2 +- html-css/animation.html | 2 +- html-css/canvas-svg.html | 2 +- html-css/drag.html | 2 +- html-css/flex.html | 2 +- html-css/interview.html | 2 +- html-css/principle.html | 2 +- html-css/selector.html | 2 +- html-css/temop.html | 2 +- index.html | 2 +- .../\347\256\227\346\263\225\347\254\224\350\257\225.html" | 2 +- ...30\350\246\201\351\227\256\346\210\221\345\220\227.html" | 2 +- ...43\347\220\206\344\270\216\345\217\215\345\260\204.html" | 2 +- "js/\345\274\202\346\255\245\345\244\204\347\220\206.html" | 2 +- ...50\345\222\214\347\224\237\346\210\220\345\231\250.html" | 2 +- react/Fiber.html | 2 +- react/ReactRouter.html | 2 +- react/Redux.html | 2 +- react/component-communication.html | 2 +- react/context.html | 2 +- react/dva.html | 2 +- react/event.html | 2 +- react/hooks.html | 2 +- react/index.html | 2 +- react/lifecycle.html | 2 +- react/react-interview.html | 2 +- react/react-redux-router.html | 2 +- react/render.html | 2 +- react/transition.html | 2 +- react/umi.html | 2 +- react/utils.html | 2 +- ts/TypeScript-onePage.html | 2 +- vue/SSR.html | 2 +- vue/challages.html | 2 +- vue/component-communication.html | 2 +- vue/computed.html | 2 +- vue/diff.html | 2 +- vue/directive.html | 2 +- vue/interviewer.html | 2 +- vue/keep-alive-LRU.html | 2 +- vue/lifecycle.html | 2 +- vue/nextTick.html | 2 +- vue/reactive.html | 2 +- vue/slot.html | 2 +- vue/v-model.html | 2 +- vue/vdom.html | 2 +- vue/vs.html | 2 +- vue/vue-cli.html | 2 +- vue/vue-compile.html | 2 +- vue/vue-interview.html | 2 +- vue/vue-router.html | 2 +- vue/vue3-onepage.html | 2 +- vue/vuex.html | 2 +- 105 files changed, 105 insertions(+), 105 deletions(-) delete mode 100644 assets/front-end-engineering_node.md.dc95de0f.js delete mode 100644 assets/front-end-engineering_node.md.dc95de0f.lean.js create mode 100644 assets/front-end-engineering_node.md.e4a62891.js create mode 100644 assets/front-end-engineering_node.md.e4a62891.lean.js diff --git a/404.html b/404.html index 6fbf0d36..f3ac6410 100644 --- a/404.html +++ b/404.html @@ -12,7 +12,7 @@
Skip to content

404

PAGE NOT FOUND

But if you don't change your direction, and if you keep looking, you may end up where you are heading.
- + diff --git "a/algorithm/\360\237\224\245\345\210\267\351\242\230\344\271\213\346\216\242\347\264\242\346\234\200\344\274\230\350\247\243.html" "b/algorithm/\360\237\224\245\345\210\267\351\242\230\344\271\213\346\216\242\347\264\242\346\234\200\344\274\230\350\247\243.html" index b8dd1812..0014e511 100644 --- "a/algorithm/\360\237\224\245\345\210\267\351\242\230\344\271\213\346\216\242\347\264\242\346\234\200\344\274\230\350\247\243.html" +++ "b/algorithm/\360\237\224\245\345\210\267\351\242\230\344\271\213\346\216\242\347\264\242\346\234\200\344\274\230\350\247\243.html" @@ -87,7 +87,7 @@ }

总结:在解决问题的基础上,再进行优化。方法一使用二分复杂度为O(nlogn),使用窗口法就可以将复杂度将为O(n)。

- + diff --git a/article/cms.html b/article/cms.html index 738c3e18..bfb1142c 100644 --- a/article/cms.html +++ b/article/cms.html @@ -13,7 +13,7 @@
Skip to content
On this page

一站式-后台前端解决方案调研

TIP

搜索方式:github 搜名称

Hooks-Admin

react-admin

vue-element-admin

Antd Pro Vue - 背靠阿里,代码过硬,大型项目首选

Vue vben admin - 宝藏后台管理 基于 Vue3 UI清新 功能扎实

Naive Ui admin 适合小项目

vue3-antd-admin

gin-vue-admin

vue-pure-admin

vue3-composition-admin

vue-admin-perfect

gin-vue-admin

vue-vben-admin

Geeker-Admin

soybean-admin

vue-admin-box

vue-next-admin

vue-admin-better

v3-admin-vite

vue-manage-system

vue3-admin-plus

https://github.com/RainManGO/vue3-composition-admin

https://github.com/jzfai/vue3-admin-plus

https://github.com/tobe-fe-dalao/fast-vue3

https://github.com/ibwei/vue3-ts-base

https://github.com/jzfai/vue3-admin-ts

https://github.com/zouzhibin/vue-admin-perfect

- + diff --git a/assets/front-end-engineering_node.md.dc95de0f.js b/assets/front-end-engineering_node.md.dc95de0f.js deleted file mode 100644 index de09bd68..00000000 --- a/assets/front-end-engineering_node.md.dc95de0f.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o,c as a,a as d}from"./app.1ee4e414.js";const n="/blog/assets/2024-06-16-15-36-15.5742d5bf.png",u=JSON.parse('{"title":"Node 组成原理","description":"","frontmatter":{},"headers":[{"level":2,"title":"Node.js 的特点包括:","slug":"node-js-的特点包括","link":"#node-js-的特点包括","children":[]},{"level":2,"title":"Node.js 组成","slug":"node-js-组成","link":"#node-js-组成","children":[]},{"level":2,"title":"其他资料","slug":"其他资料","link":"#其他资料","children":[]}],"relativePath":"front-end-engineering/node.md","lastUpdated":1718523458000}'),i={name:"front-end-engineering/node.md"},s=d('

Node 组成原理

Node.js 是一个开源的、跨平台的 JavaScript 运行环境,依赖于 Google V8 引擎,用于构建高性能的网络应用程序。Node.js 采用事件驱动、非阻塞 I/O 模型,使得它能够处理大量并发连接,适用于构建实时应用、高吞吐量的后端服务和网络代理等。 Node.js 广泛应用于 Web 开发、服务器端开发、实时通信、大数据处理等领域,被许多大型互联网公司和开发者使用和推崇。

Node.js 的特点包括:

  1. 单线程和事件驱动:Node.js 采用单线程的事件循环模型,通过异步 I/O 和事件驱动处理并发请求,避免了传统多线程模型中的线程切换和资源开销,提高了性能和可扩展性。
  2. 跨平台:Node.js 可运行于多个操作系统平台,包括 Windows、Linux 和 Mac OS 等。
  3. 高性能:由于基于 V8 引擎和非阻塞 I/O 模型,Node.js 具有快速的执行速度和高吞吐量,适用于处理大量并发请求的场景。
  4. 模块化和包管理:Node.js 支持模块化开发,可以通过 npm(Node Package Manager)进行包的管理和发布,方便了代码的组织和复用。
  5. 强大的社区支持:Node.js 拥有庞大的开发者社区,提供了丰富的第三方模块和工具,方便开发者进行开发和调试。

Node.js 组成

  1. 本地模块:Node.js 内置了一些核心模块,这些模块提供了基础的功能,如文件操作(fs 模块)、网络通信(http 模块)、加密(crypto 模块)、操作系统信息(os 模块)等。这些模块可以直接通过 require 函数进行引入使用。
  2. 内置模块:Node.js 有一个丰富的第三方模块生态系统,开发者可以通过 NPM 安装这些模块,并在自己的项目中引入使用。
  3. libuv:libuv 是一个跨平台的异步 I/O 库,它为 Node.js 提供了非阻塞的事件驱动的 I/O 操作。它可以处理文件系统操作、网络请求、定时器等等,在 Node.js 中用于处理事件循环。
  4. os api:将 Node.js 可运行于多个操作系统平台,包括 Windows、Linux 和 Mac OS 等。
  5. V8 引擎:Node.js 使用了 Google 开发的 V8 引擎作为其 JavaScript 执行引擎。V8 引擎可以将 JavaScript 代码直接转化为机器码,以提供高性能的执行效率。

其他资料

https://github.com/theanarkh/understand-nodejs

',9),t=[s];function r(l,h,c,_,p,j){return o(),a("div",null,t)}const g=e(i,[["render",r]]);export{u as __pageData,g as default}; diff --git a/assets/front-end-engineering_node.md.dc95de0f.lean.js b/assets/front-end-engineering_node.md.dc95de0f.lean.js deleted file mode 100644 index e06cea57..00000000 --- a/assets/front-end-engineering_node.md.dc95de0f.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o,c as a,a as d}from"./app.1ee4e414.js";const n="/blog/assets/2024-06-16-15-36-15.5742d5bf.png",u=JSON.parse('{"title":"Node 组成原理","description":"","frontmatter":{},"headers":[{"level":2,"title":"Node.js 的特点包括:","slug":"node-js-的特点包括","link":"#node-js-的特点包括","children":[]},{"level":2,"title":"Node.js 组成","slug":"node-js-组成","link":"#node-js-组成","children":[]},{"level":2,"title":"其他资料","slug":"其他资料","link":"#其他资料","children":[]}],"relativePath":"front-end-engineering/node.md","lastUpdated":1718523458000}'),i={name:"front-end-engineering/node.md"},s=d("",9),t=[s];function r(l,h,c,_,p,j){return o(),a("div",null,t)}const g=e(i,[["render",r]]);export{u as __pageData,g as default}; diff --git a/assets/front-end-engineering_node.md.e4a62891.js b/assets/front-end-engineering_node.md.e4a62891.js new file mode 100644 index 00000000..f929757c --- /dev/null +++ b/assets/front-end-engineering_node.md.e4a62891.js @@ -0,0 +1 @@ +import{_ as e,o,c as d,a}from"./app.1ee4e414.js";const i="/blog/assets/2024-06-16-15-36-15.5742d5bf.png",g=JSON.parse('{"title":"Node 组成原理","description":"","frontmatter":{},"headers":[{"level":2,"title":"Node.js 的特点包括:","slug":"node-js-的特点包括","link":"#node-js-的特点包括","children":[]},{"level":2,"title":"Node.js 组成","slug":"node-js-组成","link":"#node-js-组成","children":[]},{"level":2,"title":"其他资料","slug":"其他资料","link":"#其他资料","children":[]}],"relativePath":"front-end-engineering/node.md","lastUpdated":1718523698000}'),n={name:"front-end-engineering/node.md"},s=a('

Node 组成原理

Node.js 是一个开源的、跨平台的 JavaScript 运行环境,依赖于 Google V8 引擎,用于构建高性能的网络应用程序。Node.js 采用事件驱动、非阻塞 I/O 模型,使得它能够处理大量并发连接,适用于构建实时应用、高吞吐量的后端服务和网络代理等。

Node.js 广泛应用于 Web 开发、服务器端开发、实时通信、大数据处理等领域,被许多大型互联网公司和开发者使用和推崇。

Node.js 的特点包括:

  1. 单线程和事件驱动:Node.js 采用单线程的事件循环模型,通过异步 I/O 和事件驱动处理并发请求,避免了传统多线程模型中的线程切换和资源开销,提高了性能和可扩展性。
  2. 跨平台:Node.js 可运行于多个操作系统平台,包括 Windows、Linux 和 Mac OS 等。
  3. 高性能:由于基于 V8 引擎和非阻塞 I/O 模型,Node.js 具有快速的执行速度和高吞吐量,适用于处理大量并发请求的场景。
  4. 模块化和包管理:Node.js 支持模块化开发,可以通过 npm(Node Package Manager)进行包的管理和发布,方便了代码的组织和复用。
  5. 强大的社区支持:Node.js 拥有庞大的开发者社区,提供了丰富的第三方模块和工具,方便开发者进行开发和调试。

Node.js 组成

  1. 用户代码:JS 代码,开发者编写的
  2. 第三方库:大部分仍然是 JS 代码,由其他开发者编写
  3. 本地模块:Node.js 内置了一些核心模块,这些模块提供了基础的功能,如文件操作(fs 模块)、网络通信(http 模块)、加密(crypto 模块)、操作系统信息(os 模块)等。这些模块可以直接通过 require 函数进行引入使用。
  4. 内置模块:Node.js 有一个丰富的第三方模块生态系统,开发者可以通过 NPM 安装这些模块,并在自己的项目中引入使用。
  5. libuv:libuv 是一个跨平台的异步 I/O 库,它为 Node.js 提供了非阻塞的事件驱动的 I/O 操作。它可以处理文件系统操作、网络请求、定时器等等,在 Node.js 中用于处理事件循环。
  6. os api:将 Node.js 可运行于多个操作系统平台,包括 Windows、Linux 和 Mac OS 等。
  7. V8 引擎:Node.js 使用了 Google 开发的 V8 引擎作为其 JavaScript 执行引擎。V8 引擎可以将 JavaScript 代码直接转化为机器码,以提供高性能的执行效率。(c/c++代码,作用:把 JS 代码解释成为机器码。可以通过 v8 引擎的某种机制,扩展其功能。V8 引擎的扩展和对扩展的编译,是通过一个工具:gyp 工具。某些第三方库需要使用 node-gyp 工具进行构建,因此需要先安装 node-gyp)

其他资料

https://github.com/theanarkh/understand-nodejs

',10),t=[s];function l(r,c,h,p,_,j){return o(),d("div",null,t)}const u=e(n,[["render",l]]);export{g as __pageData,u as default}; diff --git a/assets/front-end-engineering_node.md.e4a62891.lean.js b/assets/front-end-engineering_node.md.e4a62891.lean.js new file mode 100644 index 00000000..f6f0c1d4 --- /dev/null +++ b/assets/front-end-engineering_node.md.e4a62891.lean.js @@ -0,0 +1 @@ +import{_ as e,o,c as d,a}from"./app.1ee4e414.js";const i="/blog/assets/2024-06-16-15-36-15.5742d5bf.png",g=JSON.parse('{"title":"Node 组成原理","description":"","frontmatter":{},"headers":[{"level":2,"title":"Node.js 的特点包括:","slug":"node-js-的特点包括","link":"#node-js-的特点包括","children":[]},{"level":2,"title":"Node.js 组成","slug":"node-js-组成","link":"#node-js-组成","children":[]},{"level":2,"title":"其他资料","slug":"其他资料","link":"#其他资料","children":[]}],"relativePath":"front-end-engineering/node.md","lastUpdated":1718523698000}'),n={name:"front-end-engineering/node.md"},s=a("",10),t=[s];function l(r,c,h,p,_,j){return o(),d("div",null,t)}const u=e(n,[["render",l]]);export{g as __pageData,u as default}; diff --git a/fe-utils/git.html b/fe-utils/git.html index a06cc36f..be96b935 100644 --- a/fe-utils/git.html +++ b/fe-utils/git.html @@ -198,7 +198,7 @@ 7. git push origin first 切回主分支:git checkout master

参考资料

local(每一个人的计算机上面也有一个项目仓库)

配置用户名

git config --global user.name 你的名字

配置邮箱

git config --global user.emial 你的邮箱

查看配置

- + diff --git "a/fe-utils/js\345\267\245\345\205\267\345\272\223.html" "b/fe-utils/js\345\267\245\345\205\267\345\272\223.html" index 71f5eb03..087192a5 100644 --- "a/fe-utils/js\345\267\245\345\205\267\345\272\223.html" +++ "b/fe-utils/js\345\267\245\345\205\267\345\272\223.html" @@ -291,7 +291,7 @@ 例如: 2020-08-27T08:01:44.000Z

GMT、UTC、ISO 8601都表示的是零时区的时间

Unix 时间戳

Unix 时间戳(Unix Timestamp)是Unix系统最早提出的概念

它将UTC时间1970年1月1日凌晨作为起始时间,到指定时间经过的秒数(毫秒数)

程序中的时间处理

程序对时间的计算、存储务必使用UTC时间,或者时间戳

在和用户交互时,将UTC时间或时间戳转换为更加友好的文本

思考下面的问题:

  1. 用户的生日是本地时间还是UTC时间?
  2. 如果要比较两个日期的大小,是比较本地时间还是比较UTC时间?
  3. 如果要显示文章的发布日期,是显示本地时间还是显示UTC时间?
  4. 北京时间2020-8-28 10:00:00格林威治2020-8-28 02:00:00,两个时间哪个大,哪个小?
  5. 北京的时间戳为0格林威治的时间戳为0,它们的时间一样吗?
  6. 一个中国用户注册时填写的生日是1970-1-1,它出生的UTC时间是多少?时间戳是多少?

Moment的核心用法

Moment的使用分为两个部分:

  1. 获得Moment对象
  2. 针对Moment对象做各种操作
- + diff --git a/fe-utils/tool.html b/fe-utils/tool.html index 701cc3e8..5cbbf988 100644 --- a/fe-utils/tool.html +++ b/fe-utils/tool.html @@ -13,7 +13,7 @@
Skip to content
On this page

涌现出来的新tools

apifox:一款国产的 API 管理神器

集成了 Postman + Swagger + Mock + JMeter 众多功能

可以让生成 api 文档、api调试、api Mock、api 自动化测试 变的十分高效,简单。

eolink | 国内第一个集Swagger+Postman+Mock+Jmeter单点工具于一身的 api 管理平台 | 以 api 为中心的前后端开发流程

- + diff --git a/fragment/Monorepo.html b/fragment/Monorepo.html index e019b4b0..084cadff 100644 --- a/fragment/Monorepo.html +++ b/fragment/Monorepo.html @@ -13,7 +13,7 @@
Skip to content
On this page

monorepo

npm 的 install 流程

package-lock.json

package-lock.json 的作用是进行锁版本号,保证整个开发团队的版本号统一,使用 monorepo 的项目有可能会提到一个最外层进行一个管理。

  • 为什么需要这个 package-lock.json package.json 的 semantic versioning(语意化版本控制),在不同的时间会安装不同的版本,如果没有 package-lock.json,不同的开发者可能就会得到不同版本的依赖,如果因为这个出现了一个 bug 的话,那排查起来也许会非常困难。
  • 更新规则 Npm v 5.4.2 以上:当 package.json 声明的版本依赖规范和 package-lock.json 安装版本兼容,则根据 package-lock json 安装依赖:如果两者不兼容,那么按照 package.json 安装依赖,并更新 package- lock.json 跟随 package.json 的语意化版本控制来进行更新,如果 package.json 中的依赖 a 的版本是^1.0.0,在这个时候如果 package-lock.json 中的版本锁定为 1.12.1 就是符合要求的不必重写,但如果是 0.12.11 即第一个数字变了,那就需要重写 package-lock.json 了。
  • 版本规则
    • ^: 只会执行不更改最左边非零数字的更新。 如果写入的是 ^0.13.0,则当运行 npm update 时,可以更新到 0.13.1、0.13.2 等,但不能更新到 0.14.0 或更高版本。 如果写入的是 ^1.13.0,则当运行 npm update 时,可以更新到 1.13.1、1.14.0 等,但不能更新到 2.0.0 或更高版本。
    • ~: 如果写入的是 〜0.13.0,则当运行 npm update 时,会更新到补丁版本:即 0.13.1 可以,但 0.14.0 不可以。
    • : 接受高于指定版本的任何版本。

    • =: 接受等于或高于指定版本的任何版本。

    • =: 接受确切的版本。
    • -: 接受一定范围的版本。例如:2.1.0 - 2.6.2。
    • ||: 组合集合。例如 < 2.1 || > 2.6。

npm 和 yarn

缺点。下面将两者进行比较

  1. 性能

每当 Yarn 或 npm 需要安装包时,它们都会执行一系列任务。在 npm 中,这些任务是按包顺序执行的,这意味着它会等待一个包完全安装,然后再继续下一个。相比之下,Yarn 并行执行这些任务,从而提高了性能。 虽然这两个管理器都提供缓存机制,但 Yarn 似乎做得更好一些。 尽管 Yarn 有一些优势,但 Yarn 和 npm 在它们的最新版本中的速度相当。所以我们不能评判孰优孰劣。

  1. 依赖版本

早期的时候 yarn 有 yarn.lock 来锁定版本,这一点上比 package.json 要强很多,而后面 npm 也推出了 package-lock.json,所以这一点上已经没太多差异了。

  1. 安全性

从版本 6 开始,npm 会在安装过程中审核软件包并告诉您是否发现了任何漏洞。我们可以通过 npm audit 针对已安装的软件包运行来手动执行此检查。如果发现任何漏洞,npm 会给我们安全建议。 Yarn 和 npm 都使用加密哈希算法来确保包的完整性。

  1. 工作区

工作区允许您拥有一个 monorepo 来管理跨多个项目的依赖项。这意味着您有一个单一的顶级根包,其中包含多个称为工作区的子包。

  1. 用哪个?

目前 2021 年,yarn 的安装速度还是比 npm 快,其他地方的差异并不大,基本上可以忽略,用哪个都行。

monorepo

monorepo 方案的优势

  1. 代码重用将变得非常容易:由于所有的项目代码都集中于一个代码仓库,我们将很容易抽离出各个项目共用的业务组件或工具,并通过 TypeScript,Lerna 或其他工具进行代码内引用;
  2. 依赖管理将变得非常简单:同理,由于项目之间的引用路径内化在同一个仓库之中,我们很容易追踪当某个项目的代码修改后,会影响到其他哪些项目。通过使用一些工具,我们将很容易地做到版本依赖管理和版本号自动升级;
  3. 代码重构将变得非常便捷:想想究竟是什么在阻止您进行代码重构,很多时候,原因来自于「不确定性」,您不确定对某个项目的修改是否对于其他项目而言是「致命的」,出于对未知的恐惧,您会倾向于不重构代码,这将导致整个项目代码的腐烂度会以惊人的速度增长。而在 monorepo 策略的指导下,您能够明确知道您的代码的影响范围,并且能够对被影响的项目可以进行统一的测试,这会鼓励您不断优化代码;
  4. 它倡导了一种开放,透明,共享的组织文化,这有利于开发者成长,代码质量的提升:在 monorepo 策略下,每个开发者都被鼓励去查看,修改他人的代码(只要有必要),同时,也会激起开发者维护代码,和编写单元测试的责任心(毕竟朋友来访之前,我们从不介意自己的房子究竟有多乱),这将会形成一种良性的技术氛围,从而保障整个组织的代码质量。

monorepo 方案的劣势

  1. 项目粒度的权限管理变得非常复杂:无论是 Git 还是其他 VCS 系统,在支持 monorepo 策略中项目粒度的权限管理上都没有令人满意的方案,这意味着 A 部门的 a 项目若是不想被 B 部门的开发者看到就很难了。(好在我们可以将 monorepo 策略实践在「项目级」这个层次上,这才是我们这篇文章的主题,我们后面会再次明确它);
  2. 新员工的学习成本变高:不同于一个项目一个代码仓库这种模式下,组织新人只要熟悉特定代码仓库下的代码逻辑,在 monorepo 策略下,新人可能不得不花更多精力来理清各个代码仓库之间的相互逻辑,当然这个成本可以通过新人文档的方式来解决,但维护文档的新鲜又需要消耗额外的人力;
  3. 对于公司级别的 monorepo 策略而言,需要专门的 VFS 系统,自动重构工具的支持:设想一下 Google 这样的企业是如何将十亿行的代码存储在一个仓库之中的?开发人员每次拉取代码需要等待多久?各个项目代码之间又如何实现权限管理,敏捷发布?任何简单的策略乘以足够的规模量级都会产生一个奇迹(不管是好是坏),对于中小企业而言,如果没有像 Google,Facebook 这样雄厚的人力资源,把所有项目代码放在同一个仓库里这个美好的愿望就只能是个空中楼阁。

如何取舍?

没错,软件开发领域从来没有「银弹」。monorepo 策略也并不完美,并且,我在实践中发现,要想完美在组织中运用 monorepo 策略,所需要的不仅是出色的编程技巧和耐心。团队日程,组织文化和个人影响力相互碰撞的最终结果才决定了想法最终是否能被实现。

但是请别灰心的太早,因为虽然让组织作出改变,统一施行 monorepo 策略困难重重,但这却并不意味着我们需要彻底跟 monorepo 策略说再见。我们还可以把 monorepo 策略实践在「项目」这个级别,即从逻辑上确定项目与项目之间的关联性,然后把相关联的项目整合在同一个仓库下,通常情况下,我们不会有太多相互关联的项目,这意味着我们能够免费得到 monorepo 策略的所有好处,并且可以拒绝支付大型 monorepo 架构的利息。

- + diff --git a/fragment/api-no-repeat.html b/fragment/api-no-repeat.html index 4aa9def0..1d3ea6e2 100644 --- a/fragment/api-no-repeat.html +++ b/fragment/api-no-repeat.html @@ -309,7 +309,7 @@ return Object.prototype.toString.call(config.data) === "[object FormData]"; }

demo

- + diff --git a/fragment/auto-try-catch.html b/fragment/auto-try-catch.html index db84f892..db17443d 100644 --- a/fragment/auto-try-catch.html +++ b/fragment/auto-try-catch.html @@ -891,7 +891,7 @@ toArray, };

本文章作者的 github 仓库

- + diff --git a/fragment/babel-console.html b/fragment/babel-console.html index 3fcf296e..9fb1e83a 100644 --- a/fragment/babel-console.html +++ b/fragment/babel-console.html @@ -543,7 +543,7 @@ }; }; - + diff --git a/fragment/const.html b/fragment/const.html index d392c089..21b2e760 100644 --- a/fragment/const.html +++ b/fragment/const.html @@ -75,7 +75,7 @@ } a = 20; // 报错 - + diff --git a/fragment/disable-debugger.html b/fragment/disable-debugger.html index a103a823..038ca942 100644 --- a/fragment/disable-debugger.html +++ b/fragment/disable-debugger.html @@ -179,7 +179,7 @@ } catch (err) {} })(); - + diff --git a/fragment/fetch-pause.html b/fragment/fetch-pause.html index 3e50f8ea..1c7e87b7 100644 --- a/fragment/fetch-pause.html +++ b/fragment/fetch-pause.html @@ -147,7 +147,7 @@ result.resume(); }, 4000);

执行原理

流程设计上是这样,设计一个控制器,发起请求并请求返回后,判断控制器的状态,若控制器不处于 “暂停” 状态时,正常返回数据;当控制器处于 “暂停状态” 时,控制器将置为 “一旦调用恢复方法就返回数据” 的状态。 代码中是利用了 Promise.all 捆绑一个控制器 Promise ,如果控制器处于暂停状态下,则不释放 Promise.all ,再将对应的 pause 方法和 resume 方法暴露出去给外界使用。

补充

有些同学错误的认为网络请求和响应是绝对不可以暂停的,我特意在文章前面提到了有关数据传输的内容,并且挂了一句“理论上应用层的协议可以通过类似于标记数据包序列号等等一系列手段来实现暂停机制”,这句话的意思是,如果你魔改 HTTP 或者自己设计实现一个应用层协议(例如像 socket、vmess 这些协议),只要双端支持该协议,是可以实现请求暂停或者响应暂停的,而且这不会影响到 TCP 连接,但是实现暂停机制需要对各种场景和 TCP 策略兜底才能有较好的可靠性。 例如,提供一类控制报文用于控制传输暂停,首先需要对所有数据包的序列号标记顺序,当需要暂停时,发送该序列号的暂停报文给接收端,接收端收到暂停报文就将已接收数据包的块标记返回给发送端等等(这和分片上传机制一样)。

- + diff --git a/fragment/fetch.html b/fragment/fetch.html index f657cd5e..7c2a0fe4 100644 --- a/fragment/fetch.html +++ b/fragment/fetch.html @@ -109,7 +109,7 @@ }; }

说实话,我对取消 Fetch 的方法并不感到兴奋。在理想的世界中,通过 Fetch 返回的 Promise 中的 .cancel() 会很酷,但是也会带来一些问题。无论如何,我为能够取消 Fetch 调用而感到高兴,你也应该如此!

[译] 如何取消你的 Promise?

- + diff --git a/fragment/forEach.html b/fragment/forEach.html index 0ed67854..8fe5381d 100644 --- a/fragment/forEach.html +++ b/fragment/forEach.html @@ -95,7 +95,7 @@ Array.prototype.every(); Array.prototype.some();

如何根据不同的业务场景,选择使用对应的工具函数来更有效地处理业务逻辑,才是我们真正应该思考的,或许这也是面试当中真正想考察的吧。

- + diff --git a/fragment/nextTick.html b/fragment/nextTick.html index 17b30ff1..4485fd73 100644 --- a/fragment/nextTick.html +++ b/fragment/nextTick.html @@ -259,7 +259,7 @@ // 否则就可能出现一直循环的情况, // 所以需要将 callbacks 复制一份出来然后清空,再遍历备份列表执行回调 - + diff --git a/fragment/npm-scripts.html b/fragment/npm-scripts.html index 79141c99..e93ec455 100644 --- a/fragment/npm-scripts.html +++ b/fragment/npm-scripts.html @@ -31,7 +31,7 @@ "dev": "./node_modules/.bin/nodemon bin/www" }

当执行 npm run dev 时,相当于执行 "./node_modules/.bin/nodemon bin/www",其中 bin/www 作为参数传入。

总之:先去 package.json 的 scripts 属性中查找 xxx1,再去./node_modules/.bin 中查找 xxx1 对应的 sss1,执行 sss1 文件)

问题分析

TIP

问题 1: 为什么不直接执行 sss 而要执行 xxx 呢?

直接执行 sss 会报错,因为操作系统中不存在 sss 这一条指令。

TIP

问题 2: 为什么执行 npm run xxx(或者 yarn xxx)时就能成功呢?

因为我们在安装依赖的时候,是通过 npm i xxx (或者 yarn ...)来执行的,例如 npm i @vue/cli-service,npm 在 安装这个依赖的时候,就会 node_modules/.bin/ 目录中创建好名为 vue-cli-service 的几个可执行文件了。

.bin 目录下的文件,是一个个的软链接,打开文件可以看到文件顶部写着 #!/bin/sh ,表示这是一个脚本,可由 node 来执行。当使用 npm run xxx 执行 sss 时,虽然没有安装 sss 的全局命令,但是 npm 会到 ./node_modules/.bin 中找到 sss 文件作为脚本来执行,则相当于执行了 ./node_modules/.bin/sss。

即: 运行 npm run xxx 的时候,npm 会先在当前目录的 node_modules/.bin 查找要执行的程序,如果找到则运行;没有找到则从全局的 node_modules/.bin 中查找; 全局目录还是没找到,就会从 window 的 path 环境变量中查找有没有其他同名的可执行程序。

浅析 npm install 机制阅读

- + diff --git a/fragment/promise-cancel.html b/fragment/promise-cancel.html index 51f0b142..7a372816 100644 --- a/fragment/promise-cancel.html +++ b/fragment/promise-cancel.html @@ -365,7 +365,7 @@ p.notify((x) => console.log(x)); p.then((res) => console.log("Get All Data:", res));

End

关于取消功能在红宝书上 TC39 委员会也曾准备增加这个特性,但相关提案最终被撤回了。结果 ES6 Promise 被认为是“激进的”:只要 Promise 的逻辑开始执行,就没有办法阻止它执行到完成。

实际上我们学了这么久的 Promise 也默认了这一点,因此这个取消功能反而就不太符合常理,而且十分鸡肋。比如说我们有使用 then 回调接收数据,但因为你点击了取消按钮造成 then 回调不执行,我们知道 Promise 支持链式调用,那如果还有后续操作都将会被中断,这种中断行为 debug 时也十分痛苦,更何况最麻烦的一点是你还需要传入一个 delay 来表示取消的期限,而这个期限到底要设置多少才合适呢...

- + diff --git a/fragment/react-duplicate.html b/fragment/react-duplicate.html index 249dc6a3..d3d204ad 100644 --- a/fragment/react-duplicate.html +++ b/fragment/react-duplicate.html @@ -37,7 +37,7 @@ }, }; - + diff --git a/fragment/react-hooks-timer.html b/fragment/react-hooks-timer.html index a158202b..4789685b 100644 --- a/fragment/react-hooks-timer.html +++ b/fragment/react-hooks-timer.html @@ -223,7 +223,7 @@ export default CountDown;

错误示例会出现的问题

原因

解决方案

如上示例 1 , 2 ,因为方式 1 打破了 React 纯函数的规则,所以更加建议方式 2

TIP

第一段代码使用了普通的变量 cd 和 timer,它们被定义在组件函数的顶层。这意味着每次组件函数被调用时,都会重新创建新的 cd 和 timer,而不是保留它们的状态。这样做违反了 React 纯函数组件的规则,因为它引入了外部的状态管理。

第二段代码使用了 useRef 来创建了 cd 和 timer 这两个变量的引用。这意味着它们会被保存在组件的生命周期之外,并且在组件的多次渲染之间保持不变。因此,即使组件函数被重新调用,这些引用的值也会保持不变。这样做遵守了 React 纯函数组件的规则,因为它不引入外部状态管理。

因此,第二段代码符合 React 纯函数组件的规则,而第一段代码打破了这些规则。

【注意】

- + diff --git a/fragment/react-useState.html b/fragment/react-useState.html index 0775796a..8243a77f 100644 --- a/fragment/react-useState.html +++ b/fragment/react-useState.html @@ -367,7 +367,7 @@

首先,在 useSyncCallback 中创建一个标示 proxyState,初始的时候会把 proxyState 的 current 值赋成 false,在 callback 执行之前会先判断 current 是否为 true,如果为 true 就允许 callback 执行,若果为 false,就跳过不执行,因为 useEffect 在组件 render 之后,只要依赖项有变化就会执行,所以我们无法掌控我们的函数执行,在 useSyncCallback 中创建一个新的函数 Func,并返回,通过这个 Func 来模拟函数调用,

js
const [proxyState, setProxyState] = useState({ current: false });
 
const [proxyState, setProxyState] = useState({ current: false });
 

在这个函数中我们要做的就是变更 prxoyState 的 current 值为 true,来使得让 callback 被调用的条件成立,同时触发 react 组件 render 这样内部的 useEffect 就会执行,随后调用 callback 实现我们想要的效果。

- + diff --git a/fragment/return-await.html b/fragment/return-await.html index 8744308e..679b75d8 100644 --- a/fragment/return-await.html +++ b/fragment/return-await.html @@ -97,7 +97,7 @@ foo2(); // undefined

如果想要在调试的堆栈中得到 bar() 抛出的错误信息,那么此时应该使用 return await 。

- + diff --git a/fragment/setTimeout.html b/fragment/setTimeout.html index 925e2405..a6a5907a 100644 --- a/fragment/setTimeout.html +++ b/fragment/setTimeout.html @@ -105,7 +105,7 @@ }, speed); } - + diff --git a/fragment/tree-shaking.html b/fragment/tree-shaking.html index cc5b8820..e26dc411 100644 --- a/fragment/tree-shaking.html +++ b/fragment/tree-shaking.html @@ -13,7 +13,7 @@
Skip to content
- + diff --git a/fragment/useRequest.html b/fragment/useRequest.html index cf4921b0..5776c859 100644 --- a/fragment/useRequest.html +++ b/fragment/useRequest.html @@ -265,7 +265,7 @@ }, [config, ready]); }; - + diff --git a/fragment/var-array.html b/fragment/var-array.html index 30346e2b..1a04b68c 100644 --- a/fragment/var-array.html +++ b/fragment/var-array.html @@ -69,7 +69,7 @@ return Object.values(this)[Symbol.iterator](); };

这段代码是将 Object.prototype 上的 [Symbol.iterator] 方法重新定义为一个新的函数。新的函数通过调用 Object.values(this) 方法获取对象的所有值,并返回这些值的迭代器对象。 通过这个代码,我们可以使得任何 JavaScript 对象都具有了迭代能力。例如,对于一个对象 obj ,我们可以直接使用 for...of 循环或者 ... 操作符来遍历它的所有值。

- + diff --git a/fragment/video.html b/fragment/video.html index d989a42b..6ae2f601 100644 --- a/fragment/video.html +++ b/fragment/video.html @@ -527,7 +527,7 @@ }); }

以上就是回放的关键流程实现代码, rrweb 中不仅仅是做了这些,还包含数据压缩,移动端处理,隐私问题等等细节处理,有兴趣可自行查看源码。

- + diff --git "a/fragment/\345\276\256\345\206\205\346\240\270\346\236\266\346\236\204.html" "b/fragment/\345\276\256\345\206\205\346\240\270\346\236\266\346\236\204.html" index 0f80f784..acb6c4f4 100644 --- "a/fragment/\345\276\256\345\206\205\346\240\270\346\236\266\346\236\204.html" +++ "b/fragment/\345\276\256\345\206\205\346\240\270\346\236\266\346\236\204.html" @@ -285,7 +285,7 @@ install(app, options) {}, };

类似 jQuery、window 从某种角度上来说也可以看做是内核,通过 $.fn() 或者在 prototype 中扩展方法也是一种插件的形式。

小结

最后我们再来巩固一下这篇文章的核心思想,微内核架构主要由两部分组成: Core + Plugin,其中: Core 需要具备的能力有:

🔗 原文链接: https://juejin.cn/post/716307803160...

- + diff --git "a/fragment/\346\216\245\345\217\243\350\256\276\350\256\241.html" "b/fragment/\346\216\245\345\217\243\350\256\276\350\256\241.html" index 8db7c163..3218056f 100644 --- "a/fragment/\346\216\245\345\217\243\350\256\276\350\256\241.html" +++ "b/fragment/\346\216\245\345\217\243\350\256\276\350\256\241.html" @@ -13,7 +13,7 @@
Skip to content
On this page

接口设计

防止用户伪造请求:如果接口允许用户直接携带 ID,很容易被恶意用户利用,尝试修改其他用户的信息。使用 cookie(尤其是带有认证信息的 cookie)可以确保请求来自于正确的用户,并且认证信息通常在服务器端进行验证,增加了安全性。

- + diff --git "a/fragment/\346\262\231\347\233\222.html" "b/fragment/\346\262\231\347\233\222.html" index bc961e4d..81b41ada 100644 --- "a/fragment/\346\262\231\347\233\222.html" +++ "b/fragment/\346\262\231\347\233\222.html" @@ -463,7 +463,7 @@ let img = new Image(); img.src = "http://www.test.com/img.gif";

黑名单中添加'Image'字段,堵上这个漏洞

- + diff --git "a/fragment/\351\273\221\347\231\275.html" "b/fragment/\351\273\221\347\231\275.html" index f1906aa1..fdd6370f 100644 --- "a/fragment/\351\273\221\347\231\275.html" +++ "b/fragment/\351\273\221\347\231\275.html" @@ -49,7 +49,7 @@ -webkit-filter: grayscale(0.95); }

filter 样式加到 html 还是 body 上

把 filter 样式加到了 <body> 元素上。通常这没有问题。

但如果你的网页内有「绝对和固定定位」元素,一定要把 filter 样式加到 <html> 上。 原因见: drafts.fxtf.org/filter-effe…

引用:

A value other than none for the filter property results in the creation of a >containing block for absolute and fixed positioned descendants unless the element it > applies to is a document root element in the current browsing context.

翻译:

若 filter 属性的值不是 none,会给「绝对和固定定位的后代」创建一个 containing block

除非 filter 对应的元素是「当前浏览上下文中的文档根元素」(即 <html> )。

因此,兼容性最好的方法是把 filter 样式加到 <html> 上。这样不会影响「绝对和固定定位的后代」。 这里小程序有个坑,如果你的页面代码有「绝对和固定定位的后代」,就不能把 filter 样式 加到 <page> 上,而是要找个元素,这个元素没有「绝对和固定定位的后代」,你可以把 filter 样式加到这个元素上。

- + diff --git "a/front-end-engineering/CSS \351\242\204\345\244\204\347\220\206\345\231\250\344\271\213SCSS.html" "b/front-end-engineering/CSS \351\242\204\345\244\204\347\220\206\345\231\250\344\271\213SCSS.html" index 527b6055..5438a31d 100644 --- "a/front-end-engineering/CSS \351\242\204\345\244\204\347\220\206\345\231\250\344\271\213SCSS.html" +++ "b/front-end-engineering/CSS \351\242\204\345\244\204\347\220\206\345\231\250\344\271\213SCSS.html" @@ -2981,7 +2981,7 @@ .....; }
  1. 多使用混合指令:混合指令可以将公共的部分抽离出来,提高了代码的复用性。但是要清楚混合指令和 @extend 之间的区别,具体使用哪一个,取决于你写项目时的具体场景,不是说某一个就比另一个绝对的好。
  2. 使用函数:可以编写自定义函数来处理一些复杂的计算和操作。而且 Sass 还提供了很多非常好用的内置函数。
  3. 遵循常见的 Sass 编码规范:

Sass 未来发展

我们如果想要获取到 Sass 的最新动向,通常可以去 Sass 的社区看一下。

注意:一门成熟的技术,是一定会有对应社区的。理论上来讲,社区的形式是不限的,但是通常是以论坛的形式存在的,大家可以在论坛社区自由的讨论这门技术相关的话题。

社区往往包含了这门技术最新的动态,甚至有一些优秀的技术解决方案是先来自于社区,之后才慢慢成为正式的标准语法的。

目前市面上又很多 CSS 库都是基于 Sass 来进行构建了,例如:

  1. Compass - 老牌 Sass 框架,提供大量 Sass mixins 和函数,方便开发。
  2. Bourbon - 轻量级的 Sass mixin 库,提供常用的 mixins,简化 CSS 开发。
  3. Neat - 构建具有响应式网格布局的网站,基于 SassBourbon,容易上手。
  4. Materialize - 实现 Material Design 风格,基于 Sass 构建,提供丰富组件和元素。
  5. Bulma - 现代 CSS 框架,提供弹性网格和常见组件,可与 Sass 一起使用。
  6. Foundation - 老牌前端框架,基于 Sass,提供全面的组件和工具,适合构建复杂项目。
  7. Semantic UI - 设计美观的 UI 套件,基于 Sass 构建,提供丰富样式和交互。
  8. Spectre.css - 轻量级、响应式和现代的 CSS 框架,可以与 Sass 结合使用。

因此,基本上目前 Sass 已经成为了前端开发人员首选的 CSS 预处理器。因为 Sass 相比其他两个 CSS 预处理器,功能是最强大的,特性是最多的,社区也是最活跃的。

关于 Sass 官方团队,未来再对 Sass 进行更新的时候,基本上会往以下几个方面做出努力:

本文所有源码均在 https://github.com/Sunny-117/blog/tree/main/code/sass-demo

- + diff --git "a/front-end-engineering/CSS\345\267\245\347\250\213\345\214\226.html" "b/front-end-engineering/CSS\345\267\245\347\250\213\345\214\226.html" index 8bb15d17..7e16a3a9 100644 --- "a/front-end-engineering/CSS\345\267\245\347\250\213\345\214\226.html" +++ "b/front-end-engineering/CSS\345\267\245\347\250\213\345\214\226.html" @@ -677,7 +677,7 @@ ], };

配置生成的文件名

output.filename的含义一样,即根据 chunk 生成的样式文件名

配置生成的文件名,例如[name].[contenthash:5].css

默认情况下,每个 chunk 对应一个 css 文件

原子化 CSS(了解)

- + diff --git a/front-end-engineering/PackageManager.html b/front-end-engineering/PackageManager.html index 1fbdb022..3c11bc20 100644 --- a/front-end-engineering/PackageManager.html +++ b/front-end-engineering/PackageManager.html @@ -247,7 +247,7 @@

nvm

nvm 并非包管理器,它是用于管理多个 node 版本的工具

在实际的开发中,可能会出现多个项目分别使用的是不同的 node 版本,在这种场景下,管理不同的 node 版本就显得尤为重要

nvm 就是用于切换版本的一个工具

1.下载和安装

最新版下载地址:https://github.com/coreybutler/nvm-windows/releases

下载 nvm-setup.zip 后,直接安装 一路下一步,不要改动安装路径(开发类工具尽量不该动)

2.使用 nvm

nvm 提供了 CLI 工具,用于管理 node 版本

管理员运行输入 nvm,以查看各种可用命令 nvm arch:打印系统版本和默认 node 架构类型 nvm install 8.5.4:nvm 安装指定的 node 版本 nvm list :列出目前电脑上使用的以及已经安装过的那些 node 版本 npm list available:node 版本

为了加快下载速度,建议设置淘宝镜像 node 淘宝镜像:https://npm.taobao.org/mirrors/node/ npm 淘宝镜像:https://npm.taobao.org/mirrors/npm/

nvm node _mirror https://npm.taobao.org/mirrors/node/ nvm npm_mirror https://npm.taobao.org/mirrors/npm/ 查看全局安装包:npm -g list --depth=0 切换 node 版本:nvm use 10.18.0 看 node 版本:node -v 卸载:nvm uninstall 10.18.0

pnpm

pnpm 是一种新起的包管理器,从 npm 的下载量看,目前还没有超过 yarn,但它的实现方式值得主流包管理器学习,某些开发者极力推荐使用 pnpm

从结果上来看,它具有以下优势:

  1. 目前,安装效率高于 npm 和 yarn 的最新版
  2. 极其简洁的 node_modules 目录
  3. 避免了开发时使用间接依赖的问题(之前的包管理器可以使用间接依赖不报错)pnpm 没有把间接依赖直接放入 node_modules 里面
  4. 能极大的降低磁盘空间的占用
  5. 使用缓存

1.安装和使用

全局安装 pnpm

shell
npm install -g pnpm
 
npm install -g pnpm
 

之后在使用时,只需要把 npm 替换为 pnpm 即可

如果要执行安装在本地的 CLI,可以使用 pnpx,它和 npx 的功能完全一样,唯一不同的是,在使用 pnpx 执行一个需要安装的命令时,会使用 pnpm 进行安装

比如npx mocha执行本地的mocha命令时,如果mocha没有安装,则 npx 会自动的、临时的安装 mocha,安装好后,自动运行 mocha 命令 类似:npx create-react-app my-app 临时下载 create-react-app,然后自动运行命令

2.pnpm 原理

  1. 同 yarn 和 npm 一样,pnpm 仍然使用缓存来保存已经安装过的包,以及使用 pnpm-lock.yaml 来记录详细的依赖版本

缓存位于工程所在盘的根目录,所以位置不固定

  1. 不同于 yarn 和 npm, pnpm 使用符号链接和硬链接(可将它们想象成快捷方式)的做法来放置依赖,从而规避了从缓存中拷贝文件的时间,使得安装和卸载的速度更快
  2. 由于使用了符号链接和硬链接,pnpm 可以规避 windows 操作系统路径过长的问题,因此,它选择使用树形的依赖结果,有着几乎完美的依赖管理。也因为如此,项目中只能使用直接依赖,而不能使用间接依赖

3.注意事项

由于 pnpm 会改动 node_modules 目录结构,使得每个包只能使用直接依赖,而不能使用间接依赖,因此,如果使用 pnpm 安装的包中包含间接依赖,则会出现问题(现在不会了,除非使用了绝对路径)

由于 pnpm 超高的安装卸载效率,越来越多的包开始修正之前的间接依赖代码

bower

浏览器端

过时了

- + diff --git a/front-end-engineering/engineering-onepage.html b/front-end-engineering/engineering-onepage.html index 86deafa6..6e5d50c6 100644 --- a/front-end-engineering/engineering-onepage.html +++ b/front-end-engineering/engineering-onepage.html @@ -475,7 +475,7 @@ # 设置缓存位置 npm config set cache "新的缓存路径" - + diff --git a/front-end-engineering/jscompatibility.html b/front-end-engineering/jscompatibility.html index 8d69bb01..d045c50c 100644 --- a/front-end-engineering/jscompatibility.html +++ b/front-end-engineering/jscompatibility.html @@ -225,7 +225,7 @@ extends: 'airbnb' # 配置继承自 airbnb }

企业开发的实际情况

我们要做什么?

- + diff --git a/front-end-engineering/modularization.html b/front-end-engineering/modularization.html index fe170e10..7e6319ac 100644 --- a/front-end-engineering/modularization.html +++ b/front-end-engineering/modularization.html @@ -245,7 +245,7 @@ export { k, default, a as m2a } from "./m2.js"; export const r = "m-r"; - + diff --git a/front-end-engineering/node.html b/front-end-engineering/node.html index 657e093b..28ab0056 100644 --- a/front-end-engineering/node.html +++ b/front-end-engineering/node.html @@ -7,13 +7,13 @@ - + -
Skip to content
On this page

Node 组成原理

Node.js 是一个开源的、跨平台的 JavaScript 运行环境,依赖于 Google V8 引擎,用于构建高性能的网络应用程序。Node.js 采用事件驱动、非阻塞 I/O 模型,使得它能够处理大量并发连接,适用于构建实时应用、高吞吐量的后端服务和网络代理等。 Node.js 广泛应用于 Web 开发、服务器端开发、实时通信、大数据处理等领域,被许多大型互联网公司和开发者使用和推崇。

Node.js 的特点包括:

  1. 单线程和事件驱动:Node.js 采用单线程的事件循环模型,通过异步 I/O 和事件驱动处理并发请求,避免了传统多线程模型中的线程切换和资源开销,提高了性能和可扩展性。
  2. 跨平台:Node.js 可运行于多个操作系统平台,包括 Windows、Linux 和 Mac OS 等。
  3. 高性能:由于基于 V8 引擎和非阻塞 I/O 模型,Node.js 具有快速的执行速度和高吞吐量,适用于处理大量并发请求的场景。
  4. 模块化和包管理:Node.js 支持模块化开发,可以通过 npm(Node Package Manager)进行包的管理和发布,方便了代码的组织和复用。
  5. 强大的社区支持:Node.js 拥有庞大的开发者社区,提供了丰富的第三方模块和工具,方便开发者进行开发和调试。

Node.js 组成

  1. 本地模块:Node.js 内置了一些核心模块,这些模块提供了基础的功能,如文件操作(fs 模块)、网络通信(http 模块)、加密(crypto 模块)、操作系统信息(os 模块)等。这些模块可以直接通过 require 函数进行引入使用。
  2. 内置模块:Node.js 有一个丰富的第三方模块生态系统,开发者可以通过 NPM 安装这些模块,并在自己的项目中引入使用。
  3. libuv:libuv 是一个跨平台的异步 I/O 库,它为 Node.js 提供了非阻塞的事件驱动的 I/O 操作。它可以处理文件系统操作、网络请求、定时器等等,在 Node.js 中用于处理事件循环。
  4. os api:将 Node.js 可运行于多个操作系统平台,包括 Windows、Linux 和 Mac OS 等。
  5. V8 引擎:Node.js 使用了 Google 开发的 V8 引擎作为其 JavaScript 执行引擎。V8 引擎可以将 JavaScript 代码直接转化为机器码,以提供高性能的执行效率。

其他资料

https://github.com/theanarkh/understand-nodejs

- +
Skip to content
On this page

Node 组成原理

Node.js 是一个开源的、跨平台的 JavaScript 运行环境,依赖于 Google V8 引擎,用于构建高性能的网络应用程序。Node.js 采用事件驱动、非阻塞 I/O 模型,使得它能够处理大量并发连接,适用于构建实时应用、高吞吐量的后端服务和网络代理等。

Node.js 广泛应用于 Web 开发、服务器端开发、实时通信、大数据处理等领域,被许多大型互联网公司和开发者使用和推崇。

Node.js 的特点包括:

  1. 单线程和事件驱动:Node.js 采用单线程的事件循环模型,通过异步 I/O 和事件驱动处理并发请求,避免了传统多线程模型中的线程切换和资源开销,提高了性能和可扩展性。
  2. 跨平台:Node.js 可运行于多个操作系统平台,包括 Windows、Linux 和 Mac OS 等。
  3. 高性能:由于基于 V8 引擎和非阻塞 I/O 模型,Node.js 具有快速的执行速度和高吞吐量,适用于处理大量并发请求的场景。
  4. 模块化和包管理:Node.js 支持模块化开发,可以通过 npm(Node Package Manager)进行包的管理和发布,方便了代码的组织和复用。
  5. 强大的社区支持:Node.js 拥有庞大的开发者社区,提供了丰富的第三方模块和工具,方便开发者进行开发和调试。

Node.js 组成

  1. 用户代码:JS 代码,开发者编写的
  2. 第三方库:大部分仍然是 JS 代码,由其他开发者编写
  3. 本地模块:Node.js 内置了一些核心模块,这些模块提供了基础的功能,如文件操作(fs 模块)、网络通信(http 模块)、加密(crypto 模块)、操作系统信息(os 模块)等。这些模块可以直接通过 require 函数进行引入使用。
  4. 内置模块:Node.js 有一个丰富的第三方模块生态系统,开发者可以通过 NPM 安装这些模块,并在自己的项目中引入使用。
  5. libuv:libuv 是一个跨平台的异步 I/O 库,它为 Node.js 提供了非阻塞的事件驱动的 I/O 操作。它可以处理文件系统操作、网络请求、定时器等等,在 Node.js 中用于处理事件循环。
  6. os api:将 Node.js 可运行于多个操作系统平台,包括 Windows、Linux 和 Mac OS 等。
  7. V8 引擎:Node.js 使用了 Google 开发的 V8 引擎作为其 JavaScript 执行引擎。V8 引擎可以将 JavaScript 代码直接转化为机器码,以提供高性能的执行效率。(c/c++代码,作用:把 JS 代码解释成为机器码。可以通过 v8 引擎的某种机制,扩展其功能。V8 引擎的扩展和对扩展的编译,是通过一个工具:gyp 工具。某些第三方库需要使用 node-gyp 工具进行构建,因此需要先安装 node-gyp)

其他资料

https://github.com/theanarkh/understand-nodejs

+ diff --git a/front-end-engineering/performance.html b/front-end-engineering/performance.html index 65da552b..b34a8a06 100644 --- a/front-end-engineering/performance.html +++ b/front-end-engineering/performance.html @@ -1319,7 +1319,7 @@ }

浏览器刷新页面的频率1s 60s

每16.7mm刷新一次

gpu 可以再一帧里渲染好页面,那么当你改动页面的元素或者实现动画的时候,将会非常流畅

documentFragment 是什么?用它跟直接操作 DOM 的区别是什么?

MDN中对documentFragment的解释:

DocumentFragment,文档片段接口,一个没有父对象的最小文档对象。它被作为一个轻量版的 Document使用,就像标准的document一样,存储由节点(nodes)组成的文档结构。与document相比,最大的区别是DocumentFragment不是真实 DOM 树的一部分,它的变化不会触发 DOM 树的重新渲染,且不会导致性能等问题。

当我们把一个 DocumentFragment 节点插入文档树时,插入的不是 DocumentFragment 自身,而是它的所有子孙节点。在频繁的DOM操作时,我们就可以将DOM元素插入DocumentFragment,之后一次性的将所有的子孙节点插入文档中。和直接操作DOM相比,将DocumentFragment 节点插入DOM树时,不会触发页面的重绘,这样就大大提高了页面的性能。

防抖函数

如何对项目中的图片进行优化?

  1. 不用图片。很多时候会使用到很多修饰类图片,其实这类修饰图片完全可以用 CSS 去代替。
  2. 对于移动端来说,屏幕宽度就那么点,完全没有必要去加载原图浪费带宽。一般图片都用 CDN 加载,可以计算出适配屏幕的宽度,然后去请求相应裁剪好的图片。
  3. 小图使用 base64 格式
  4. 将多个图标文件整合到一张图片中(雪碧图)
  5. 选择正确的图片格式:

常见的图片格式及使用场景

(1)BMP,是无损的、既支持索引色也支持直接色的点阵图。这种图片格式几乎没有对数据进行压缩,所以BMP格式的图片通常是较大的文件。

(2)GIF是无损的、采用索引色的点阵图。采用LZW压缩算法进行编码。文件小,是GIF格式的优点,同时,GIF格式还具有支持动画以及透明的优点。但是GIF格式仅支持8bit的索引色,所以GIF格式适用于对色彩要求不高同时需要文件体积较小的场景。

(3)JPEG是有损的、采用直接色的点阵图。JPEG的图片的优点是采用了直接色,得益于更丰富的色彩,JPEG非常适合用来存储照片,与GIF相比,JPEG不适合用来存储企业Logo、线框类的图。因为有损压缩会导致图片模糊,而直接色的选用,又会导致图片文件较GIF更大。

(4)PNG-8是无损的、使用索引色的点阵图。PNG是一种比较新的图片格式,PNG-8是非常好的GIF格式替代者,在可能的情况下,应该尽可能的使用PNG-8而不是GIF,因为在相同的图片效果下,PNG-8具有更小的文件体积。除此之外,PNG-8还支持透明度的调节,而GIF并不支持。除非需要动画的支持,否则没有理由使用GIF而不是PNG-8。

(5)PNG-24是无损的、使用直接色的点阵图。PNG-24的优点在于它压缩了图片的数据,使得同样效果的图片,PNG-24格式的文件大小要比BMP小得多。当然,PNG24的图片还是要比JPEG、GIF、PNG-8大得多。

(6)SVG是无损的矢量图。SVG是矢量图意味着SVG图片由直线和曲线以及绘制它们的方法组成。当放大SVG图片时,看到的还是线和曲线,而不会出现像素点。这意味着SVG图片在放大时,不会失真,所以它非常适合用来绘制Logo、Icon等。

(7)WebP是谷歌开发的一种新图片格式,WebP是同时支持有损和无损压缩的、使用直接色的点阵图。从名字就可以看出来它是为Web而生的,什么叫为Web而生呢?就是说相同质量的图片,WebP具有更小的文件体积。现在网站上充满了大量的图片,如果能够降低每一个图片的文件大小,那么将大大减少浏览器和服务器之间的数据传输量,进而降低访问延迟,提升访问体验。目前只有Chrome浏览器和Opera浏览器支持WebP格式,兼容性不太好。WebP图片格式支持图片透明度,一个无损压缩的WebP图片,如果要支持透明度只需要22%的格外文件大小。

优化首屏响应

觉得快

loading

vue页面需要通过js构建,因此在js下载到本地之前,页面上什么也没有 一个非常简单有效的办法,即在页面中先渲染一个小的加载中效果,等到js下载到本地并运行后,即会自动替换

nprogress

源码分析地址:https://blog.csdn.net/qq_31968791/article/details/106790179 使用到的库是什么 nprogress 进度条的实现原理知道吗 Nprogress的原理非常简单,就是页面启动的时候,构建一个方法,创建一个div,然后这个div靠近最顶部,用fixed定位住,至于样式就是按照自个或者默认走了。 怎么使用这个库的 主要采用的两个方法是nprogress.start和nprogress.done 如何使用: 在请求拦截器中调用nprogress.start 在响应拦截器中调用nprogress.done

betterScroll

https://blog.csdn.net/weixin_37719279/article/details/82084342 使用的库是什么:better-scroll

骨架屏

骨架屏的原理:https://blog.csdn.net/csdn_yudong/article/details/103909178

你能说说为啥使用骨架屏吗?

现在的前端开发领域,都是前后端分离,前端框架主流的都是 SPA,MPA;这就意味着,页面渲染以及等待的白屏时间,成为我们需要解决的问题点;而且大项目,这个问题尤为突出。 webpack 可以实现按需加载,减小我们首屏需要加载的代码体积;再配合上 CDN 以及一些静态代码(框架,组件库等等…)缓存技术,可以很好的缓解这个加载渲染的时间过长的问题。 但即便如此,首屏的加载依然还是存在这个加载以及渲染的等待时间问题; 现在的前端开发领域,都是前后端分离,前端框架主流的都是 SPA,MPA;这就意味着,页面渲染以及等待的白屏时间,成为我们需要解决的问题点;而且大项目,这个问题尤为突出。 webpack 可以实现按需加载,减小我们首屏需要加载的代码体积;再配合上 CDN 以及一些静态代码(框架,组件库等等…)缓存技术,可以很好的缓解这个加载渲染的时间过长的问题。 目前主流,常见的解决方案是使用骨架屏技术,包括很多原生的APP,在页面渲染时,也会使用骨架屏。(下图中,红圈中的部分,即为骨架屏在内容还没有出现之前的页面骨架填充,以免留白)

骨架屏的要怎么使用呢?骨架屏的原理知道吗?

  1. 在 index.html 中的 div#app 中来实现骨架屏,程序渲染后就会替换掉 index.html 里面的 div#app 骨架屏内容;
  2. 使用一个Base64的图片来作为骨架屏

使用图片作为骨架屏; 简单暴力,让UI同学花点功夫吧;小米商城的移动端页面采用的就是这个方法,它是使用了一个Base64的图片来作为骨架屏。 按照方案一的方案,将这个 Base64 的图片写在我们的 index.html 模块中的 div#app 里面。

  1. 使用 .vue 文件来完成骨架屏

真实快

webpack 怎么进行首屏加载的优化?

  1. CDN 如果工程中使用了一些知名的第三方库,可以考虑使用 CDN,而不进行打包
  2. 抽离公共模块 如果工程中用到了一些大的公共库,可以考虑将其分割出来单独打包
  3. 异步加载 对于那些不需要在一开始就执行的模块,可以考虑使用动态导入的方式异步加载它们,以尽量减少主包的体积
  4. 压缩、混淆
  5. tree shaking 尽量使用 ESM 语法进行导入导出,充分利用 tree shaking 去除无用代码
  6. gzip 开启 gzip 压缩,进一步减少包体积
  7. 环境适配 有些打包结果中包含了大量兼容性处理的代码,但在新版本浏览器中这些代码毫无意义。因此,可以把浏览器分为多个层次,为不同层次的浏览器给予不同的打包结果。
- + diff --git "a/front-end-engineering/pnpm\345\216\237\347\220\206.html" "b/front-end-engineering/pnpm\345\216\237\347\220\206.html" index 5b150a70..89de7b90 100644 --- "a/front-end-engineering/pnpm\345\216\237\347\220\206.html" +++ "b/front-end-engineering/pnpm\345\216\237\347\220\206.html" @@ -23,7 +23,7 @@ # /d表示创建的是目录的符号链接,不写则是文件的符号链接

早期的 windows 系统不支持符号链接,但它提供了一个工具 junction 来达到类似的功能。

5、符号链接和硬链接的区别

  1. 硬链接仅能链接文件,而符号链接可以链接目录
  2. 硬链接在链接完成后仅和文件内容关联,和之前链接的文件没有任何关系。而符号链接始终和之前链接的文件关联,和文件内容不直接相关。

6、快捷方式

快捷方式类似于符号链接,是 windows 系统早期就支持的链接方式。它不仅仅是一个指向其他文件或目录的指针,其中还包含了各种信息:如权限、兼容性启动方式等其他各种属性,由于快捷方式是 windows 系统独有的,在跨平台的应用中一般不会使用。

7、node 环境对硬链接和符号链接的处理

硬链接:

硬链接是一个实实在在的文件,node 不对其做任何特殊处理,也无法区别对待,实际上,node 根本无从知晓该文件是不是一个硬链接

符号链接:

由于符号链接指向的是另一个文件或目录,当 node 执行符号链接下的 JS 文件时,会使用原始路径。比方说:我在 D 盘装了 LOL,在桌面创建了 LOL 快捷方式,相当于是符号链接,双击快捷方式运行游戏,在运行游戏的时候是按照 LOL 原始路径(D 盘路径)运行的。

8、pnpm 原理

pnpm 使用符号链接和硬链接来构建 node_modules 目录

下面用一个例子来说明它的构建方式

假设两个包 a 和 b,a 依赖 b:

假设我们的工程为 proj,直接依赖 a,则安装时,pnpm 会做下面的处理:

  1. 通过 package.json 查询依赖关系,得到最终要安装的包:a 和 b

  2. 在工程 proj 根目录中查看 a 和 b 是否已经有缓存,如果没有,下载到缓存中,如果有,则进入下一步

  3. 在 proj 中创建 node_modules 目录,并对目录进行结构初始化

  4. 从缓存的对应包中使用硬链接放置文件到相应包代码目录中

  5. 使用符号链接,将每个包的直接依赖放置到自己的目录中

    这样做的目的,是为了保证 a 的代码在执行过程中,可以读取到它们的直接依赖

  6. 新版本的 pnpm 为了解决一些书写不规范的包(读取间接依赖)的问题,又将所有的工程非直接依赖,使用符号链接加入到了 .pnpm/node_modules 中。如果 b 依赖 c,a 又要直接用 c,这种不规范的用法现在 pnpm 通过这种方式支持了。但对于那些使用绝对路径的奇葩写法,可能没有办法支持。

  7. 在工程的 node_modules 目录中使用符号链接,放置直接依赖

转载自 https://juejin.cn/post/6916101419703468045

- + diff --git a/front-end-engineering/theme.html b/front-end-engineering/theme.html index 2670a55c..237f10dd 100644 --- a/front-end-engineering/theme.html +++ b/front-end-engineering/theme.html @@ -675,7 +675,7 @@ } }

所有源码均在:https://github.com/Sunny-117/blog/tree/main/code/theme-switch

- + diff --git a/front-end-engineering/webpack5-mf.html b/front-end-engineering/webpack5-mf.html index aa04e838..b7c1047b 100644 --- a/front-end-engineering/webpack5-mf.html +++ b/front-end-engineering/webpack5-mf.html @@ -115,7 +115,7 @@ ] }

webpack会根据需要从合适的位置引入合适的版本

- + diff --git "a/front-end-engineering/webpack\345\270\270\347\224\250\346\213\223\345\261\225.html" "b/front-end-engineering/webpack\345\270\270\347\224\250\346\213\223\345\261\225.html" index 354326d6..9c36a015 100644 --- "a/front-end-engineering/webpack\345\270\270\347\224\250\346\213\223\345\261\225.html" +++ "b/front-end-engineering/webpack\345\270\270\347\224\250\346\213\223\345\261\225.html" @@ -217,7 +217,7 @@
$('#item'); // <= 起作用
 _.drop([1, 2, 3], 2); // <= 起作用
 
- + diff --git "a/front-end-engineering/\343\200\220\345\256\214\347\273\223\343\200\221\345\211\215\347\253\257\345\267\245\347\250\213\345\214\226.html" "b/front-end-engineering/\343\200\220\345\256\214\347\273\223\343\200\221\345\211\215\347\253\257\345\267\245\347\250\213\345\214\226.html" index c09e7946..c380fa73 100644 --- "a/front-end-engineering/\343\200\220\345\256\214\347\273\223\343\200\221\345\211\215\347\253\257\345\267\245\347\250\213\345\214\226.html" +++ "b/front-end-engineering/\343\200\220\345\256\214\347\273\223\343\200\221\345\211\215\347\253\257\345\267\245\347\250\213\345\214\226.html" @@ -477,7 +477,7 @@ # 设置缓存位置 npm config set cache "新的缓存路径" - + diff --git "a/front-end-engineering/\345\220\216\345\244\204\347\220\206\345\231\250.html" "b/front-end-engineering/\345\220\216\345\244\204\347\220\206\345\231\250.html" index bd3d3e5e..7ab0068e 100644 --- "a/front-end-engineering/\345\220\216\345\244\204\347\220\206\345\231\250.html" +++ "b/front-end-engineering/\345\220\216\345\244\204\347\220\206\345\231\250.html" @@ -803,7 +803,7 @@ }; module.exports.postcss = true; - + diff --git a/getting-started.html b/getting-started.html index c3d3c006..4645798f 100644 --- a/getting-started.html +++ b/getting-started.html @@ -13,7 +13,7 @@
Skip to content
On this page

My Projects

TODO:分类

js-challenges

https://github.com/Sunny-117/js-challenges

✨✨✨ Challenge your JavaScript programming limits step by step

mini-anythings

https://github.com/Sunny-117/mini-anything

🚀 Explore the source code of the front-end library and implement a super mini version

BOSScript

https://github.com/Sunny-117/BOSScript

Boss's direct recruitment and delivery, shutdown, one-stop service of the oil monkey script, allowing you to submit resumes overseas in just 2 minutes

rc-design

https://github.com/Sunny-117/rc-design

🗃️ rc-design is a component library developed for react, providing developers with a more lightweight and concise component library choice. Use tsx to write logic, less to write styles, dumi2 to write documentation sites, and jest+ts-jest+react-testing-library for unit testing.

cherry

https://github.com/Sunny-117/cherry

✨ A lightweight JavaScript packaging library based on magic-string and acorn, supporting tree-shaking

rollup-plugin-alias

https://github.com/Sunny-117/rollup-plugin-alias

🍣 A Rollup plugin for defining aliases when bundling packages.

commencer

https://github.com/Sunny-117/commencer

Starter template for xxx

tiny-react

https://github.com/Sunny-117/tiny-react

🌱 The closest implementation to the React source code

treejs

https://github.com/Sunny-117/treejs

🌱 Easy to learn, high-performance, and highly scalable Tree components, supporting Vuejs and React simultaneously

lodash-ts

https://github.com/Sunny-117/lodash-ts

tiny-vue

https://github.com/Sunny-117/tiny-vue

Native-project

https://github.com/Sunny-117/Native-project

Native JavaScript project collection, Github China latest version

shooks

https://github.com/Sunny-117/shooks

📦️ A high-quality & reliable React Hooks library.

mini-webpack

https://github.com/Sunny-117/mini-webpack

手写一个简易版的webpack

ts-lib-vite

https://github.com/Sunny-117/ts-lib-vite

A foundation for developing front-end utility libraries using Vite and TypeScript.

esbuild-plugins

https://github.com/Sunny-117/esbuild-plugins

packages of esbuild plugins

tiny-vite

https://github.com/Sunny-117/tiny-vite

⚡️ a lightweight frontend build tool designed to deliver swift development experiences and efficient build processes

vite-plugins

https://github.com/Sunny-117/vite-plugins

tiny-complier

实现超级 mini 的编译器 | codegen&compiler 生成代码 | 只需要 200 行代码 | 前端编译原理

https://github.com/Sunny-117/tiny-complier

keep-everyday

https://github.com/Sunny-117/keep-everyday

使用 Github Actions 来完成自动创建 issues 任务

text-image

https://github.com/Sunny-117/text-image

🐛🐛🐛 text-image 可以将文字、图片、视频进行「文本化」,只需要通过简单的配置即可使用

jsx-compilation

https://github.com/Sunny-117/jsx-compilation

🍻 实现 JSX 语法转成 JS 语法的编译器

awesome-native

https://github.com/Sunny-117/awesome-native

🔧 Collection of native JavaScript projects

vsc-delete-func

https://github.com/Sunny-117/vsc-delete-func

🍻🍻🍻 vscode plugins

eslint-plugin-reviewget

https://github.com/Sunny-117/eslint-plugin-reviewget

🚀当用户使用 getXXX get开头的函数的时候 如果不返回值的话 那么就会报错 🐛可以 fix 🎉用户可以自行配置是否 fix

babel-plugin-dev-debug

https://github.com/Sunny-117/babel-plugin-dev-debug

an babel plugin that for dev debug

webpack-expand-lib

https://github.com/Sunny-117/webpack-expand-lib

🚀 some expansion libs of webpack

network-speed-js

https://github.com/Sunny-117/network-speed-js

A small tool for testing network speed. It also has the ability to test internal and external networks.

TODO ...

- + diff --git a/hashmap.json b/hashmap.json index a151c517..1cdd3dd9 100644 --- a/hashmap.json +++ b/hashmap.json @@ -1 +1 @@ -{"article_cms.md":"1db797e6","fragment_const.md":"5702cb72","algorithm_🔥刷题之探索最优解.md":"41261772","fragment_disable-debugger.md":"079495f0","fragment_fetch.md":"a8b42ad6","fragment_fetch-pause.md":"10622b0a","fragment_npm-scripts.md":"a5e03266","fragment_api-no-repeat.md":"ac61adb1","fe-utils_git.md":"fd0526af","fe-utils_js工具库.md":"1e6ddee0","fragment_monorepo.md":"7e93c79e","fe-utils_tool.md":"d7084753","fragment_foreach.md":"a494d026","fragment_react-duplicate.md":"d6cd1c3b","fragment_nexttick.md":"a9446a03","fragment_babel-console.md":"e055d779","fragment_promise-cancel.md":"b9256032","fragment_return-await.md":"b2bca79d","fragment_tree-shaking.md":"2f624c45","fragment_react-hooks-timer.md":"ab1861d9","fragment_auto-try-catch.md":"795f44a9","fragment_settimeout.md":"9d37d7a8","fragment_var-array.md":"04a8721e","fragment_react-usestate.md":"bd7085a3","fragment_userequest.md":"857b6086","fragment_接口设计.md":"7b9e341e","fragment_黑白.md":"6b2cdd1f","fragment_微内核架构.md":"46d1614d","fragment_沙盒.md":"b3debab8","fragment_video.md":"32c415c4","front-end-engineering_css工程化.md":"1810f979","front-end-engineering_packagemanager.md":"3b71662c","front-end-engineering_node.md":"dc95de0f","front-end-engineering_jscompatibility.md":"8f33e3fb","front-end-engineering_engineering-onepage.md":"a4ddf299","front-end-engineering_modularization.md":"921ac988","front-end-engineering_pnpm原理.md":"ff625e33","front-end-engineering_webpack5-mf.md":"e4c15103","front-end-engineering_webpack常用拓展.md":"29e1b7a6","front-end-engineering_css 预处理器之scss.md":"0d8d09a0","front-end-engineering_theme.md":"cf64487f","getting-started.md":"b2d7318e","front-end-engineering_【完结】前端工程化.md":"da279999","front-end-engineering_performance.md":"08504e29","front-end-engineering_后处理器.md":"d458ef23","html-css_html.md":"eb3a0589","html-css_flex.md":"da9ee751","html-css_drag.md":"cf0ae65e","html-css_principle.md":"6930dcb0","html-css_animation.md":"c998f03f","html-css_canvas-svg.md":"e2ff6624","html-css_temop.md":"233fcb7e","index.md":"06e06ccc","interview_面试官:你还有问题要问我吗.md":"263beb57","interview_算法笔试.md":"ead05e36","html-css_css.md":"9c9d8a32","html-css_selector.md":"81f437f1","js_代理与反射.md":"c9520d6c","html-css_interview.md":"912390e1","js_异步处理.md":"01fba58b","react_fiber.md":"7867436c","js_迭代器和生成器.md":"9c646ab4","react_redux.md":"c9163688","react_component-communication.md":"5b932ed8","react_reactrouter.md":"f795da7a","react_dva.md":"9c53349f","react_event.md":"50a277c1","react_context.md":"b962ca3a","react_hooks.md":"4e1d1f24","react_lifecycle.md":"c4db6e2c","react_index.md":"b13cd8d2","react_react-redux-router.md":"8ddf584d","react_transition.md":"2fe5688f","react_umi.md":"d31b6477","react_utils.md":"b40a882d","react_render.md":"3fa7b370","vue_ssr.md":"efb28cdd","vue_challages.md":"c6ae4728","vue_computed.md":"d70c4c97","react_react-interview.md":"f8db0f8d","vue_component-communication.md":"e806c81b","vue_interviewer.md":"1e30c648","vue_keep-alive-lru.md":"a6d0c9a3","vue_directive.md":"db07d9e7","vue_diff.md":"f8fe76c6","vue_lifecycle.md":"f4424b89","vue_nexttick.md":"1a3b9c15","vue_slot.md":"0a1f8353","vue_v-model.md":"5e01b53a","vue_vdom.md":"a71aa0d1","vue_vs.md":"69aafdac","vue_vue-cli.md":"cef17c2f","vue_vue-compile.md":"fff35cb6","vue_reactive.md":"0bc25164","ts_typescript-onepage.md":"5ce7b35d","vue_vue-router.md":"e054a826","vue_vuex.md":"72893add","vue_vue-interview.md":"feb0c04f","vue_vue3-onepage.md":"ab0446bc"} +{"fe-utils_tool.md":"d7084753","algorithm_🔥刷题之探索最优解.md":"41261772","fragment_monorepo.md":"7e93c79e","fragment_disable-debugger.md":"079495f0","fragment_fetch-pause.md":"10622b0a","article_cms.md":"1db797e6","fragment_const.md":"5702cb72","fragment_fetch.md":"a8b42ad6","fe-utils_js工具库.md":"1e6ddee0","fe-utils_git.md":"fd0526af","fragment_api-no-repeat.md":"ac61adb1","fragment_foreach.md":"a494d026","fragment_npm-scripts.md":"a5e03266","fragment_babel-console.md":"e055d779","fragment_nexttick.md":"a9446a03","fragment_react-duplicate.md":"d6cd1c3b","fragment_tree-shaking.md":"2f624c45","fragment_return-await.md":"b2bca79d","fragment_settimeout.md":"9d37d7a8","fragment_react-hooks-timer.md":"ab1861d9","fragment_promise-cancel.md":"b9256032","fragment_var-array.md":"04a8721e","fragment_auto-try-catch.md":"795f44a9","fragment_react-usestate.md":"bd7085a3","fragment_userequest.md":"857b6086","fragment_接口设计.md":"7b9e341e","fragment_黑白.md":"6b2cdd1f","fragment_微内核架构.md":"46d1614d","fragment_video.md":"32c415c4","fragment_沙盒.md":"b3debab8","front-end-engineering_packagemanager.md":"3b71662c","front-end-engineering_node.md":"e4a62891","front-end-engineering_css工程化.md":"1810f979","front-end-engineering_jscompatibility.md":"8f33e3fb","front-end-engineering_pnpm原理.md":"ff625e33","front-end-engineering_modularization.md":"921ac988","front-end-engineering_webpack5-mf.md":"e4c15103","front-end-engineering_webpack常用拓展.md":"29e1b7a6","front-end-engineering_engineering-onepage.md":"a4ddf299","getting-started.md":"b2d7318e","front-end-engineering_theme.md":"cf64487f","front-end-engineering_后处理器.md":"d458ef23","front-end-engineering_【完结】前端工程化.md":"da279999","front-end-engineering_css 预处理器之scss.md":"0d8d09a0","front-end-engineering_performance.md":"08504e29","html-css_html.md":"eb3a0589","html-css_animation.md":"c998f03f","html-css_drag.md":"cf0ae65e","html-css_flex.md":"da9ee751","html-css_canvas-svg.md":"e2ff6624","html-css_principle.md":"6930dcb0","html-css_css.md":"9c9d8a32","index.md":"06e06ccc","html-css_temop.md":"233fcb7e","interview_面试官:你还有问题要问我吗.md":"263beb57","interview_算法笔试.md":"ead05e36","html-css_selector.md":"81f437f1","html-css_interview.md":"912390e1","js_代理与反射.md":"c9520d6c","react_fiber.md":"7867436c","react_redux.md":"c9163688","react_component-communication.md":"5b932ed8","js_迭代器和生成器.md":"9c646ab4","react_reactrouter.md":"f795da7a","react_event.md":"50a277c1","react_dva.md":"9c53349f","react_hooks.md":"4e1d1f24","react_context.md":"b962ca3a","js_异步处理.md":"01fba58b","react_lifecycle.md":"c4db6e2c","react_react-redux-router.md":"8ddf584d","react_transition.md":"2fe5688f","react_render.md":"3fa7b370","react_utils.md":"b40a882d","react_umi.md":"d31b6477","react_index.md":"b13cd8d2","react_react-interview.md":"f8db0f8d","vue_ssr.md":"efb28cdd","ts_typescript-onepage.md":"5ce7b35d","vue_computed.md":"d70c4c97","vue_challages.md":"c6ae4728","vue_directive.md":"db07d9e7","vue_component-communication.md":"e806c81b","vue_interviewer.md":"1e30c648","vue_keep-alive-lru.md":"a6d0c9a3","vue_diff.md":"f8fe76c6","vue_nexttick.md":"1a3b9c15","vue_lifecycle.md":"f4424b89","vue_slot.md":"0a1f8353","vue_v-model.md":"5e01b53a","vue_vue-cli.md":"cef17c2f","vue_vue-compile.md":"fff35cb6","vue_vs.md":"69aafdac","vue_vdom.md":"a71aa0d1","vue_vue-interview.md":"feb0c04f","vue_reactive.md":"0bc25164","vue_vue-router.md":"e054a826","vue_vuex.md":"72893add","vue_vue3-onepage.md":"ab0446bc"} diff --git a/html-css/CSS.html b/html-css/CSS.html index 4b8cc042..d2ae9fad 100644 --- a/html-css/CSS.html +++ b/html-css/CSS.html @@ -3047,7 +3047,7 @@ 区视口宽高中最大的一边分成100份 vmin 区视口宽高中最小的一边分成100份 css样式引入 媒体查询不占用权重 - + diff --git a/html-css/HTML.html b/html-css/HTML.html index 556e72a4..755b6957 100644 --- a/html-css/HTML.html +++ b/html-css/HTML.html @@ -663,7 +663,7 @@ this.postMessage(result); };

worker.js 里面可以通过 importScripts("./index.js")引入外部 js 文件

- + diff --git a/html-css/animation.html b/html-css/animation.html index 283b4bdf..87b7e736 100644 --- a/html-css/animation.html +++ b/html-css/animation.html @@ -1573,7 +1573,7 @@ ---- > paint喷色 (reflow重构) (repaint) 逻辑图(多层矢量图) -----> 实际绘制(栅格化) 不设置就用cpu绘制 google chrome 自动调用 gpu - + diff --git a/html-css/canvas-svg.html b/html-css/canvas-svg.html index 0c50e58e..52195743 100644 --- a/html-css/canvas-svg.html +++ b/html-css/canvas-svg.html @@ -809,7 +809,7 @@ <line x1="100" y1="100" x2="200" y2="100" class="line1"></line> </svg>

总结:SVG 开发中不太用

- + diff --git a/html-css/drag.html b/html-css/drag.html index 3eaec386..4a28086d 100644 --- a/html-css/drag.html +++ b/html-css/drag.html @@ -343,7 +343,7 @@
oDragTarget.ondrop = function (e) { e.dataTransfer.dropEffect =
 "link";//放下时候的效果,只在drop里面设置 }
 

试验不通过??

- + diff --git a/html-css/flex.html b/html-css/flex.html index 725715d0..aa81ff1b 100644 --- a/html-css/flex.html +++ b/html-css/flex.html @@ -153,7 +153,7 @@ } </style>

虽然都是充分分配容器的尺寸,但是 flex:1 的尺寸表现更为内敛(优先牺牲自己的尺寸), flex:auto 的尺寸表现则更为霸道(优先扩展自己的尺寸)。

适合使用 flex:1 的场景

当希望元素充分利用剩余空间,同时不会侵占其他元素应有的宽度的时候,适合使用 flex:1 ,这样的场景在 Flex 布局中非常的多。

例如所有的等分列表,或者等比例列表都适合使用 flex:1 或者其他 flex 数值,适合的布局效果轮廓如下图所示。

适合使用 flex:auto 的场景

当希望元素充分利用剩余空间,但是各自的尺寸按照各自内容进行分配的时候,适合使用 flex:auto 。例如导航栏。整体设置为 200px,内部设置 flex:auto,会自动按照内容比例进行分配宽度。

- + diff --git a/html-css/interview.html b/html-css/interview.html index e8891165..d814f888 100644 --- a/html-css/interview.html +++ b/html-css/interview.html @@ -2507,7 +2507,7 @@ margin: 150px top: 250px

CSS 中 px、em、rem 的区别

px:绝对单位,页面按精确像素显示; em:相对单位,基准点为父节点字体的大小; rem:相对单位,可理解为“root em”,相对根结点 html 的字体大小来计算,CSS3 新属性;

div 设置成 inline-block,有空隙是什么原因呢?如何解决

https://juejin.cn/post/6991762098111905806

css 中 z-index 属性考察

问:box1 在 box2 上面,还是 box2 在 box1 上面? 答案:box1 在 box2 上面 考察是否对z-index生效条件了解,概念: z-index 只对定位元素有效,这里的定位指:absolute、relative、fixed 和 inherit,其中 inherit 取决于父元素,如果父元素没有设置定位(absolute、relative、fixed)则 z-index 无效,注意低版本 IE 浏览器不支持这个值 除了给出答案,还了解 z-index 属性其他注意点,浏览器兼容性等 4. 4.0 分:

css3 GPU 加速

css3 中 2d 变化和 3d 变化有什么区别,性能对比如何,是否用到了 gpu 加速?

  1. 对于 translate 和 scale,2d 变换时有两条轴,对于 rotate,2d 变化时只有一条轴。3d 变化时,都存在相对 x、y、z 轴的变化。
  2. 2d 和 3d 变化都用到了 gpu,因为 gpu 擅长图像的变化操作
  3. 3d 变化性能更好,虽然 2d 和 3d 都用到了 gpu,但是 2d 变换的元素只有在动画过程中才会提到复合层(composite layer),而 3d 变换的元素一开始就被提到了符合层。

请问 HTML 中如何通过<script>加载 JAVAScript 脚本?如果同步阻塞加载,脚本过大影响页面渲染,如何优化?

默认情况下,浏览器是同步加载 JavaScript 脚本,即渲染引擎遇到

回答出浏览器同步加载,渲染引擎遇到 script 标签停止渲染,等待脚本加载执行完成,回答出脚本加载执行加载时机,通过(defer 或 async)异步加载方式优化防止渲染阻塞

如何 html 中开启和关闭 DNS 预读取

在文档中使用值为 http-equiv 的   标签:可以通过将 content 的参数设置为“on”来改变设置 然后通过使用 rel 属性值为 link type 中的 dns-prefetch 的   标签来对特定域名进行预读取: 总分 4 分,不知道不得分,知道通过 meta 值设置给 2 分, meta 写正确+1 分,link 写正确+1

要求:知道 dns 预读取和预读取的意义。如何开启 dns 预读取

请简述 transform 原理

transform 同时设置多个值,比如 translate(30px,0px) rotate(45deg)和 rotate(45deg) translate(30px,0px)效果是否相同,为什么?不相同 两个变换是有先后顺序的,是先转后移还是先移后转,比较容易比划出来 从原理上来说,变换最后是用矩阵乘向量来运算的,矩阵乘法不具有交换律,因此 M1_M2 和 M2_M1 的结果是不同的

答对了,原理也说出来了,为什么要用矩阵来实现变换也说得出来

如何解决 1px 问题

核心思路: 在 web 中,浏览器为我们提供了 window.devicePixelRatio 来帮助我们获取 dpr。在 css 中,可以使用媒体查询 min-device-pixel-ratio,区分 dpr 我们根据这个像素比,来算出他对应应该有的大小,但是暴露个非常大的兼容问题

解决过程会出现什么问题呢?

说说什么是视口吧(viewport)

viewport 即视窗、视口,用于显示网页部分的区域,在 PC 端视口即是浏览器窗口区域,在移动端,为了让页面展示更多的内容,视窗的宽度默认不为设备的宽度,在移动端视窗有三个概念:布局视窗、视觉视窗、理想视窗

移动端视口配置怎么配的?

移动端适配有哪些方案知道吗?

1、rem 布局 2、vw、vh 布局 3、媒体查询响应式布局

说说媒体查询吧?

通过媒体查询,可以针对不同的屏幕进行单独设置,但是针对所有的屏幕尺寸做适配显然是不合理的,但是可以用来处理极端情况(例如 IPad 大屏设备)或做简单的适配(隐藏元素或改变元素位置)

说说 rem 适配吧

rem 是 CSS3 新增的一个相对单位,这个单位引起了广泛关注。这个单位与 em 有什么区别呢?区别在于使用 rem 为元素设定字体大小时,仍然是相对大小,但相对的只是 HTML 根元素。这个单位可谓集相对大小和绝对大小的优点于一身,通过它既可以做到只修改根元素就成比例地调整所有字体大小,又可以避免字体大小逐层复合的连锁反应。目前,除了 IE8 及更早版本外,所有浏览器均已支持 rem。对于不支持它的浏览器,应对方法也很简单,就是多写一个绝对单位的声明。这些浏览器会忽略用 rem 设定的字体大小

rem 的具体适配方案知道吗?

flexible.js 适配:阿里早期开源的一个移动端适配解决方案

因为当年 viewport 在低版本安卓设备上还有兼容问题,而 vw,vh 还没能实现所有浏览器兼容,所以 flexible 方案用 rem 来模拟 vmin 来实现在不同设备等比缩放的“通用”方案,之所以说是通用方案,是因为他这个方案是根据设备大小去判断页面的展示空间大小即屏幕大小,然后根据屏幕大小去百分百还原设计稿,从而让人看到的效果(展示范围)是一样的,这样一来,苹果 5 和苹果 6p 屏幕如果你按照设计稿还原的话,字体大小实际上不一样,而人们在一样的距离上希望看到的大小其实是一样的,本质上,用户使用更大的屏幕,是想看到更多的内容,而不是更大的字

rem 的弊端知道吗

弊端之一:和根元素 font-size 值强耦合,系统字体放大或缩小时,会导致布局错乱 弊端之二:html 文件头部需插入一段 js 代码

说说 vw/vh 适配

vh、vw 方案即将视觉视口宽度 window.innerWidth 和视觉视口高度 window.innerHeight 等分为 100 份

vw 和 vh 有啥不足吗?

vw 和 vh 的兼容性: Android 4.4 之下和 iOS 8 以下的版本有一定的兼容性问题(但是目前这两版本已经很少有人使用了) rem 的兼容性:

- + diff --git a/html-css/principle.html b/html-css/principle.html index 24f26b85..96702c5b 100644 --- a/html-css/principle.html +++ b/html-css/principle.html @@ -43,7 +43,7 @@ /*红*/ }

像素:--->红绿蓝像点----->空间混色

最小的单位:像点。像素由 3 个像点构成。

空间混色法应用

crt 显示屏

lcd 液晶屏

点距:crt 显示屏求点距的方法的意义,是几乎所有屏幕都通用的

像素的大小:点距

物理像素:设备出厂时,像素的大小

dpi:1 英寸所能容纳的像素点数

1 英寸= 2.54cm

dpi 打印机在一英寸屏幕里面可以打印多少墨点

ppi 一英寸所能容纳的像素点数(点距数)

参照像素

96dpi 一臂之遥的视角去看,显示出的具体大小

标杆 1/96*英寸

css 像素=逻辑像素

设备像素比 dpr = 物理像素/css 像素

衡量屏幕好不好:不看分辨率(分辨率:固定宽高下,展示的像素点数)

看的是 dpi

- + diff --git a/html-css/selector.html b/html-css/selector.html index b3e0ca4d..d574c62f 100644 --- a/html-css/selector.html +++ b/html-css/selector.html @@ -1329,7 +1329,7 @@ </body> </html> - + diff --git a/html-css/temop.html b/html-css/temop.html index 3a7c598e..d967a793 100644 --- a/html-css/temop.html +++ b/html-css/temop.html @@ -13,7 +13,7 @@
Skip to content
On this page
- + diff --git a/index.html b/index.html index 06727ed9..9634091d 100644 --- a/index.html +++ b/index.html @@ -13,7 +13,7 @@
Skip to content

Sunny's blog

前端历险记

前端历险记
- + diff --git "a/interview/\347\256\227\346\263\225\347\254\224\350\257\225.html" "b/interview/\347\256\227\346\263\225\347\254\224\350\257\225.html" index deff61f0..d1048cf8 100644 --- "a/interview/\347\256\227\346\263\225\347\254\224\350\257\225.html" +++ "b/interview/\347\256\227\346\263\225\347\254\224\350\257\225.html" @@ -299,7 +299,7 @@ let line = gets(10000).trim(); print(line.length);

trim()

- + diff --git "a/interview/\351\235\242\350\257\225\345\256\230\357\274\232\344\275\240\350\277\230\346\234\211\351\227\256\351\242\230\350\246\201\351\227\256\346\210\221\345\220\227.html" "b/interview/\351\235\242\350\257\225\345\256\230\357\274\232\344\275\240\350\277\230\346\234\211\351\227\256\351\242\230\350\246\201\351\227\256\346\210\221\345\220\227.html" index 06d6a50e..015da30c 100644 --- "a/interview/\351\235\242\350\257\225\345\256\230\357\274\232\344\275\240\350\277\230\346\234\211\351\227\256\351\242\230\350\246\201\351\227\256\346\210\221\345\220\227.html" +++ "b/interview/\351\235\242\350\257\225\345\256\230\357\274\232\344\275\240\350\277\230\346\234\211\351\227\256\351\242\230\350\246\201\351\227\256\346\210\221\345\220\227.html" @@ -13,7 +13,7 @@
Skip to content
On this page

面试官:你还有问题要问我吗?

下面列表里的问题对于参加技术面试的人来说可能有些用。

列表里的问题并不一定适用于某个特定的职位或者工作类型,也没有排序

最开始的时候这只是我自己的问题列表,但是慢慢地添加了一些我觉得可能让我对这家公司亮红牌的问题。

我也注意到被我面试的人提问我的问题太少了,感觉他们挺浪费机会的。

预期使用方式

  • 检查一下哪些问题你感兴趣
  • 检查一下哪些是你可以自己在网上找到答案的
  • 找不到的话就向面试官提问

绝对不要想把这个列表里的每个问题都问一遍。(尊重面试官的时间,而且你可以通过查找已经发布的答案来显示 你的主动性)

请记住事情总是灵活的,组织的结构调整也会经常发生。拥有一个 bug 追踪系统并不会保证高效处理 bug。 CI/CD (持续集成系统) 也不一定保证交付时间会很短。

职责

  • On-call (电话值班)的计划或者规定是什么?值班或者遇到问题加班时候有加班费吗?
  • 我的日常工作是什么?
  • 有给我设定的特定目标吗?
  • 团队里面初级和高级工程师的比例是多少?(有计划改变吗)
  • 入职培训 (onboarding) 会是什么样的?
  • 每个开发者有多大的自由来做出决定?
  • 在你看来,这个工作做到什么程度算成功?
  • 你期望我在最初的一个月 / 三个月能够完成什么?
  • 试用期结束的时候,你会怎么样衡量我的绩效?
  • 自己单独的开发活动和按部就班工作的比例大概是怎样的?
  • 一个典型的一天或者一周的工作是怎样安排的?
  • 对我的申请你有什么疑虑么?
  • 在这份工作上,我将会和谁紧密合作?
  • 我的直接上级他们的上级都是什么样的管理风格?(事无巨细还是着眼宏观)
  • 我在这个岗位上应该如何发展?会有哪些机会?
  • 每天预期 / 核心工作时间是多少小时?
  • 我入职的岗位是新增还是接替之前离职的同事?(是否有技术债需要还)?(zh)
  • 入职之后在哪个项目组,项目是新成立还是已有的?(zh)

技术

  • 公司常用的技术栈是什么?
  • 你们怎么使用源码控制系统?
  • 你们怎么测试代码?
  • 你们怎么追踪 bug?
  • 你们怎样监控项目?
  • 你们怎么集成和部署代码改动?是使用持续集成和持续部署吗 (CI/CD)?
  • 你们的基础设施搭建在版本管理系统里吗?或者是代码化的吗?
  • 从计划到完成一项任务的工作流是什么样的?
  • 你们如何准备故障恢复?
  • 有标准的开发环境吗?是强制的吗?
  • 你们需要花费多长时间来给产品搭建一个本地测试环境?(分钟 / 小时 / 天)
  • 你们需要花费多长时间来响应代码或者依赖中的安全问题?
  • 所有的开发者都可以使用他们电脑的本地管理员权限吗?
  • 介绍一下你们的技术原则或者展望。
  • 你们的代码有开发文档吗?有没有单独的供消费者阅读的文档?
  • 你们有更高层次的文档吗?比如说 ER 图,数据库范式
  • 你们使用静态代码分析吗?
  • 你们如何管理内部和外部的数字资产?
  • 你们如何管理依赖?
  • 公司是否有技术分享交流活动?有的话,多久一次呢?(zh)
  • 你们的数据库是怎么进行版本控制的?(zh)
  • 业务需求有没有文档记录?是如何记录的?(zh)

团队

  • 工作是怎么组织的?
  • 团队内 / 团队间的交流通常是怎样的?
  • 你们使用什么工具来做项目组织?你的实际体会是什么?
  • 如果遇到不同的意见怎样处理?
  • 谁来设定优先级 / 计划?
  • 如果团队没能赶上预期发布日期怎么办?
  • 每周都会开什么类型的会议?
  • 会有定期的和上级的一对一谈话吗?
  • 产品 / 服务的规划是什么样的?(n 周一发布 / 持续部署 / 多个发布流 / ...)
  • 生产环境发生事故了怎么办?是否有不批评人而分析问题的文化?
  • 有没有一些团队正在经历还尚待解决的挑战?
  • 你们如何跟踪进度?
  • 预期和目标是如何设定的?谁来设定?
  • Code Review 如何实施?
  • 给我介绍下团队里一个典型的 sprint
  • 你们如何平衡技术和商业目标?
  • 你们如何共享知识?
  • 团队有多大?
  • 公司技术团队的架构和人员组成?(zh)
  • 团队内开发、产品、运营哪一方是需求的主要提出方?哪一方更强势?(zh)

问未来的同事

  • 开发者倾向于从哪里学习?
  • 你对在这里工作最满意的地方是?
  • 最不满意的呢?
  • 如果可以的话,你想改变哪里?
  • 团队最老的成员在这里多久了?
  • 在小团队中,有没有出现成员性格互相冲突的情况?最后是如何解决的?

公司

  • 公司为什么在招人?(产品发展 / 新产品 / 波动...)
  • 有没有会议 / 旅行预算?使用的规定是什么?
  • 晋升流程是怎样的?要求 / 预期是怎样沟通的?
  • 绩效评估流程是怎样的?
  • 技术和管理两条职业路径是分开的吗?
  • 对于多元化招聘的现状或者观点是什么?
  • 有公司级别的学习资源吗?比如电子书订阅或者在线课程?
  • 有获取证书的预算吗?
  • 公司的成熟度如何?(早期寻找方向 / 有内容的工作 / 维护中 / ...)
  • 我可以为开源项目做贡献吗?是否需要审批?
  • 你认为公司未来五年或者十年会发展成什么样子?
  • 公司的大多数员工是如何看待整洁代码的?
  • 你上次注意到有人成长是什么时候?他们在哪方面成长了?
  • 在这里成功的定义是什么?如何衡量成功?
  • 有体育活动或者团建么?
  • 有内部的黑客马拉松活动吗?
  • 公司支持开源项目吗?
  • 有竞业限制或者保密协议需要签吗?
  • 你们认为公司文化中的空白是什么?
  • 能够跟我说一公司处于不良情况,以及如何处理的故事吗?
  • 您在这工作了多久了?您觉得体验如何?(zh)
  • 大家为什么会喜欢这里?(zh)
  • 公司的调薪制度是如何的?(zh)

社会问题

  • 你们关于多元化招聘什么看法?
  • 你们的公司文化如何?你认为有什么空白么?
  • 这里的工作生活平衡地怎么样?
  • 公司对气候变化有什么态度吗?

冲突

  • 不同的意见如何处理?
  • 如果被退回了会怎样?(“这个在预计的时间内做不完”)
  • 当团队有压力并且在超负荷工作的时候怎么处理?
  • 如果有人注意到了在流程或者技术等其他方面又改进的地方,怎么办?
  • 当管理层的预期和工程师的绩效之间有差距的时候如何处理?
  • 能给我讲一个公司深处有毒环境以及如何处理的故事吗?
  • 如果在公司内你的同事因涉嫌性侵犯他人而被调查,请问你会如何处理?
  • 假设我自己很不幸是在公司内被性侵的受害者,在公司内部有没有争取合法权益的渠道?

商业

  • 你们现在盈利吗?
  • 如果没有的话,还需要多久?
  • 公司的资金来源是什么?谁影响或者制定高层计划或方向?
  • 你们如何挣钱?
  • 什么阻止了你们挣更多的钱?
  • 公司未来一年的增长计划怎样?五年呢?
  • 你们认为什么是你们的竞争优势?
  • 你们的竞争优势是什么?
  • 公司未来的商业规划是怎样的?有上市的计划吗?(zh)

远程工作

  • 远程工作和办公室工作的比例是多少?
  • 公司提供硬件吗?更新计划如何?
  • 使用自己的硬件办公可以吗?现在有政策吗?
  • 额外的附件和家具可以通过公司购买吗?这方面是否有预算?
  • 有共享办公或者上网的预算吗?
  • 多久需要去一次办公室?
  • 公司的会议室是否一直是视频会议就绪的?

办公室布局

  • 办公室的布局如何?(开放的 / 小隔间 / 独立办公室)
  • 有没有支持 / 市场 / 或者其他需要大量打电话的团队在我的团队旁边办公?

终极问题

  • 该职位为何会空缺?
  • 公司如何保证人才不流失?
  • 这份工作 / 团队 / 公司最好和最坏的方面是?
  • 你最开始为什么选择了这家公司?
  • 你为什么留在这家公司?

待遇

  • 如果有奖金计划的话,奖金如何分配?
  • 如果有奖金计划的话,过去的几年里通常会发百分之多少的奖金?
  • 有五险一金(zh)/401k(us)或者其他退休养老金等福利吗?
  • 五险一金中,补充公积金一般交多少比例?/401k一般交多少比例?我可以自己选择这一比例吗?
  • 有什么医疗保险吗?如果有的话何时开始?
  • 有额外商业保险吗?例如人寿保险和额外的养老/医疗保险?
  • 更换工作地点,公司付费吗?

休假

  • 带薪休假时间有多久?
  • 病假和事假是分开的还是一起算?
  • 我可以提前使用假期时间吗?也就是说应休假期是负的?
  • 假期的更新策略是什么样的?也就是说未休的假期能否滚入下一周期
  • 照顾小孩的政策如何?
  • 无薪休假政策是什么样的?
  • 学术性休假政策是怎么样的?

参考链接

Find more inspiration for questions in:

https://github.com/viraptor/reverse-interview

翻译:

English

Korean

Portuguese

繁體中文

- + diff --git "a/js/\344\273\243\347\220\206\344\270\216\345\217\215\345\260\204.html" "b/js/\344\273\243\347\220\206\344\270\216\345\217\215\345\260\204.html" index 56dc80a6..df22c9f4 100644 --- "a/js/\344\273\243\347\220\206\344\270\216\345\217\215\345\260\204.html" +++ "b/js/\344\273\243\347\220\206\344\270\216\345\217\215\345\260\204.html" @@ -973,7 +973,7 @@ const sumProxy = validatorFunction(sum, "number", "number"); console.log(sumProxy(1, 2));

其他 proxy 资料:

https://cloud.tencent.com/developer/article/1890562

https://juejin.cn/post/6844904101218631694

- + diff --git "a/js/\345\274\202\346\255\245\345\244\204\347\220\206.html" "b/js/\345\274\202\346\255\245\345\244\204\347\220\206.html" index 13a9aeae..0ff21b50 100644 --- "a/js/\345\274\202\346\255\245\345\244\204\347\220\206.html" +++ "b/js/\345\274\202\346\255\245\345\244\204\347\220\206.html" @@ -2129,7 +2129,7 @@ }); console.log('script end'); - + diff --git "a/js/\350\277\255\344\273\243\345\231\250\345\222\214\347\224\237\346\210\220\345\231\250.html" "b/js/\350\277\255\344\273\243\345\231\250\345\222\214\347\224\237\346\210\220\345\231\250.html" index 47b280c6..60cefcac 100644 --- "a/js/\350\277\255\344\273\243\345\231\250\345\222\214\347\224\237\346\210\220\345\231\250.html" +++ "b/js/\350\277\255\344\273\243\345\231\250\345\222\214\347\224\237\346\210\220\345\231\250.html" @@ -1241,7 +1241,7 @@ } } - + diff --git a/react/Fiber.html b/react/Fiber.html index 573980ef..122364a2 100644 --- a/react/Fiber.html +++ b/react/Fiber.html @@ -13,7 +13,7 @@
Skip to content
On this page

Fiber

React V15 在渲染时,会递归比对 VirtualDOM 树,找出需要变动的节点,然后同步更新它们, 一气呵成。这个过程期间, React 会占据浏览器资源,这会导致用户触发的事件得不到响应,并且会导致掉帧,导致用户感觉到卡顿

为了给用户制造一种应用很快的“假象”,不能让一个任务长期霸占着资源。 可以将浏览器的渲染、布局、绘制、资源加载(例如 HTML 解析)、事件响应、脚本执行视作操作系统的“进程”,需要通过某些调度策略合理地分配 CPU 资源,从而提高浏览器的用户响应速率, 同时兼顾任务执行效率。

所以 React 通过 Fiber 架构,让这个执行过程变成可被中断。“适时”地让出 CPU 执行权,除了可以让浏览器及时地响应用户的交互,还有其他好处:

  • 分批延时对 DOM 进行操作,避免一次性操作大量 DOM 节点,可以得到更好的用户体验;
  • 给浏览器一点喘息的机会,它会对代码进行编译优化(JIT)及进行热代码优化,或者对 reflow 进行修正。

核心思想: Fiber 也称协程或者纤程。它和线程并不一样,协程本身是没有并发或者并行能力的(需要配合线程),它只是一种控制流程的让出机制。让出 CPU 的执行权,让 CPU 能在这段时间执行其他的操作。渲染的过程可以被中断,可以将控制权交回浏览器,让位给高优先级的任务,浏览器空闲后再恢复渲染。

- + diff --git a/react/ReactRouter.html b/react/ReactRouter.html index 748662e5..8d8515eb 100644 --- a/react/ReactRouter.html +++ b/react/ReactRouter.html @@ -621,7 +621,7 @@ o 支持监听action的分发,更新状态(dispatch(action)); o 支持订阅store的变更(subscribe(listener));

Mobx 是一个透明函数响应式编程的状态管理库,它使得状态管理简单可伸缩 ∶

对比总结:

Redux 和 Vuex 有什么区别,它们的共同思想

(1)Redux 和 Vuex 区别

通俗点理解就是,vuex 弱化 dispatch,通过 commit 进行 store 状态的一次更变;取消了 action 概念,不必传入特定的 action 形式进行指定变更;弱化 reducer,基于 commit 参数直接对数据进行转变,使得框架更加简易;

(2)共同思想

本质上 ∶ redux 与 vuex 都是对 mvvm 思想的服务,将数据从视图中抽离的一种方案。

Redux 中间件是怎么拿到 store 和 action? 然后怎么处理?

redux 中间件本质就是一个函数柯里化。redux applyMiddleware Api 源码中每个 middleware 接受 2 个参数, Store 的 getState 函数和 dispatch 函数,分别获得 store 和 action,最终返回一个函数。该函数会被传入 next 的下一个 middleware 的 dispatch 方法,并返回一个接收 action 的新函数,这个函数可以直接调用 next(action),或者在其他需要的时刻调用,甚至根本不去调用它。调用链中最后一个 middleware 会接受真实的 store 的 dispatch 方法作为 next 参数,并借此结束调用链。所以,middleware 的函数签名是({ getState,dispatch })=> next => action。

Redux 中的 connect 有什么作用

connect 负责连接 React 和 Redux

(1)获取 state

connect 通过 context 获取 Provider 中的 store,通过store.getState() 获取整个 store tree 上所有 state

(2)包装原组件

将 state 和 action 通过 props 的方式传入到原组件内部 wrapWithConnect 返回—个 ReactComponent 对 象 Connect,Connect 重 新 render 外部传入的原组件 WrappedComponent ,并把 connect 中传入的 mapStateToProps,mapDispatchToProps 与组件上原有的 props 合并后,通过属性的方式传给 WrappedComponent

(3)监听 store tree 变化

connect 缓存了 store tree 中 state 的状态,通过当前 state 状态 和变更前 state 状态进行比较,从而确定是否调用 this.setState()方法触发 Connect 及其子组件的重新渲染

- + diff --git a/react/Redux.html b/react/Redux.html index 2375d1c7..c660a56f 100644 --- a/react/Redux.html +++ b/react/Redux.html @@ -51,7 +51,7 @@ payload: 1, // 用户id为1 };

Redux

在 Flux 基础上,引入了 reducer 的概念

reducer:处理器,用于根据 action 来处理数据,处理后的数据会被仓库重新保存。

Action

  1. action 是一个 plain-object(平面对象)
    1. 它的proto指向 Object.prototype
  2. 通常,使用 payload 属性表示附加数据(没有强制要求)
  3. action 中必须有 type 属性,该属性用于描述操作的类型
    1. 但是,没有对 type 的类型做出要求
  4. 在大型项目,由于操作类型非常多,为了避免硬编码(hard code),会将 action 的类型存放到一个或一些单独的文件中(样板代码)。

  1. 为了方面传递 action,通常会使用 action 创建函数(action creator)来创建 action

  2. action 创建函数应为无副作用的纯函数

    1. 不能以任何形式改动参数
    2. 不可以有异步
    3. 不可以对外部环境中的数据造成影响
  3. 为了方便利用 action 创建函数来分发(触发)action,redux 提供了一个函数bindActionCreators,该函数用于增强 action 创建函数的功能,使它不仅可以创建 action,并且创建后会自动完成分发。

Reducer

Reducer 是用于改变数据的函数

  1. 一个数据仓库,有且仅有一个 reducer,并且通常情况下,一个工程只有一个仓库,因此,一个系统,只有一个 reducer
  2. 为了方便管理,通常会将 reducer 放到单独的文件中。
  3. reducer 被调用的时机
    1. 通过 store.dispatch,分发了一个 action,此时,会调用 reducer
    2. 当创建一个 store 的时候,会调用一次 reducer
      1. 可以利用这一点,用 reducer 初始化状态
      2. 创建仓库时,不传递任何默认状态
      3. 将 reducer 的参数 state 设置一个默认值。创建仓库不写默认值,传递 reducer 的时候传递默认值
  4. reducer 内部通常使用 switch 来判断 type 值
  5. reducer 必须是一个没有副作用的纯函数
    1. 为什么需要纯函数
      1. 纯函数有利于测试和调式
      2. 有利于还原数据
      3. 有利于将来和 react 结合时的优化
    2. 具体要求
      1. 不能改变参数,因此若要让状态变化,必须得到一个新的状态
      2. 不能有异步
      3. 不能对外部环境造成影响
  6. 由于在大中型项目中,操作比较复杂,数据结构也比较复杂,因此,需要对 reducer 进行细分。
    1. redux 提供了方法,可以帮助我们更加方便的合并 reducer
    2. combineReducers: 合并 reducer,得到一个新的 reducer,该新的 reducer 管理一个对象,该对象中的每一个属性交给对应的 reducer 管理。

Store

Store:用于保存数据

通过 createStore 方法创建的对象。

该对象的成员:

createStore

返回一个对象:

bindActionCreators

combineReducers

组装 reducers,返回一个 reducer,数据使用一个对象表示,对象的属性名与传递的参数对象保持一致

Redux 中间件(Middleware)

中间件:类似于插件,可以在不影响原本功能、并且不改动原本代码的基础上,对其功能进行增强。在 Redux 中,中间件主要用于增强 dispatch 函数。

实现 Redux 中间件的基本原理,是更改仓库中的 dispatch 函数。

Redux 中间件书写:

redux-actions

不维护了:https://github.com/redux-utilities/redux-actions#looking-for-maintainers

该库用于简化 action-types、action-creator 以及 reducer 官网文档:https://redux-actions.js.org/

createAction(s)

createAction

该函数用于帮助你创建一个 action 创建函数(action creator)

createActions

该函数用于帮助你创建多个 action 创建函数

handleAction(s)

handleAction

简化针对单个 action 类型的 reducer 处理,当它匹配到对应的 action 类型后,会执行对应的函数

handleActions

简化针对多个 action 类型的 reducre 处理

combineActions

配合 createActions 和 handleActions 两个函数,用于处理多个 action-type 对应同一个 reducer 处理函数。

- + diff --git a/react/component-communication.html b/react/component-communication.html index fc68bcdd..ae011bf9 100644 --- a/react/component-communication.html +++ b/react/component-communication.html @@ -13,7 +13,7 @@
Skip to content
On this page

React 组件通信

1. 父子组件的通信方式?

父组件向子组件通信:父组件通过 props 向子组件传递需要的信息。

子组件向父组件通信:: props+回调的方式。

2. 跨级组件的通信方式?

父组件向子组件的子组件通信,向更深层子组件通信:

  • 使用 props,利用中间组件层层传递,但是如果父组件结构较深,那么中间每一层组件都要去传递 props,增加了复杂度,并且这些 props 并不是中间组件自己需要的。
  • 使用 context,context 相当于一个大容器,可以把要通信的内容放在这个容器中,这样不管嵌套多深,都可以随意取用,对于跨越多层的全局数据可以使用 context 实现。

3. 非嵌套关系组件的通信方式?

即没有任何包含关系的组件,包括兄弟组件以及不在同一个父级中的非兄弟组件。

  • 可以使用自定义事件通信(发布订阅模式)
  • 可以通过 redux 等进行全局状态管理
  • 如果是兄弟组件通信,可以找到这两个兄弟节点共同的父节点, 结合父子间通信方式进行通信。
- + diff --git a/react/context.html b/react/context.html index e25faee5..c51df7fe 100644 --- a/react/context.html +++ b/react/context.html @@ -811,7 +811,7 @@ ); }

对 React context 的理解

在 React 中,数据传递一般使用 props 传递数据,维持单向数据流,这样可以让组件之间的关系变得简单且可预测,但是单项数据流在某些场景中并不适用。单纯一对的父子组件传递并无问题,但要是组件之间层层依赖深入,props 就需要层层传递显然,这样做太繁琐了。

Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。

可以把 context 当做是特定一个组件树内共享的 store,用来做数据传递。简单说就是,当你不想在组件树中通过逐层传递 props 或者 state 的方式来传递数据时,可以使用 Context 来实现跨层级的组件数据传递。

JS 的代码块在执行期间,会创建一个相应的作用域链,这个作用域链记录着运行时 JS 代码块执行期间所能访问的活动对象,包括变量和函数,JS 程序通过作用域链访问到代码块内部或者外部的变量和函数。

假如以 JS 的作用域链作为类比,React 组件提供的 Context 对象其实就好比一个提供给子组件访问的作用域,而 Context 对象的属性可以看成作用域上的活动对象。由于组件 的 Context 由其父节点链上所有组件通 过 getChildContext()返回的 Context 对象组合而成,所以,组件通过 Context 是可以访问到其父组件链上所有节点组件提供的 Context 的属性。

为什么 React 并不推荐优先考虑使用 Context?

- + diff --git a/react/dva.html b/react/dva.html index a4ac05c7..cf02e5fe 100644 --- a/react/dva.html +++ b/react/dva.html @@ -13,7 +13,7 @@
Skip to content
On this page

dva

DANGER

已经不维护了,仅供学习

官方网站:https://dvajs.com dva 不仅仅是一个第三方库,更是一个框架,它主要整合了 redux 的相关内容,让我们处理数据更加容易,实际上,dva 依赖了很多:react、react-router、redux、redux-saga、react-redux、connected-react-router 等。

dva 的使用

  1. dva 默认导出一个函数,通过调用该函数,可以得到一个 dva 对象
  2. dva 对象.router:路由方法,传入一个函数,该函数返回一个 React 节点,将来,应用程序启动后,会自动渲染该节点。
  3. dva 对象.start: 该方法用于启动 dva 应用程序,可以认为启动的就是 react 程序,该函数传入一个选择器,用于选中页面中的某个 dom 元素,react 会将内容渲染到该元素内部。
  4. dva 对象.model: 该方法用于定义一个模型,该模型可以理解为 redux 的 action、reducer、redux-saga 副作用处理的整合,整合成一个对象,将该对象传入 model 方法即可。
  5. namespace:命名空间,该属性是一个字符串,字符串的值,会被作为仓库中的属性保存
  6. state:该模型的默认状态
  7. reducers: 该属性配置为一个对象,对象中的每个方法就是一个 reducer,dva 约定,方法的名字,就是匹配的 action 类型
  8. effects: 处理副作用,底层是使用 redux-saga 实现的,该属性配置为一个对象,对象中的每隔方法均处理一个副作用,方法的名字,就是匹配的 action 类型。
    1. 函数的参数 1:action
    2. 参数 2:封装好的 saga/effects 对象
  9. subscriptions:配置为一个对象,该对象中可以写任意数量任意名称的属性,每个属性是一个函数,这些函数会在模型加入到仓库中后立即运行。
  10. 在 dva 中同步路由到仓库
  11. 在调用 dva 函数时,配置 history 对象
  12. 使用 ConnectedRouter 提供路由上下文
  13. 配置:
  14. history:同步到仓库的 history 对象
  15. initialState:创建 redux 仓库时,使用的默认状态
  16. onError: 当仓库的运行发生错误的时候,运行的函数
  17. onAction: 可以配置 redux 中间件
    1. 传入一个中间件对象
    2. 传入一个中间件数组
  18. onStateChange: 当仓库中的状态发生变化时运行的函数
  19. onReducer:对模型中的 reducer 的进一步封装
  20. onEffect:类似于对模型中的 effect 的进一步封装
  21. extraReducers:用于配置额外的 reducer,它是一个对象,对象的每一个属性是一个方法,每个方法就是一个需要合并的 reducer,方法名即属性名。
  22. extraEnhancers: 它是用于封装 createStore 函数的,dva 会将原来的仓库创建函数作为参数传递,返回一个新的用于创建仓库的函数。函数必须放置到数组中。

dva 插件

通过dva对象.use(插件),来使用插件,插件本质上就是一个对象,该对象与配置对象相同,dva 会在启动时,将传递的插件对象混合到配置中。

dva-loading

该插件会在仓库中加入一个状态,名称为 loading,它是一个对象,其中有以下属性

  • global:全局是否正在处理副作用(加载),只要有任何一个模型在处理副作用,则该属性为 true
  • models:一个对象,对象中的属性名以及属性的值,表示哪个对应的模型是否在处理副作用中(加载中)
  • effects:一个对象,对象中的属性名以及属性的值,表示是哪个 action 触发了副作用
- + diff --git a/react/event.html b/react/event.html index ef03c962..14bd2646 100644 --- a/react/event.html +++ b/react/event.html @@ -15,7 +15,7 @@
Skip to content
On this page

React 事件机制

React 事件机制

jsx
<div onClick={this.handleClick.bind(this)}>点我</div>
 
<div onClick={this.handleClick.bind(this)}>点我</div>
 

React 并不是将 click 事件绑定到了 div 的真实 DOM 上,而是在 document 处监听了所有的事件,当事件发生并且冒泡到 document 处的时候,React 将事件内容封装并交由真正的处理函数运行。这样的方式不仅仅减少了内存的消耗,还能在组件挂在销毁时统一订阅和移除事件。

除此之外,冒泡到 document 上的事件也不是原生的浏览器事件,而是由 react 自己实现的合成事件(SyntheticEvent)。因此如果不想要是事件冒泡的话应该调用 event.preventDefault()方法,而不是调用 event.stopProppagation()方法。

JSX 上写的事件并没有绑定在对应的真实 DOM 上,而是通过事件代理的方式,将所有的事件都统一绑定在了 document 上。这样的方式不仅减少了内存消耗,还能在组件挂载销毁时统一订阅和移除事件。

另外冒泡到 document 上的事件也不是原生浏览器事件,而是 React 自己实现的合成事件。因此我们如果不想要事件冒泡的话,调用 event.stopPropagation 是无效的,而应该调用 event.preventDefault

实现合成事件的目的如下:

  • 合成事件首先抹平了浏览器之间的兼容问题,另外这是一个跨浏览器原生事件包装器,赋予了跨浏览器开发的能力;
  • 对于原生浏览器事件来说,浏览器会给监听器创建一个事件对象。如果你有很多的事件监听,那么就需要分配很多的事件对象,造成高额的内存分配问题。但是对于合成事件来说,有一个事件池专门来管理它们的创建和销毁,当事件需要被使用时,就会从池子中复用对象,事件回调结束后,就会销毁事件对象上的属性,从而便于下次复用事件对象。

React 的事件和普通的 HTML 事件有什么不同?

区别:

  • 对于事件名称命名方式,原生事件为全小写,react 事件采用小驼峰;
  • 对于事件函数处理语法,原生事件为字符串,react 事件为函数;
  • react 事件不能采用 return false 的方式来阻止浏览器的默认行为,而必须要地明确地调用preventDefault()来阻止默认行为。

合成事件是 react 模拟原生 DOM 事件所有能力的一个事件对象,其优点如下:

  • 兼容所有浏览器,更好的跨平台;
  • 将事件统一存放在一个数组,避免频繁的新增与删除(垃圾回收)。
  • 方便 react 统一管理和事务机制。

事件的执行顺序为原生事件先执行,合成事件后执行,合成事件会冒泡绑定到 document 上,所以尽量避免原生事件与合成事件混用,如果原生事件阻止冒泡,可能会导致合成事件不执行,因为需要冒泡到 document 上合成事件才会执行。

React 组件中怎么做事件代理?它的原理是什么?

React 基于 Virtual DOM 实现了一个 SyntheticEvent 层(合成事件层),定义的事件处理器会接收到一个合成事件对象的实例,它符合 W3C 标准,且与原生的浏览器事件拥有同样的接口,支持冒泡机制,所有的事件都自动绑定在最外层上。

在 React 底层,主要对合成事件做了两件事:

  • 事件委派: React 会把所有的事件绑定到结构的最外层,使用统一的事件监听器,这个事件监听器上维持了一个映射来保存所有组件内部事件监听和处理函数。
  • 自动绑定: React 组件中,每个方法的上下文都会指向该组件的实例,即自动绑定 this 为当前组件。
- + diff --git a/react/hooks.html b/react/hooks.html index 6da23069..c1822479 100644 --- a/react/hooks.html +++ b/react/hooks.html @@ -303,7 +303,7 @@

这里,首先假定 ExampleComponent 可见,然后再改变它的状态,让它不可见 。映射为真实的 DOM 操作是这样的,React 会创建一个 div 节点。

javascript
<div class="visible">visbile</div>
 
<div class="visible">visbile</div>
 

当把 visbile 的值变为 false 时,就会替换 class 属性为 hidden,并重写内部的 innerText 为 hidden。这样一个生成补丁、更新差异的过程统称为 diff 算法。

diff 算法可以总结为三个策略,分别从树、组件及元素三个层面进行复杂度的优化:

策略一:忽略节点跨层级操作场景,提升比对效率。(基于树进行对比)

这一策略需要进行树比对,即对树进行分层比较。树比对的处理手法是非常“暴力”的,即两棵树只对同一层次的节点进行比较,如果发现节点已经不存在了,则该节点及其子节点会被完全删除掉,不会用于进一步的比较,这就提升了比对效率。

策略二:如果组件的 class 一致,则默认为相似的树结构,否则默认为不同的树结构。(基于组件进行对比)

在组件比对的过程中:

只要父组件类型不同,就会被重新渲染。这也就是为什么 shouldComponentUpdate、PureComponent 及 React.memo 可以提高性能的原因。

策略三:同一层级的子节点,可以通过标记 key 的方式进行列表对比。(基于节点进行对比)

元素比对主要发生在同层级中,通过标记节点操作生成补丁。节点操作包含了插入、移动、删除等。其中节点重新排序同时涉及插入、移动、删除三个操作,所以效率消耗最大,此时策略三起到了至关重要的作用。通过标记 key 的方式,React 可以直接移动 DOM 节点,降低内耗。

React key

Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。

在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染此外,React 还需要借助 Key 值来判断元素与本地状态的关联关系。

注意事项:

虚拟 DOM 的引入与直接操作原生 DOM 相比,哪一个效率更高,为什么

虚拟 DOM 相对原生的 DOM 不一定是效率更高,如果只修改一个按钮的文案,那么虚拟 DOM 的操作无论如何都不可能比真实的 DOM 操作更快。在首次渲染大量 DOM 时,由于多了一层虚拟 DOM 的计算,虚拟 DOM 也会比 innerHTML 插入慢。它能保证性能下限,在真实 DOM 操作的时候进行针对性的优化时,还是更快的。所以要根据具体的场景进行探讨。

在整个 DOM 操作的演化过程中,其实主要矛盾并不在于性能,而在于开发者写得爽不爽,在于研发体验/研发效率。虚拟 DOM 不是别的,正是前端开发们为了追求更好的研发体验和研发效率而创造出来的高阶产物。虚拟 DOM 并不一定会带来更好的性能,React 官方也从来没有把虚拟 DOM 作为性能层面的卖点对外输出过。虚拟 DOM 的优越之处在于,它能够在提供更爽、更高效的研发模式(也就是函数式的 UI 编程方式)的同时,仍然保持一个还不错的性能。

React 与 Vue 的 diff 算法

diff 算法是指生成更新补丁的方式,主要应用于虚拟 DOM 树变化后,更新真实 DOM。所以 diff 算法一定存在这样一个过程:触发更新 → 生成补丁 → 应用补丁。

React 的 diff 算法,触发更新的时机主要在 state 变化与 hooks 调用之后。此时触发虚拟 DOM 树变更遍历,采用了深度优先遍历算法。但传统的遍历方式,效率较低。为了优化效率,使用了分治的方式。将单一节点比对转化为了 3 种类型节点的比对,分别是树、组件及元素,以此提升效率。

以上是经典的 React diff 算法内容。自 React 16 起,引入了 Fiber 架构。为了使整个更新过程可随时暂停恢复,节点与树分别采用了 FiberNode 与 FiberTree 进行重构。fiberNode 使用了双链表的结构,可以直接找到兄弟节点与子节点。整个更新过程由 current 与 workInProgress 两株树双缓冲完成。workInProgress 更新完成后,再通过修改 current 相关指针指向新节点。

Vue 的整体 diff 策略与 React 对齐,虽然缺乏时间切片能力,但这并不意味着 Vue 的性能更差,因为在 Vue 3 初期引入过,后期因为收益不高移除掉了。除了高帧率动画,在 Vue 中其他的场景几乎都可以使用防抖和节流去提高响应性能。

- + diff --git a/react/index.html b/react/index.html index 9e885b78..673594af 100644 --- a/react/index.html +++ b/react/index.html @@ -929,7 +929,7 @@ ); }

错误边界

默认情况下,若一个组件在渲染期间(render)发生错误,会导致整个组件树全部被卸载

错误边界:是一个组件,该组件会捕获到渲染期间(render)子组件发生的错误,并有能力阻止错误继续传播

让某个组件捕获错误

  1. 编写生命周期函数 getDerivedStateFromError
    1. 静态函数
    2. 运行时间点:渲染子组件的过程中,发生错误之后,在更新页面之前
    3. 注意:只有子组件发生错误,才会运行该函数。自己发生错误处理不了
    4. 该函数返回一个对象,React 会将该对象的属性覆盖掉当前组件的 state
    5. 参数:错误对象
    6. 通常,该函数用于改变状态
  2. 编写生命周期函数 componentDidCatch
    1. 实例方法
    2. 运行时间点:渲染子组件的过程中,发生错误,更新页面之后,由于其运行时间点比较靠后,因此不太会在该函数中改变状态
    3. 通常,该函数用于记录错误消息

细节

某些错误,错误边界组件无法捕获

  1. 自身的错误
  2. 异步的错误
  3. 事件中的错误

这些错误,需要用 try catch 处理

总结:仅处理渲染子组件期间的同步错误

React 中的事件

这里的事件:React 内置的 DOM 组件中的事件

  1. 给 document 注册事件
  2. 几乎所有的元素的事件处理,均在 document 的事件中处理
    1. 一些不冒泡的事件,是直接在元素上监听
    2. 一些 document 上面没有的事件,直接在元素上监听
  3. 在 document 的事件处理,React 会根据虚拟 DOM 树的完成事件函数的调用
  4. React 的事件参数,并非真实的 DOM 事件参数,是 React 合成的一个对象,该对象类似于真实 DOM 的事件参数
    1. stopPropagation,阻止事件在虚拟 DOM 树中冒泡
    2. nativeEvent,可以得到真实的 DOM 事件对象
    3. 为了提高执行效率,React 使用事件对象池来处理事件对象

注意事项

  1. 如果给真实的 DOM 注册事件,阻止了事件冒泡,则会导致 react 的相应事件无法触发
  2. 如果给真实的 DOM 注册事件,事件会先于 React 事件运行
  3. 通过 React 的事件中阻止事件冒泡,无法阻止真实的 DOM 事件冒泡
  4. 可以通过 nativeEvent.stopImmediatePropagation(),阻止 document 上剩余事件的执行
  5. 在事件处理程序中,不要异步的使用事件对象,如果一定要使用,需要调用 persist 函数
- + diff --git a/react/lifecycle.html b/react/lifecycle.html index f679b273..c7e2edd4 100644 --- a/react/lifecycle.html +++ b/react/lifecycle.html @@ -279,7 +279,7 @@ }

state 和 props 触发更新的生命周期分别有什么区别?

state 更新流程:

这个过程当中涉及的函数:

  1. shouldComponentUpdate: 当组件的 state 或 props 发生改变时,都会首先触发这个生命周期函数。它会接收两个参数:nextProps, nextState——它们分别代表传入的新 props 和新的 state 值。拿到这两个值之后,我们就可以通过一些对比逻辑来决定是否有 re-render(重渲染)的必要了。如果该函数的返回值为 false,则生命周期终止,反之继续;

注意:此方法仅作为性能优化的方式而存在。不要企图依靠此方法来“阻止”渲染,因为这可能会产生 bug。应该考虑使用内置的 PureComponent 组件,而不是手动编写 shouldComponentUpdate()

  1. componentWillUpdate:当组件的 state 或 props 发生改变时,会在渲染之前调用 componentWillUpdate。componentWillUpdate 是 React16 废弃的三个生命周期之一。过去,我们可能希望能在这个阶段去收集一些必要的信息(比如更新前的 DOM 信息等等),现在我们完全可以在 React16 的 getSnapshotBeforeUpdate 中去做这些事;
  2. componentDidUpdate:componentDidUpdate() 会在 UI 更新后会被立即调用。它接收 prevProps(上一次的 props 值)作为入参,也就是说在此处我们仍然可以进行 props 值对比(再次说明 componentWillUpdate 确实鸡肋哈)。

props 更新流程:

相对于 state 更新,props 更新后唯一的区别是增加了对 componentWillReceiveProps 的调用。关于 componentWillReceiveProps,需要知道这些事情:

React 中发起网络请求应该在哪个生命周期中进行?为什么?

对于异步请求,最好放在 componentDidMount 中去操作,对于同步的状态改变,可以放在 componentWillMount 中,一般用的比较少。

如果认为在 componentWillMount 里发起请求能提早获得结果,这种想法其实是错误的,通常 componentWillMount 比 componentDidMount 早不了多少微秒,网络上任何一点延迟,这一点差异都可忽略不计。

react 的生命周期: constructor() -> componentWillMount() -> render() -> componentDidMount()

上面这些方法的调用是有次序的,由上而下依次调用。

总结:

React 16 中新生命周期有哪些

关于 React16 开始应用的新生命周期:

可以看出,React16 自上而下地对生命周期做了另一种维度的解读:

与此同时,新的生命周期在流程方面,仍然遵循“挂载”、“更新”、“卸载”这三个广义的划分方式。它们分别对应到:

- + diff --git a/react/react-interview.html b/react/react-interview.html index ed5dc339..18266d2e 100644 --- a/react/react-interview.html +++ b/react/react-interview.html @@ -1443,7 +1443,7 @@ name: PropTypes.string, };

当然,如果项目汇中使用了 TypeScript,那么就可以不用 PropTypes 来校验,而使用 TypeScript 定义接口来校验 props。

- + diff --git a/react/react-redux-router.html b/react/react-redux-router.html index 1aa49a39..b91a7360 100644 --- a/react/react-redux-router.html +++ b/react/react-redux-router.html @@ -13,7 +13,7 @@
Skip to content
On this page

react-redux

  • React: 组件化的 UI 界面处理方案
  • React-Router: 根据地址匹配路由,最终渲染不同的组件
  • Redux:处理数据以及数据变化的方案(主要用于处理共享数据)

如果一个组件,仅用于渲染一个 UI 界面,而没有状态(通常是一个函数组件),该组件叫做展示组件 如果一个组件,仅用于提供数据,没有任何属于自己的 UI 界面,则该组件叫做容器组件,容器组件纯粹是为了给其他组件提供数据。

react-redux 库:链接 redux 和 react

  • Provider 组件:没有任何 UI 界面,该组件的作用,是将 redux 的仓库放到一个上下文中。
  • connect:高阶组件,用于链接仓库和组件的
    • 细节一:如果对返回的容器组件加上额外的属性,则这些属性会直接传递到展示组件
    • 第一个参数:mapStateToProps:
      • 参数 1:整个仓库的状态
      • 参数 2:使用者传递的属性对象
    • 第二个参数:
      • 情况 1:传递一个函数 mapDispatchToProps
        • 参数 1:dispatch 函数
        • 参数 2:使用者传递的属性对象
        • 函数返回的对象会作为属性传递到展示组件中(作为事件处理函数存在)
      • 情况 2:传递一个对象,对象的每个属性是一个 action 创建函数,当事件触发时,会自动的 dispatch 函数返回的 action
    • 细节二:如果不传递第二个参数,通过 connect 连接的组件,会自动得到一个属性:dispatch,使得组件有能力自行触发 action,但是,不推荐这样做。

知识

  1. chrome 插件:redux-devtools
  2. 使用 npm 安装第三方库:redux-devtools-extension

redux 和 router 的结合(connected-react-router)

希望把路由信息放进仓库统一管理的时候才需要这个库

用于将 redux 和 react-router 进行结合

本质上,router 中的某些数据可能会跟数据仓库中的数据进行联动

该组件会将下面的路由数据和仓库保持同步

  1. action:它不是 redux 的 action,它表示当前路由跳转的方式(PUSH、POP、REPLACE)
  2. location:它记录了当前的地址信息

该库中的内容:

connectRouter

这是一个函数,调用它,会返回一个用于管理仓库中路由信息的 reducer,该函数需要传递一个参数,参数是一个 history 对象。该对象,可以使用第三方库 history 得到。

routerMiddleware

该函数会返回一个 redux 中间件,用于拦截一些特殊的 action

ConnectedRouter

这是一个组件,用于向上下文提供一个 history 对象和其他的路由信息(与 react-router 提供的信息一致)

之所以需要新制作一个组件,是因为该库必须保证整个过程使用的是同一个 history 对象

一些 action 创建函数

  • push
  • replace
- + diff --git a/react/render.html b/react/render.html index da157c11..ecb3f667 100644 --- a/react/render.html +++ b/react/render.html @@ -241,7 +241,7 @@ } }

面试题 3:div 包住两个 App,再问执行顺序

队列:Comp1 App Comp1 App 左边执行完在执行右边而已(递归)

为什么不能写对象

对象可以构成 React 元素,但是没法构建节点,节点需要渲染,{a:1,b:2} 没法渲染

更新节点

更新的场景:

  1. 重新调用 ReactDOM.render,触发根节点更新
  2. 在类组件的实例对象中调用 setState,会导致该实例所在的节点更新

节点的更新

以上两点的后续步骤:

  1. 更新虚拟 DOM 树
  2. 完成真实的 DOM 更新
  3. 依次调用执行队列中的 componentDidMount
  4. 依次调用执行队列中的 getSnapshotBeforeUpdate
  5. 依次调用执行队列中的 componentDidUpdate

对比更新

将新产生的节点,对比之前虚拟 DOM 中的节点,发现差异,完成更新

问题:对比之前 DOM 树中哪个节点

React 为了提高对比效率,做出以下假设

  1. 假设节点不会出现层次的移动(对比时,直接找到旧树中对应位置的节点进行对比)
  2. 不同的节点类型会生成不同的结构
    1. 相同的节点类型:节点本身类型相同,如果是由 React 元素生成,type 值还必须一致
    2. 其他的,都属于不相同的节点类型
  3. 多个兄弟通过唯一标识(key)来确定对比的新节点

key 值的作用:用于通过旧节点,寻找对应的新节点,如果某个旧节点有 key 值,则其更新时,会寻找相同层级中的相同 key 值的节点,进行对比。

key 值应该在一个范围内唯一(兄弟节点中),并且应该保持稳定

找到了对比的目标

判断节点类型是否一致

根据不同的节点类型,做不同的事情

空节点:不做任何事情

DOM 节点

  1. 直接重用之前的真实 DOM 对象
  2. 将其属性的变化记录下来,以待将来统一完成更新(现在不会真正的变化)
  3. 遍历该新的 React 元素的子元素,递归对比更新

文本节点

  1. 直接重用之前的真实 DOM 对象
  2. 将新的文本变化记录下来,将来统一完成更新

组件节点

函数组件:重新调用函数,得到一个节点对象,进入递归对比更新

类组件

  1. 重用之前的实例
  2. 调用生命周期方法 getDerivedStateFromProps
  3. 调用生命周期方法 shouldComponentUpdate,若该方法返回 false,终止
  4. 运行 render,得到新的节点对象,进入递归对比更新
  5. 将该对象的 getSnapshotBeforeUpdate 加入队列
  6. 将该对象的 componentDidUpdate 加入队列

数组节点:遍历数组进行递归对比更新

整体上,卸载旧的节点,全新创建新的节点

创建新节点

进入新节点的挂载流程

卸载旧节点

  1. 文本节点、DOM 节点、数组节点、空节点、函数组件节点:直接放弃该节点,如果节点有子节点,递归卸载节点
  2. 类组件节点
    1. 直接放弃该节点
    2. 调用该节点的 componentWillUnMount 函数
    3. 递归卸载子节点

没有找到对比的目标

新的 DOM 树中有节点被删除

新的 DOM 树中有节点添加

- + diff --git a/react/transition.html b/react/transition.html index b72fde40..11bedbf3 100644 --- a/react/transition.html +++ b/react/transition.html @@ -13,7 +13,7 @@
Skip to content
On this page

React 动画

React 动画库:react-transition-group 文档在 npm 搜索https://reactcommunity.org/react-transition-group/

React 动画 - CSSTransition

当进入时,发生:

  1. 为 CSSTransition 内部的 DOM 根元素(后续统一称之为 DOM 元素)添加样式 enter
  2. 在一下帧(enter 样式已经完全应用到了元素),立即为该元素添加样式 enter-active
  3. 当 timeout 结束后,去掉之前的样式,添加样式 enter-done

当退出时,发生:

  1. 为 CSSTransition 内部的 DOM 根元素(后续统一称之为 DOM 元素)添加样式 exit
  2. 在一下帧(exit 样式已经完全应用到了元素),立即为该元素添加样式 exit-active
  3. 当 timeout 结束后,去掉之前的样式,添加样式 exit-done

设置classNames属性,可以指定类样式的名称

  1. 字符串:为类样式添加前缀
  2. 对象:为每个类样式指定具体的名称(非前缀)

关于首次渲染时的类样式,appear、apear-active、apear-done,它和 enter 的唯一区别在于完成时,会同时加入 apear-done 和 enter-done

还可以与 Animate.css 联用

React 动画 - SwitchTransition

和 CSSTransition 的区别:用于有秩序的切换内部组件

默认情况下:out-in 先退出后进入

  1. 当 key 值改变时,会将之前的 DOM 根元素添加退出样式(exit,exit-active)
  2. 退出完成后,将该 DOM 元素移除
  3. 重新渲染内部 DOM 元素
  4. 为新渲染的 DOM 根元素添加进入样式(enter, enter-active, enter-done)

in-out:

  1. 重新渲染内部 DOM 元素,保留之前的元素
  2. 为新渲染的 DOM 根元素添加进入样式(enter, enter-active, enter-done)
  3. 将之前的 DOM 根元素添加退出样式(exit,exit-active)
  4. 退出完成后,将该 DOM 元素移除

该库寻找 dom 元素的方式,是使用已经过时的 API:findDomNode,该方法可以找到某个组件下的 DOM 根元素,先保留,创建新的之后在删除

React 动画 - TransitionGroup

该组件的 children,接收多个 Transition 或 CSSTransition 组件,该组件用于根据这些子组件的 key 值,控制他们的进入和退出状态

- + diff --git a/react/umi.html b/react/umi.html index 47cc9c3b..9207d5d9 100644 --- a/react/umi.html +++ b/react/umi.html @@ -13,7 +13,7 @@
Skip to content
On this page

umijs 简介

官网:https://umijs.org/

umijs, nextjs(ssr 服务端渲染),antd,antd-pro(antd+umijs)

  • 插件化
  • 开箱即用
  • 约定式路由

全局安装 umi

提供了一个命令行工具:umi,通过该命令可以对 umi 工程进行操作

umi 还可以使用对应的脚手架

  • dev: 使用开发模式启动工程
  • build:打包

约定式路由

umi 对路由的处理,主要通过两种方式:

  1. 约定式:使用约定好的文件夹和文件,来代表页面,umi 会根据开发者书写的页面,生成路由配置。
  2. 配置式:直接书写路由配置文件

路由匹配

  • umi 约定,工程中的 pages 文件夹中存放的是页面。如果工程包含 src 目录,则 src/pages 是页面文件夹。

  • umi 约定,页面的文件名,以及页面的文件路径,是该页面匹配的路由

  • umi 约定,如果页面的文件名是 index(不写 index 才能访问,写了反而不能访问了),则可以省略文件名(首页)(注意避免文件名和当前目录中的文件夹名称相同)

  • umi 约定,如果 src/layout 目录存在,则该目录中的 index.js 表示的是全局的通用布局,布局中的 children 则会添加具体的页面。

  • umi 约定,如果 pages 文件夹中包含_layout.js,则 layout.js 所在的目录以及其所有的子目录中的页面,共用该布局。

  • 404 约定,umi 约定,pages/404.js,表示 404 页面,如果路由无匹配,则会渲染该页面。该约定在开发模式中无效,只有部署后生效。

  • 使用$名称,会产生动态路由

路由跳转

  • 跳转链接: 导入umi/linkumi/navlink
  • 代码跳转: 导入umi/router

导入模块时,@表示 src 目录

路由信息的获取

所有的页面、布局组件,都会通过属性 props,收到下面的属性

  • match:等同于 react-router 的 match
  • history:等同于 react-router 的 history(history.location.query 被封装成了一个对象,使用的是 query-string 库进行的封装)
  • location:等同于 react-router 的 location(location.query 被封装成了一个对象,使用的是 query-string 库进行的封装)
  • route:对应的是路由配置

如果需要在普通组件中获取路由信息,则需要使用 withRouter 封装,可以通过umi/withRouter导入

配置式路由

当使用了路由配置后,约定式路由全部失效。

两种方式书写 umi 配置:

  1. 使用根目录下的文件.umirc.js
  2. 使用根目录下的文件config/config.js

进行路由配置时,每个配置就是一个匹配规则,并且,每个配置是一个对象,对象中的某些属性,会直接形成 Route 组件的属性

注意:

  • component 配置项,需要填写页面组件的路径,路径相对于 pages 文件夹
  • 如果配置项没有 exact,则会自动添加 exact 为 true
  • 每一个路由配置,可以添加任何属性
  • Routes 属性是一个数组,数组的每一项是一个组件路径,路径相对于项目根目录,当匹配到路由后,会转而渲染该属性指定的组件,并会将 component 组件作为 children 放到匹配的组件中

路由配置中的信息,同样可以放到约定式路由中,方式是,为约定式路由添加第一个文档注释(注释的格式的 YAML),需要将注释放到最开始的位置

YAML 格式

  • 键值对,冒号后需要加上空格
  • 如果某个属性有多个键或多个值,需要进行缩进(空格,不能 tab)

使用 dva

官方插件集 umi-plugin-react 文档:https://umijs.org/zh/plugin/umi-plugin-react.html

dva 插件和 umi 整合后,将模型分为两种:

  1. 全局模型:所有页面通用,工程一开始启动后,模型就会挂载到仓库
  2. 局部模型:只能被某些页面使用,访问具体的页面时才会挂载到仓库

定义全局模型

src/models目录下定义的 js 文件都会被看作是全局模型,默认情况下,模型的命名空间和文件名一致。

定义局部模型

局部模型定义在 pages 文件夹或其子文件夹中,在哪个文件夹定义的模型,会被该文件夹中的所有页面以及子页面、以及该文件夹的祖先文件夹中的页面所共享。

局部模型的定义和全局模型的约定类似,需要创建一个 models 文件夹

使用样式

解决两个问题:

  1. 保证类样式名称的唯一性:css-module
  2. 样式代码的重复:less 或 sass

局部样式和全局样式

底层使用了 webpack 的加载器:css-loader(内部包含了 css-module 的功能)

css 文件 -> css-module -> 对象

  1. 某个组件特有的样式,不与其他组件共享,通常,将该样式文件与组件放置在同一个目录(非强制性)(要保证类样式名称唯一)
  2. 如果某些样式可能被某些组件共享,这样的样式,通常放到 assets/css 文件夹中。(要保证类样式名称唯一)
  3. 全局样式,名称一定唯一,不需要 css-module 处理。umijs 约定,src/global.css 样式,是全局样式,不会交给 css-module 处理。

less

less 代码 -> less-loader -> css 代码 -> css-module -> 对象

代理和数据模拟

代理

代理用于解决跨域问题

配置.umirc.js中的 proxy,配置方式和 devServer 中的 proxy 配置相同

数据模拟

用于解决前后端协同开发的问题

数据模拟可以让前端开发者在开发时,无视后端接口是否真正完成,因为使用的是模拟的数据

umijs 约定:

  1. mock 文件夹中的文件
  2. src/pages 文件夹中的_mock.js 文件

以上两种 JS 文件,均会被 umijs 读取,并作为数据模拟的配置

可以自行发挥,添加模拟数据,通常,我们会和 mockjs 配合。

配置

额外的约定文件

  • src/pages/document.ejs: 页面模板文件
  • src/global.js:在 umi 最开始启动时运行的 js 文件
  • src/app.js:作运行时配置的代码
    • patchRoutes: 函数,该函数会在 umi 读取完所有静态路由配置后执行
    • dva
      • config: 相当于 new dva(配置)
      • plugins: 相当于 dva.use(插件)
  • .env: 配置环境变量,这些变量会在 umi 编译期间发挥作用
    • UMI_ENV:umi 的环境变量值,可以是任意值,该值会影响到.umirc.js
    • PORT
    • MOCK

umirc 配置

umi 配置

书写在.umirc.js 文件中的配置

  • plugins:配置 umijs 的插件
  • routes:配置路由(会导致约定式路由失效)
  • history:history 对象模式(默认是 browser)
  • outputPath:使用 umi build 后,打包的目录名称,默认./dist
  • base: 相当于之前 BrowserRouter 中的 basename
  • publicPath: 指定静态资源所在的目录
  • exportStatic: 开启该配置后,会打包成多个静态页面,每个页面对应一个路由,开启多静态页面应用的前提条件是:没有动态路由

webpack 配置

umi 脚手架

create-umi

- + diff --git a/react/utils.html b/react/utils.html index f58088bb..ec7153d5 100644 --- a/react/utils.html +++ b/react/utils.html @@ -13,7 +13,7 @@
Skip to content
On this page

工具

严格模式

StrictMode(React.StrictMode),本质是一个组件,该组件不进行 UI 渲染(React.Fragment <> </>),它的作用是,在渲染内部组件时,发现不合适的代码。

  • 识别不安全的生命周期
  • 关于使用过时字符串 ref API 的警告
  • 关于使用废弃的 findDOMNode 方法的警告
  • 检测意外的副作用
    • React 要求,副作用代码仅出现在以下生命周期函数中
    • ComponentDidMount
    • ComponentDidUpdate
    • ComponentWillUnMount

副作用:一个函数中,做了一些会影响函数外部数据的事情,例如:

  1. 异步处理
  2. 改变参数值
  3. setState
  4. 本地存储
  5. 改变函数外部的变量

相反的,如果一个函数没有副作用,则可以认为该函数是一个纯函数

在严格模式下,虽然不能监控到具体的副作用代码,但它会将不能具有副作用的函数调用两遍,以便发现问题。(这种情况,仅在开发模式下有效)

  • 检测过时的 context API

Profiler

性能分析工具

分析某一次或多次提交(更新),涉及到的组件的渲染时间

火焰图:得到某一次提交,每个组件总的渲染时间以及自身的渲染时间

排序图:得到某一次提交,每个组件自身渲染时间的排序

组件图:某一个组件,在多次提交中,自身渲染花费的时间

- + diff --git a/ts/TypeScript-onePage.html b/ts/TypeScript-onePage.html index 753af519..f32b438f 100644 --- a/ts/TypeScript-onePage.html +++ b/ts/TypeScript-onePage.html @@ -2207,7 +2207,7 @@

声明文件

概述、编写、发布

概述

  1. 什么是声明文件?

.d.ts结尾的文件

  1. 声明文件有什么作用?

为 JS 代码提供类型声明

  1. 声明文件的位置

编写声明文件

手动编写   自动生成

工程是使用 ts 开发的,发布(编译)之后,是 js 文件,发布的是 js 文件。

如果发布的文件,需要其他开发者使用,可以使用声明文件,来描述发布结果中的类型。

配置tsconfig.json中的declaration:true即可

  1. 对已有库,它是使用 js 书写而成,并且更改该库的代码为 ts 成本较高,可以手动编写声明文件
  2. 对一些第三方库,它们使用 js 书写而成,并且这些第三方库没有提供声明文件,可以手动编写声明文件。

全局声明

声明一些全局的对象、属性、变量

namespace: 表示命名空间,可以将其认为是一个对象,命名空间中的内容,必须通过命名空间.成员名访问

模块声明

三斜线指令

在一个声明文件中,包含另一个声明文件

发布

  1. 当前工程使用 ts 开发

编译完成后,将编译结果所在文件夹直接发布到 npm 上即可

  1. 为其他第三方库开发的声明文件

发布到@types/**中。

1) 进入 github 的开源项目:https://github.com/DefinitelyTyped/DefinitelyTyped

2) fork 到自己的开源库中

3) 从自己的开源库中克隆到本地

4) 本地新建分支(例如:mylodash4.3),在新分支中进行声明文件的开发

在types目录中新建文件夹,在新的文件夹中开发声明文件
 
在types目录中新建文件夹,在新的文件夹中开发声明文件
 

5) push 分支到你的开源库

6) 到官方的开源库中,提交 pull request

7) 等待官方管理员审核(1 天)

审核通过之后,会将你的分支代码合并到主分支,然后发布到 npm。

之后,就可以通过命令npm install @types/你发布的库名

- + diff --git a/vue/SSR.html b/vue/SSR.html index 86b57c77..810b8203 100644 --- a/vue/SSR.html +++ b/vue/SSR.html @@ -13,7 +13,7 @@
Skip to content
On this page

SSR

SPA

SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。

优点:

  • 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
  • 基于上面一点,SPA 相对对服务器压力小;
  • 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;

缺点:

  • 初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载;
  • 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
  • SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。

Vue SSR 的实现原理

  • app.js 作为客户端与服务端的公用入口,导出 Vue 根实例,供客户端 entry 与服务端 entry 使用。客户端 entry 主要作用挂载到 DOM 上,服务端 entry 除了创建和返回实例,还需要进行路由匹配与数据预获取。
  • webpack 为客服端打包一个 ClientBundle,为服务端打包一个 ServerBundle
  • 服务器接收请求时,会根据 url,加载相应组件,获取和解析异步数据,创建一个读取 Server BundleBundleRenderer,然后生成 html 发送给客户端。
  • 客户端混合,客户端收到从服务端传来的 DOM 与自己的生成的 DOM 进行对比,把不相同的 DOM 激活,使其可以能够响应后续变化,这个过程称为客户端激活(也就是转换为单页应用)。为确保混合成功,客户 端与服务器端需要共享同一套数据。在服务端,可以在渲染之前获取数据,填充到 store 里,这样,在客户端挂载到 DOM 之前,可以直接从 store 里取数据。首屏的动态数据通过 window.INITIAL_STATE 发送到客户端
  • VueSSR 的原理,主要就是通过 vue-server-rendererVue 的组件输出成一个完整 HTML,输出到客户端,到达客户端后重新展开为一个单页应用。
- + diff --git a/vue/challages.html b/vue/challages.html index e5de5da0..b9033c29 100644 --- a/vue/challages.html +++ b/vue/challages.html @@ -495,7 +495,7 @@ // 设置没有的属性,删除属性监控不到,所以要用$set,$delete - + diff --git a/vue/component-communication.html b/vue/component-communication.html index 880e5cdb..d1b738ab 100644 --- a/vue/component-communication.html +++ b/vue/component-communication.html @@ -399,7 +399,7 @@ } }

缺点:无法跟踪数据的变化,如果组件数变得复杂,任何组件都有权改动他,仓库数据出了问题,难以判断那个步骤出现问题。

eventbus

组件通知事件总线发生了某件事,事件总线通知其他监听该事件的所有组件运行某个函数

$emit$listeners通信的异同

相同点:均可实现子组件向父组件传递消息

差异点:

- + diff --git a/vue/computed.html b/vue/computed.html index ab1d9cb3..6466aa8f 100644 --- a/vue/computed.html +++ b/vue/computed.html @@ -13,7 +13,7 @@
Skip to content
On this page

computed

Vue2 中 computed 源码解读

methods

vue 对 methods 的处理比较简单,只需要遍历 methods 配置中的每个属性,将其对应的函数使用 bind 绑定当前组件实例后复制其引用到组件实例中即可

computed

当组件实例触发生命周期函数beforeCreate后,它会做一系列事情,其中就包括对 computed 的处理

它会遍历 computed 配置中的所有属性,为每一个属性创建一个 Watcher 对象,并传入一个函数,该函数的本质其实就是 computed 配置中的 getter,这样一来,getter 运行过程中就会收集依赖

但是和渲染函数不同,为计算属性创建的 Watcher 不会立即执行,因为要考虑到该计算属性是否会被渲染函数使用,如果没有使用,就不会得到执行。因此,在创建 Watcher 的时候,它使用了 lazy 配置,lazy 配置可以让 Watcher 不会立即执行。

收到lazy的影响,Watcher 内部会保存两个关键属性来实现缓存,一个是value,一个是dirty

value属性用于保存 Watcher 运行的结果,受lazy的影响,该值在最开始是undefined

dirty属性用于指示当前的value是否已经过时了,即是否为脏值,受lazy的影响,该值在最开始是true

Watcher创建好后,vue 会使用代理模式,将计算属性挂载到组件实例中

当读取计算属性时,vue 检查其对应的 Watcher 是否是脏值,如果是,则运行函数,计算依赖,并得到对应的值,保存在 Watcher 的 value 中,然后设置 dirty 为 false,然后返回。

如果 dirty 为 false,则直接返回 watcher 的 value,即为缓存的原理 巧妙的是,在依赖收集时,被依赖的数据不仅会收集到计算属性的 Watcher,还会收集到组件的 Watcher

当计算属性的依赖变化时,会先触发计算属性的 Watcher执行,此时,它只需设置**dirty**为 true 即可,不做任何处理。

由于依赖同时会收集到组件的 Watcher,因此组件会重新渲染,而重新渲染时又读取到了计算属性,由于计算属性目前已为 dirty,因此会重新运行 getter 进行运算

而对于计算属性的 setter,则极其简单,当设置计算属性时,直接运行 setter 即可

Vue3 中 computed 源码解读

TODO

常见问题

面试官:computed 和 methods 有什么区别?

我:

  1. 在使用时,computed 当做属性使用,而 methods 则当做方法调用
  2. computed 可以具有 getter 和 setter,因此可以赋值,而 methods 不行
  3. computed 无法接收多个参数,而 methods 可以
  4. computed 具有缓存,而 methods 没有

面试官:回去等通知吧!

更深入的回答:↑

watch 与 computed 的区别是什么?

  1. 都是观察数据变化的(相同)
  2. 计算属性将会混入到 vue 的实例中,所以需要监听自定义变量;watch 监听 data 、props 里面数据的变化;
  3. computed 有缓存,它依赖的值变了才会重新计算,watch 没有;
  4. watch 支持异步,computed 不支持;
  5. watch 是一对多(监听某一个值变化,执行对应操作);computed 是多对一(监听属性依赖于其他属性)
  6. watch 监听函数接收两个参数,第一个是最新值,第二个是输入之前的值;
  7. computed 属性是函数时,都有 get 和 set 方法,默认走 get 方法,get 必须有返回值(return)

watch 的 参数:deep:深度监听;immediate :组件加载立即触发回调函数执行

对于 Computed:

  • 它支持缓存,只有依赖的数据发生了变化,才会重新计算
  • 不支持异步,当 Computed 中有异步操作时,无法监听数据的变化
  • computed 的值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于 data 声明过,或者父组件传递过来的 props 中的数据进行计算的。
  • 如果一个属性是由其他属性计算而来的,这个属性依赖其他的属性,一般会使用 computed
  • 如果 computed 属性的属性值是函数,那么默认使用 get 方法,函数的返回值就是属性的属性值;在 computed 中,属性有一个 get 方法和一个 set 方法,当数据发生变化时,会调用 set 方法。

对于 Watch:

  • 它不支持缓存,数据变化时,它就会触发相应的操作
  • 支持异步监听
  • 监听的函数接收两个参数,第一个参数是最新的值,第二个是变化之前的值
  • 当一个属性发生变化时,就需要执行相应的操作
  • 监听数据必须是 data 中声明的或者父组件传递过来的 props 中的数据,当发生变化时,会触发其他操作,函数有两个的参数:
    • immediate:组件加载立即触发回调函数
    • deep:深度监听,发现数据内部的变化,在复杂数据类型中使用,例如数组中的对象发生变化。需要注意的是,deep 无法监听到数组和对象内部的变化。

当想要执行异步或者昂贵的操作以响应不断的变化时,就需要使用 watch。 总结:

  • computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。
  • watch 侦听器 : 更多的是观察的作用,无缓存性,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作。

运用场景:

  • 当需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时都要重新计算。
  • 当需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许执行异步操作 ( 访问一个 API ),限制执行该操作的频率,并在得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

Vue 中要获取当前时间你会放到 computed 还是 methods 里?

放在 computed 里面。因为 computed 只有在它的相关依赖发生改变时才会重新求值。相比而言,方法只要发生重新渲染,methods 调用总会执行所有函数。

- + diff --git a/vue/diff.html b/vue/diff.html index 96593241..a4b25419 100644 --- a/vue/diff.html +++ b/vue/diff.html @@ -205,7 +205,7 @@ }; </script>

解决:只要不是索引即可,比如,直接使用 item。这样,key 就是永远不变的,更新前后都是一样的,并且又由于节点的内容本来就没变,所以 Diff 算法完美生效,只需将新节点添加到真实 DOM 就行了。

总结

当组件创建和更新时,vue 均会执行内部的 update 函数,该函数使用 render 函数生成的虚拟 dom 树,将新旧两树进行对比,找到差异点,最终更新到真实 dom

对比差异的过程叫 diff,vue 在内部通过一个叫 patch 的函数完成该过程

在对比时,vue 采用深度优先、同层比较的方式进行比对。

在判断两个节点是否相同时,vue 是通过虚拟节点的 key 和 tag来进行判断的

具体来说

这样一直递归的遍历下去,直到整棵树完成对比。

注意:

random 是生成随机数,有一定概率多个 item 会生成相同的值,不能保证唯一。

如果是根据数据来生成 item,数据具有 id 属性,那么就可以使用 id 来作为 key

如果不是根据数据生成 item,那么最好的方式就是使用时间戳来作为 key。或者使用诸如 uuid 之类的库来生成唯一的 id

使用 index 作为 key 和没写基本上没区别,因为不管数组的顺序怎么颠倒,index 都是 0, 1, 2...这样排列, 导致 Vue 会复用错误的旧子节点,做很多额外的工作。

对比 Vue3

简单来说,diff 算法有以下过程

正常 Diff 两个树的时间复杂度是 O(n^3),但实际情况下我们很少会进行跨层级的移动 DOM,所以 VueDiff 进行了优化,从O(n^3) -> O(n),只有当新旧 children 都为多个子节点时才需要用核心的 Diff 算法进行同层级比较。

Vue2 的核心 Diff 算法采用了双端比较的算法,同时从新旧 children 的两端开始进行比较,借助 key 值找到可复用的节点,再进行相关操作。相比 ReactDiff 算法,同样情况下可以减少移动节点次数,减少不必要的性能损耗,更加的优雅。

Vue3.x 借鉴了 ivi 算法和 inferno 算法

在创建 VNode 时就确定其类型,以及在 mount/patch 的过程中采用位运算来判断一个 VNode 的类型,在这个基础之上再配合核心的 Diff 算法,使得性能上较 Vue2.x 有了提升。该算法中还运用了动态规划的思想求解最长递归子序列。

- + diff --git a/vue/directive.html b/vue/directive.html index f4073f2b..65d95a6a 100644 --- a/vue/directive.html +++ b/vue/directive.html @@ -13,7 +13,7 @@
Skip to content
On this page

Vue 指令篇

描述下 Vue 自定义指令

在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。 一般需要对 DOM 元素进行底层操作时使用,尽量只用来操作 DOM 展示,不修改内部的值。当使用自定义指令直接修改 value 值时绑定 v-model 的值也不会同步更新;如必须修改可以在自定义指令中使用 keydown 事件,在 vue 组件中使用 change 事件,回调中修改 vue 数据;

(1)自定义指令基本内容

  • 全局定义:Vue.directive("focus",{})

  • 局部定义:directives:{focus:

  • 钩子函数:指令定义对象提供钩子函数

    • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
    • inSerted:被绑定元素插入父节点时调用(仅保证父节点存在,但不一定已被插入文档中)。
    • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前调用。指令的值可能发生了改变,也可能没有。但是可以通过比较更新前后的值来忽略不必要的模板更新。
    • ComponentUpdate:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
    • unbind:只调用一次,指令与元素解绑时调用。
  • 钩子函数参数

    • el:绑定元素
    • bing: 指令核心对象,描述指令全部信息属性
    • name
    • value
    • oldValue
    • expression
    • arg
    • modifers
    • vnode   虚拟节点
    • oldVnode:上一个虚拟节点(更新钩子函数中才有用)

(2)使用场景

  • 普通 DOM 元素进行底层操作的时候,可以使用自定义指令
  • 自定义指令是用来操作 DOM 的。尽管 Vue 推崇数据驱动视图的理念,但并非所有情况都适合数据驱动。自定义指令就是一种有效的补充和扩展,不仅可用于定义任何的 DOM 操作,并且是可复用的。

(3)使用案例 初级应用:

  • 鼠标聚焦
  • 下拉菜单
  • 相对时间转换
  • 滚动动画

高级应用:

  • 自定义指令实现图片懒加载
  • 自定义指令集成第三方插件
- + diff --git a/vue/interviewer.html b/vue/interviewer.html index 19381c05..91844c60 100644 --- a/vue/interviewer.html +++ b/vue/interviewer.html @@ -13,7 +13,7 @@
Skip to content
On this page

今天我是面试官

vue 中 key 的功能是什么?

  • 用于虚拟节点树之间,diff 时更好地比较 如果不设置 key,或者用 index 作为 key 会有什么问题?

为什么用 vuex,不用 event bus? 这两者有什么区别?

vue router 的实现原理

  • Location   href, path
  • 知道多页面应用中,如何区分前端路由和后端路由?
  • 路由哈希模式和 history 模式的区别 -
  • 把 nextTick 中的回调放到事件循环的微任务队列中。
  1. vue 发送请求在 created 生命周期钩子中,尽量避免 DOM 渲染挂载后再发送请求,更新数据,更改 DOM;
  2. vue router 组件懒加载;
  3. index.html 文件控制在 14kb 之内,这是基于 TCP 的慢开始规则,第一个响应包的大小就是 14kb,应该包含浏览器开始渲染页面所需的所有内容,或者至少包含页面模板(第一次渲染所需的 CSS 和 HTML)

以头条为例,采用组件化方式设计一个信息流?

  • 组件功能抽象能力
  • 组件设计思路
  • 功能划分:多种文章显示格式,刷新提示,评论/媒体/时间等信息显示,refresh/loadmore 功能,dislike 功能,动态广告
  • 卡片形式抽象:单图、多图、无图、视频、ugc、刷新条等

要求:有清晰思路,良好的信息流组件设计模式,可扩展性强并给出核心功能的实现方式

请简述什么是双向数据绑定和单向数据流,以及它们的区别

双向数据绑定

双向数据绑定意味着 UI 动态地绑定到模型数据,这样当 UI 改变时,模型数据就随之变化,反之亦然。

单向数据流

单向数据流意味着 model 是唯一来源。UI 触发消息的变化,将用户行为标记为 model。只有 model 具有访问更改应用程序状态的权限。其效果是数据总是朝一个方向流动,这使得理解起来更容易。

二者有什么优缺点

单向数据流是确定性的,数据流动方向可以跟踪,流动单一,追查问题的时候可以跟快捷。缺点就是写起来不太方便。要使 UI 发生变更就必须创建各种 action 来维护对应的 state 双向绑定,优点是使用方便,值和 UI 双绑定,但是由于各种数据相互依赖相互绑定,导致数据问题的源头难以被跟踪到,子组件修改父组件,兄弟组件互相修改有有违设计原则

可以介绍一下模板引擎的原理,比如实现类似 html 这种模板将其中变量替换为对应值的方式。

介绍下你所理解的 MVVM 框架,如 Angular、React、Vue 都解决了什么问题?

要求:能够从每个框架的生态系统,甚至结合之前的项目及不同的业务特点,给出框架的优劣

Vue 框架中组件消息通信方式

父子之间层级过多时,当父子组件之间层级不多的时候,父组件可以一层层的向子组件传递数据或者子组件一层层向父组件发送消息,代码上没有太难维护的地方。可是,一旦父子组件之间层级变多后,传递一个数据或者发送一个消息就变得麻烦。这块如果了解开源的 Element 组件库,就会知道其实现方式:构造一个函数自动向上/向下查询父亲节点,以[组件名, 消息名, 参数]三元组进行消息传递,降低长链传播成本;

具体实现参考:https://github.com/ElemeFE/element/blob/dev/src/mixins/emitter.js

如何理解虚拟 DOM?

对虚拟 dom 和 diff 算法中的一些细节理解

https://github.com/livoras/blog/issues/13

要求:写出 diff 算法的核心部分

- + diff --git a/vue/keep-alive-LRU.html b/vue/keep-alive-LRU.html index bd255e37..10206ce9 100644 --- a/vue/keep-alive-LRU.html +++ b/vue/keep-alive-LRU.html @@ -23,7 +23,7 @@ this.keys = [] }

keep-alive 中的生命周期哪些

keep-alive 是 Vue 提供的一个内置组件,用来对组件进行缓存——在组件切换过程中将状态保留在内存中,防止重复渲染 DOM。

如果为一个组件包裹了 keep-alive,那么它会多出两个生命周期:deactivated、activated。同时,beforeDestroy 和 destroyed 就不会再被触发了,因为组件不会被真正销毁。

当组件被换掉时,会被缓存到内存中、触发 deactivated 生命周期;当组件被切回来时,再去缓存里找这个组件、触发 activated 钩子函数。

LRU 缓存算法

TODO

- + diff --git a/vue/lifecycle.html b/vue/lifecycle.html index 4834df38..4aec7666 100644 --- a/vue/lifecycle.html +++ b/vue/lifecycle.html @@ -65,7 +65,7 @@ new Vue(vnode.componentOptions);
  1. 运行生命周期钩子函数created
  2. 渲染:生成render函数:如果有配置,直接使用配置的render,如果没有,使用运行时编译器,把模板编译为render
  3. 运行生命周期钩子函数beforeMount
  4. 创建一个Watcher,传入一个函数updateComponent,该函数会运行render,把得到的vnode再传入_update函数执行。 在执行render函数的过程中,会收集所有依赖,将来依赖变化时会重新运行updateComponent函数 在执行_update函数的过程中,触发patch函数,由于目前没有旧树,因此直接为当前的虚拟 dom 树的每一个普通节点生成 elm 属性,即真实 dom。 如果遇到创建一个组件的 vnode,则会进入组件实例化流程,该流程和创建 vue 实例流程基本相同,递归,最终会把创建好的组件实例挂载 vnode 的componentInstance属性中,以便复用。
  5. 运行生命周期钩子函数mounted

Vue 父子组件挂载顺序

重渲染?

  1. 数据变化后,所有依赖该数据的Watcher均会重新运行,这里仅考虑updateComponent函数对应的Watcher
  2. Watcher会被调度器放到nextTick中运行,也就是微队列中,这样是为了避免多个依赖的数据同时改变后被多次执行
  3. 运行生命周期钩子函数beforeUpdate
  4. updateComponent函数重新执行
  1. 运行生命周期钩子函数updated

Vue 父子组件重新渲染顺序

总体流程

它可以总共分为 8 个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后。

注意:

常见问题

接口请求一般放在哪个生命周期中?

接口请求可以放在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。

但是推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:

Vue 子组件和父组件执行顺序

加载渲染过程:

  1. 父组件 beforeCreate
  2. 父组件 created
  3. 父组件 beforeMount
  4. 子组件 beforeCreate
  5. 子组件 created
  6. 子组件 beforeMount
  7. 子组件 mounted
  8. 父组件 mounted

更新过程:

  1. 父组件 beforeUpdate
  2. 子组件 beforeUpdate
  3. 子组件 updated
  4. 父组件 updated

销毁过程:

  1. 父组件 beforeDestroy
  2. 子组件 beforeDestroy
  3. 子组件 destroyed
  4. 父组件 destoryed
- + diff --git a/vue/nextTick.html b/vue/nextTick.html index 67740bf6..726741b7 100644 --- a/vue/nextTick.html +++ b/vue/nextTick.html @@ -15,7 +15,7 @@
Skip to content
On this page

$nextTick 工作原理

DANGER

写作中

Vue 的 nextTick 其本质是对 JavaScript 执行原理 EventLoop 的一种应用。

作用:vue 更新 DOM 是异步更新的,数据变化,DOM 的更新不会马上完成,nextTick 的回调是在下次 DOM 更新循环结束之后执行的延迟回调。

nextTick 的核心是利用了如 Promise 、MutationObserver、setImmediate、setTimeout 的原生 JavaScript 方法来模拟对应的微/宏任务的实现,本质是为了利用 JavaScript 的这些异步回调任务队列来实现 Vue 框架中自己的异步回调队列。

nextTick 不仅是 Vue 内部的异步队列的调用方法,同时也允许开发者在实际项目中使用这个方法来满足实际应用中对 DOM 更新数据时机的后续逻辑处理

nextTick 是典型的将底层 JavaScript 执行原理应用到具体案例中的示例,引入异步更新队列机制的原因 ∶

  • 如果是同步更新,则多次对一个或多个属性赋值,会频繁触发 UI/DOM 的渲染,可以减少一些无用渲染
  • 同时由于 VirtualDOM 的引入,每一次状态发生变化后,状态变化的信号会发送给组件,组件内部使用 VirtualDOM 进行计算得出需要更新的具体的 DOM 节点,然后对 DOM 进行更新操作,每次更新状态后的渲染过程需要更多的计算,而这种无用功也将浪费更多的性能,所以异步渲染变得更加至关重要

Vue 采用了数据驱动视图的思想,但是在一些情况下,仍然需要操作 DOM。有时候,可能遇到这样的情况,DOM1 的数据发生了变化,而 DOM2 需要从 DOM1 中获取数据,那这时就会发现 DOM2 的视图并没有更新,这时就需要用到了 nextTick 了。

由于 Vue 的 DOM 操作是异步的,所以,在上面的情况中,就要将 DOM2 获取数据的操作写在$nextTick 中。

vue
this.$nextTick(() => { // 获取数据的操作...})
 
this.$nextTick(() => { // 获取数据的操作...})
 

所以,在以下情况下,会用到 nextTick:

  • 在数据变化后执行的某个操作,而这个操作需要使用随数据变化而变化的 DOM 结构的时候,这个操作就需要方法在 nextTick()的回调函数中。
  • 在 vue 生命周期中,如果在 created()钩子进行 DOM 操作,也一定要放在 nextTick()的回调函数中。

因为在 created()钩子函数中,页面的 DOM 还未渲染,这时候也没办法操作 DOM,所以,此时如果想要操作 DOM,必须将操作的代码放在 nextTick()的回调函数中。 nextTick:可以做什么不可以做什么? nextTick:里面调用 update 会是什么情况? 如果我循环更新 dom 节点并且执行它,会有什么结果? 循环调用的话 nextTick:里面有容错机制吗?

实现原理:nextTick 主要使用了宏任务和微任务。根据执行环境分别尝试采用

  • Promise:可以将函数延迟到当前函数调用栈最末端
  • MutationObserver :是 H5 新加的一个功能,其功能是监听 DOM 节点的变动,在所有 DOM 变动完成后,执行回调函数
  • setImmediate:用于中断长时间运行的操作,并在浏览器完成其他操作(如事件和显示更新)后立即运行回调函数
  • 如果以上都不行则采用 setTimeout 把函数延迟到 DOM 更新之后再使用

原因是宏任务消耗大于微任务,优先使用微任务,最后使用消耗最大的宏任务。

参考资料

https://juejin.cn/post/6844903557372575752

- + diff --git a/vue/reactive.html b/vue/reactive.html index 22d92561..d46dff67 100644 --- a/vue/reactive.html +++ b/vue/reactive.html @@ -717,7 +717,7 @@ } } - + diff --git a/vue/slot.html b/vue/slot.html index 3829bf50..c9a5903a 100644 --- a/vue/slot.html +++ b/vue/slot.html @@ -13,7 +13,7 @@
Skip to content
On this page

一篇精通 slot

DANGER

写作中

slot 是什么?有什么作用?

slot 又名插槽,是 Vue 的内容分发机制,组件内部的模板引擎使用 slot 元素作为承载分发内容的出口。插槽 slot 是子组件的一个模板标签元素,而这一个标签元素是否显示,以及怎么显示是由父组件决定的。slot 又分三类,默认插槽,具名插槽和作用域插槽。

  • 默认插槽:又名匿名查抄,当 slot 没有指定 name 属性值的时候一个默认显示插槽,一个组件内只有有一个匿名插槽。
  • 具名插槽:带有具体名字的插槽,也就是带有 name 属性的 slot,一个组件可以出现多个具名插槽。
  • 作用域插槽:默认插槽、具名插槽的一个变体,可以是匿名插槽,也可以是具名插槽,该插槽的不同点是在子组件渲染作用域插槽时,可以将子组件内部的数据传递给父组件,让父组件根据子组件的传递过来的数据决定如何渲染该插槽。本质是子组件可以通过插槽的位置绑定一些数据,让父组件插槽位置可以用这个数据。

实现原理

当子组件 vm 实例化时,获取到父组件传入的 slot 标签的内容,存放在 vm.slot 中,默认插槽为 um. slot.default,具名插槽为 vm.slot.xxx,xxx 为插槽名,当组件执行渲染函数时候,遇到 slot 标签,使用 slot 中的内容进行替换,此时可以为插槽传递数据,若存在数据,则可称该插槽为作用域插槽。

- + diff --git a/vue/v-model.html b/vue/v-model.html index 6eaed0fb..b58bf7dd 100644 --- a/vue/v-model.html +++ b/vue/v-model.html @@ -41,7 +41,7 @@ <!-- 等效于 --> <Comp :number="data" @change="data=$event" />

总结

首先要对数据进行劫持监听,所以我们需要设置一个监听器 Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者 Watcher 看是否需要更新。

因为订阅者是有很多个,所以我们需要有一个消息订阅器 Dep 来专门收集这些订阅者,然后在监听器 Observer 和订阅者 Watcher 之间进行统一管理的。

接着,我们还需要有一个指令解析器 Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者 Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者 Watcher 接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。

因此接下去我们执行以下 3 个步骤,实现数据的双向绑定:

  1. 实现一个监听器 Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。

  2. 实现一个订阅者 Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。

  3. 实现一个解析器 Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。

- + diff --git a/vue/vdom.html b/vue/vdom.html index 52c6da83..1e6a215a 100644 --- a/vue/vdom.html +++ b/vue/vdom.html @@ -67,7 +67,7 @@

这个过程主要分析出哪些是静态节点,给其打一个标记,为后续更新渲染可以直接跳过静态节点做优化

深度遍历 AST,查看每个子树的节点元素是否为静态节点或者静态节点根。如果为静态节点,他们生成的 DOM 永远不会改变,这对运行时模板更新起到了极大的优化作用。

(3)生成代码

javascript
const code = generate(ast, options);
 
const code = generate(ast, options);
 

generate 将 ast 抽象语法树编译成 render 字符串并将静态部分放到 staticRenderFns 中,最后通过 new Function(render) 生成 render 函数。

- + diff --git a/vue/vs.html b/vue/vs.html index 3ac33c1e..8b81fc12 100644 --- a/vue/vs.html +++ b/vue/vs.html @@ -13,7 +13,7 @@
Skip to content
On this page

Vue 和 React 的核心区别

VueAngular 以及 React 的区别是什么?

关于 Vue 和其他框架的不同,官方专门写了一篇文档,从性能、体积、灵活性等多个方面来进行了说明。 详细可以参阅:https://cn.vuejs.org/v2/guide/comparison.html

Composition API 与 React Hook 很像,区别是什么

从 React Hook 的实现角度看,React Hook 是根据 useState 调用的顺序来确定下一次重渲染时的 state 是来源于哪个 useState,所以出现了以下限制

  • 不能在循环、条件、嵌套函数中调用 Hook
  • 必须确保总是在你的 React 函数的顶层调用 Hook
  • useEffect、useMemo 等函数必须手动确定依赖关系

而 Composition API 是基于 Vue 的响应式系统实现的,与 React Hook 的相比

  • 声明在 setup 函数内,一次组件实例化只调用一次 setup,而 React Hook 每次重渲染都需要调用 Hook,使得 React 的 GC 比 Vue 更有压力,性能也相对于 Vue 来说也较慢
  • Compositon API 的调用不需要顾虑调用顺序,也可以在循环、条件、嵌套函数中使用
  • 响应式系统自动实现了依赖收集,进而组件的部分的性能优化由 Vue 内部自己完成,而 React Hook 需要手动传入依赖,而且必须必须保证依赖的顺序,让 useEffect、useMemo 等函数正确的捕获依赖变量,否则会由于依赖不正确使得组件性能下降。

虽然 Compositon API 看起来比 React Hook 好用,但是其设计思想也是借鉴 React Hook 的。

Vue 和 React 区别

定位相同:处理 UI 层的,vue 提倡渐进式处理,react 没有 vue 推崇模版写法,react 是 all in js jsx, vue 支持 jsx, react 不支持 vue 的 template react hooks, vue3 借鉴了,都有 hoos 风格的 api UI 更新策略:react 传入一个新数据,不能修改旧数据,vue 会根据两次数据渲染 dom 的 diff 更新 UI,数据变化,就会计划更新 UI,都会延迟更新 vue 文化:全部封装好;React 推崇第三方库结合 vue 有 keep-alive, react 没有,重新渲染,需要自己实现 vue css scoped;react 需要第三方:css modules/style-component

相似之处:

  • 都将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库;
  • 都有自己的构建工具,能让你得到一个根据最佳实践设置的项目模板;
  • 都使用了 Virtual DOM(虚拟 DOM)提高重绘性能;
  • 都有 props 的概念,允许组件间的数据传递;
  • 都鼓励组件化应用,将应用分拆成一个个功能明确的模块,提高复用性。

不同之处 :

1)数据流 Vue 默认支持数据双向绑定,而 React 一直提倡单向数据流 2)虚拟 DOM Vue2.x 开始引入"Virtual DOM",消除了和 React 在这方面的差异,但是在具体的细节还是有各自的特点。

  • Vue 宣称可以更快地计算出 Virtual DOM 的差异,这是由于它在渲染过程中,会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。
  • 对于 React 而言,每当应用的状态被改变时,全部子组件都会重新渲染。当然,这可以通过 PureComponent/shouldComponentUpdate 这个生命周期方法来进行控制,但 Vue 将此视为默认的优化。

3)组件化 React 与 Vue 最大的不同是模板的编写。

  • Vue 鼓励写近似常规 HTML 的模板。写起来很接近标准 HTML 元素,只是多了一些属性。
  • React 推荐你所有的模板通用 JavaScript 的语法扩展——JSX 书写。

具体来讲:React 中 render 函数是支持闭包特性的,所以 import 的组件在 render 中可以直接调用。但是在 Vue 中,由于模板中使用的数据都必须挂在 this 上进行一次中转,所以 import 一个组件完了之后,还需要在 components 中再声明下。 4)监听数据变化的实现原理不同

  • Vue 通过 getter/setter 以及一些函数的劫持,能精确知道数据变化,不需要特别的优化就能达到很好的性能
  • React 默认是通过比较引用的方式进行的,如果不优化(PureComponent/shouldComponentUpdate)可能导致大量不必要的 vDOM 的重新渲染。这是因为 Vue 使用的是可变数据,而 React 更强调数据的不可变。

5)高阶组件 react 可以通过高阶组件(HOC)来扩展,而 Vue 需要通过 mixins 来扩展。 高阶组件就是高阶函数,而 React 的组件本身就是纯粹的函数,所以高阶函数对 React 来说易如反掌。相反 Vue.js 使用 HTML 模板创建视图组件,这时模板无法有效的编译,因此 Vue 不能采用 HOC 来实现。 6)构建工具 两者都有自己的构建工具:

  • React ==> Create React APP
  • Vue ==> vue-cli

7)跨平台

  • React ==> React Native
  • Vue ==> Weex

Vue 的优点

  • 轻量级框架:只关注视图层,是一个构建数据的视图集合,大小只有几十 kb ;
  • 简单易学:国人开发,中文文档,不存在语言障碍 ,易于理解和学习;
  • 双向数据绑定:保留了 angular 的特点,在数据操作方面更为简单;
  • 组件化:保留了 react 的优点,实现了 html 的封装和重用,在构建单页面应用方面有着独特的优势;
  • 视图,数据,结构分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作;
  • 虚拟 DOM:dom 操作是非常耗费性能的,不再使用原生的 dom 操作节点,极大解放 dom 操作,但具体操作的还是 dom 不过是换了另一种方式;
  • 运行速度更快:相比较于 react 而言,同样是操作虚拟 dom,就性能而言, vue 存在很大的优势。
- + diff --git a/vue/vue-cli.html b/vue/vue-cli.html index 79527c87..5031f997 100644 --- a/vue/vue-cli.html +++ b/vue/vue-cli.html @@ -13,7 +13,7 @@
Skip to content
On this page

vue-cli 到底帮我们做了什么

vue-cli 中的工程化

  1. vue.js:vue-cli 工程的核心,主要特点是双向数据绑定和组件系统。
  2. vue-router:vue 官方推荐使用的路由框架。
  3. vuex:专为 Vue.js 应用项目开发的状态管理器,主要用于维护 vue 组件间共用的一些 变量 和 方法。
  4. axios(或者 fetch、ajax):用于发起 GET 、或 POST 等 http 请求,基于 Promise 设计。
  5. vux 等:一个专为 vue 设计的移动端 UI 组件库。
  6. webpack:模块加载和 vue-cli 工程打包器。
  7. eslint:代码规范工具

vue-cli 工程常用的 npm 命令有哪些?

下载 node_modules 资源包的命令:npm install

启动 vue-cli 开发环境的 npm 命令:npm run dev

vue-cli 生成 生产环境部署资源 的 npm 命令:npm run build

用于查看 vue-cli 生产环境部署资源文件大小的 npm 命令:npm run build --report

- + diff --git a/vue/vue-compile.html b/vue/vue-compile.html index 773a2acf..6a031502 100644 --- a/vue/vue-compile.html +++ b/vue/vue-compile.html @@ -13,7 +13,7 @@
Skip to content
On this page

Vue 编译器为什么如此强大

说一下 vue 模版编译的原理是什么

简单说,Vue 的编译过程就是将 template 转化为 render 函数的过程。会经历以下阶段:

  • 生成 AST
  • 优化
  • codegen

首先解析模版,生成 AST 语法树(一种用 JavaScript 对象的形式来描述整个模板)。 使用大量的正则表达式对模板进行解析,遇到标签、文本的时候都会执行对应的钩子进行相关处理。

Vue 的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变化,对应的 DOM 也不会变化。那么优化过程就是深度遍历 AST 树,按照相关条件对树节点进行标记。这些被标记的节点(静态节点)我们就可以跳过对它们的比对,对运行时的模板起到很大的优化作用。

编译的最后一步是将优化后的 AST 树转换为可执行的代码。

说一下 Vue complier 的实现原理是什么样的?

在使用 vue 的时候,我们有两种方式来创建我们的 HTML 页面,第一种情况,也是大多情况下,我们会使用模板 template 的方式,因为这更易读易懂也是官方推荐的方法;第二种情况是使用 render 函数来生成 HTML,它比 template 更接近最终结果。

complier 的主要作用是解析模板,生成渲染模板的 render, 而 render 的作用主要是为了生成 VNode

complier 主要分为 3 大块:

  • parse:接受 template 原始模板,按着模板的节点和数据生成对应的 ast
  • optimize:遍历 ast 的每一个节点,标记静态节点,这样就知道哪部分不会变化,于是在页面需要更新时,通过 diff 减少去对比这部分 DOM,提升性能
  • generate 把前两步生成完善的 ast,组成 render 字符串,然后将 render 字符串通过 new Function 的方式转换成渲染函数

Vue 模版编译原理

vue 中的模板 template 无法被浏览器解析并渲染,因为这不属于浏览器的标准,不是正确的 HTML 语法,所有需要将 template 转化成一个 JavaScript 函数,这样浏览器就可以执行这一个函数并渲染出对应的 HTML 元素,就可以让视图跑起来了,这一个转化的过程,就成为模板编译。模板编译又分三个阶段,解析 parse,优化 optimize,生成 generate,最终生成可执行函数 render。

  • 解析阶段:使用大量的正则表达式对 template 字符串进行解析,将标签、指令、属性等转化为抽象语法树 AST。
  • 优化阶段:遍历 AST,找到其中的一些静态节点并进行标记,方便在页面重渲染的时候进行 diff 比较时,直接跳过这一些静态节点,优化 runtime 的性能。
  • 生成阶段:将最终的 AST 转化为 render 函数字符串。

源码实现

TODO

- + diff --git a/vue/vue-interview.html b/vue/vue-interview.html index 831b676f..69c7a582 100644 --- a/vue/vue-interview.html +++ b/vue/vue-interview.html @@ -319,7 +319,7 @@
Vue.mixin({
   beforeCreate() {        // ...逻辑        // 这种方式会影响到每个组件的 beforeCreate 钩子函数    }})
 

虽然文档不建议在应用中直接使用 mixin,但是如果不滥用的话也是很有帮助的,比如可以全局混入封装好的 ajax 或者一些工具函数等等。

mixins 应该是最常使用的扩展组件的方式了。如果多个组件中有相同的业务逻辑,就可以将这些逻辑剥离出来,通过 mixins 混入代码,比如上拉下拉加载数据这种逻辑等等。 另外需要注意的是 mixins 混入的钩子函数会先于组件内的钩子函数执行,并且在遇到同名选项的时候也会有选择性的进行合并。

内置组件 Transition

官网详细文档:https://cn.vuejs.org/v2/guide/transitions.html

时机

Transition组件会监控slot唯一根元素的出现和消失,并会在其出现和消失时应用过渡效果 Transition不生成任何元素,只是为了生成过渡效果 具体的监听内容是:

流程

类名规则:

  1. 如果transition上没有定义name,则类名为v-xxxx
  2. 如果transition上定义了name,则类名为${name}-xxxx
  3. 如果指定了类名,直接使用指定的类名

指定类名见:自定义过渡类名

1. 进入效果

2. 消失效果

过渡组

Transision可以监控其内部的单个 dom 元素的出现和消失,并为其附加样式

如果要监控一个 dom 列表,就需要使用TransitionGroup组件

它会对列表的新增元素应用进入效果,删除元素应用消失效果,对被移动的元素应用v-move样式

被移动的元素之所以能够实现过渡效果,是因为TransisionGroup内部使用了 Flip 过渡方案

- + diff --git a/vue/vue-router.html b/vue/vue-router.html index b637db32..b7bf1ac8 100644 --- a/vue/vue-router.html +++ b/vue/vue-router.html @@ -305,7 +305,7 @@ }

二、Vue 路由钩子在生命周期函数的体现

  1. 完整的路由导航解析流程(不包括其他生命周期)
  1. 触发钩子的完整顺序

路由导航、keep-alive、和组件生命周期钩子结合起来的,触发顺序,假设是从 a 组件离开,第一次进入 b 组件 ∶

  1. 导航行为被触发到导航完成的整个过程

Vue-router 跳转和 location.href 有什么区别

params 和 query 的区别

用法:query 要用 path 来引入,params 要用 name 来引入,接收参数都是类似的,分别是 this.$route.query.namethis.$route.params.name

url 地址显示:query 更加类似于 ajax 中 get 传参,params 则类似于 post,说的再简单一点,前者在浏览器地址栏中显示参数,后者则不显示

注意:query 刷新不会丢失 query 里面的数据 params 刷新会丢失 params 里面的数据。

Vue-router 导航守卫有哪些

- + diff --git a/vue/vue3-onepage.html b/vue/vue3-onepage.html index e4cb29a9..c6c7a95f 100644 --- a/vue/vue3-onepage.html +++ b/vue/vue3-onepage.html @@ -783,7 +783,7 @@ }; </script>

说一下 vue3.0 是如何变得更快的?

优化 Diff 算法

相比 Vue 2Vue 3 采用了更加优化的渲染策略。去掉不必要的虚拟 DOM 树遍历和属性比较,因为这在更新期间往往会产生最大的性能开销。

这里有三个主要的优化:

在没有动态改变节点结构的模板指令(例如 v-ifv-for)的情况下,节点结构保持完全静态。

当更新节点时,不再需要递归遍历 DOM 树。所有的动态绑定部分将在一个平面数组中跟踪。这种优化通过将需要执行的树遍历量减少一个数量级来规避虚拟 DOM 的大部分开销。

编译器还根据需要执行的更新类型,为每个具有动态绑定的元素生成一个优化标志。

例如,具有动态类绑定和许多静态属性的元素将收到一个标志,提示只需要进行类检查。运行时将获取这些提示并采用专用的快速路径。

综合起来,这些技术大大改进了渲染更新基准,Vue 3.0 有时占用的 CPU 时间不到 Vue 2 的十分之一。

体积变小

重写后的 Vue 支持了 tree-shaking,像修剪树叶一样把不需要的东西给修剪掉,使 Vue 3.0 的体积更小。

需要的模块才会打入到包里,优化后的 Vue 3.0 的打包体积只有原来的一半(13kb)。哪怕把所有的功能都引入进来也只有 23kb,依然比 Vue 2.x 更小。像 keep-alive、transition 甚至 v-for 等功能都可以按需引入。

并且 Vue 3.0 优化了打包方法,使得打包后的 bundle 的体积也更小。

官方所给出的一份惊艳的数据:打包大小减少 41%,初次渲染快 55%,更新快 133%,内存使用减少 54%

说一说相比 vue3.x 对比 vue2.x 变化

  1. 源码组织方式变化:使用 TS 重写
  2. 支持 Composition API:基于函数的 API,更加灵活组织组件逻辑(vue2 用的是 options api)
  3. 响应式系统提升:Vue3 中响应式数据原理改成 proxy,可监听动态新增删除属性,以及数组变化
  4. 编译优化:vue2 通过标记静态根节点优化 diff,Vue3 标记和提升所有静态根节点,diff 的时候只需要对比动态节点内容
  5. 打包体积优化:移除了一些不常用的 api(inline-template、filter)
  6. 生命周期的变化:使用 setup 代替了之前的 beforeCreate 和 created
  7. Vue3 的 template 模板支持多个根标签
  8. Vuex 状态管理:创建实例的方式改变,Vue2 为 new Store , Vue3 为 createStore
  9. Route 获取页面实例与路由信息:vue2 通过 this 获取 router 实例,vue3 通过使用 getCurrentInstance/ userRoute 和 userRouter 方法获取当前组件实例
  10. Props 的使用变化:vue2 通过 this 获取 props 里面的内容,vue3 直接通过 props
  11. 父子组件传值:vue3 在向父组件传回数据时,如使用的自定义名称,如 backData,则需要在 emits 中定义一下
- + diff --git a/vue/vuex.html b/vue/vuex.html index 2f45ca56..2365b74c 100644 --- a/vue/vuex.html +++ b/vue/vuex.html @@ -73,7 +73,7 @@ })

如何在组件中批量使用 Vuex

使用 mapGetters 辅助函数, 利用对象展开运算符将 getter 混入 computed 对象中 使用 mapMutations 辅助函数,在组件中这么使用

- +