跳到主要内容

JS实现根据域名查找IP

· 阅读需 1 分钟
素明诚
Full stack development

直接上代码了,很简单,有 Node 就可以

const dns = require('dns');
const http = require('http');
const url = require('url');

const server = http.createServer((req, res) => {
// 添加CORS跨域头部信息
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');

// 判断URL是否以/getIp开始
if (req.url.startsWith('/getIp')) {
// 解析URL获取查询参数
const path = url.parse(req.url, true).query;
console.log(path.domain);

// DNS查询
dns.lookup(path.domain, (err, address, family) => {
if (err) {
res.writeHead(500, {'Content-Type': 'text/plain'});
res.end('获取IP地址时出错。');
console.error('DNS查询时出错:', err);
return;
}
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(address);
console.log('IP地址:', address);
console.log('IP版本:', family);
});

console.log('有人访问了我们的web服务器。');
} else {
res.writeHead(404, {'Content-Type': 'text/plain'});
res.end('未找到');
}
});

server.listen(3636, () => {
console.log('服务器运行在 http://localhost:3636');
});

mvcmvpmvvm三种模式的异同

· 阅读需 4 分钟
素明诚
Full stack development

MVC、MVP 和 MVVM 是三种常用的软件设计模式,特别是在构建用户界面(User Interface,UI)时。这三种模式都致力于实现关注点分离,即将数据、逻辑和显示层分开,但具体实现方式存在差异。

MVC(Model-View-Controller)

  • Model(模型):代表数据结构和业务逻辑。通常与数据库相关操作有关。
  • View(视图):显示数据,即用户界面部分。
  • Controller(控制器):接受用户的输入,调用模型来处理该输入,然后更新视图。

特点:Controller 负责处理用户输入,从而使模型和视图分离。

MVP(Model-View-Presenter)

  • Model(模型):与 MVC 中的模型相同,代表数据结构和业务逻辑。
  • View(视图):显示数据。但与 MVC 中不同,MVP 的 View 更加主动,通常会实现一个接口,该接口允许 Presenter 进行数据更新。
  • Presenter(提供器):从 View 获取用户输入,调用 Model 进行处理,然后更新 View。不同于 MVC 的 Controller,Presenter 和 View 是双向绑定的。

特点:Presenter 直接与 View 交互,负责更新 View。View 是被动的,只负责显示。

MVVM(Model-View-ViewModel)

  • Model(模型):与之前两种模式中的模型相同。
  • View(视图):显示数据,但通常不包含任何业务逻辑。它只负责声明性地描述其外观和行为。
  • ViewModel(视图模型):它是 View 的抽象,负责将 Model 的数据转换为 View 可以显示的数据。在 MVVM 中,通常会使用双向数据绑定技术,这意味着当数据发生变化时,View 和 ViewModel 会自动更新。

特点:MVVM 使用双向数据绑定,ViewModel 不直接引用 View,这使得 MVVM 更加适合于现代 UI 技术(例如 WPF、Xamarin 和 Angular)。

异同

  • 在 MVC 中,Controller 直接更新 View;而在 MVP 中,Presenter 更新 View;在 MVVM 中,使用数据绑定技术自动更新。
  • MVVM 的 ViewModel 和 View 之间的双向数据绑定,使得 ViewModel 不需要直接引用 View。
  • MVP 的 View 相对于 MVC 更加主动,因为它通常会实现一个接口供 Presenter 更新。
  • 所有这三种模式都旨在实现关注点的分离,即将业务逻辑、数据和 UI 分离。
  • 都有 Model 作为数据和业务逻辑的代表。

三种模式的历史

MVC (Model-View-Controller)

  • 时间:1970s
  • 历史事件
  • MVC 最早是在 1970 年代由 Trygve Reenskaug 在 Smalltalk-80 编程语言中提出的。这是为了将用户界面(UI)从业务逻辑和数据模型中分离出来。
  • 在 1980s 和 1990s,MVC 在面向对象编程社区中得到了广泛的接受和应用。
  • 随着 Web 开发的崛起,MVC 也被应用于 Web 应用,其中 Model 代表数据和业务逻辑,View 代表网页显示,Controller 处理用户的 HTTP 请求。

