Sunday, July 24, 2022

SegmentFault 最新的文章

SegmentFault 最新的文章


从原生 JavaScript 到 React

Posted: 19 Jul 2022 09:50 PM PDT

从头开始理解 React

作者:Stéphane Bégaudeau于 2018 年 10 月 1 日

React 是一个用于构建用户界面的 JavaScript 框架。它可用于通过动态操作页面内容来创建 JavaScript 应用程序。浏览器已经提供了在页面中创建元素的 API,即 DOM,所以新手可能想知道 React 带来了什么以及它与 DOM 的关系。

原生 JavaScript 和 DOM

在 JavaScript 中,就像在大多数编程语言中一样,您将可以访问具有各种对象和函数的全局范围,您可以操纵这些对象和函数来构建您的应用程序。在 Web 环境中运行的 JavaScript 应用程序中,您将有权访问文档对象模型 (DOM) API。如果您在基于节点的应用程序中使用 JavaScript,您将无法访问 DOM,但您可以导入替代实现,例如JSDOM。

DOM 是一个简单的 API,可让您以几乎任何您想要的方式操作页面的 HTML 文档。由于全局document 对象,您可以开始使用它。

document 这里开始,您可以轻松地创建新元素、修改它们的属性,甚至将它们添加为其他元素的子元素。多亏了 DOM,您可以通过编程方式创建任何 HTML 文档,即使这样做会非常冗长。

在下面的示例中,我们将以编程方式在 HTML 文档中创建一个简单的标题。

<!DOCTYPE html> <html>   <head>     <script src="app.js"></script>   </head>   <body>     <div id="app" />   </body> </html>

为此,我们将创建一个h1元素,该元素将插入到 HTML 页面的正文中。

// The document object is accessible since it is in the global scope const h1Element = document.createElement('h1'); h1Element.setAttribute('class', 'title'); const textElement = document.createTextNode('I am Groot'); h1Element.appendChild(textElement);  // document.getElementById('app') will retrieve the div with the identifier app document.getElementById('app').appendChild(element);

上面的代码首先创建一个新属性,然后向该元素h1添加一个class带有值为title的新属性。 它还创建一个简单的文本节点并将文本 'I am Groot' 添加为元素h1的子元素。最后,它使用 HTML 文档将 h1 的标签添加到 div 中。app 执行此代码后,生成的 HTML 文档将如下所示:

<!DOCTYPE html> <html>   <head>     <script src="app.js"></script>   </head>   <body>     <div id="app">       <h1 class="title">I am Groot</h1>     </div>   </body> </html>

借助 DOM,我们还可以通过 className 属性直接操作元素的类属性(因为名称 class 是 JavaScript 中的保留关键字)。因此,以下代码将产生完全相同的结果。

const h1Element = document.createElement('h1'); // h1Element.setAttribute('class', 'title'); h1Element.className = 'title'; const textElement = document.createTextNode('I am Groot'); h1Element.appendChild(textElement);  document.getElementById('app').appendChild(element);

React 的基础

大多数 React 教程会让你从直接使用 React 的所有奇迹开始。我们将采用另一种方法,因为我们将从编写一些你可能永远不会再编写的 React 代码开始,以便更好地理解 React 的工作方式。

React 的创建考虑了 Web 应用场景,因此,在其核心,它的一些 API 感觉就像 DOM。为了说明这一点,我们将看一下最重要的 React API 之一,React.createElement.

要使用 React 操作 DOM,您将需要两个依赖项 React 和 ReactDOM. React.createElement将让您创建一个廉价且快速的数据结构,称为虚拟 DOM,代表您的用户界面的结构。ReactDOM将在您的 Web 应用程序的真实 DOM 中呈现这个虚拟 DOM。

React.createElement将需要三个参数来创建虚拟 DOM 的元素:

  • 要创建的元素的名称
  • 它的属性
  • 它的孩子
import React from 'react';  const name = 'h1'; const props = { className: 'title' }; const children = 'I am Groot'; const element = React.createElement(name, props, children);

React.createElement 也可以接受包含要创建的元素的所有子元素的数组。

import React from 'react';  const name = 'h1'; const props = { className: 'title' }; const children = ['I am Groot']; const element = React.createElement(name, props, children);

