无标题文章

# React进阶

## npm

`npm`(Node package manager)是随Node.js发布的包管理工具,最初主要是用来管理Node.js依赖包,如今已经扩展到整个JavaScript生态。

在没有 `npm` 之前,如果我们想要在自己的JS项目中引用第三方的框架或库,比如 `React`、`Bootstrap` 等等,需要把代码一个一个下载回来添加到自己的工程中,随着项目的扩大,依赖会变得越来越多,而第三方库可能也会有自己的依赖,一旦有版本升级,以来的管理很容易失控。

`npm` 的工作方式大致如下:

- 架设一个中心化的代码仓库服务器(registry),用来存放共享的代码,官方的npm网站为 [https://www.npmjs.com/](https://www.npmjs.com/),在国内我们通常会使用阿里的 `npm` 镜像,下载速度会更快,切换方式如下

  ```

  npm config set registry https://registry.npm.taobao.org

  ```

- 开源软件的作者将自己的代码封装成npm包(`package`),并且确定一个在registry中唯一的名字,如 `react`,然后将代码publish到registry

- 其他开发者想要使用 `react` 这个包,在自己的项目中运行 `npm i react` ,npm就会自动帮他们下载代码。

- 下载完成的代码会出现在项目根目录的 `node_modules` 目录

- 包也可以依赖npm上面其他的包,npm在安装的时候会自动解析、安装这些依赖

## package.json

[https://docs.npmjs.com/creating-a-package-json-file](https://docs.npmjs.com/creating-a-package-json-file)

`package.json` 是一个包或者工程的描述文件,里面记录了项目的名字、版本、依赖等信息,代表当前目录是一个独立的npm工程,一个基本的 `package.json` 内容大致如下:

```json

{

  "name": "react-template",

  "version": "1.0.0",

  "main": "index.js",

  "license": "MIT",

  "dependencies": {

    "react": "^17.0.2",

    "react-dom": "^17.0.2"

  },

  "devDependencies": {

    "@types/react": "^17.0.5",

    "@types/react-dom": "^17.0.3"

  }

}

```

一些基本的字段意义如下:

- name::包名,必填字段,不可以和依赖中的包名重复,如果将来要发布到npm仓库,需要保持在npm仓库中唯一

- version:版本号,必填字段,遵循 [semantic-versioning](https://docs.npmjs.com/about-semantic-versioning) 规则

- main:包入口代码文件,其他代码来引用此模块的时候,会自动引入此文件

- license:开源协议

- dependencies:生产环境依赖的包列表,通常是运行时依赖的库,会被安装在 `node_modules` 目录

- devDependencies:开发环境依赖的包列表,通常是辅助开发构建用到的一些工具,比如 `webpack`,也会安装到 `node_modules` 目录

### 创建package.json

当我们想要初始化一个项目的时候,我们可以通过下面的步骤创建一个 `package.json` 文件来管理整个工程依赖

- 创建一个新的工程目录,如 `react-template`

- 进入该目录,执行命令 `npm init` ,按照提示一步一步填写信息

执行完毕之后,该目录会生成对应内容的 `package.json`,然后我们就可以在这个目录安装依赖了

### npm scripts

在 `package.json` 里面有一个 `scripts` 属性,它可以添加一些自定义的命令,让我们非常方便地去执行一些构建程序,如

```json

{

  "dev": "webpack --config scripts/webpack.config.js",

  "start": "webpack serve --config scripts/webpack.config.js --open",

  "build": "webpack --config scripts/webpack.prod.js"

}

```

对于其中定义的任务,我们可以直接通过 `npm run <name>` 来执行,如

```

npm run build

```

实际执行的就是

```

webpack --config scripts/webpack.prod.js

```

这里 `webpack` 是作为一个项目依赖安装到 `node_modules` 目录的,因为 `webpack` 提供了命令行的支持,我们在 `npm scripts` 中调用它时就可以正常找到它,而不需要专门安装成一个全局命令。

有一些名字是 `npm` 默认支持的,比如 `start`,我们可以直接使用 `npm start` 来调用。

### 安装依赖

如果我们要安装一个生产环境依赖,如 `React`, 可以在项目根目录(package.json所在目录)执行命令

```

npm i react

```

安装完之后,依赖关系会自动记录在 `package.json` 文件的 `dependencies` 中,如果我们要安装一个开发环境依赖,如 `webpack`,可以添加一个 `--save-dev` 参数,如

```

npm i webpack --save-dev

```

安装完之后,依赖关系会自动记录在 `package.json` 文件的 `devDependencies` 中,这两种方式安装的依赖,代码都会被下载到项目根目录的 `node_modules` 目录,除此之外还会生成一个名为 `package-lock.json`,这里面记录了整个已安装的依赖关系和下载地址,可以帮助我们锁定依赖版本。`node_modules` 通常比较庞大,通常来说当我们发布项目或者将项目代码提交到Git仓库时,需要将 `node_modules` 目录添加到忽略列表,只提交 `package.json`、`package-lock.json` 即可,其他人拿到代码之后只需要在项目根目录执行命令

```

npm i

```

npm就会根据 `package.json`、`package-lock.json` 中的记录自动帮你安装所需要的依赖模块

> 需要注意,在执行npm安装模块的命令时,一定要保证该目录存在正确的 `package.json` 文件,否则npm会一层一层往上递归寻找 `package.json`,直到分区根目录

### 全局安装

有些npm模块提供了命令行调用的支持,我们可以用 `-g` 来将它安装成一个全局命令,比如之前我们安装过的 `typescript`

```

npm i typescript -g

```

通过这种方式安装的模块安装为一个命令,比如 `typescript` 定义的命令为 `tsc`,我们就可以全局使用这个命令来编译TS代码了,需要注意的是,通过 `-g` 安装的模块并不会存在于本地项目的 `node_modules` 中,而是由npm全局管理,也不能在代码中直接引用。

### 删除依赖

如果我们想要删除一个已安装的依赖,如 `react`,只需要在 `package.json` 所在的目录执行命令

```

npm remove react

```

如果要删除一个全局安装的模块,需要再加上 `-g` 的参数,如

```

npm remove typescript -g

```

## types

在我们使用TypeScript的过程中避免不了需要引用第三方的库,例如react,有些库自身就使用TS开发,发布到npm的时候也携带了对应的类型定义文件,对于这种库TS可以直接识别它的类型,但是大部分的库还是采用传统的JS开发,TS无法直接识别它的类型,所以TS采取了一种机制,可以为传统的JS模块编写类型定义文件,来描述他们的接口类型,然后通过npm仓库 `@types/<name>` 来发布,比如react是标准JS开发的,社区为它编写了TS类型定义模块,可以让TS识别react中的接口和数据类型,然后通过 `@types/react` 来发布,对应的地址为 [https://www.npmjs.com/package/@types/react](https://www.npmjs.com/package/@types/react),我们前面的课程示例中也有用到它,可以用下面的方式来安装

```

npm i @types/react --save-dev

```

在以后的开发中,如果你要在TS中使用某一个第三方的npm模块,需要先看一下它自身有没有携带类型定义文件,如果没有的话,再去 `@types` 下面看一下有没有社区提供的版本,目前主流的常用库基本都覆盖了。

## webpack

[https://webpack.js.org/](https://webpack.js.org/)

上面我们学习了如何通过npm来安装、管理依赖,现在我们来看一下如何使用这些依赖。

npm最早是针对Node.js依赖管理设计的,后来随着 `webpack` 之类的打包工具的出现,让前端也可以利用npm生态来做模块依赖管理了。

在原生Modules出现之前,前端只能通过一些模拟的方式来实现模块化,`webpack` 最核心的功能是可以将依赖的模块代码进行转换,然后合并在一起,这样开发效率、加载效率都会有明显提升,例如我们可以用下面的方式来引用 `react`

```ts

import * as React from 'react'

```

有了npm之后,我们import导入的代码就会分为两种情况,一种是npm安装的模块,另外一种就是我们自己本地编写的代码,这两种的引用方式有所不同,对于npm安装的模块,我们引用的时候 `from` 后面直接写模块名,就像上面的引用 `react` 的方式

```ts

import * as React from 'react'

import * as ReactDOM from 'react-dom'

```

对于本地模块代码,就是我们之前熟悉的方式了,通过相对路径的方式来引用,通过 `webpack` 的配置,我们可以省略掉 `.js` 或者 `.ts` 的后缀名,如

```ts

import Button from './components/Button'

import Modal from './components/Modal'

```

除了JS代码之外,webpack通过加载器(loader)机制还可以用来处理其他类型的资源,例如图片、CSS、字体等等,还可以通过插件(plugin)机制来自动生成HTML模板、合并输出CSS文件等,我们通过demo的代码来看一下简单的webpack配置,webpack是基于Node.js开发的,它的配置文件就是一个Node.js脚本

```js

const path = require('path')

const HtmlWebpackPlugin = require('html-webpack-plugin')

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {

  entry: {

    // 入口模块

    index: './src/index.tsx'

  },

  output: {

    // 输出路径

    path: path.resolve('./dist'),

    // 输出的文件名格式,这里采用名字+内容hash片段的方式,用于清理缓存

    filename: 'scripts/[name].[contenthash:6].js',

    // 添加公共路径,主要用于history路由模式下

    publicPath: '/'

  },

  resolve: {

    // 支持的脚本后缀名,可以让我们在导入的时候省略掉

    extensions: ['.ts', '.tsx', '.js']

  },

  // 模式,development开发环境,production生产环境,会压缩代码

  mode: 'development',

  // 生成sourcemap

  devtool: 'source-map',

  module: {

    // 加载器规则

    rules: [

      {

        // 使用ts-loader处理ts|tsx后缀的代码

        test: /\.(ts|tsx)$/,

        exclude: /node_modules/,

        loader: 'ts-loader'

      },

      {

        // 使用MiniCssExtractPlugin.loader、css-loader处理css后缀的代码

        test: /\.css$/,

        use: [MiniCssExtractPlugin.loader, 'css-loader']

      },

      {

        // 使用file-loader处理jpg|png|svg|ico格式的文件

        test: /\.(jpg|png|svg|ico)$/,

        loader: 'file-loader',

        options: {

          // 输出的文件名格式

          name: 'imgs/[name].[contenthash:6].[ext]'

        }

      }

    ]

  },

  plugins: [

    // 清理插件,可以在每次编译之前清空dist目录

    new CleanWebpackPlugin(),

    // 可以将引入的样式合并输出到css文件

    new MiniCssExtractPlugin({

      filename: 'styles/[name].[contenthash:6].css'

    }),

    // 根据模板生成html文件

    new HtmlWebpackPlugin({

      filename: 'index.html',

      template: './src/index.html'

    })

  ]

}

```

### loader

[https://webpack.js.org/loaders/](https://webpack.js.org/loaders/)

在我们的代码中会引用很多不同类型的资源,例如TS、图片、CSS,我们可以像引用JS模块一样来引用他们,例如

```js

import './style.css'

import logo from '../imgs/logo.png'

import Button from '../components/Button.tsx'

```

但是很明显,这几种都不是合法的JS模块,无法被正常执行,这时候就需要webpack的各种loader来帮我们处理这些类型的文件,以上面的代码为例,我们希望import进来的css最终输出成css文件,并且自动插入到html页面中,这里我们用到了两个loader,分别是 `MiniCssExtractPlugin.loader` 和 `css-loader`,webpack会从右往左来调用指定的loader,其中 `css-loader` 可以获得css路径、内容等信息,`MiniCssExtractPlugin.loader` 可以用来将css内容导出到文件。

对于图片类型如png,我们使用了 `file-loader`,webpack在打包代码的时候如果遇到导入png,就会把这个任务交给 `file-loader`,此时它会按照我们配置的格式,将文件输出到指定位置,并返回新的路径名,上面我们得到的 `logo` 就是输出之后该图片的路径,这样我们就可以在JSX中插值引用该图片了。

对于TS代码,我们使用把它交给了 `ts-loader` 来处理,它会帮我们自动完成TS到JS的编译。

经过上面几个loader对不同资源的处理,我们得到了可以在浏览器中正常执行的代码。loader也是一个npm模块,需要通过npm来安装

### plugin

[https://webpack.js.org/plugins/](https://webpack.js.org/plugins/)

loader用来处理不同类型的资源,而plugin可以用来扩展webpack的功能,webpack内置了一些插件,也有很多社区提供的,上面的例子中我们使用了三个插件

- CleanWebpackPlugin:这个插件可以帮助我们清理输出目录,避免混入旧版本的文件

- MiniCssExtractPlugin:这个插件让我们把导入的css代码合并输出到css文件

- HtmlWebpackPlugin:这个插件可以根据模板来生成我们的入口html文件,而且会自动把输出的JS、CSS文件引入进来

### webpack-dev-server

`webpack-dev-server` 是一个小型嵌入式的Web服务器,它可以和webpack配合使用,能够支持静态文件、API代理、自动刷新等功能,通过它我们可以在开发环境给每一个前端工程启动一个独立的http server,方便我们调试,而部署到生产环境的时候再使用Nginx。`webpack-dev-server` 的配置格式大致如下,可以直接添加到webpack的配置文件:

```js

devServer: {

  // 静态文件目录

  contentBase: path.resolve('./dist'),

  // 自动刷新

  hot: true,

  // 监听端口

  port: 3360,

  // 支持history路由模式

  historyApiFallback: true,

  // API代理

  proxy: {

    '/data': {

      target: 'https://data.example.com',

      changeOrigin: true

    }

  }

}

```

添加完配置之后,我们可以通过 `npm scripts` 来定义一个启动服务的命令,如

```json

"scripts": {

  "start": "webpack serve --config scripts/webpack.config.js --open"

}

```

然后我们执行命令 `npm start` 就可以启动http服务了,也会自动打开默认浏览器进行访问,当我们修改了代码引起输出文件的变化时,会通知浏览器自动刷新。

`webpack-dev-server` 默认会将webpack输出的文件写入到内存中,而不是磁盘中,所以在启动它的时候,dist目录是没有输出文件的。

> react有一个著名的脚手架工具 `create-react-app`,有兴趣的同学以后可以自己研究一下,不在本课程的讨论范围。

## 组件的生命周期

[https://react.docschina.org/docs/react-component.html](https://react.docschina.org/docs/react-component.html)

一个React组件从初始化到被删除,会经历一个完整的生命周期,我们可以为类组件添加一些特殊方法,在组件生命周期的的一些关键节点,React会来执行这些方法。

### componentDidMount()

`componentDidMount()` 会在组件挂载之后(插入DOM树中)立即调用,依赖于DOM节点的初始化操作应该放在这里,通常我们会在这里通过网络获取组件所需的数据、订阅一些事件通知等等

### componentDidUpdate()

`componentDidUpdate()` 会在更新后被立即调用,首次渲染不会执行此方法,可以让我们监控组件传入数据发生的变化。

### componentWillUnmount()

`componentWillUnmount()` 会在组件卸载及销毁之前直接调用,在此方法中执行必要的清理操作,例如:清除定时器,取消网络请求或者之前在 `componentDidMount()` 中创建的事件订阅等。在此方法中不应该调用 `setState()`,因为该组件将永远不会重新渲染,组件实例卸载后,将永远不会再挂载它。

## 路由

[https://reactrouter.com/web/guides/quick-start](https://reactrouter.com/web/guides/quick-start)

目前我们所接触到的react例子都是单页面的,只有一个页面组件,在实际项目中通常都会有多个页面存在,可以通过链接来进行切换,达到多页面的效果。要实现这个功能,我们需要借助于React的路由模块 `react-router-dom` 来实现,它可以让我们监听浏览器地址的变化,并且解析这个URL对象,然后router根据URL的路径匹配到路由对应页面组件,最后正确地渲染对应地组件,这个就是路由工作的基本原理。

我们平时会使用的主要是两种路由模式:

- HashRouter:这种模式基于浏览器location的hash片段来实现,实现比较简单,不需要服务器的支持,缺点是url样式不够优雅,而且hash参数容易丢失,如下:


  ```

  http://example.com/#/home/files

  ```

- BrowserRouter:这种模式基于浏览器的history API,可以让我们创建一个像 `http://example.com/home/files` 这样真实的URL,而且切换url不会引起页面的刷新,用户体验比较好,是我们比较推荐的路由方式,不过这种模式需要服务器比如Nginx的支持,因为路径 `/home/files` 只是一个前端定义的路由,当用户刷新页面的时候浏览器会去向服务器请求这个资源,服务器因为没有对应的这个资源,就会返回404,导致页面无法显示,所以需要Nginx将所有404的请求返回入口文件 `/index.html`,大概配置如下

  ```plain

  location / {

    root d:/www;

    try_files $uri $uri/ /index.html;

  }

  ```

### 路由的定义

我们可以把React组件分为路由组(页面)件和普通组件,路由组件对应的就是一个路由页面,我们可以用下面的方式来定义一个路由组件 `About`

```ts

import * as React from 'react'

import { RouteComponentProps, Link } from 'react-router-dom'

export default class About extends React.Component<RouteComponentProps> {

  render() {

    return (

      <h1>About</h1>

    )

  }

}

```

这里我们要给组件添加一个props的泛型 `RouteComponentProps`,添加之后TS就可以识别到传入的和路由控制相关的props参数,具体参考示例代码

然后我们需要定义一个根组件 `App`,用来确定路由和组件的映射关系,如下

```ts

import * as React from 'react'

import { BrowserRouter, Route, Switch } from 'react-router-dom'

import Main from '../Main'

import About from '../About'

export default class App extends React.Component {

  render() {

    return (

      <BrowserRouter>

        <div className="container">

          <Switch>

            <Route path="/" exact component={Main} />

            <Route path="/about" component={About} />

          </Switch>

        </div>

      </BrowserRouter>

    )

  }

}

```

在上面的代码中我们定义了两个路由规则,放在了 `Switch` 中,路由规则的定义通过 `Route` 组件来进行,它有两个关键的参数,分别是:

- `path`:要匹配的URL路径

- `component`:该路由对应的页面组件

需要注意的是,path的路径默认会匹配到子路径,例如 `/detail` 也会匹配到 `/detail/123`,如果不像匹配子路径,需要加上 `exact` 属性。

`Switch` 会根据当前的URL路径匹配它下面的 `Route`,然后将匹配到的第一个组件渲染到当前位置。

### 带参数的路由

有时候我们的页面会接收一些参数,比如根据传入的关键词去进行搜索,`react-router-dom` 支持路由参数,我们可以用下面的方式来定义

```ts

<Route path="/search/:keyword" component={Search} />

```

然后我们可以在对应的路由组件 `Search` 中来接收这些参数,通过下面的方式来进行

首先我们需要定义合适的 `Props` 类型,`RouteComponentProps` 也支持传入参数泛型,如下

```ts

interface Params {

  keyword: string

}

type Props = RouteComponentProps<Params>

export default class Search extends React.Component<Props> {

  render() {

    return (

      <div>{this.props.match.params.keyword}</div>

    )

  }

}

```

我们首先定义了当前路由组件接收的参数类型 `Params`,跟我们在 `Route` 中定义的参数一致,然后我们可以在组件内通过 `this.props.match.params` 来访问到我们的路由参数对象,它的类型就是 `Params`

### 路由导航

我们可以使用两种方式来在页面中切换路由,`react-router-dom` 提供了一个 `Link` 组件,我们可以用它来替代传统的a标签,因为我们使用的 `history` 模式的 `BrowserRouter`,它其实是利用浏览器的 `history.pushState` 来实现的,它可以在浏览器添加历史状态,修改当前的URL而不会引起页面的刷新,`Link` 可以帮我们拦截用户的点击,然后完成 `pushState` 相关的操作,这样就可以在切换路由的同时保持页面的状态,而不是刷新页面。它的基本用法如下:

```ts

import { Link } from 'react-router-dom'

export default class Main extends React.Component {

  render() {

    return <Link to="/about">About</Link>

  }

}

```

参数 `to` 就是要导航到的路由,需要注意,Link标签只用来切换应用内的路由,如果你想要跳转到外部链接,例如 `MDN`,那你应该使用传统的a标签,如

```ts

<a href="https://developer.mozilla.org/zh-CN/">MDN</a>

```

还有一种方式是通过API来进行导航,对于路由组件,`react-router-dom` 会通过 `props` 传入一些数据和API,我们可以通过调用下面的API来进行路由导航

```ts

this.props.history.push('/about')

```

需要注意的是,`this.props.history` 只在路由组件内部才可以使用,如果你想要在普通组件内部也能访问路由信息,需要使用 `withRouter` 来包裹一下,例如

```ts

import * as React from 'react'

import { Link, withRouter, RouteComponentProps } from 'react-router-dom'

class Nav extends React.Component<RouteComponentProps> {

  componentDidMount() {

    console.log(this.props.history)

  }

  render() {

    return (

      <nav className="nav">

        <Link to="/" className="nav-item">

          Main

        </Link>

        <Link to="/about" className="nav-item">

          About

        </Link>

      </nav>

    )

  }

}

export default withRouter(Nav)

```

这样我们就可以访问路由信息了

### NavLink

`react-router-dom` 还提供了一个特殊的Link组件叫 `NavLink`,它可以让我们来创建一些导航元素,当前的路由如果和它的链接匹配的话,可以自动帮我们添加选中的状态,例如上面的 `Nav` 组件我们可以用 `NavLink` 来替换一下

```ts

import * as React from 'react'

import { NavLink, withRouter, RouteComponentProps } from 'react-router-dom'

class Nav extends React.Component<RouteComponentProps> {

  render() {

    return (

      <nav className="nav">

        <NavLink to="/" className="nav-item" exact activeClassName="nav-active">

          Main

        </NavLink>

        <NavLink to="/about" className="nav-item" activeClassName="nav-active">

          About

        </NavLink>

      </nav>

    )

  }

}

export default withRouter(Nav)

```

当路由和 `NavLink` 中的 `to` 匹配时,会自动添加一个 `nav-active` 类名

## 课后任务

本周任务是完成一个简化版的什么值得买多路由页面,设计参考task目录下面的效果图,具体说明如下:

该任务一共有三个路由页面,分别为 `Posts`、`News`、`Detail`,所需数据通过远程API的形式提供,不在需要本地JSON文件,`devServer` 中已经配置好了代理,全部通过GET方式调用,数据结构说明如下,文章内容只会在 `detail` 接口中返回,列表接口不会返回,具体的API请求已经在 `services/api.ts` 中封装好,直接调用即可

### 技术要求

- 拷贝 `template` 中的文件、按照我们约定的文件结构进行代码补全开发,使用 `webpack` 进行构建

- 使用rem布局,不需要支持响应式,只需要支持手机设备的正常缩放

- 效果图是750px宽,2倍屏,需要自行计算rem的缩放比例,不需要监控 `onresize`,只需要页面刷新的时候计算

- 三个页面分别编写路由组件,默认显示好文推荐 `Posts`

- 排行榜的导航要固定在顶部,下面的列表部分可以滚动

- 导航项需要跟随路由自动选中,使用 `NavLink`

- 好文推荐的前五条记录左上角需要显示排名图标

- 点击文章列表项可以跳转到文章详情页,携带文章ID,不能引起页面的重新加载

- 详情页面顶部栏需要固定在上方,下方文章区域可以滚动,注意文章内容部分需要自己编写样式,让文字和图片合理排版。

- 点击详情左上角的返回按钮可以回退到上一页 `this.props.history.goBack()`

- 顶部显示作者的头像和名字

- 修改 `README.md` 记录作业完成的思路和遇到的问题

- 代码在下周四之前推送至 `task` 仓库的 `week-07` 目录

### 提示

- 所需知识点在两个demo工程 `react-routes`、`webpack-react` 中基本都有覆盖,可以直接借鉴参考

- 合理拆分组件,如果在列表项组件中访问路由的Props,记得使用 `withRouter` 来包裹

- 如果组件有多个Props,可以用类型组合的写法来传递,如

```ts

interface Params {

  id: string

}

interface Props {

  post: IArticle

}

class PostItem extends React.Component<Props & RouteComponentProps<Params>> {

}

```

- 排行榜的导航栏需要根据路由自动选中,但是只出现在 `Posts`、`News` 两个组件中,`Detail` 组件中并不包含

- 所需的两个字体图标分别为 `icon-comments`、`icon-likes`,已经定义好,可以通过这样来引用

```ts

<i className="iconfont icon-comments" />

<i className="iconfont icon-likes" />

```

- 热点资讯列表项的配图区域是固定尺寸,而我们的banner图都是宽图,这里需要处理一下样式让图片正常显示而不出现拉伸。可以借助于 `background-size`、`background-position`,让图片能够垂直方向铺满,水平方向居中显示,超出的部分则不用显示。

- 文章内容部分是富文本,也就是包含html标签,直接通过插值的方式引用的话,React会将其转义,需要使用 [dangerouslySetInnerHTML](https://zh-hans.reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml) 来进行渲染,如

```ts

<article dangerouslySetInnerHTML={{__html: '<h1>Hello</h1>'}} />

```

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352