MVP (Model-View-Presenter)

  • 时间:1990s
  • 历史事件
  • MVP 是为了解决某些桌面应用开发中 MVC 所面临的问题而诞生的。在 MVC 中,Controller 与 UI 界面有很强的耦合,这在某些情况下不是很理想。
  • MVP 更进一步地将 UI 逻辑从 View 中分离出来,由 Presenter 来管理,这使得 Presenter 可以不依赖任何 UI 框架进行单元测试。
  • 该模式在 Microsoft 的某些技术,如 Windows Forms 中,变得尤为流行。

MVVM (Model-View-ViewModel)

  • 时间:2000s
  • 历史事件
  • MVVM 最早是由 Microsoft 在 2005 年为 Windows Presentation Foundation (WPF) 和 Silverlight 提出的,这两种技术都支持丰富的数据绑定能力。
  • MVVM 的目的是进一步分离 GUI 的逻辑和表现,特别是在使用双向数据绑定的框架中。ViewModel 可以看作是 View 的状态和行为的抽象。
  • MVVM 不仅在 Microsoft 技术中流行,也被应用于其他技术框架中,如 Angular、KnockoutJS 等。

Vue 响应式系统数据双向绑定v-model 指令的区别

· 阅读需 2 分钟
素明诚
Full stack development
特性描述作用关联
响应式系统Vue.js 的核心特性之一,通过 Object.defineProperty 对数据对象的所有属性进行监听。当数据对象的属性发生变化时,Vue.js 会知道并且重新渲染相关的组件。自动跟踪数据的改变,并对改变做出响应。当数据改变时,自动更新视图。响应式系统是数据双向绑定和 v-model 的基础。
数据双向绑定当数据模型更改时,视图会自动更新;当视图更改(例如用户输入)时,数据模型也会自动更新。这通过 Vue.js 的响应式系统和一些特殊的指令(如 v-model)实现。实现数据模型和视图之间的同步。当视图或数据模型改变时,另一方也会相应地改变。数据双向绑定依赖于响应式系统,并且通常通过 v-model 指令实现。
v-model 指令v-model 是 Vue.js 提供的一个指令,用于在表单元素上实现数据模型和视图之间的双向绑定。实际上,v-model 是 v-bind 和 v-on 的语法糖。简化数据双向绑定的实现。当用在表单元素上时,实现视图和数据模型之间的双向绑定。v-model 是实现数据双向绑定的一种方式,它依赖于响应式系统。

这三个特性是密切相关的。响应式系统是基础,它使得 Vue.js 能够知道何时需要更新视图。数据双向绑定建立在响应式系统的基础上,它使得视图和数据模型能够保持同步。而 v-model 指令则是数据双向绑定的一个实现机制,它使得开发者可以方便地在表单元素上实现双向绑定。

再有面试官问 v-model 是什么,放心的答:语法糖

使用 v-model

<input v-model="message" />

使用 v-bindv-on

<input v-bind:value="message" v-on:input="message = $event.target.value" />

v-model 提供了一种更简洁、更易于理解的方式来实现数据双向绑定,所以说 v-modelv-bindv-on 的语法糖。

React 和 Vue 的区别

· 阅读需 7 分钟
素明诚
Full stack development

总体区别

1. 设计理念:

  • React 是一个灵活的库,主张 "everything is JavaScript",这意味着你可以使用 JavaScript 来创建组件、管理状态、处理 DOM 更新等等。这种设计理念使得 React 能够在大型应用中更好地进行抽象,并有更高的灵活性和可组合性。然而,这也意味着你可能需要更多的配置和工具,例如 Redux 或 MobX 进行状态管理,React Router 进行路由管理,等等。
  • Vue 是一个框架,它提供了一种结合声明式模板和组件化的方法,用于构建用户界面。Vue 的设计理念是 "easy to use, hard to misuse",即让开发者尽可能容易地做对事情。这使得 Vue 在小型到中型的应用中更易于上手,但在大型应用中可能会遇到一些限制。不过,Vue 也提供了一些进阶功能,如 Vuex 进行状态管理,Vue Router 进行路由管理,等等。