参数 children 也是元素的常规属性,因此它可以是 props 对象的一部分。

import React from 'react';  const props = {   className: 'title',   children: ['I am Groot'] }; const element = React.createElement('h1', props);

为了在 DOM 中渲染这个元素,我们需要选择它在 DOM 中的渲染位置,在我们的例子中是div带有标识符app并告诉 ReactDOM 渲染它。

import React from 'react'; import ReactDOM from 'react-dom';  const props = {   className: 'title',   children: ['I am Groot'] }; const element = React.createElement('h1', ...props);  ReactDOM.render(element, document.getElementById('app'));

此处显示的所有代码示例都可以通过将它们与未打包版本的 React 和 Babel 一起使用来进行测试。这样的配置应该只用于简单的测试,因为它们没有像生产构建那样优化。在这种特定情况下,应删除 和 的导入(此处均作为全局变量公开react)。react-dom

<!DOCTYPE html> <html>   <head>     <title>React</title>     <script src="https://unpkg.com/react@16/umd/react.development.js"></script>     <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>     <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>      <script type="text/babel">     const props = {       className: 'title',       children: ['I am Groot']     };     const element = React.createElement('h1', props);      ReactDOM.render(element, document.getElementById('app'));     </script>   </head>   <body>     <div id="app" />   </body> </html>

咱老百姓也能学会的 JSX

虽然我们可以使用这种方法创建 Web 应用程序的所有页面,但它仍然非常冗长。为了使操作 DOM 变得容易,React 提供了一种名为JSX的简单而强大的语言。

预处理器使用 JSX 在构建期间将其转换为常规 JavaScript。一个常规的 React 项目使用预处理器来将 JSX 代码转换为对 React.createElement. 因此,JSX 永远不会被 React 直接解释,你可以在没有一行 JSX 的情况下使用 React。因此,下面的两段代码完全相同。首先,以编程方式使用 React:

import React from 'react';  const props = { className: 'title' }; const children = ['I am Groot']; const element = React.createElement('h1', props, children);

或使用 JSX 声明:

import React from 'react';  const element = <h1 className="title">I am Groot</h1>;

由于 JSX 代码将使用 转换为调用 React.createElement,因此您需要导入 React,即使它似乎没有被使用。

使用 JSX,您可以非常快速地以声明方式创建大部分 DOM,而 React 只会看到对React.createElement. 由于 JSX 元素只是对 的调用 React.createElement,因此 children 仍然是常规属性。因此,您也可以像这样编写前面的示例:

import React from 'react';  const element = <h1 className="title" children="I am Groot"/>;

借助 JSX,您可以通过花括号访问变量:

import React from 'react'; const title = 'title'; const text = 'I am Groot';  const element = <h1 className={title} children={text}/>;

当然,我们也可以将变量命名为我们想要操作的属性

import React from 'react';  const className = 'title'; const children = 'I am Groot';  const element = <h1 className={className} children={children}/>;

这将允许我们使用扩展语法来获得更简洁的代码

import React from 'react';  const props = {   className: 'title',   children: ['I am Groot'] }; const element = <h1 {...props}/>;

最后,我们可以像以前一样在 DOM 中渲染这个元素 React.createElement。

import React from 'react'; import ReactDOM from 'react-dom';  const props = {   className: 'title',   children: ['I am Groot'] };  ReactDOM.render(<h1 {...props}/>, document.getElementById('app')); 

现在我们已经使用 JSX 通过 React 渲染了我们的第一块虚拟 DOM,我们准备好看看如何使用 React 构建一个基本的应用程序。

奔跑吧,去用 React 组件开发更多动态代码。

如何提出一个高质量的问题?

Posted: 17 Jul 2022 02:01 AM PDT

封面图源自:Pexels

前言

看到标题的一瞬间,你或许在想 "什么样的问题算是好问题?",在思否,每天或多或少都有十几个(休息日除外)新的问题被提出来,而这里面充斥着不少的 "质量" 问题。

如果你有曾读过 《提问的智慧》 这本书,那你就应该知道该如何去提出一个问题,本文也算是对这本书的一个衍生补充。

下面,我将以我的视野,带你提出一个好的问题。

真的需要提出这个问题吗?

