Learn React 17 system and combine TS to create a tourism e-commerce platform

React 17 system intensive lecture combined with TS to create a tourism e-commerce platform

勤学如春起之苗,不见其增,日有所长;
辍学如磨刀之石,不见其损,日有所亏。

Each lesson is submitted to Code Cloud separately. If you want to see the code content of a specific section, you can find the corresponding submission record on Code Cloud.查看代码改动

Code cloud warehouse address :https://gitee.com/WebJianHong/react-travel

2-2 [Environment Construction] Start our first React project

Scaffolding create-react-app
node version:14.15.0 LTS

npx create-react-app my-app

2-3 [Project Start] Use create-react-app to quickly build React

2-5 [TypeScript configuration] tsconfig.json detailed explanation

TS pain points

  • The learning cost is very high and the learning curve is very steep

Use create-react-appthe react project that builds TS

npx create-react-app my-app-ts --template typescript

TypeScript

  • TypeScript is a superset of JS
  • Add static type checking to native JS
  • Like ES6, it cannot be directly read by mainstream browsers

Compilation of TypeScript

  • Compiler babel-loader(create-react-app project comes with it)
  • configuration file:tsconfig.json

tsconfig.json

{
    
    
  "compilerOptions": {
    
    
    "noImplicitAny": false, // 不需要显示地声明变量的类型any
    "target": "es5", // 编译后目标JS版本
    "lib": [
      "dom", // document.getElementById("root")
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true, // 允许混合编译JS文件
    "skipLibCheck": true,
    "esModuleInterop": true, // 允许我们使用commonjs的方式import默认文件 import React from 'react'
    // "esModuleInterop": false, import * as React from 'react'
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext", // 配置的是我们代码的模块系统 常见的有Node.js的CommonJS ES6标准的esnext requestJs的AMD
    "moduleResolution": "node", // 决定了我们编译器的工作方式
    "resolveJsonModule": true,
    "isolatedModules": true, // 编译器会将每个文件作为单独的模块来使用
    "noEmit": true, // 表示当编译发生错误的时候 编译器不要生成ES5或者ES6 JS代码
    "jsx": "react-jsx" // 允许编译器编译react代码
  },
  "include": [
    "src"
  ]
}

2-6 [Extended reading] tsconfig.json compiler configuration document

[Extended reading] tsconfig.jsonCompiler configuration documentation

Generally speaking, TS编译器配置the entirety of the project is stored in 项目根目录下的tsconfig.json文件.

When the compiler starts, it first reads tsconfig.jsonfor instructions on how to compile the project (for example, which source files to compile, where to store the output, etc.).

However, if the compiler configuration file is saved in the location, we can also tell the specific location of the compiler configuration file 其他特殊位by using : Now, let's study the more common compiler options that appear in the above figure:-p选项
rn8VAJ.png
/tsconfig

  1. “target”: “es5”

This attribute defines you 编译后的目标javascript版本,

Generally speaking, we need to compile it to es5 so that it can be interpreted by mainstream browsers.

Of course, you said that my react code is not for the browser to read. For example, if you are using a react-nativemobile phone app, you can choose the options here es6.

In addition to es5 and es6, we have many other common options, ES5, ES6/ES2015, ES2016, ES2017, ES2018, ES2019, ES2020, ESNext, etc.

2. “lib”: [“dom”, “dom.iterable”, “esnext”]

This property lists 编译期间需要被包括进来的库文件, through these library files, tells the TypeScript compiler which functions are available.

For example, we have a dom library file here, this file will tell the compiler the interface of the dom api, so when we use dom in the ts code, for example, when executing this sentence, the compiler will know “document.getElementById("root")”the How to check.

If we don't set this option, then the compiler also has its own default list of library files, generally speaking, ["dom", "es6","DOM.Iterable"]etc.

  1. “allowJs”: true

allow混合编译JavaScript文件

  1. “esModuleInterop”: true

This option allows us 使用ES6的方式import默认文pieces. For example, when this option is not enabled, we need to write like this to reference react:

import * as React from 'react'

But when we enable this option, the import method is no different from ordinary JavaScript, which can be written as:

import React from 'react'

It is more natural to handle project imports this way.

  1. “module”: “esnext”

What is configured here is the module system of our code, the more common ones are Node.js的CommonJS系统, ES6标准的esnext系统, and requirejs的AMD系统. We are using the ES6 standard esnext system here, but it is also possible to replace it with CommonJS.

  1. “moduleResolution”: “node”

This option 决定了我们编译器的工作方式also determines us 各个文件之间调用, import的工作流程. There used to be two options, "node" and "classic", but the "classic" option was deprecated in December 2019.

  1. “isolatedModules”: “node”

After enabling this option, 编译器it will be used 每个文件作为单独的模块in the future .

  1. "noEmit": true,
    enabling this option means that when an error occurs, the compiler will not generate JavaScript code.

  2. “jsx”: “react”

Obviously, this option允许编译器支持编译react代码

  1. “include”: [“src/**/*”]

Use this option to list our 需要编译的文files, the "File Path" option requires a relative or absolute path to the file, for example:

  • "**" - any subdirectory
  • "*" - any filename
    = "?" - as long as a character follows a "?", this character is considered an ignorable character ( e.g., "src/*.tsx?"则同时指代"src/*.tsx"与"src/*.ts")
  1. “files”: [“./file1.ts”, “./file2.d.ts”, …]

Use this option to list 编译器应始终包含在编译中的文件. All files included with this option will be compiled regardless of whether the "exclude" option is used.

  1. “exclude”: [“node_modules”, “**//.test.ts”]

This option will be listed 从编译中排除的文件. It takes the same pattern as the "include" option, we can use this option to filter the files specified with the "include" option. However, the "exclude" option does not affect the "files" option.

Typically, we exclude node_modules, 测试文件, and 编译输出目录.

If this option is omitted, the compiler will use outDirthe folder specified by the " " option.

If both the "files" and "include" options are not specified, the compiler will compile all TS files in the root directory and any subdirectories, excluding files specified with the "exclude" option.

2-7 【TypeScript configuration】Dig deep into the TS compilation process

The video talks about how to change js project to ts project

3-1 Learning with questions

3-2 Chapter Overview

Typescipt + React

3-3 [Conceptual understanding] The past and present of React

React Features

one-way data flow

  • Data and interface binding
  • one-way rendering
  • Just like a function with the same input and the same output

Virtual DOM

Componentization

3-4 [Componentization] First understanding of React functional components

Create a new project

npx create-react-app robot-gallert --template typescript

In the tsconfig.json file

{
    
    
  "compilerOptions": {
    
    
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": [
    "src"
  ]
}

There are the following two attributes in the tsx file to import the JSON file in order to be normal, otherwise an error will be reported

"moduleResolution": "node",
"resolveJsonModule": true,

3-5 [Conceptual understanding] JSX programming thinking

React believes that the essence of view is the internal unity of rendering logic and UI view performance

React couples HTML and rendering logic to form JSX

Use TSX file suffix

  • File extension.tsx
  • tsconfig.jsonEnable jsx option in configuration
{
    
    
    "compilerOptions": {
    
    
        "jsx": "react-jsx"
    }
}

3-6 [Componentization] Configuring CSS modules for React

File location
CSS files and component files are placed in the same directory

Naming conventions
.module.css

React project introduces css in two ways

  1. Directly import the entire css file (prone to global style pollution)
import './index.css'
<div className="app" />
  1. JSS modular import components
import style from './index.css'
<div className={
    
    style.app} />

Additional configuration is required

TS definition statement

  • *.d.ts (.d.ts files will be automatically recognized by ts)
  • Contains only type declarations without logic
  • Will not be compiled nor packaged by webpack

Create a new custom.d.ts file under the src path

src/custom.d.ts

declare module "*.css" {
    
    
  const css: {
    
    
    [key: string]: string
  };
  export default css;
}

Only with the above files can it be used normally

import style from './index.css'

This way is js dynamic injection style

CSS in JS(JSS)

plug-intypescript-plugin-css-modules

Parse the css file to generate the reference type corresponding to ts

tsconfig.json configuration

{
    
    
    "compilerOptions": {
    
    
        "plugins": [
              {
    
    
                "name": "typescript-plugin-css-modules"
              }
    ]
    }
}

vscode configuration (introduce ts sdk)

.vscode/settings.json

{
    
    
  "typescript.tsdk": "node_modules/typescript/lib",
  "typescript.enablePromptUseWorkspaceTsdk": true
}

After the configuration is complete, a smart prompt such as styles.app will appear after accessing styles

css file name suffix specification.module.css

// App.tsx
import React from 'react';
// import './App.css';
import robots from './mockdata/robots.json'
import Robot from './components/Robots';
import styles from './App.module.css'
console.log('styles:', styles);

function App() {
    
    
  return (
    <div className={
    
    styles.app}>
      <div className={
    
    styles.robotList}>
      {
    
    
        robots.map(r => (
          <Robot id={
    
    r.id} name={
    
    r.name} key={
    
    r.id}  email={
    
    r.email}></Robot>
        ))
      }
    </div>
    </div>
    
  );
}
export default App;

styles is an object similar to the following

styles= {
    
    
    App: "App_App__2UtqW",
    App-header: "App_App-header__127PR",
    App-link: "App_App-link__1y4cp",
    App-logo: "App_App-logo__2zsEF",
    App-logo-spin: "App_App-logo-spin__2uRb4",
    app: "App_app__12zEe",
    robotList: "App_robotList__3DmDk"
}

3-7 【Extended reading】CSS in JS (JSS)

What is JSS

To put it simply, in one sentence CSS in JS (JSS), it is "inline style" (inline style) and "inline script" (inline script).

Because, since the emergence of React, based on the requirements of componentization, it is mandatory to bundle HTML, CSS, and JavaScript together, and encapsulate the structure, style, and logic in the same file.

Although this violates the principle of "separation of concerns" in the early days of HTML invention, it is more conducive to the isolation between components. And each component contains all the code that needs to be used, does not need to depend on the external environment, and there is no coupling between components. Therefore, with the popularity of React and the popularity of the component model, the principle of "separation of concerns" has gradually faded out of people's vision, and the principle of "mixing of concerns" brought by React has gradually become the mainstream. [1]

React's encapsulation of CSS is very simple, that is, it follows the style attribute object of DOM. CSS-in-JS is a technique (technique), not a specific library implementation (library). 简单来说CSS-in-JS就是将应用的CSS样式写在JavaScript文件里面, instead of being independent files such as .css, .scss or less, so that you can use some language features belonging to JS such as module declaration, variable definition, function call and conditional judgment in CSS to provide flexible and reliable Extended style definitions.

It is worth mentioning that although CSS-in-JS is not a very new technology, its popularity in China does not seem to be very high. It first appeared because of some component-based Web frameworks (such as React, Vue and Angular) make developers also want to encapsulate the CSS style of the component into the component to solve a series of problems in native CSS writing. In addition, CSS-in-JS is the most popular in the React community. This is because React itself does not care about how users define styles for components, while Vue and Angular have their own set of definition styles that belong to the framework. plan. [2]

The good and bad of css in JS

Benefits of JSS

    1. Local Styles - Scoping Styles
    1. Avoid useless CSS style accumulation
  • 3 Critical CSS
    1. State-Based Style Definition
    1. A better packaged component library

Disadvantages of JSS

    1. steep learning curve
    1. runtime consumption
    1. poor code readability
    1. There is no unified industry standard

3-8 [Resource configuration] Load media and font files

Scaffolding automatically helps to declare different types of files

node_modules\react-scripts\lib\react-app.d.ts

/// <reference types="node" />
/// <reference types="react" />
/// <reference types="react-dom" />

declare namespace NodeJS {
    
    
  interface ProcessEnv {
    
    
    readonly NODE_ENV: 'development' | 'production' | 'test';
    readonly PUBLIC_URL: string;
  }
}

declare module '*.avif' {
    
    
  const src: string;
  export default src;
}

declare module '*.bmp' {
    
    
  const src: string;
  export default src;
}

declare module '*.gif' {
    
    
  const src: string;
  export default src;
}

declare module '*.jpg' {
    
    
  const src: string;
  export default src;
}

declare module '*.jpeg' {
    
    
  const src: string;
  export default src;
}

declare module '*.png' {
    
    
  const src: string;
  export default src;
}

declare module '*.webp' {
    
    
    const src: string;
    export default src;
}

declare module '*.svg' {
    
    
  import * as React from 'react';

  export const ReactComponent: React.FunctionComponent<React.SVGProps<
    SVGSVGElement
  > & {
    
     title?: string }>;

  const src: string;
  export default src;
}

declare module '*.module.css' {
    
    
  const classes: {
    
     readonly [key: string]: string };
  export default classes;
}

declare module '*.module.scss' {
    
    
  const classes: {
    
     readonly [key: string]: string };
  export default classes;
}

declare module '*.module.sass' {
    
    
  const classes: {
    
     readonly [key: string]: string };
  export default classes;
}

3-10 [Extended reading] React's inline style and CSS

How to add CSS class to component?

render() {
    
    
  return <span className="menu navigation-menu">Menu</span>
}

It's common for a CSS class to depend on a component's props or state:

render() {
    
    
  let className = 'menu';
  if (this.props.isActive) {
    
    
    className += ' menu-active';
  }
  return <span className={
    
    className}>Menu</span>
}

React 会自动添加 ”px” 后缀到内联样式为数字的属性后。To use units other than "px", set the value to a string of numbers and the desired units. For example:

// Result style: '10px'
<div style={
    
    {
    
     height: 10 }}>
  Hello World!
</div>

// Result style: '10%'
<div style={
    
    {
    
     height: '10%' }}>
  Hello World!
</div>

But not all style properties are converted to pixel strings. Some style properties are unitless (eg zoom, order, flex).

From a performance perspective, CSS classes are usually better than inline styles.

3-11 [Conceptual understanding] State vs Props

State updates are asynchronous

  • The state will not change immediately after calling setState, which is an asynchronous operation
  • Do not rely on the current state to calculate the next state

props are read-only

3-12 [Event-driven] React Event event processing

event object

  • target describes the element on which the event occurs
  • currentTarget describes the element to which the event handler is bound

3-13 [Asynchronous processing] Get network API data

The interface data can be obtained at the following address
https://jsonplaceholder.typicode.com/users

interface Props  {
    
    }
interface State  {
    
    
  robotGallery: any[]
}

class App extends React.Component<Props, State> {
    
    
  constructor(props) {
    
    
    super(props)
    this.state = {
    
    
      robotGallery: []
    }
  }

  componentDidMount() {
    
    
    fetch("https://jsonplaceholder.typicode.com/users")
    .then((res) => res.json()).then((data) => {
    
    
      console.log('data:', data);
      this.setState({
    
    
        robotGallery: data
      })
      
    })
  }

    render(): React.ReactNode {
    
    
      return (
        <div className={
    
    styles.app}>
          <div>
            <img src={
    
    logo} className={
    
    styles.appLogo} alt=""  />
            <h1>这里是标题这里是标题这里是标题</h1>
          </div>
          
          <div className={
    
    styles.robotList}>
          {
    
    
            this.state.robotGallery.map(r => (
              <Robot id={
    
    r.id} name={
    
    r.name} key={
    
    r.id}  email={
    
    r.email}></Robot>
            ))
          }
        </div>
        </div>
        
      );
    }

}

export default App;

3-14 [Asynchronous processing] Asynchronous development of setState

Asynchronous updates performed synchronously

setState() itself is not asynchronous, but the state processing mechanism gives people an asynchronous illusion. State processing generally occurs when the statement cycle changes.

this.setState({
    
    count: this.state.count + 1})
// 此时this.state.count的值还是更新之前的值
console.log('this.state.count:', this.state.count);
<button onClick={
    
    ()=> {
    
    
    this.setState((prevState, prevProps)=> {
    
    
      console.log('prevProps:', prevProps)
      console.log('prevState:', prevState)
      return {
    
    count: prevState.count + 1}
    })
    }}按钮
</button>
<span>{
    
    this.state.count}</span>
 <button onClick={
    
    ()=> {
    
    
    this.setState({
    
    count: this.state.count + 1}, ()=> {
    
    
      // 更新state后的回调函数
      console.log('this.state.count:', this.state.count);
    })
    
  }}>按钮</button>
  <span>{
    
    this.state.count}</span>

3-15 [Death and Rebirth] Exploring the life cycle of React components

  • Mounting: Create virtual DOM to render UI
  • Updating: Update the virtual DOM and re-render the UI
  • Unmounting: remove virtual DOM remove UI

Initialization => Build function => getDerivedStateFromProps => render(): render UI => componentDidMount

getDerivedStateFromProps=>shouldComponentUpdate=>render: rendering UI=>update=>componentDidUpdate

componentWillUnmount => destroy

3-16 [Conceptual understanding] React 17 version changes

  • The event delegation mechanism is changed (the old version adds a new one on the document and operates on the virtual DOM)
  • Closer to the native browser (onScroll onFocus ...)
  • delete event pool
  • useEffect cleaning operation changed to asynchronous operation
  • JSX cannot return undefined
  • Delete some private APIs (mainly used in React Native)

4-1 Learning with questions

4-2 [Conceptual understanding] What are hooks?

A special class of functions to inject special functionality into your functional components

Some class components are lengthy and complex and difficult to reuse

The purpose of Hooks is to add state to functional components

4-3 [State Hook] Use useState to manage component state

4-4 [Concept understanding] side effects

pure function

Passing the same parameters to a function will always return the same data

Enter the same parameters (props) to the react component, and the rendered UI should always be the same

Side effects are the opposite of pure functions, which means that a function handles things that have nothing to do with the return value

4-5 [Side Effect Hook] Use useEffect to get data asynchronously

4-6 [Side Effect Hook] useEffect User Guide

Using async await in useEffect

  useEffect(()=> {
    const fetchData = async () => {
      const res = await fetch("https://jsonplaceholder.typicode.com/users")
      console.log({res});
      const data = await res.json()
      console.log({data});
    }
    fetchData()
  })

4-7 [Global data transfer] Context and useContext

4-8 [Global Data Transfer] Component Context Provider

4-9 [High-level component HOC] withAddToCart()

HOC

const hoc = higherOrder(wrappedComponent)
  • A high-level component (HOC) is a function that returns a component
  • Add more functions to subcomponents through component nesting
  • Takes a component as a parameter and returns a new, modified component

The naming convention
begins withAddToCard with

4-10 [Custom Hook] useAddToCart()

  • Hooks are functions
  • name starts with "use"
  • Other Hook functions can be called internally
  • Not a React feature

5-2 [Project Planning] Website Development and Design Guidelines

5-3 [Project Start] System Design and Project Initialization

Create a project using scaffolding

npx create-react-app react-travel --template typescript

Reuse the tsconfig.json vscode configuration and custom.d.ts of the test project

tsconfig.json

{
  "compilerOptions": {
    "noImplicitAny": false,
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "plugins": [
      {
        "name": "typescript-plugin-css-modules"
      }
    ]
  },
  "include": [
    "src"
, "custom.d.ts"  ]
}

custom.d.ts

declare module "*.css" {
  const css: {
    [key: string]: string
  };
  export default css;
}

setting.json

{
  "typescript.tsdk": "node_modules/typescript/lib",
  "typescript.enablePromptUseWorkspaceTsdk": true
}

Install a css dependency only used in the development environment

 npm install typescript-plugin-css-modules --save-dev

Change src/App.css to src/App.module.css according to the specification

modular reference css

src\App.tsx

import style from './App.modulce.css';

function App() {
    
    
  return (
    <div className={
    
    style.App}>
      <header className={
    
    style["App-header"]}>
        <img src={
    
    logo} className={
    
    style["App-logo"]} alt="logo" />
        <p>
          Edit <code>src/App.tsx</code> and save to reload.
        </p>
        <a
          className={
    
    style["App-link"]}
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

5-4 [Home Page Development] Header, Footer

Introduce ant Design UI framework

yarn add antd @ant-design/icons
//
cnpm install antd @ant-design/icons --save

5-5 [Project Refactoring] Componentization Thought and Practice

5-6 [Home page development] Multiple menus of carousel and sidebar

5-7 [Home Page Development] Recommended Popular Products

5-9 [Discussion Questions] Software Development Life Cycle

The correct way to start a project is always to start with the business, summarize the business requirements, establish a business model, determine the business process of the website through the business model, and finally start with the business process to determine the relationship and quantity of pages, and the last step of business analysis is UI Layout. Through the study of this chapter, let's discuss several issues:

  • What's the first thing you need to do with a new website
  • Could you please analyze the business needs of the course project?
  • Please students supplement the BRD and SRS of the course project.

6-1 Learning with questions

6-2 [Concept Understanding] Routing and SPA

SPA (Single Page Website Application)

  • JS CSS HTML is packaged into a super large file and thrown to the browser at one time
  • JS hijacks browser routing to generate virtual routing to dynamically render page DOM elements
  • In line with the trend of front-end and back-end separation, the server is not responsible for UI output but focuses on data support
  • At the same time, it supports desktop App, mobile App and URL App

The routing system used by React websites is all virtual

  • There is no one-to-one relationship with the back-end server and the actual file
  • Home page http://localhost:3000
  • Search page http://localhost:3000/search
  • Tourist route details page localhost:3000/touristRoutes/{id}

React routing framework

  • Comprehensive routing framework react-router
  • Browser routing framework react-keeper
  • Mobile app framework (react-native): react-navigation

This project uses react-router,
the most mainstream and complete React routing solution

6-3 [Routing initialization] Configure react-router

Install
version 5

cnpm install react-router-dom --save

ts support

The ts type definition will only be used during the development process and will not be used for compilation and online

cnpm install @types/react-router-dom --save-dev

react-router

  • react-router-dom is used by the browser to process the routing of Web App
  • react-router-native is used for React Native to handle the routing of mobile apps
  • react-router-redux provides the integration of routing middleware processing redux
  • react-router-config is used to statically configure routing

After installing react-router-dom

  • The react-router core framework will be installed automatically
  • Components can render labels
    • Components use H5 Api to implement routing switching
    • Components use window.location.hash in native JS to implement routing switching

The course uses "react-router-dom": "^5.2.0", and the latest version "react-router-dom": "^6.0.2", the
syntax is a bit different

Here it is better to install according to the version of the course so that learning will be smoother

“react-router-dom”: “^5.2.0”,

import React from 'react'
import styles from './App.module.css'
import {BrowserRouter, Route} from 'react-router-dom'
import {HomePage} from './pages'

function App() {
  return (
    <div className={styles.App}>
      <BrowserRouter>
        <Route path="/" component={HomePage}/>
      </BrowserRouter>
    </div>
  )
}

export default App

“react-router-dom”: “^6.0.2”

Refer to the tutorial
https://github.com/remix-run/react-router/blob/main/docs/getting-started/tutorial.md

https://github.com/remix-run/react-router/blob/main/docs/getting-started/installation.md

import React from 'react'
import styles from './App.module.css'
import {BrowserRouter,Routes, Route} from 'react-router-dom'
import {HomePage} from './pages'



function App() {
  return (
    <div className={styles.App}>
      <BrowserRouter>
        <Routes>
        <Route path="/" element={<HomePage />}/>
        </Routes>
      </BrowserRouter>
    </div>
  )
}

export default App

6-4 [Routing Architecture] Basic Routing System

react-router official website

Website Routing System Requirements

  • Routing navigation is consistent with native browser operation behavior
  • The path analysis principle of routing is consistent with that of native browsers, which can automatically identify url paths
  • Path switching is done in units of pages without page stacking

6-5 [routing construction] page navigation

routing parameters

6-6 [routing construction] withRouter and useRouter

Parameter passing between different routes

Passing data across components

  • Use the context object context to achieve
  • HOC Higher Order Components
  • Functional components, using hooks

Chapter 7 [Introduction to Redux] Practical project architecture design

7-2 [Concept understanding] What is redux?

React is really just a Ui framework

  • Generate dynamic dom rendering UI through JSX
  • No Architecture No Templates No Design Patterns No Routing No Data Management

Design Patterns

  • MVC MVVM MV*
  • For React projects there is Redux
  • Angular Observable(RxJS)
  • Vue: Vuex

Redux

  • Strip component data (state)
  • The data is uniformly stored in the store
  • Components subscribe to store to get data
  • The store pushes data updates synchronously

Redux saves the data in a unified manner and is responsible for processing data binding while isolating the data and UI

When to use Redux

  • When components need to share data
  • When a state needs to be accessible from anywhere at any time
  • When a component needs to change the state of another component
  • Language switching Dark mode switching User login Global data sharing

Redux workflow

  • React components (UI)
  • Actions
  • Store
  • Reducer
    insert image description here

7-3 【Create state】createStore

cnpm install redux --save

7-4 [Access state] Get store data

src/redux/store.ts

import {createStore} from 'redux'
import languageReducer from './languageReducer'

const store = createStore(languageReducer)

export default store;

src/redux/languageReducer.ts

export interface LanguageState {
  language: "en" | "zh",
  languageList: {
    name: string,
    code: string
  }[];
}

const defaultState:LanguageState = {
  language: 'zh',
  languageList: [
    {
      name: '中文',code: 'zh'
    },
    {
      name: 'English', code: 'en'
    }
  ]
}

export default (state=defaultState, action) => {
  return state
}

class component

src\components\header\Header.class.tsx

import store from '../../redux/store'
import {
    
    LanguageState}  from '../../redux/languageReducer'

interface State extends LanguageState {
    
    }

class HeaderComponent extends React.Component<RouteComponentProps, LanguageState> {
    
    

  constructor(props) {
    
    
    super(props)
    const storeState = store.getState()
    this.state = {
    
    
      language: storeState.language,
      languageList: storeState.languageList
    }
  }

7-5 [Update state] Action and Reducer processing

7-6 [Subscription state] store connection and subscription

class component

import store from '../../redux/store'
import { LanguageState } from '../../redux/languageReducer'

interface State extends LanguageState { }

class HeaderComponent extends React.Component<RouteComponentProps, LanguageState> {
  constructor(props) {
    super(props)
    const storeState = store.getState()
    this.state = {
      language: storeState.language,
      languageList: storeState.languageList
    }
    store.subscribe(this.handleSubscribe)
  }

  handleSubscribe = ()=>  {
    const storeState = store.getState()
    this.setState(
      { 
        language: storeState.language, 
        languageList: storeState.languageList 
      }
    )
  }

  menuClick = (e)=> {
    if (e.key === "add_new") {
      const action = {
        type: 'add_language',
        payload: {
          name: '新语言', code: 'new'
        },
      }
      store.dispatch(action)
    } else {
      const action = {
        type: 'change_language',
        payload: e.key
      }
      store.dispatch(action)
    }

  }

src\redux\languageReducer.ts

export interface LanguageState {
  language: "en" | "zh",
  languageList: {
    name: string,
    code: string
  }[];
}

const defaultState:LanguageState = {
  language: 'zh',
  languageList: [
    {
      name: '中文',code: 'zh'
    },
    {
      name: 'English', code: 'en'
    }
  ]
}

export default (state=defaultState, action) => {
  switch (action.type) {
    case 'add_language':
      return {
        ...state, languageList: [...state.languageList, action.payload]
      }
    case 'change_language':
      return {...state, language: action.payload}
    default: 
      return state
  }
}

7-7 [i18n] complete website language switching

[i18n]
Internationalization There are 18 characters between the first and last characters in

Display the corresponding interface according to different languages ​​and regions

principle

  • The language pack is saved separately as a static resource xml json
  • One file per language
  • When switching language settings, the language file will also switch

I18n tools

// installer dependencies

npm install react-i18next i18next --save

Both plugins support native TS

7-8 [redux refactoring] action splitting and unification

Action Creator (factory mode)

https://www.runoob.com/design-pattern/factory-pattern.html

7-9 [redux package] use react-redux in class components

React-redux
React Redux 7.1+ requires React 16.8.3 or later, in order to make use of React Hooks.

cnpm install react-redux --save

cnpm install @types/react-redux --save-dev

connect

7-10 [redux package] use react-redux in functional building

7-11 [Discussion Question] In-depth analysis of redux

By using the redux architecture, we can strip the state in the component, that is, strip the component data, and manage all the data in a unified manner. Through the study of this chapter, let's discuss several issues:

  • How to implement subscription and push of redux state?
  • Please describe the workflow of redux?
  • Please give an example to describe what the factory pattern is?
  • What is the relationship between redux and react-redux?

Chapter 8 [Attack on Redux] Asynchronous AJAX and redux middleware

8-1 Learning with questions

8-2 [Concept understanding] RESTful

REST
REpresentational State Transfer
representative state transfer

Basic Features

  • no status

  • Oriented "resource"
    paths will generally not have verbs
    Please add a picture description

  • Verbs using HTTP
    Please add a picture description

  • HATOAS Hypertext
    As The En-gine Of Application State

Easy to use: facing objects (resources) such as adding, deleting, modifying and checking

Not easy to use: facing process such as login

8-3 [API Description] Where is the backend of the course?

Request header
x-icode: 12623592D652DDFC valid for 30 days

8-4 [API connection] AJAX asynchronously obtains recommendation data

Use Axios

  • Easy to use API is close to Jquery and simpler than native fetch
  • Compatible browsers can be compatible with IE7. If you use fetch, you have to handle the compatibility yourself.
  • Good versatility, can use the same API in node and browser
cnpm install axios --save

8-5 [Conceptual understanding] Redux vs MVC

current design pattern

  • mash up
  • The Header navigation bar uses the redux architecture
  • The content of the homepage is more inclined to the MVC architecture

What is MVC

  • An architectural pattern is also an idea
  • Model Model View View and Controller Controller
  • Separate business operation UI display logic control

ViewView

  • user interface
  • Only display data without processing data
  • JSX code for React project

Model Model

  • The core of the MVC architecture
  • Represents a business model or data model
  • Business logic, such as algorithm implementation, data management, output object encapsulation, etc.

Controller Controller

  • Accept user input and call models and views to complete user request processing
  • do not process data
  • React -> MVVM 或 MV*(whatever)

React projects do not advocate the use of MVC architecture
Please add a picture description

Data two-way binding can lead to an infinite loop

Please add a picture description

Redux Architecture

insert image description here

insert image description here

8-6 【reducer管理】combineReducers

This course realizes the transformation from MVC to Redux

8-7 [Middleware] Use redux-thunk middle price to realize asynchronous action

redux-thunk npm

insert image description here

8-8 [Middleware] What is middleware

functional programming

conform to function code

const hello = function(x) {
    
    
    return `Hello, ${
      
      x}`
}

const happy = function(x) {
    
    
    return `${
      
      x}:)`
}

const compose = function (f, g) {
    
    
    return function(x) {
    
    
        return f(g(x))
    }
}

const happyHello = compose(hello, happy)
happyHello('阿莱克斯') // 'Hello, 阿莱克斯:)'

Currying

const curriedAdd = (a) => {
    
    
    return (b) => {
    
    return a + b}
}

const addTen = curriedAdd(10)

addTen(12) // 22

Why do you need middleware

  • The core of redux is to control and manage all data input and output
  • So there is a store reducer and action dispatch

Assuming the following requirements

  • We are required to record the action information of each dispatch
  • Each dispatch has to record the data state of the current store
  • Not only to print the current state but also to print the updated state

Solution: Encapsulate dispatch

let next = store.dispatch

store.dispatch = function dispatchAndLog(action) {
    
    
    console.log('dispatching', action)
    console.log('当前state', store.getState())
    next(action)
    console.log('更新后的state', store.getState())
}

Future extension packages

const applyMiddleware = function (middleware) {
    
    
    let next = store.dispatch;
    // 这里传入store 因为中间件有可能会用到getState方法获取数据 比如打印store
    store.dispatch = middleware(store)(next)
}

applyMiddleware(dispatcAndLog)

Redux middleware mechanism

  • A total of one opportunity to classify and process actions

The following is the case without asynchronous operations
insert image description here

After adding middleware, you can do additional processing on the action

insert image description here

Asynchronous processing in Redux

  • redux-thunk
  • redux-promise
  • redux-saga

The implementation principles of the three plug-ins are basically the same

Redux middleware announcement

const middleware = (store) => (next) => (action) => {
    
    }

8-9 [Middleware] Custom middleware actionLog

8-10 [RESTful Advanced (Elective)] Richardson Maturity Model and HATOAS

Chinese translation of Martin's blog
insert image description here

insert image description here

insert image description here

insert image description here

9-2 Chapter Overview (redux-toolkit)

The author highly recommends redux-toolkit (RTK) to reduce template code

  • RTK has become a very mainstream redux architecture pattern
  • React official and redux official support

The default architecture of create-react-app is RTK

insert image description here

insert image description here

9-3 [Detail page construction 1] Page frame, details and date selection

9-4 [Detail page construction 2] Page frame, details and date selection

JSX handles html strings directly

<div dangerouslySetInnerHTML={
     
     {__html: product.features}}
            style={ 
        { 
        margin: 50}}
          >
          </div>

9-5 [Concept understanding] What is redux-toolkit

redux-toolkit

9-6 【redux-toolkit】createSlice、reducer、与immer

cnpm install @reduxjs/toolkit

9-7 [Concept understanding] The theoretical basis of createAsyncThunk

redux-toolkit的createAsyncThunk

9-8 [redux-toolkit] sotre configuration (configureStore) and asynchronous processing (createAsyncThunk)

9-9 【Comprehensive use】Search page

10-2 [Layout Refactoring] Page Layout

10-3 [Registration page] Antd + ts form processing

10-4 [Registration page] Registration business logic processing

10-5 [Concept understanding] The importance of Status Code

HTTP status code

  • The user can know whether the server has processed the request normally or if something went wrong
  • A three-digit status code and a status message in string format
  • Numeric codes are easier for programs to process and message strings are easier for programmers (humans) to understand

HTTP status codes are divided into 5 categories
insert image description here

Common HTTP Status Codes
insert image description here

10-6 [Login page] User login form and layout

10-7 【Concept understanding】JWT principle analysis

  • JSON Web Token
  • The role of JWT is user authorization (Authorization) rather than user authentication (Authentication)

Authorization VS Authentication

  • User authentication refers to the use of username and password to verify the identity of the current user (error status code: 401 Unauthorized)
  • User authorization means that the current user has sufficient permissions to access specific resources (error status code: 403 forbidden (forbidden)

Traditional Session login (stateful login)

  • After the user logs in, the server will save the logged in session information
  • Session ID will be passed to the front end through cookie
  • HTTP requests will be accompanied by cookies

JWT changes the process of user authorization and authentication

  • replace cookies
  • JWT information only needs to be saved on the client
  • stateless login

Stateful login (Session) VS stateless login (JWT)

stateful login
insert image description here

Stateless login (JWT)
insert image description here

If the JWT is valid, the server returns the resource

Session VS JWT

  • Session needs to be saved on the server and the Session ID is saved in the front-end Cookie
  • JWT information only needs to be saved on the client
  • Advantages of stateless login: distributed deployment

10-8 [JWT example] JWT and single sign-on example explanation

JWT official website
insert image description here

Asymmetric encryption algorithm RSA private key in the server

insert image description here

Advantages of JWT

  • Stateless, simple and convenient, perfectly supports distributed deployment
  • Asymmetric encryption Token is highly secure

Disadvantages of JWT

  • Once the stateless token is released, it cannot be canceled (no solution)
  • The security of passing Token in clear text is low (it can be solved by using https)

10-9 【Login page】SignIn login business processing

10-10 [Login page] SignOut logout business processing

Decode JWT (jwt-decode plugin)

import jwt_decode, {
    
     JwtPayload as DefaultJwtPayload } from 'jwt-decode'

interface JwtPayload extends DefaultJwtPayload {
    
    
  username: string
}

useEffect(() => {
    
    
if (jwt) {
    
    
  const token = jwt_decode<JwtPayload>(jwt)
  console.log('jwt decode:', token)
  setUserName(token.username)
}
}, [jwt])

10-11 [redux-persist] login persistence

redux-persist

cnpm install redux-persist --save

Cookie、session和web Storage
insert image description here

Stateless login: JWT

  • You can only choose to use cookies or web storage to save
  • The course uses web storage for data persistence

Benefits of Web Storage

  • Effectively reduce network traffic
  • Quickly display data
  • temporary storage

Types of Web Storage

  • SessionStorage: valid only until the current browser window is closed
  • localStorage: always valid window or browser is closed and saved
const persistConfig = {
    
    
  key: 'root',
  storage,
  whitelist: ['user'] // 白名单 只存白名单列表里的  黑名单 除了黑名单列表 其他都存
}

insert image description here

11-1 Learning with questions

11-2 [Advanced Routing] Build Private Routing

11-3 [UI Construction] Shopping Cart Page Initialization

11-4 [Redux Creation] Shopping Cart Slice

11-5 [redux connection] load shopping cart

11-6 [Complete the shopping module] Place an order in the shopping cart

11-7 [UI Construction] Online Payment Page Initialization

11-8 [Redux Creation] Order Slice

11-9 [redux connection] complete online payment

12-2 [Chapter Overview] Deployment Solution Introduction

deployment plan

  • Option 1 uses a local server to host the website (node)
  • Solution 2 Containerized Deployment

Course launch plan

  • Containerization solution
  • Use Alibaba Cloud to handle containerized services

Alibaba Cloud is online

  • ECS instance
  • Linux64
  • docker repository

12-3 [Static deployment] local server hosting

Deployment – ​​create-react-app-official website

Static Server

For environments using Node, the easiest way to handle this is to install serve and let it handle the rest:

npm install -g serve
serve -s build

PS: serve -s buildBefore execution, you need to package with npm run build. The build folder generated after packaging is the root directory of the local server

serve -s buildYour static site will be served on port 3000. Like many of serve's internal settings, the port can be adjusted with the -l or --listen flag:

serve -s build -l 4000

Run this command for a full list of available options:

serve -h

12-4 [Concept understanding] 5 minutes to take you to know Docker

  • Open source containerized virtual technology
  • Portable (Developers can package their application dependencies and wait until a portable image package can be freely released to any window or linux or Mac machine)
  • Very lightweight (the container itself uses a sandbox mechanism to separate the application from the host system and only share the kernel)
  • out of hardware
  • The docker container runs directly on the host kernel
    insert image description here

Three core concepts of Docker

  • Registry image warehouse: such as Docker Hub: https://hub.docker.com/node
  • Image image: docker command to pull image docker pull node
  • Container container: container start command docker run (docker run -it /bin/base)

Three relationship
insert image description here

Docker common commands
insert image description here

12-5 [Cheat Sheet] Commonly used docker commands

Course related Docker commands

docker installation and setup

#安装 CentOS已经将Docker软件包放在了Extras软件源中,直接利用即可
yum install docker-io -y
#查看docker的版本 version
docker -v
#开启Docker服务
systemctl start docker.service
#开机启动Docker服务
systemctl enable docker.service
#查看Docker服务启动状态
systemctl status docker.service
#重启Docker服务
systemctl restart docker.service

docker image files and container commands

#查看所有镜像
docker images
#删除一个imageid的镜像
docker rmi [IMAE_ID] 
#删除所有镜像
sudo docker rmi $(docker images -q) 
#查看所有容器运行状态
docker ps -a 
docker container ls -all
#删除一个containerid的容器(实例)
docker rm 6f0c67de4b72 
#删除所有容器
docker rm $(sudo docker ps -a -q)
容器日志
#查看指定时间后的日志,只显示最后100行:
docker logs -f -t --since="2019-06-08" --tail=100 CONTAINER_ID
#查看某时间之后的日志:
docker logs -t --since="2019-06-08" CONTAINER_ID
#查看某时间段日志:
docker logs -t --since="2019-06-08" --until "2019-06-09" CONTAINER_ID
#查看最近30分钟的日志:
docker logs --since 30m CONTAINER_ID
# 设置启动策略, docker 容器自动启动(在容器退出或断电开机后,docker可以通过在容器创建时的 --restart 来指定重启策略)
#--restart 参数:
# no,不自动重启容器. (默认值)
# on-failure, 容器发生error而退出(容器退出状态不为0)重启容器,可以指定重启的最大次数,如:on-failure:10
# unless-stopped, 在容器已经stop掉或Docker stoped/restarted的时候才重启容器,手动stop的不算
# always, 在容器已经stop掉或Docker stoped/restarted的时候才重启容器
docker run --restart always -it -p {本机端口}:{容器端口} {镜像名称}
#如果容器已经被创建,但处于停止状态,重新启动:
docker start {容器ID}
#如果容器已经被创建,我们想要修改容器的重启策略
docker update --restart always {容器ID}

12-6 [Environment Construction] Containerization Solution and Docker Configuration

12-7 【Cheat sheet】Dockerfile syntax

【Cheat sheet】Dockerfile syntax

Dockerfile syntax consists of two parts, comments and commands + parameters

simple example

#第一行必须指令基于的基础镜像
FROM ubutu
#维护者信息
MAINTAINER docker_user [email protected]
#镜像的操作指令
RUN apt-get update && apt-get install -y ngnix 
RUN echo "\ndaemon off;">>/etc/ngnix/nignix.conf
#容器启动时执行指令
CMD /usr/sbin/ngnix

Dockerfile commands

  1. FROM

The base image can be any image. If the base image is not found, Docker will try to find the image from the Docker image index. The FROM command must be the first command in the Dockerfile. If the same DockerFile creates multiple images, multiple FROM instructions can be used (once for each image)

# Usage: FROM [image name]
FROM ubuntu 
  1. MAINTAINER

Specifies the maintainer's information and should be placed after FROM.

# Usage: MAINTAINER [name]
MAINTAINER authors_name 
  1. RUN

The RUN command is the core part of the Dockerfile to execute commands. It accepts commands as arguments and is used to create images. Unlike the CMD command, the RUN command is used to create a mirror (form a new layer on top of a previously committed layer).
The format is Run or Run ["executable", "Param1", "param2"],
the former runs on the shell terminal, that is, /bin/sh -C, and the latter uses exec to run. For example: RUN ["/bin/bash", "-c", "echo hello"]
Each run command is executed on the current base image, and a new image is submitted. When the command is relatively long, you can use "/" to break the line.

# Usage: RUN [command]
RUN apt-get update 
  1. USER

The format is USER daemon.
Specify the user name or UID when running the container, and subsequent RUN will also use the specified user.
When the service does not require administrator privileges, this command can be used to specify the running user. And you can create the required users before, for example: RUN groupadd -r postgres && useradd -r -g postgres postgres . To gain administrator privileges temporarily, use gosu instead of sudo.

# Usage: USER [UID]
USER 751
  1. VOLUME

The VOLUME command is used to allow your container to access directories on the host machine.
The format is VOLUME["/data"].
Create a mount point that can be mounted from the local host or other containers, generally used to store databases and data that needs to be kept.

# Usage: VOLUME ["/dir_1", "/dir_2" ..]
VOLUME ["/my_files", "/app_files"]
  1. WORKDIR

The WORKDIR command is used to set the running directory of the command specified by CMD.
The format is WORKDIR /path/to/workdir .
Configure the working directory for subsequent RUN , CMD , ENTRYPOINT commands.
Multiple WORKDIR directives can be used, and subsequent commands, if the parameter is a relative path, will be based on the path specified by the previous command. For example:

WORKDIR /a 
WORKDIR b 
WORKDIR c 
RUN pwd 
# 最终路径为 /a/b/c 。
  1. CMD

Three formats are supported:
CMD ["executable", "Param1", "param2"] is executed with exec, CMD command param1 param2 is recommended, and CMD ["Param1", "param2"] is provided to ENTRYPOINT for execution
on /bin/sh
Default parameters.
Each container can only execute one CMD command. When there are multiple CMD commands, only the last one is executed.

# Usage 1: CMD application "argument", "argument", ..
CMD "echo" "Hello docker!"
  1. ENV

The format is ENV. Specify an environment variable that will be used by subsequent RUN instructions and persisted while the container is running.

ENV TZ "Asia/Shanghai"
ENV LANG en_US.UTF-8
ENV LC_ALL en_US.UTF-8
  1. ADD

The ADD command takes two parameters, source and destination. Its basic role is to copy files from the file system of the source system to the file system of the target container. If the source is a URL, the content of that URL will be downloaded and copied into the container. If the file is in a recognized compressed format, docker will help decompress it.

# Usage: ADD [source directory or URL] [destination directory]
ADD /my_app_folder /my_app_folder 
  1. COPY (almost indistinguishable from ADD)

COPY copies files from path < src to path inside the container.

COPY <src> <dest>
  1. EXPOSE

Specify the port specified when docker allows it to be forwarded

EXPOSE <port>[<port>...]
  1. ENTRYPOINT

Two formats:

  • ENTRYPOINT ["executable", "param1", "param2"]
  • ENTRYPOINT command param1 param2(shell中执行)

Configure the command to be executed after the container is started, and cannot be overridden by the parameters provided by docker run.
There can only be one ENTRYPOINT in each Dockerfile. When multiple ENTRYPOINTs are specified, only the last one will take effect.

  1. ONBUILD

The command specified by ONBUILD is not executed when the image is built, but is executed in its sub-image

source:

https://juejin.cn/post/6844903889301422088
https://github.com/qianlei90/Blog/issues/35
https://www.jianshu.com/p/690844302df5
https://www.jianshu.com/p/5f4b1ade9dfc

12-8 [Container transformation] Understand the whole process of Dockerfile

previous lesson

  • Created a doker virtual machine and installed nginx hosting server
  • It is also possible to build a Docker image of custom content
  • You can directly download the official Nginx Docker image

Two ways to build a Docker image

  • docker commit
  • docker build + Dockerfile (reproducible standardization)

Dockerfilefiles in the root directory

Dockerfile is the script used to build the docker image

# 第一个阶段: 拉取node镜像来打包React项目
## 拉取node镜像 版本14 命名为build
FROM node:14 as build
## 设置docker命令运行目录
WORKDIR /app
## 复制对应文件到app文件夹中 app/
COPY package*.json ./
## 安装项目依赖
RUN npm install
## 复制对应文件到app文件夹中 app/
COPY tsconfig.json ./
## 复制public文件夹并创建public文件夹
COPY public public/
## 复制src文件夹并创建src文件夹
COPY src src/
## 构建react项目
RUN npm run build

# 第二个阶段: 创建并运行Ngnix服务器,并且把打包好的文件复制粘贴到服务器文件夹中
## 拉取nginx服务器镜像 :执行版本
FROM nginx:alpine
## 将第一阶段通过npm run build构建好的react项目复制到nginx服务器镜像中
COPY --from=build /app/build/ /usr/share/nginx/html
## 暴露nginx服务器端口
EXPOSE 80
## 使用CMD命令来启动nginx命令
CMD ["nginx", "-g", "daemon off;"]

Build the image and execute the command in the root directory of the react project

docker build -t react-web .

Docer will execute step by step according to the Dockerfile

After the image is built, you can start deploying

Use the following command to check whether the mirror exists

docker images

insert image description here

Containerized react-web image

12231 is the local port is not limited 80 is the port exposed by the nginx server react-web mirror

docker run -d -p 12231:80 react-web

The following command checks the list of containers to verify successful deployment

docker ps

insert image description here

At this time, you can visit localhost:12231 normally.

12-9 【Containerized Online】Realize Alibaba Cloud Deployment

Each lesson is submitted to Code Cloud separately. If you want to see the code content of a specific section, you can find the corresponding submission record on Code Cloud.查看代码改动

Code cloud warehouse address :https://gitee.com/WebJianHong/react-travel

recommended reading

Vue source code learning directory

Vue source code learning complete directory

Connect the dots - the road to front-end growth

Connect the dots - the road to front-end growth


谢谢你阅读到了最后~
期待你关注、收藏、评论、点赞~
让我们一起 变得更强

Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description
Please add a picture description

Guess you like

Origin blog.csdn.net/weixin_42752574/article/details/122594056