2. 模板和渲染:

  • React 使用 JSX,这是一个 JavaScript 语法扩展,允许你在 JavaScript 中编写类似 HTML 的代码。因为 JSX 最终会被转译为 JavaScript,你可以在 JSX 中直接使用 JavaScript 表达式。这为组件的渲染提供了极大的灵活性,但也可能增加了一些复杂性。
  • Vue 使用基于 HTML 的模板语法,这使得它对于初学者更易于理解和使用。你可以使用特殊的指令(如 v-if, v-for, v-bind 等)来控制模板的渲染。另外,Vue 也支持使用渲染函数和 JSX,尽管这不是主流的做法。

3. 组件和状态管理:

  • React 使用 props 和 state 来管理组件的数据。props 是父组件传递给子组件的数据,而 state 是组件自己管理的数据。你可以使用 setState 方法来更新组件的状态,并触发组件的重新渲染。React 也支持使用 context API 来实现跨组件的状态共享。
  • Vue 使用 props 和 data 来管理组件的数据。props 是父组件传递给子组件的数据,而 data 是组件自己管理的数据。你可以使用 this.$set 方法来更新组件的数据,并触发组件的重新渲染。Vue 也支持使用 provide/inject API 来实现跨组件的状态共享。

4. 生命周期方法:

  • React 的生命周期方法包括 componentDidMount, componentDidUpdate, componentWillUnmount, 等等。在 React 16.8 之后的版本中,通过引入 Hooks(如 useEffect),React 可以在函数组件中实现相同的功能。
  • Vue 的生命周期方法包括 created, mounted, updated, destroyed 等等。这些方法的名称更直观,更容易理解它们的用途。

5. 社区和生态系统:

  • React 的社区和生态系统非常庞大和成熟,有大量的第三方库和工具可以使用。React 的学习资源也非常丰富,包括官方文档、教程、课程、博客文章、Stack Overflow 问题等等。
  • Vue 的社区和生态系统也在快速发展,虽然相对于 React 来说规模较小,但也有很多高质量的第三方库和工具。Vue 的学习资源也很丰富,尤其是官方文档,被广大开发者誉为非常友好和易于理解。

技术区别

1. 状态管理:

  • React 通常与 Redux 或 MobX 等库一起使用来进行全局状态管理。React 本身也提供了 Context API 以实现跨组件的状态共享。在 React 16.8 之后的版本中,引入的 Hooks(如 useState, useReducer)提供了更灵活的状态管理方式。
  • Vue 有一个官方的状态管理库 Vuex。Vuex 的设计受到了 Flux 架构和 Elm 架构的影响,它使用单向数据流和时间旅行调试功能来处理应用的全局状态。此外,Vue 的 v-model 指令提供了一种简单的方式来实现双向数据绑定。

2. 开发体验:

  • React 的开发体验可能会因为需要配置的工具和库的数量而变得复杂。然而,Create React App、Next.js 和 Gatsby 等脚手架和框架可以帮助你快速开始一个新项目。此外,React DevTools 是一个非常强大的浏览器扩展,它可以帮助你调试你的 React 应用。
  • Vue 的开发体验被设计为尽可能地简单和愉快。Vue CLI 提供了一个全功能的脚手架,用于快速启动一个新项目,它包括热重载、代码分割、生产环境优化等功能。此外,Vue DevTools 是一个类似于 React DevTools 的浏览器扩展,它也可以帮助你调试你的 Vue 应用。

3. 性能:

  • React 使用虚拟 DOM 和高效的差异算法来优化渲染性能。在 React 16(也被称为 "React Fiber")中,引入了一种新的调和算法,它可以实现异步渲染,从而提高了大型应用的性能。
  • Vue 也使用虚拟 DOM,但它还使用了一些额外的优化技术,例如静态模板分析和异步渲染。这使得 Vue 在某些场景下的性能可能会优于 React。

