diff --git a/README-en.md b/README-en.md index 305770b..1812e8f 100755 --- a/README-en.md +++ b/README-en.md @@ -2,6 +2,12 @@ > Click :star:if you like the project. Pull Request are highly appreciated. +<<前端进阶篇 v1.1>> PDF 出炉了 — 「阿宝哥」,精心准备的 9 万多字 242 页的前端进阶资料。发布后 5 天内下载量为 **3170**。 + + + +点击此 [全栈修仙之路 - 前端进阶篇](http://book.bugstack.cn/#s/6I-ldR3A) 链接,即可免费下载电子书。 + ### Table of Contents | No. | Questions | @@ -315,16 +321,15 @@ |299| [How to add a bootstrap for a react application?](#how-to-add-a-bootstrap-for-a-react-application)| |300| [Can you list down top websites or applications using react as front end framework?](#can-you-list-down-top-websites-or-applications-using-react-as-front-end-framework)| |301| [Is it recommended to use CSS In JS technique in React?](#is-it-recommended-to-use-css-in-js-technique-in-react)| +|302| [Do I need to rewrite all my class components with hooks?](#do-i-need-to-rewrite-all-my-class-components-with-hooks)| +|303| [How to fetch data with React Hooks?](#how-to-fetch-data-with-react-hooks)| +|304| [Is Hooks cover all use cases for classes?](#is-hooks-cover-all-use-cases-for-classes)| ## Core React 1. ### What is React? React is an **open-source frontend JavaScript library** which is used for building user interfaces especially for single page applications. It is used for handling view layer for web and mobile apps. React was created by Jordan Walke, a software engineer working for Facebook. React was first deployed on Facebook's News Feed in 2011 and on Instagram in 2012. - - React 是一个开源的前端 JavaScript 框架,可用于构建用户界面特别是单页应用程序。它被用于作为网页和移动应用的视图层。React 由 Facebook - 的工程师 Jordan Walke 创建。在 2011 年 React 应用首次被部署到 Facebook 的信息流上,之后于 2012 年被应用到 Instagram 中。 - 2. ### What are the major features of React? @@ -335,12 +340,6 @@ * Follows *Unidirectional** data flow or data binding. * Uses **reusable/composable** UI components to develop the view. - React 的主要特性有: - * 考虑到真实的 DOM 操作比较昂贵,它使用 VirtualDOM 来替代真实的 DOM - * 支持服务端渲染 - * 遵从单向数据流或数据绑定 - * 使用可重用/可组合的 UI 组件开发视图 - 3. ### What is JSX? *JSX* is a XML-like syntax extension to ECMAScript (the acronym stands for *JavaScript XML*). Basically it just provides syntactic sugar for the `React.createElement()` function, giving us expressiveness of JavaScript along with HTML like template syntax. @@ -358,31 +357,11 @@ } } ``` - JSX 是一个对 ECMAScript 进行类 XML 的语法的扩展。它只是为 `React.createElement()` 函数提供语法糖,从而让在我们在 JavaScript 中, - 使用类 HTML 模板的语法,进行页面描述。 - - 在以下示例中,在 `

` 标签中的文本会以 JavaScript 函数的形式返回给渲染函数。 - - ```jsx harmony - class App extends React.Component { - render() { - return( -
-

{'Welcome to React world!'}

-
- ) - } - } - ``` - 4. ### What is the difference between Element and Component? An *Element* is a plain object describing what you want to appear on the screen in terms of the DOM nodes or other components. *Elements* can contain other *Elements* in their props. Creating a React element is cheap. Once an element is created, it is never mutated. - 一个 Element 是一个普通的对象,以 DOM 节点或其他组件的方式描述你期望在屏幕上呈现的内容。 - - React 元素的对象表示如下: The object representation of React Element would be as follows: ```javascript @@ -394,7 +373,6 @@ ``` The above `React.createElement()` function returns an object: - 上面的 `React.createElement()` 函数会返回一个对象。 ``` { @@ -407,25 +385,19 @@ ``` And finally it renders to the DOM using `ReactDOM.render()`: - 最终使用 `ReactDOM.render()` 方法渲染到 DOM: ```html
Login
``` - Whereas a **component** can be declared in several different ways. It can be a class with a `render()` method. Alternatively, in simple cases, it can be defined as a function. In either case, it takes props as an input, and returns an JSX tree as the output: - - 然而一个组件可以以多种不同方式进行声明。它可以是一个含有 `render()` 方法的类。在简单的场景中,它能被定义成函数。在其它场景,它接收 props - 作为输入,返回 JSX 树作为输出: + Whereas a **component** can be declared in several different ways. It can be a class with a `render()` method. Alternatively, in simple cases, it can be defined as a function. In either case, it takes props as an input, and returns a JSX tree as the output: ```javascript const Button = ({ onLogin }) =>
``` - Then JSX gets transpiled to `React.createElement()` function tree: - - 接着 JSX 被转译为 `React.createElement()` 函数树: + Then JSX gets transpiled to a `React.createElement()` function tree: ```javascript const Button = ({ onLogin }) => React.createElement( @@ -438,15 +410,12 @@ 5. ### How to create components in React? There are two possible ways to create a component. - 这里有两种可行方式来创建一个组件: 1. **Function Components:** This is the simplest way to create a component. Those are pure JavaScript functions that accept props object as first parameter and return React elements: - 1. **Function Components:** 这是创建组件最简单的方式 - ```jsx harmony function Greeting({ message }) { - return

{`Hello, ${message}`}

+ return

{`Hello, ${message}`}


 } ``` @@ -477,12 +446,12 @@ class User extends React.Component { constructor(props) { super(props) - + this.state = { message: 'Welcome to React world' } } - + render() { return (
@@ -492,9 +461,9 @@ } } ``` - + ![state](images/state.jpg) - + State is similar to props, but it is private and fully controlled by the component. i.e, It is not accessible to any component other than the one that owns and sets it. 9. ### What are props in React? @@ -1109,7 +1078,7 @@ 45. ### Why React uses `className` over `class` attribute? - `class` is a keyword in JavaSript, and JSX is an extension of JavaScript. That's the principal reason why React uses `className` instead of `class`. Pass a string as the `className` prop. + `class` is a keyword in JavaScript, and JSX is an extension of JavaScript. That's the principal reason why React uses `className` instead of `class`. Pass a string as the `className` prop. ```jsx harmony render() { @@ -3833,6 +3802,7 @@ handleSubmit = () => { console.log("Input Value is: ", this.input.value) } + ``` render () { @@ -3847,19 +3817,19 @@ } } ``` - + But our expectation is for the ref callback to get called once, when the component mounts. One quick fix is to use the ES7 class property syntax to define the function - + ```jsx class UserForm extends Component { handleSubmit = () => { console.log("Input Value is: ", this.input.value) } - + setSearchInput = (input) => { this.input = input } - + render () { return (
@@ -3879,6 +3849,7 @@ 217. ### What are HOC factory implementations? There are two main ways of implementing HOCs in React. 1. Props Proxy (PP) and 2. Inheritance Inversion (II). They follow different approaches for manipulating the *WrappedComponent*. + **Props Proxy** In this approach, the render method of the HOC returns a React Element of the type of the WrappedComponent. We also pass through the props that the HOC receives, hence the name **Props Proxy**. @@ -3894,6 +3865,7 @@ } ``` **Inheritance Inversion** + In this approach, the returned HOC class (Enhancer) extends the WrappedComponent. It is called Inheritance Inversion because instead of the WrappedComponent extending some Enhancer class, it is passively extended by the Enhancer. In this way the relationship between them seems **inverse**. ```jsx @@ -4173,7 +4145,7 @@ The render() method is the only required method in a class component. i.e, All methods other than render method are optional for a class component. 239. ### What are the possible return types of render method? Below are the list of following types used and return from render method, - 1. **React elements:** Elements that instruct React to render a DOM node. It includes html elements such as
and user defined elements. + 1. **React elements:** Elements that instruct React to render a DOM node. It includes html elements such as `
` and user defined elements. 2. **Arrays and fragments:** Return multiple elements to render as Arrays and Fragments to wrap multiple elements 3. **Portals:** Render children into a different DOM subtree. 4. **String and numbers:** Render both Strings and Numbers as text nodes in the DOM @@ -5046,6 +5018,7 @@ ```javascript npm install bootstrap `` + ``` 3. React Bootstrap Package: In this case, you can add Bootstrap to our React app is by using a package that has rebuilt Bootstrap components to work particularly as React components. Below packages are popular in this category, 1. react-bootstrap @@ -5064,4 +5037,40 @@ 9. Netflix 10. PayPal 301. ### Is it recommended to use CSS In JS technique in React? - React does not have any opinion about how styles are defined but if you are a beginner then good starting point is to define your styles in a separate *.css file as usual and refer to them using className. This functionality is not part of React but came from third-party libraries. But If you want to try a different approach(CSS-In-JS) then styled-components library is a good option. \ No newline at end of file + React does not have any opinion about how styles are defined but if you are a beginner then good starting point is to define your styles in a separate *.css file as usual and refer to them using className. This functionality is not part of React but came from third-party libraries. But If you want to try a different approach(CSS-In-JS) then styled-components library is a good option. +302. ### Do I need to rewrite all my class components with hooks? + No. But you can try Hooks in a few components(or new components) without rewriting any existing code. Because there are no plans to remove classes in ReactJS. +303. ### How to fetch data with React Hooks? + The effect hook called `useEffect` is used to fetch the data with axios from the API and to set the data in the local state of the component with the state hook’s update function. + Let's take an example in which it fetches list of react articles from the API + ```javascript + import React, { useState, useEffect } from 'react'; + import axios from 'axios'; + + function App() { + const [data, setData] = useState({ hits: [] }); + + useEffect(async () => { + const result = await axios( + 'http://hn.algolia.com/api/v1/search?query=react', + ); + + setData(result.data); + }, []); + + return ( + + ); + } + + export default App; + ``` + Remember we provided an empty array as second argument to the effect hook to avoid activating it on component updates but only for the mounting of the component. i.e, It fetches only for component mount. +304. ### Is Hooks cover all use cases for classes? + Hooks doesn't cover all use cases of classes but there is a plan to add them soon. Currently there are no Hook equivalents to the uncommon **getSnapshotBeforeUpdate** and **componentDidCatch** lifecycles yet. diff --git a/README.md b/README.md index e9c70e8..b422d1f 100755 --- a/README.md +++ b/README.md @@ -9,10 +9,21 @@ 这里再次感谢 **[liaoyongfu](https://github.com/liaoyongfu)** 的大力支持🌹! +**阅读更多关于 Angular、TypeScript、Node.js/Java 、Spring 等技术文章,欢迎访问我的个人博客或关注我的公众号 —— [全栈修仙之路](http://www.semlinker.com/)** + +![fer_road_qrcode](./images/fer_road_qrcode.jpg) + +<<前端进阶篇 v1.1>> PDF 出炉了 — 「阿宝哥」,精心准备的 9 万多字 242 页的前端进阶资料。发布后 5 天内下载量为 **3170**。 + + + +点击此 [全栈修仙之路 - 前端进阶篇](http://book.bugstack.cn/#s/6I-ldR3A) 链接,即可免费下载电子书。 + ### 目录 + | 序号. | 问题 | -| --- | --------- | +| --- | --------- | | | [Core React](#core-react) | |1 | [什么是 React?](#什么是-react) | |2 | [React 的主要特点是什么?](#react-的主要特点是什么) | @@ -31,7 +42,7 @@ |15 | [如何将参数传递给事件处理程序或回调函数?](#如何将参数传递给事件处理程序或回调函数) | |16 | [React 中的合成事件是什么?](#react-中的合成事件是什么) | |17 | [什么是内联条件表达式?](#什么是内联条件表达式) | -|18 | [什么是 "key" 属性,在元素数组中使用它们有什么好处?](#什么是-"key"-属性在元素数组中使用它们有什么好处) | +|18 | [什么是 "key" 属性,在元素数组中使用它们有什么好处?](#什么是-key-属性在元素数组中使用它们有什么好处) | |19 | [refs 有什么用?](#refs-有什么用) | |20 | [如何创建 refs?](#如何创建-refs) | |21 | [什么是 forward refs?](#什么是-forward-refs) | @@ -322,6 +333,9 @@ |299 | [如何为 React 应用程序添加 bootstrap?](#如何为-react-应用程序添加-bootstrap) | |300 | [你能否列出使用 React 作为前端框架的顶级网站或应用程序?](#你能否列出使用-react-作为前端框架的顶级网站或应用程序) | |301 | [是否建议在 React 中使用 CSS In JS 技术?](#是否建议在-react-中使用-css-in-js-技术) | +|302 | [我需要用 hooks 重写所有类组件吗?](#我需要用-hooks-重写所有类组件吗) | +|303 | [如何使用 React Hooks 获取数据?](#如何使用-react-hooks-获取数据) | +|304 | [Hooks 是否涵盖了类的所有用例?](#hooks-是否涵盖了类的所有用例) | ## Core React @@ -1679,7 +1693,7 @@ } } ``` - + **[⬆ 返回顶部](#目录)** 67. ### 如何有条件地渲染组件? @@ -2450,7 +2464,7 @@ React.render(, document.getElementById('container')) ``` - + **[⬆ 返回顶部](#目录)** 106. ### 为什么你不能更新 React 中的 props? @@ -4350,7 +4364,7 @@
) ``` - + **[⬆ 返回顶部](#目录)** 215. ### 为什么不建议使用内联引用回调或函数? @@ -4362,7 +4376,7 @@ handleSubmit = () => { console.log("Input Value is: ", this.input.value) } - + render () { return ( @@ -4375,19 +4389,19 @@ } } ``` - + 但我们期望的是当组件挂载时,ref 回调只会被调用一次。一个快速修复的方法是使用 ES7 类属性语法定义函数。 - + ```jsx class UserForm extends Component { handleSubmit = () => { console.log("Input Value is: ", this.input.value) } - + setSearchInput = (input) => { this.input = input } - + render () { return ( @@ -4417,7 +4431,7 @@ 在这种方法中,HOC 的 render 方法返回 WrappedComponent 类型的 React 元素。我们通过 HOC 收到 props,因此定义为**属性代理**。 ```jsx - + function ppHOC(WrappedComponent) { return class PP extends React.Component { render() { @@ -4473,7 +4487,7 @@ import ReactDOM from 'react-dom'; import App from './App'; import registerServiceWorker from './registerServiceWorker'; - + ReactDOM.render(, document.getElementById('root')); registerServiceWorker(); ``` @@ -4498,7 +4512,7 @@ ```jsx const OtherComponent = React.lazy(() => import('./OtherComponent')); - + function MyComponent() { return (
@@ -4582,24 +4596,24 @@ ```jsx class Counter extends Component { state = { value: 0 }; - + handleIncrement = () => { this.setState(prevState => ({ value: prevState.value + 1 })); }; - + handleDecrement = () => { this.setState(prevState => ({ value: prevState.value - 1 })); }; - + render() { return (
{this.state.value} - +
@@ -4616,11 +4630,11 @@ ```jsx import { useState } from 'react'; - + function Example() { // Declare a new state variable, which we'll call "count" const [count, setCount] = useState(0); - + return (

You clicked {count} times

@@ -4633,7 +4647,7 @@ ``` 阅读资源: - + 1. [掘金 - 30分钟精通React Hooks](https://juejin.im/post/5be3ea136fb9a049f9121014) **[⬆ 返回顶部](#目录)** @@ -4737,7 +4751,7 @@ super(props); this.state = { error: null }; } - + handleClick = () => { try { // Do something that could throw @@ -4745,7 +4759,7 @@ this.setState({ error }); } } - + render() { if (this.state.error) { return

Caught an error.

@@ -4860,11 +4874,11 @@ class MyButton extends React.Component { // ... } - + MyButton.defaultProps = { color: 'red' }; - + ``` 如果未设置 props.color,则会使用默认值 `red`。 也就是说,每当你试图访问 color 属性时,它都使用默认值。 @@ -4901,18 +4915,18 @@ super(props); this.state = { hasError: false }; } - + static getDerivedStateFromError(error) { // Update state so the next render will show the fallback UI. return { hasError: true }; } - + render() { if (this.state.hasError) { // You can render any custom fallback UI return

Something went wrong.

; } - + return this.props.children; } } @@ -4944,7 +4958,7 @@ 247. ### displayName 类属性的用途是什么? displayName 被用于调试信息。通常,你不需要显式设置它,因为它是从定义组件的函数或类的名称推断出来的。如果出于调试目的或在创建高阶组件时显示不同的名称,可能需要显式设置它。 - + 例如,若要简化调试,请选择一个显示名称,以表明它是 withSubscription HOC 的结果。 ```javascript @@ -4953,7 +4967,7 @@ WithSubscription.displayName = `WithSubscription(${getDisplayName(WrappedComponent)})`; return WithSubscription; } - + function getDisplayName(WrappedComponent) { return WrappedComponent.displayName || WrappedComponent.name || 'Component'; } @@ -4984,13 +4998,13 @@ **moduleA.js** ```javascript const moduleA = 'Hello'; - + export { moduleA }; ``` **App.js** ```javascript import React, { Component } from 'react'; - + class App extends Component { handleClick = () => { import('./moduleA') @@ -5001,7 +5015,7 @@ // Handle failure }); }; - + render() { return (
@@ -5010,7 +5024,7 @@ ); } } - + export default App; ``` @@ -5134,16 +5148,16 @@ class LogProps extends React.Component { // ... } - + function forwardRef(props, ref) { return ; } - + // Give this component a more helpful display name in DevTools. // e.g. "ForwardRef(logProps(MyComponent))" const name = Component.displayName || Component.name; forwardRef.displayName = `logProps(${name})`; - + return React.forwardRef(forwardRef); } ``` @@ -5156,7 +5170,7 @@ ```javascript - + ``` @@ -5209,7 +5223,7 @@ 260. ### 如何防止函数被多次调用? 如果你使用一个事件处理程序,如 **onClick or onScroll** 并希望防止回调被过快地触发,那么你可以限制回调的执行速度。 - + 这可以通过以下可能的方式实现: 1. **Throttling:** 基于时间的频率进行更改。例如,它可以使用 lodash 的 _.throttle 函数。 @@ -5247,7 +5261,7 @@ ); ReactDOM.render(element, document.getElementById('root')); } - + setInterval(tick, 1000); ``` @@ -5290,7 +5304,7 @@ posts: response.posts }); }); - + fetchComments().then(response => { this.setState({ comments: response.comments @@ -5325,7 +5339,7 @@ if (!props.loggedIn) { return null; } - + return (
welcome, {props.name} @@ -5340,7 +5354,7 @@ super(props); this.state = {loggedIn: false, name: 'John'}; } - + render() { return (
@@ -5436,7 +5450,7 @@ import React, { Component } from 'react'; import './App.css'; import '@vaadin/vaadin-date-picker'; - + class App extends Component { render() { return ( @@ -5446,7 +5460,7 @@ ); } } - + export default App; ``` @@ -5477,9 +5491,9 @@ ```javascript import loadable from '@loadable/component' - + const OtherComponent = loadable(() => import('./OtherComponent')) - + function MyComponent() { return (
@@ -5499,7 +5513,7 @@ ```javascript const OtherComponent = React.lazy(() => import('./OtherComponent')); - + function MyComponent() { return (
@@ -5522,10 +5536,10 @@ ```javascript import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; import React, { Suspense, lazy } from 'react'; - + const Home = lazy(() => import('./routes/Home')); const About = lazy(() => import('./routes/About')); - + const App = () => ( Loading...
}> @@ -5662,7 +5676,7 @@ } } ``` - + 可以通过把 value 的值提升到父状态中来解决这个问题: ```javascript @@ -5673,7 +5687,7 @@ value: {something: 'something'}, }; } - + render() { return ( @@ -5699,15 +5713,15 @@ console.log('old props:', prevProps); console.log('new props:', this.props); } - + render() { const {forwardedRef, ...rest} = this.props; - + // Assign the custom prop "forwardedRef" as a ref return ; } } - + return React.forwardRef((props, ref) => { return ; }); @@ -5721,7 +5735,7 @@ focus() { // ... } - + // ... } export default logProps(FancyButton); @@ -5731,7 +5745,7 @@ ```javascript import FancyButton from './FancyButton'; - + const ref = React.createRef(); ref.current.focus(); Hello {this.props.message}
; } } - + ReactDOM.render( , document.getElementById('root') @@ -5807,7 +5821,7 @@ return React.createElement('div', null, `Hello ${this.props.message}`); } } - + ReactDOM.render( React.createElement(Greeting, {message: 'World'}, null), document.getElementById('root') @@ -5870,7 +5884,7 @@
  • Duke
  • Villanova
  • - +
    • Connecticut
    • Duke
    • @@ -5899,7 +5913,7 @@

      The mouse position is {mouse.x}, {mouse.y}

      )}/> ``` - + 实际上,以上的 children 属性不一定需要在 JSX 元素的 `attributes` 列表中命名。反之,你可以将它直接放在元素内部: ```javascript @@ -6055,6 +6069,55 @@ React 对如何定义样式没有任何意见,但如果你是初学者,那么好的起点是像往常一样在单独的 *.css 文件中定义样式,并使用类名引用它们。此功能不是 React 的一部分,而是来自第三方库。但是如果你想尝试不同的方法(JS中的CSS),那么 styled-components 库是一个不错的选择。 + **[⬆ 返回顶部](#目录)** + +302. ### 我需要用 hooks 重写所有类组件吗? + + 不需要。但你可以在某些组件(或新组件)中尝试使用 hooks,而无需重写任何已存在的代码。因为在 ReactJS 中目前没有移除 classes 的计划。 + + **[⬆ 返回顶部](#目录)** + +303. ### 如何使用 React Hooks 获取数据? + + 名为 useEffect 的 effect hook 可用于使用 axios 从 API 中获取数据,并使用 useState 钩子提供的更新函数设置组件本地状态中的数据。让我们举一个例子,它从 API 中获取 react 文章列表。 + + ```jsx + import React, { useState, useEffect } from 'react'; + import axios from 'axios'; + + function App() { + const [data, setData] = useState({ hits: [] }); + + useEffect(async () => { + const result = await axios( + 'http://hn.algolia.com/api/v1/search?query=react', + ); + + setData(result.data); + }, []); + + return ( + + ); + } + + export default App; + ``` + + 记住,我们为 effect hook 提供了一个空数组作为第二个参数,以避免在组件更新时再次激活它,它只会在组件挂载时被执行。比如,示例中仅在组件挂载时获取数据。 + + **[⬆ 返回顶部](#目录)** + +304. ### Hooks 是否涵盖了类的所有用例? + + Hooks 并没有涵盖类的所有用例,但是有计划很快添加它们。目前,还没有与不常见的 getSnapshotBeforeUpdate 和componentDidCatch 生命周期等效的钩子。 + **[⬆ 返回顶部](#目录)** diff --git a/images/fer_road_qrcode.jpg b/images/fer_road_qrcode.jpg new file mode 100644 index 0000000..ccfb3dc Binary files /dev/null and b/images/fer_road_qrcode.jpg differ diff --git a/images/frontend-advanced-intro-v1.1.jpg b/images/frontend-advanced-intro-v1.1.jpg new file mode 100644 index 0000000..cb01e93 Binary files /dev/null and b/images/frontend-advanced-intro-v1.1.jpg differ