在提问之前,你应该先使用 Google、百度、必应等搜索引擎尝试寻找答案,因为在大部分时候,你能遇到的问题,其他人可能已经遇到过了,然后按照其中的解决方案进行处理,问题或许就解决了。

比如:Vue路由的实现原理? - SegmentFault 思否

一般可以描述出来的问题,或许更加容易检索,没准你花半个小时搜索一番,你的问题就已经解决了。

而如果你选择直接提出一个问题的话,大概率在三十分钟内得不到解决。

如何检索问题,大部分时候比较容易,但是遇到一些特殊情况,就变得麻烦了,比如我们在运行命令的时候出错了,那么可能会打印出来无比冗长的错误信息,而且基本上都是英文,从中提取出有用的信息至关重要。

举个例子,就像 下面这一堆错误信息

D:\workplace\newcodebdc\back> npm install npm ERR! code ERESOLVE npm ERR! ERESOLVE could not resolve npm ERR! npm ERR! While resolving: vue-antd-jeecg@2.0.0 npm ERR! Found: webpack@4.46.0 npm ERR! node_modules/webpack npm ERR! webpack@"^4.0.0" from @vue/cli-plugin-babel@3.12.1 npm ERR! node_modules/@vue/cli-plugin-babel npm ERR! dev @vue/cli-plugin-babel@"^3.3.0" from the root project npm ERR! webpack@"^4.0.0" from @vue/cli-plugin-eslint@3.12.1 npm ERR! node_modules/@vue/cli-plugin-eslint npm ERR! dev @vue/cli-plugin-eslint@"^3.3.0" from the root project npm ERR! 3 more (@vue/cli-service, less-loader, sass-loader) npm ERR! npm ERR! Could not resolve dependency: npm ERR! vue-loader@"^15.7.0" from the root project npm ERR! npm ERR! Conflicting peer dependency: webpack@5.73.0 npm ERR! node_modules/webpack npm ERR! peer webpack@"^5.0.0" from css-loader@6.7.1 npm ERR! node_modules/css-loader npm ERR! peer css-loader@"*" from vue-loader@15.10.0 npm ERR! node_modules/vue-loader npm ERR! vue-loader@"^15.7.0" from the root project npm ERR! npm ERR! Fix the upstream dependency conflict, or retry npm ERR! this command with --force, or --legacy-peer-deps npm ERR! to accept an incorrect (and potentially broken) dependency resolution. npm ERR! npm ERR! See C:\Users\MaxThunder\AppData\Local\npm-cache\eresolve-report.txt for a full report.  npm ERR! A complete log of this run can be found in:

这时候我们只需要拷贝这部分错误信息,先进行翻译,尝试去理解为什么错,如果不能理解,就到搜索引擎进行搜索,大多数情况下,你都能得到答案。

当产生错误的时候,我们应该先在报错信息中寻找一个关键字 Error ,大部分时候,出现这个关键字的时候,紧随其后都会打印出具体的错误,部分的还会打印出一个错误的 CODE ,比如 MySQL 的报错,你顺着这个 CODE 加上软件名字,没准就能检索到。

如上面的里面,充斥着 ERR,这就有很多错误的,一眼也无法分辨,如果你复制的翻译后,大概就能知道,是" xxx 依赖不能解析",并且在后面还提供了一段 fix(修复) 方法,让你尝试在命令后面加上 --force 或者 --legacy-peer-deps 进行重试,或许你执行后问题就解决了,你也就不再需要提出一个问题了。

提出问题

合适的标题

大部分社区,都是以标题的形式在列表展示出来的,像 思否 这种还会在列表展示标签。回答者并不是机器人,大多数时候,回答者并不会打开所有帖子进行查看,起一个合适的标题更能让回答者判断这个问题是否在自身的解决范围之内,从而点开。

不好的例子

好的例子

高质量的问题描述

在你的问题被解决前,你需要做的最终重要的一件事,就是让回答者知道你在问什么,否则别人很难给你答复。

正如思否的提问模板一样:

  • 你遇到了什么问题?
  • 你的预期是什么?
  • 你的环境是什么(运行的软件版本、操作系统)?
  • 你做了哪些工作?
  • 出现问题的代码