4. TypeScript 支持:

  • React 自 16.8 版本开始已经有了对 TypeScript 的支持。你可以在创建项目时选择 TypeScript 作为项目的语言。虽然 React 的核心并不是用 TypeScript 写的,但是它的类型定义是由社区维护并且质量很高。
  • Vue 在 2.x 版本中对 TypeScript 的支持并不是很好,主要是因为它的 API 并不是很适合静态类型检查。然而,在 Vue 3(也被称为 "Vue Composition API")中,改善了对 TypeScript 的支持,因为它的 API 设计更加符合 TypeScript 的静态类型系统。

其他区别

1. Reactivity(响应性)系统:

  • React 使用的是 pull-based reactivity(拉取式响应性)。当组件的 state 或 props 发生变化时,React 会重新渲染组件,并对比新旧 virtual DOM 来决定是否需要更新真实 DOM。这意味着 React 在每次 state 或 props 变化时都需要重新渲染。
  • Vue 使用的是 push-based reactivity(推送式响应性)。当依赖的数据变化时,Vue 会自动追踪这些变化并重新渲染组件。这使得 Vue 可以更精确地知道何时需要重新渲染,而不是每次数据变化时都重新渲染。

2. Reactivity API(响应性 API):

  • React 使用的是函数式编程和不可变数据的理念。你需要使用 setStateuseReducer 来改变状态,并返回一个新的状态对象,而不是直接修改状态。
  • Vue 允许你直接修改状态,并通过 v-model 指令实现双向数据绑定。这使得 Vue 的编程模型可能更接近于传统的命令式编程。

3. 代码组织和可维护性:

  • React 使用的是单一方向数据流,这使得状态的流动更容易预测和理解。此外,React 支持使用高阶组件和 render props 等模式来复用逻辑。
  • Vue 使用的是组件级别的状态管理,这使得组件的状态更容易管理和隔离。此外,Vue 支持使用 mixin 和 scoped slots 等模式来复用逻辑。

4. 自定义指令和过滤器:

  • React 没有内建的自定义指令和过滤器的概念。React 鼓励使用组件和函数来实现相同的功能。
  • Vue 提供了自定义指令和过滤器的功能。自定义指令可以让你创建可复用的 DOM 操作,而过滤器可以让你创建可复用的数据转换。

使用 Husky 和 Commitlint 构建 Git 提交规范

· 阅读需 2 分钟
素明诚
Full stack development

安装,这里注意使用"husky": "^4.3.8"版本,高版本需要额外的配置,使用高版本一定要去参考 GitHub 上的说明!

npm install --save-dev @commitlint/{cli,config-conventional} husky

创建一个名为 commitlint.config.js 的文件在项目的根目录,并添加以下内容

module.exports = {
extends: ['@commitlint/config-conventional']
};

package.json 文件中,添加 Husky 的配置:

{
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
}

这样就可以了,但是如果想进行限制,需要一些配置,你可以参考我的配置

export default {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'feat', // 新功能
'fix', // 修复问题
'docs', // 文档改变
'style', // 样式(不影响代码运行的变动)
'refactor', // 重构(即不是新增功能,也不是修改bug的代码变动)
'perf', // 性能优化
'test', // 增加测试
'build', // 构建过程或辅助工具的变动
'ci', // 改变持续集成的配置文件和 package 中的 scripts 命令
'chore', // 其他改变(比如依赖更新)
'revert', // 代码回退
],
],
'subject-case': [2, 'never', ['upper-case']], // 提交信息的主题不能是大写
'header-max-length': [2, 'always', 999], // 提交信息的长度不能超过999个字符
},
}

麻烦点个赞谢谢啦~

ESLint 配置

· 阅读需 3 分钟
素明诚
Full stack development

初始化配置

npx eslint --init

a6c2bc6e3a0faa3b8357eac93ff8a713

eslint/create-config 是一个用于帮助创建和管理 ESLint 配置文件的工具包

  • "To check syntax only":只进行语法检查。
  • "To check syntax and find problems":进行语法检查,并找出可能的问题。
  • "To check syntax, find problems, and enforce code style":进行语法检查,找出可能的问题,并强制执行代码风格。