按照上面的规则,描述你的问题,让回答者能更加清晰的知道你遇到的是什么问题,除此之外,你可能还需要按照提供一些额外信息,来帮助回答者更快的帮你解决问题,毕竟帮回答者节约时间,也是给你自己节约时间。

学习使用 Markdown 语法,这花不了你多少时间,顶多十分钟,但是终生受益。

1、不要使用图片上传代码,现在讨论的社区,基本都支持 Markdown 语法,花上少许的时间,学习一下如何贴代码,这样回答者也可以直接把代码复制到自己的环境上运行,快速复现/定位问题所在。

使用 3 个 ` 符号(键盘左上角,ESC下面,数字 1 前面,英文状态下输入),后面紧跟语言类型,最后再以新起一行,写下三个 ` 即可构成一个代码块。

```js
alert('Hello')
```

最终将显示为如下效果。

alert('Hello')

2、尽可能提供有用的信息

在数据库相关的问题中,尤为常见,大部分人提问的时候,都是直接把数据截图和自己写的查询语句截图发上来了。

因为 SQL 本身就是抽象的,尤其是复杂的 SQL,都要进行测试才能符合预期。

这就大幅降低了回答者的兴趣,虽然现在 OCR 技术已经足够强大,可能可以识别出来,但有时候还会要修正才行,而且,这类问题,大部分都是跟数据打交道,重要的就是数据呀,你什么都不提供,别人只能远而观之。

对于 SQL 类的提问方式,应当附上建表语句、数据填充语句、以及你自己写的查询语句。
对于建表语句,你可以使用 show create table 表名称; 来获得。

对于数据填充语句,以 MySQL 为例,你可以使用 mysqldump 这个工具导出,然而这可能复杂了一些。但是现在一些 SQL 管理工具,都提供了图形化的导出为 insert 语句。

而你要做的这些,只需要花费你几分钟的时间。

举个例子:

# 建表语句 CREATE TABLE IF NOT EXISTS `myTable` (   `id` mediumint(8) unsigned NOT NULL auto_increment,   `name` varchar(255) default NULL,   `score` mediumint default NULL,   `sex` mediumint default NULL,   PRIMARY KEY (`id`) ) AUTO_INCREMENT=1;  # 数据插入语句 INSERT INTO `myTable` (`name`,`score`,`sex`) VALUES   ("Abdul Stanley",143,2),   ("Chaney Pickett",62,2),   ("Keelie Lindsey",99,2),   ("Rae Hartman",146,2),   ("Nevada Ward",72,1); 

别人在拿到后,就可以方便的在自己的环境上进行处理了,你可能还需要提供 SQL_MODE 信息。

除此之外,还有就是你所运行的 MySQL 版本了。

当然,有时候我们的数据可能存在敏感信息,请一定要记得脱敏后发布。

你也可以使用一些数据构建工具来构建数据跟你结构相似的数据,比如:generatedata

image.png

或者:mockaroo

image.png

拒绝 XY 问题

对于X-Y Problem的意思如下:

1)有人想解决问题X
2)他觉得Y可能是解决X问题的方法
3)但是他不知道Y应该怎么做
4)于是他去问别人Y应该怎么做?

简而言之,没有去问怎么解决问题X,而是去问解决方案Y应该怎么去实现和操作。于是乎:

1)热心的人们帮助并告诉这个人Y应该怎么搞,但是大家都觉得Y这个方案有点怪异。
2)在经过大量地讨论和浪费了大量的时间后,热心的人终于明白了原始的问题X是怎么一回事。
3)于是大家都发现,Y根本就不是用来解决X的合适的方案。

X-Y Problem最大的严重的问题就是:在一个根本错误的方向上浪费他人大量的时间和精力!

举个 🌰