一般选择最后一个,毕竟安装就是为了找出代码问题

继续下一项

76ff7895e0cbc0c5a500b8e3cd6f0513

  • "JavaScript modules (import/export)":这意味着你的项目使用 ES6 模块化规范,也就是使用 importexport 关键字来导入和导出模块。
  • "CommonJS (require/exports)":这意味着你的项目使用 CommonJS 模块化规范,也就是使用 require()module.exports 或者 exports 来导入和导出模块。
  • "None of these":如果你的项目不使用上述任何一种模块化规范,可以选择这个选项。

如果你是 vite 项目,建议选择第一项

继续下一项

4b1b81c538352002d9b22375c9245ec7

选择框架

继续下一项

050647b5e910c246c409281cbaf8896f

是否使用了 TS

选择代码的运行环境,是 node 还是浏览器

继续下一项

5fe4c7a2f7698cdc27fd0bff76fbf027

  • "Use a popular style guide":使用一个流行的代码风格指南。如果你选择这个选项,ESLint 将会让你选择一个流行的代码风格指南(如 Airbnb,Standard,Google 等)作为你的代码风格规则。
  • "Answer questions about your style":回答关于你的代码风格的问题。如果你选择这个选项,ESLint 将会询问你一系列关于代码风格的问题(如你是否使用分号,你的缩进是两个空格还是四个空格等),并根据你的回答生成相应的代码风格规则。

一般选择一个流行的风格指南即可

继续下一项

7c5711029bdb8e04708f2fb48f4fcd8a

  • "JavaScript":你的 ESLint 配置文件将是一个 JavaScript 文件,通常命名为 .eslintrc.js。JavaScript 格式的配置文件允许你使用 JavaScript 代码,例如你可以使用变量和函数,也可以导入其他模块。
  • "YAML":你的 ESLint 配置文件将是一个 YAML 文件,通常命名为 .eslintrc.yaml。YAML 格式的配置文件易于阅读和编写,但不能使用 JavaScript 代码。
  • "JSON":你的 ESLint 配置文件将是一个 JSON 文件,通常命名为 .eslintrc.json。JSON 格式的配置文件也易于阅读和编写,但同样不能使用 JavaScript 代码。

我一般选择第一个

继续下一项

fcffad6c8041d597cda5dd60818838ce

  • eslint-plugin-react:这个包提供了一些特定于 React 的 linting 规则。
  • eslint-config-standard-with-typescript:这个包提供了一个集成了 Standard style 和 TypeScript 的 ESLint 配置。
  • @typescript-eslint/eslint-plugin:这个包包含了一些针对 TypeScript 的 linting 规则。
  • eslint:这是 ESLint 本身。
  • eslint-plugin-import:这个包提供了一些关于 ES6 import/export 语法的 linting 规则。
  • eslint-plugin-node:这个包提供了一些针对 Node.js 的 linting 规则。
  • eslint-plugin-promise:这个包提供了一些关于 Promise 的 linting 规则。
  • typescript:这是 TypeScript 本身。

yes

继续下一项

fe4a5f22862a60d841830522fba4ddf6

喜欢哪个安装哪个

继续下一项

446ed240d15f20d92c7f98555bcf3404

开始安装了

65cef73411dbdcb5302bff3da865c254

结束

Git 语义化提交Semantic Commits

· 阅读需 2 分钟
素明诚
Full stack development
类型描述
feat新增了一个新的功能
fix修复了一个 bug
docs对文档进行了修改或更新
style对代码的格式进行了修改(比如删除空格、格式化、缺少分号等等)。注意,这并不影响代码的逻辑或功能
refactor对代码进行了重构(即既不是新增功能,也不是修复 bug 的代码变动)
test新增或修改了测试
chore对构建过程或辅助工具进行了修改
perf提高了性能
build影响了构建系统或外部依赖关系的更改
ci对 CI 配置文件和脚本进行了修改
revert撤销了之前的 commit

这种风格的提交信息通常遵循这样的格式:<type>(<scope>): <subject>,其中:

  • <type>:提交的类型,如上表所述。
  • <scope>:影响的范围,即这次提交影响了哪些部分。这是可选的,取决于项目。
  • <subject>:提交的简短描述。

Git 提交信息的示例

新增功能

feat(user): add password reset functionality

修复 bug

fix(login): correct error handling on invalid input

更新文档

docs(readme): update installation instructions

修改代码格式

style(app): apply prettier for code formatting

重构

refactor(database): simplify query builder logic

修改系统构建

build(docker): update Dockerfile to use alpine base image

提高性能

perf(server): reduce memory usage in data processing

React Vue3 API 速查

· 阅读需 3 分钟
素明诚
Full stack development

React 组件 API:

API描述
render()返回需要渲染的元素,或者返回 null
constructor()组件的构造函数,在创建组件的时候调用
componentDidMount()在组件挂载到 DOM 后立即调用
componentDidUpdate()在组件进行更新后立即调用
componentWillUnmount()在组件卸载及销毁之前立即调用
shouldComponentUpdate()通过比较新旧 props 和 state 来决定组件是否应该更新,返回一个布尔值
static getDerivedStateFromProps()当 state 需要从 props 初始化时使用
getSnapshotBeforeUpdate()在更新后,但在 DOM 变更之前调用

React 基础 Hooks:

Hook描述
useState()让函数组件也能有 state(状态)
useEffect()用来替代生命周期函数,如 componentDidMount, componentDidUpdate, componentWillUnmount
useContext()接受一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值

额外的 Hooks:

Hook描述
useReducer()替代 useState,接受一个形如 (state, action) => newState 的 reducer,并返回当前的 state,以及与其配套的 dispatch 方法
useCallback()返回一个记忆的版本,该版本仅在其中的依赖项更改时才会更改
useMemo()返回一个记忆的值,只有当其中的依赖项更改时才会重新计算
useRef()返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)
useImperativeHandle()用于自定义父组件通过 ref 获取和使用子组件的实例属性和方法
useLayoutEffect()其函数签名与 useEffect 相同,但它在所有的 DOM 变更之后同步调用效果
useDebugValue()可以用于在 React DevTools 中显示自定义 hook 的标签

Vue.js 3 常用 API:

API描述
createApp()创建一个新的 Vue 应用实例
defineComponent()定义一个新的组件
watch()响应式地追踪依赖变动
watchEffect()立即执行一个函数并响应式地追踪其依赖,并在其依赖变更时重新运行该函数
computed()创建一个响应式的计算属性

Vue.js 3 组合 API:

API描述
ref()创建一个响应式引用
reactive()接收一个普通对象并返回其代理,等同于 Vue 2.x 的 Vue.observable()
toRefs()将 reactive 创建的响应式对象转化为普通对象
toRef()为 reactive 对象上的属性创建一个 ref
readonly()创建一个只读的响应式对象
isRef()检查一个值是否为一个 ref 对象
isReactive()检查一个对象是否是由 reactive 创建的响应式代理
isReadonly()检查一个对象是否是由 readonly 创建的只读代理
isProxy()检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
unref()如果参数是一个 ref,则返回它的 value,否则返回参数本身
proxyRefs()在 reactive/reactive 上使用 ref
customRef()创建一个自定义的 ref
shallowRef()和 ref 类似,但是只保持浅层响应式
triggerRef()手动触发 shallowRef 的更新
shallowReactive()和 reactive 类似,但只保持浅层响应式
markRaw()使一个对象永远不会被转化为 Proxy

Reactmemo 和 useMemo 应该什么时候使用

· 阅读需 4 分钟
素明诚
Full stack development
  • useMemo 是一个 React Hook,用于在函数组件内部记忆化一个值。当其依赖项改变时,它会重新计算这个值;否则,它会返回上一次计算的值。这可以帮助我们避免在每次渲染时进行昂贵的计算。
  • React.memo 是一个高阶组件,它可以让你控制一个组件何时重新渲染。当一个组件被 React.memo 包装后,只有当它的 props 发生改变时,它才会重新渲染。否则,React 将复用上一次渲染的结果,这可以避免不必要的渲染,提高应用的性能。