Q) 我怎么用Shell取得一个字符串的后3位字符?
A1) 如果这个字符的变量是$foo,你可以这样来 echo ${foo:-3}
A2) 为什么你要取后3位?你想干什么?
Q) 其实我就想取文件的扩展名
A1) 我靠,原来你要干这事,那我的方法不对,文件的扩展名并不保证一定有3位啊。
A1) 如果你的文件必然有扩展名的话,你可以这来样来:echo ${foo##*.}

帮助回答者

在提问的时候,你可以多为回答者考虑一些,比如通过一些在线服务,让回答者无需在本地环境上操作。

比如前面提到的数据库问题,我们就可以使用 db-fiddle 来创建一个在线的 MySQL 运行环境,填写完成后点击 Run 确认 OK 后,再点击 Save 将会为你生成一个唯一的链接地址,现在别人打开这个链接,就可以在这上面直接编辑操作,方便了许多。

image.png

除此之外,像前端还可以使用

这俩个都是国外的服务,可能比较难打开,你也可以选择国内的:

如果你的问题比较复杂,你可能还要考虑提供一个「最小的可重现示例(Minimal reproducible example)」将其提交到 Git 服务,以提供给回答者。(☹️ 我想一般也不会复杂到这个程度)

复杂情况

还有一个问题,就是问题很小,但是代码很多。

常见于现代化前端的 CSS 问题,很多时候,问题是在项目或者某个组件中发生的,而即便是你提供了这个组件的代码,回答者也不能很好的复现问题,比如你的 DOM 可能是由数据渲染出来的。

这种情况,提问的时候,就应该把渲染后的结果+能够复现的最少的代码贴上来,这样更容易复现问题。

或者,可以使用一些浏览器扩展,把页面保存成单个 HTML 文件,上传到免登录/注册的网盘或者 Git 上。

比如 SingleFile ,这个扩展就可以把网页所有内容保存成一个 HTML 文件,从而让回答者更加容易复现问题。

(当然,你也应该尽可能的提供原始代码)

gildas-lormeau/SingleFile: Web Extension for Firefox/Chrome/MS Edge and CLI tool to save a faithful copy of an entire web page in a single HTML file

Playground 或者 Sandbox

对于一些冷门点儿的语言,上面的这些工具,可能覆盖不到,但是你也可以尝试搜索 语言 Playground ,大概率可以找到一些可以在线编辑、运行、分享的服务,比如 Laravel playground

维护好你的问题

很多时候,我都能在不同的社区看到同一个问题,毫无疑问,这个问题也是同一个人提的。

当然,多个地方就多了一分解决问题的机会,但是这些提问者往往都少有去 "维护" 自己的问题。

维护一个问题,往往比提出一个问题,更为重要。

1、及时回应/补充你的问题,有些人可能是第一次提问,遗漏了一些东西,有时候就会有人提醒你,需要补充哪些信息,请及时处理。像思否,默认情况下就会邮件通知到你。

2、与回答者沟通,如果你的问题描述的不是那么完美无瑕,或者回答者理解偏差导致给出了错误的回答,你应该及时的跟回答者进行沟通,而不是无视别人。

这样也可以让其他回答者知道具体的情况,对于我个人而言,如果一个问题,提问者自己没有维护这个问题,那么即使我有解决方案,我也不会选择去回答。

3、不要重复提问,当你的问题几个小时或者一两天过去之后,仍然没有人回答,请不要尝试再重复提问,因为很有可能不是别人没看到,而正是因为前面的种种原因叠加。当然,也有可能你的问题太过深奥,无法给你回答。你还可以尝试邀请一些人进行回答。

4、选择合适的答案,当你的问题被解决后,应该选择帮助你解决问题的人的回答标记为答案。这样也可以很好的帮助其他人在遇到这个问题的时候,及时找到正确答案。

5、找到解决方案后应当附上,如果回答里面的都不能解决你的问题,最终你自己解决了,也应该附上答案。

6、同步解决方案,正如前面提到的,很多人都会在 N 个论坛发出同样的提问,而往往可能只是其中某个论坛的回答者解决了问题,而其他论坛则未解决。作为提问者,你应该像你当初提出问题那样,把正确的答案同步到你曾经发出过这个问题的地方,为后来者提供便利。

你做了什么?

除了上面的以外,提出一个问题的前提是,你应该尽可能在自己的能力范围内去尝试解决他,比如通过搜索引擎检索、尝试已有的方案、编写相应的代码,或许在这些过程中,你的问题就已经解决了。

好的 🌰

写在最后

  • 以上论点仅为个人观点,如果有不足或错误,还请不吝赐教。
  • 你还可以点击文章中的来源链接,了解更详细的内容。
  • 如果文中的内容侵犯到了你得权益,请与我联系处理。

参考

No comments:

Post a Comment