这两个功能都是 React 的优化工具,可以帮助我们在保持应用响应性的同时提高性能。但是要注意,他们并不能解决所有的性能问题,且在某些情况下可能会引入额外的性能开销。因此,只有在确定需要进行优化,并且这些工具可以有效地提高性能时,才应该使用他们。

举个例子

当然可以。让我们看一个具体的例子,比如一个简单的列表渲染:

function List({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}

在这个例子中,List 组件接收一个 items prop,并为每个 item 渲染一个列表项。这个组件非常简单,且性能通常是足够的。

然而,如果我们过度优化,可能会这样做:

const ListItem = React.memo(function ListItem({ item }) {
return <li>{item.name}</li>;
});

function List({ items }) {
return (
<ul>
{items.map(item => (
<ListItem key={item.id} item={item} />
))}
</ul>
);
}

在这个优化的版本中,我们创建了一个新的 ListItem 组件,并用 React.memo 对它进行了包装,以避免不必要的重新渲染。然而,这种优化实际上可能没有带来实质性的性能提升,因为原始版本的性能已经足够好。更糟糕的是,这种优化增加了代码的复杂性和难以理解性,让代码的维护变得更困难。

此外,React.memo 的浅比较可能会导致问题。在这个例子中,如果 item 对象的属性发生了改变,但 item 对象本身的引用没有改变,那么 React.memo 可能会错过这个变化,导致 UI 不正确。

总的来说,在大多数情况下,我们应该遵循 "先让它工作,然后让它更快" 的原则,也就是说,应该首先关注应用的功能和正确性,然后再关注性能。只有当性能真的成为问题时,我们才需要考虑使用 useMemoReact.memo 进行优化。

我个人觉得这是不是也和 Java 的若无必要,勿增实体的理念很相似,不仅仅适用于 React 或者 JavaScript,也适用于 Java 和其他许多编程语言。这个原则通常被称为 "YAGNI",即 "You Aren't Gonna Need It"(你不会需要它)。这个原则告诫我们避免过度设计和过度优化,除非真的有必要。

所以在使用 memo 的时候还是要多考虑是不是符合以下三点

  1. 不必要的复杂性:如果你的应用或组件并不遇到性能问题,那么引入 useMemoReact.memo 可能会增加不必要的复杂性,让代码更难理解和维护。
  2. 过度优化useMemoReact.memo 通过记忆值和避免不必要的渲染来提高性能,但这种记忆化和避免渲染本身也需要一些计算和内存。如果你过度使用这两个工具,可能会引入额外的性能开销。
  3. 浅比较React.memo 通过对 props 进行浅比较来判断是否需要重新渲染。这意味着如果 props 是复杂的对象,并且只是内部的某个属性发生了变化,React.memo 可能无法正确地判断是否需要重新渲染。

watch 和 watchEffect 区别

· 阅读需 1 分钟
素明诚
Full stack development
  • watchEffect:你提供一个函数,Vue.js 将立即运行该函数,并响应式地追踪其依赖,并在其依赖变更时重新运行该函数。这非常适合侧效应的操作,例如获取数据、订阅或手动改变 DOM 等。你不需要特别指出要观察的对象,Vue.js 会自动收集依赖。

例如:

import { ref, watchEffect } from 'vue'

const count = ref(0)

watchEffect(() => console.log(count.value))

在上述代码中,每当 count 改变,提供给 watchEffect 的函数就会重新运行。

  • watch:需要明确指出要观察的响应式对象,当被观察的响应式对象发生改变时,watch 将运行一个回调函数。这非常适合在数据变化时执行异步或者较复杂的操作。

例如:

import { ref, watch } from 'vue'

const count = ref(0)

watch(count, (newValue, oldValue) => {

console.log('The new count is: ' + newValue)
})

在上述代码中,只有 count 改变时,提供给 watch 的回调函数才会运行。

watchEffect 更为简单和直观,尤其在处理副作用时,而 watch 则提供了更细粒度的控制,包括获取旧值和新值,以及 lazy 选项等