(15)详情页开发——① “布局”和“数据管理” | React.js 项目实战:PC 端“简书”开发

复制代码
转载请注明出处,未经同意,不可修改文章内容。

🔥🔥🔥"前端一万小时"两大明星专栏------"从零基础到轻松就业"、"前端面试刷题",已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。

1 详情页布局

⚠️注:详情页的布局我不会写得太复杂,重点还是在后边的"逻辑"部分,大伙儿可以自己适当地扩展,此时的你,"布局"部分对你肯定没什么难度。

1️⃣打开 pages 目录下 detail 文件夹中的 index.js 文件:

jsx 复制代码
import React, {Component} from "react";

import {
  DetailWrapper,
  Header,
  Content
} from "./style.js";

class Detail extends Component {
  render() {
    return(
      <DetailWrapper>
        <Header>评"超时空同居"浅议爱情电影和奇幻元素的嫁接</Header>
        <Content>
          <img src="https://qdywxs.github.io/jianshu-images/detail.jpg" alt="" />
          <p>这部电影充分照顾到了八零后的情怀,九零后的笑点,零零后嘛,那就是奇幻爱情吧。奇幻爱情喜剧,三种元素都有,但哪种都不出彩,相对来说喜剧元素稍微强一点。</p>
          <p>说实话,去之前我对电影《超时空同居》的期望值并不高,首先超时空已经不是什么新鲜话题,同居也不是什么热点新闻;其次导演苏伦是个新人,主演雷佳音和佟丽娅一看就不搭,两人的票房号召力也不是很大;再者《后来的我们》刚刚刷过情怀牌,而《复联 3》又以十年一战的王者气势独占鳌头,试想此情此景,一部小成本投资的奇幻喜剧爱情电影,凭什么在这种困境下胜出呢?而最终促使我前往的是监制徐峥,一个相当有才,且在电影圈呼风唤雨的全能型电影人。</p>
          <p>可是看完影片,我就后悔了,特别想见见现实中的雷佳音是怎样的,因为这次雷大头确实雷到我了,真是应了一句话<b>"人不可貌相,海水不可斗量"</b>。</p>
          <p>那个在《我的前半生》中一张纠结脸的前夫哥不见了,取而代之的是一个在穷困潦倒时依然积极向上,始终闪现人性光辉的阳光男。<b>豆干 + 花生就能吃出烧鸡味,馒头片 + 黑芝麻粉 + 咖啡粉就能吃出巧克力蛋糕味</b>,这种生活的智慧可不是一般人能具备的。</p>
          <p>记得来之前朋友就跟我说雷佳音是那种演什么像什么的演员,我还不信,现在信了,也晚了,因为他正如我之前所愿,很配合地没能出现在搜狐的首映现场,唉,只能脑补生活中的他是怎样的了。</p>
          <p>说完了雷佳音的"雷",我想再说说《超时空》的赞。</p>
          <p>现在的电影市场上,爱情片不少,喜剧片不少,奇幻片也不少,但是要说集三者于一身的,奇幻喜剧爱情片,还真是不多见,而《超时空同居》就是这样一个集三者于一身且脑洞大开的影片。</p>
          <p>接下来,我想从以下三个方面讲讲《超时空同居》带给我的意外惊喜:</p>
          <p><b>一、差异十足的 CP 设定</b></p>
          <p>《超时空同居》从片名看就知道,是一个玩时间梗的爱情电影,雷佳音饰演的陆鸣,跟佟丽娅饰演的谷小焦,因为一个突如其来的时空意外住在了同一个房间,只不过陆鸣是在世纪之交的 1999 年,谷小焦是在当下的 2018 年。</p>
          <p>这样的时间差距,就为他们两个人在形象和性格上都建立起了巨大的反差。可以想象,当土气、邋遢、过时老气的雷佳音与漂亮、时髦、现代化的佟丽娅寄居在同一个屋檐下将是一番怎样的情景。</p>
          <p><b>二、时空重叠的叙事结构</b></p>
          <p>与同样玩时间梗的《重返二十岁》不同的是,《超时空同居》并非把时空转换作为一个"起点",此后的叙事依然固定在一个时空里,它是把不断变化的时空作为一个基本背景,叙事也在不同的时空里来回穿梭。这不仅让影片的整体风格变得复杂多变,也给故事增添了浓厚的奇幻色彩。</p>
          <p><b>三、笑中带泪的成长主题</b></p>
          <p>《超时空同居》除了各种令人爆笑的情节外,它还将更多的落脚点放在了传达勇敢真诚的爱情观上,并且以主人公的"成长"为主题,让观众在笑过之后有了更多关于自身的思考。</p>
          <p>作为现代奇幻片,与古装奇幻片不同的是,在这里未来是可以被改变的。</p>
          <p>当陆鸣看到那个未来的自己虽然外表光鲜,却为了成功不择手段时,他毅然决然地选择了采取行动,从而避免了悲剧的发生。虽然他最终没能成为大屏幕上的地产大亨,却依然赢得了属于自己的爱情。</p>
          <p>同时发布会现场也呼应了影片的这一主题,当四位主创被问及最想对 19 年前的自己说什么时,佟丽娅非常"实在"地笑称,希望<b>"能多买几套房"</b>。</p>
          <p>2004 年就进入影视圈、却直到去年才"大器晚成"的雷佳音希望对自己说:<b>就坚持这么干,没错!</b></p>
          <p>1972 年出生,2012 年才开始当导演的徐峥说:<b>要抓紧时间早一点拍戏,早一点开始自己的创作,不要浪费时间。</b></p>
          <p>来自内蒙古的 80 后新锐女导演苏伦说:<b>早点离开草原,去北京。</b></p>
          <p>你呢,最想对 19 年前的自己说什么?</p>
          <p>影片将于今天 5 月 18 日上映,我想看过之后,你会给自己一个满意的答复的。</p>
        </Content>

      </DetailWrapper>
    )
  }
}

export default Detail;

2️⃣在 pages 目录下的 detail 文件夹中新建一个 style.js 文件,用于放置"详情页"的样式:

javascript 复制代码
import styled from "styled-components";

export const DetailWrapper = styled.div`
	overflow: hidden;
	width: 620px;
	margin: 0 auto;
	padding-bottom: 100px;
`;

export const Header = styled.div`
	margin: 50px 0 20px 0;
	line-height: 44px;
	font-size: 34px;
	color: #333;
	font-weight: bold;
`;

export const Content = styled.div`
	img {
		width: 100%;
	}
	p {
		margin: 25px 0;
		font-size: 16px;
		line-height: 30px;
	}
	b {
		font-weight: bold;
	}
`;

返回页面查看效果(详情页正常显示):

2 使用 React-redux 管理"详情页"数据

上一节中,我们对"详情页"做了简易地布局。但如你所见,里边的所有数据都是写死的。实际编码中,这些数据应该都是通过"接口"返回出来的。

本节,我们先按 React-redux 的流程,将数据放在合理的位置,并分门别类地显示在"详情页"。

🚀正式编码之前,我们再理理 React-redux 的流程(或者说叫"套路"):

我的这个 Detail 组件要显示一些内容,内容应该存在哪呢?

一定要存在 Redux 的 store 里,而 store 中有什么数据则是由 reducer 来决定的。

在之前的编码过程中,reducer 被我们拆成了很多部分。即,detail 页面下的 Detail 组件,它的"数据"一定要存在 detail 这个页面对应的 reducer 中,让 detail 的 reducer 来管理整个 detail 页面的"数据"。

因此,我们需要在 detail 目录下创建一个 store 文件夹。里边添加一个 reducer.js 文件。

reducer.js 文件里,定义 detail 的 reducer 下默认的"数据"。

OK,以上把"数据"放到位后,需要考虑 Detail 这个组件怎么去用这个"数据"了?

在 detail 目录下的 index.js 文件中,用 react-redux 的 connect 方法,让 Detail 组件和 store 作"连接"。

连接好后,我们就可以从 store 里边取出我需要的"数据"并展示在页面上。

1️⃣打开 src 目录 pages 下的文件夹 detail ,detail 下有很多只属于 Detail 组件的"数据",我们就可以将这些"数据"放在 detail 里进行管理。 在 detail 目录下新建一个文件夹 store

2️⃣紧接着,在 store 文件夹下创建一个 index.js 文件,它的作用为------❗️它是整个 store 的"出口"文件("出口"文件的好处为:它可以简化引用文件时的"路径",也便于后期维护~)

3️⃣在 store 文件夹下创建一个 reducer.js 文件,让 reducer.js 来管理 detail 下的"数据":

4️⃣同理,在 store 文件夹下创建 actionTypes.jsactionCreators.js 文件备用:

小架子搭起后,我们按流程编写各文件里的代码。

5️⃣编写 reducer.js 中的代码: 5️⃣-①: reducer.js 文件返回一个"函数";

javascript 复制代码
import {fromJS} from "immutable"; /*
																  ❗️从 immutable 中引入 fromJS 方法,这个"方法"可以
																	帮我们把一个"JS 对象"转化为一个"immutable 对象"!
                                   */

const defaultState = fromJS(

)

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

  return state;
}

5️⃣-②:将 detail 目录下 index.js 文件中写死的"数据"分门别类放到 reducer.js 中;

❗️请自己将 index.js 中的 <img><p> 标签等首尾连接,做成"字符串"!

javascript 复制代码
import {fromJS} from "immutable"; 

const defaultState = fromJS({
	title: "评"超时空同居"浅议爱情电影和奇幻元素的嫁接",
  content: "<img src='https://qdywxs.github.io/jianshu-images/detail.jpg' alt='' /><p>这部电影充分照顾到了八零后的情怀,九零后的笑点,零零后嘛,那就是奇幻爱情吧。奇幻爱情喜剧,三种元素都有,但哪种都不出彩,相对来说喜剧元素稍微强一点。</p><p>说实话,去之前我对电影《超时空同居》的期望值并不高,首先超时空已经不是什么新鲜话题,同居也不是什么热点新闻;其次导演苏伦是个新人,主演雷佳音和佟丽娅一看就不搭,两人的票房号召力也不是很大;再者《后来的我们》刚刚刷过情怀牌,而《复联 3》又以十年一战的王者气势独占鳌头,试想此情此景,一部小成本投资的奇幻喜剧爱情电影,凭什么在这种困境下胜出呢?而最终促使我前往的是监制徐峥,一个相当有才,且在电影圈呼风唤雨的全能型电影人。</p><p>可是看完影片,我就后悔了,特别想见见现实中的雷佳音是怎样的,因为这次雷大头确实雷到我了,真是应了一句话<b>"人不可貌相,海水不可斗量"</b>。</p><p>那个在《我的前半生》中一张纠结脸的前夫哥不见了,取而代之的是一个在穷困潦倒时依然积极向上,始终闪现人性光辉的阳光男。<b>豆干 + 花生就能吃出烧鸡味,馒头片 + 黑芝麻粉 + 咖啡粉就能吃出巧克力蛋糕味</b>,这种生活的智慧可不是一般人能具备的。</p><p>记得来之前朋友就跟我说雷佳音是那种演什么像什么的演员,我还不信,现在信了,也晚了,因为他正如我之前所愿,很配合地没能出现在搜狐的首映现场,唉,只能脑补生活中的他是怎样的了。</p><p>说完了雷佳音的"雷",我想再说说《超时空》的赞。</p><p>现在的电影市场上,爱情片不少,喜剧片不少,奇幻片也不少,但是要说集三者于一身的,奇幻喜剧爱情片,还真是不多见,而《超时空同居》就是这样一个集三者于一身且脑洞大开的影片。</p><p>接下来,我想从以下三个方面讲讲《超时空同居》带给我的意外惊喜:</p><p><b>一、差异十足的 CP 设定</b></p><p>《超时空同居》从片名看就知道,是一个玩时间梗的爱情电影,雷佳音饰演的陆鸣,跟佟丽娅饰演的谷小焦,因为一个突如其来的时空意外住在了同一个房间,只不过陆鸣是在世纪之交的 1999 年,谷小焦是在当下的 2018 年。</p><p>这样的时间差距,就为他们两个人在形象和性格上都建立起了巨大的反差。可以想象,当土气、邋遢、过时老气的雷佳音与漂亮、时髦、现代化的佟丽娅寄居在同一个屋檐下将是一番怎样的情景。</p><p><b>二、时空重叠的叙事结构</b></p><p>与同样玩时间梗的《重返二十岁》不同的是,《超时空同居》并非把时空转换作为一个"起点",此后的叙事依然固定在一个时空里,它是把不断变化的时空作为一个基本背景,叙事也在不同的时空里来回穿梭。这不仅让影片的整体风格变得复杂多变,也给故事增添了浓厚的奇幻色彩。</p><p><b>三、笑中带泪的成长主题</b></p><p>《超时空同居》除了各种令人爆笑的情节外,它还将更多的落脚点放在了传达勇敢真诚的爱情观上,并且以主人公的"成长"为主题,让观众在笑过之后有了更多关于自身的思考。</p><p>作为现代奇幻片,与古装奇幻片不同的是,在这里未来是可以被改变的。</p><p>当陆鸣看到那个未来的自己虽然外表光鲜,却为了成功不择手段时,他毅然决然地选择了采取行动,从而避免了悲剧的发生。虽然他最终没能成为大屏幕上的地产大亨,却依然赢得了属于自己的爱情。</p><p>同时发布会现场也呼应了影片的这一主题,当四位主创被问及最想对 19 年前的自己说什么时,佟丽娅非常"实在"地笑称,希望<b>"能多买几套房"</b>。</p><p>2004 年就进入影视圈、却直到去年才"大器晚成"的雷佳音希望对自己说:<b>就坚持这么干,没错!</b></p><p>1972 年出生,2012 年才开始当导演的徐峥说:<b>要抓紧时间早一点拍戏,早一点开始自己的创作,不要浪费时间。</b></p><p>来自内蒙古的 80 后新锐女导演苏伦说:<b>早点离开草原,去北京。</b></p><p>你呢,最想对 19 年前的自己说什么?</p><p>影片将于今天 5 月 18 日上映,我想看过之后,你会给自己一个满意的答复的。</p>"
})

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

  return state;
}

5️⃣-③:打开 detail 目录下 store 中的"出口"文件 index.js ,定义一些通用的需要传递出去的东西;

javascript 复制代码
import reducer from "./reducer";
import * as actionTypes from "./actionTypes";
import * as actionCreators from "./actionCreators";

export {reducer, actionTypes, actionCreators};

❗️5️⃣-④:返回 src 目录下 store 中的 reducer.js 文件,引入上一步创建的 reducer;

javascript 复制代码
import {combineReducers} from "redux-immutable";

import {reducer as headerReducer} from "../common/header/store"; 

import {reducer as homeReducer} from "../pages/home/store";

import {reducer as detailReducer} from "../pages/detail/store"; // ❗️

const reducer = combineReducers({ 
  header: headerReducer,
  home: homeReducer,
  
  detail: detailReducer // ❗️
})


export default reducer;

6️⃣OK,通过以上的步骤,store 里边就有数据了。按照 Redux 和 React-redux 的工作流程,一旦有了数据,Detail 组件就可以连接 store,去 store 里边取数据并显示出来!

6️⃣-①:按照流程,这一步应该是让"Detail 组件"拥有获取 store 中数据的"能力"。但这一步,我们在《首页开发------① 如何在 React 中使用"路由"》中已经实现了------我们已经把 Detail 组件放到了 Provider 组件之中。

打开 src 目录下的 App.js 文件查看:

jsx 复制代码
import React, { Component } from "react";

import {GlobalStyle} from "./style";

import {GlobalIconStyle} from "./statics/iconfont/iconfont";

import {BrowserRouter, Route} from "react-router-dom";

import Header from "./common/header";

import Home from "./pages/home";
import Detail from "./pages/detail";

import { Provider } from "react-redux";

import store from "./store";

class App extends Component  {  
  render() {  
    return (
      <div>
        <GlobalStyle />
        <GlobalIconStyle />
 
        <Provider store={store}>             
          <BrowserRouter>
            <div>
              <Header />
      
              <Route path="/" exact component={Home}></Route> 
                  
              <Route path="/detail" exact component={Detail}></Route> {/* ❗️ */}
            </div>
          </BrowserRouter>
        </Provider>

      </div>
    );
  }
}

export default App; 

❗️可光有"能力"可不行,"Detail 组件"要具体怎么去获取"数据"呢?

打开 detail 目录下的 index.js 文件:

jsx 复制代码
import React, {Component} from "react";

import {
  DetailWrapper,
  Header,
  Content
} from "./style.js";

/*
❗️❗️❗️6️⃣-②:从 react-redux 中引入 connect 方法(它也是 React-redux 的核心 API 之一),
connect 的作用很明确------就是"连接"的意思!
 */
import {connect} from "react-redux";

class Detail extends Component {
  render() {
    return(
      <DetailWrapper>
      
       {/*
        ❗️❗️❗️6️⃣-⑨:"映射"上了过后,我们就可以通过调用 this.props.title 及 
        this.props.content 来"调用"store 中的 title 和 content 了!
        删除下边的代码,重新用 this.props 来改写~
        <Header>评"超时空同居"浅议爱情电影和奇幻元素的嫁接</Header>
        <Content>
          <img src="https://qdywxs.github.io/jianshu-images/detail.jpg" alt="" />
          <p>这部电影充分照顾到了八零后的情怀,九零后的笑点,零零后嘛,那就是奇幻爱情吧。奇幻爱情喜剧,三种元素都有,但哪种都不出彩,相对来说喜剧元素稍微强一点。</p>
          <p>说实话,去之前我对电影《超时空同居》的期望值并不高,首先超时空已经不是什么新鲜话题,同居也不是什么热点新闻;其次导演苏伦是个新人,主演雷佳音和佟丽娅一看就不搭,两人的票房号召力也不是很大;再者《后来的我们》刚刚刷过情怀牌,而《复联 3》又以十年一战的王者气势独占鳌头,试想此情此景,一部小成本投资的奇幻喜剧爱情电影,凭什么在这种困境下胜出呢?而最终促使我前往的是监制徐峥,一个相当有才,且在电影圈呼风唤雨的全能型电影人。</p>
          <p>可是看完影片,我就后悔了,特别想见见现实中的雷佳音是怎样的,因为这次雷大头确实雷到我了,真是应了一句话<b>"人不可貌相,海水不可斗量"</b>。</p>
          <p>那个在《我的前半生》中一张纠结脸的前夫哥不见了,取而代之的是一个在穷困潦倒时依然积极向上,始终闪现人性光辉的阳光男。<b>豆干 + 花生就能吃出烧鸡味,馒头片 + 黑芝麻粉 + 咖啡粉就能吃出巧克力蛋糕味</b>,这种生活的智慧可不是一般人能具备的。</p>
          <p>记得来之前朋友就跟我说雷佳音是那种演什么像什么的演员,我还不信,现在信了,也晚了,因为他正如我之前所愿,很配合地没能出现在搜狐的首映现场,唉,只能脑补生活中的他是怎样的了。</p>
          <p>说完了雷佳音的"雷",我想再说说《超时空》的赞。</p>
          <p>现在的电影市场上,爱情片不少,喜剧片不少,奇幻片也不少,但是要说集三者于一身的,奇幻喜剧爱情片,还真是不多见,而《超时空同居》就是这样一个集三者于一身且脑洞大开的影片。</p>
          <p>接下来,我想从以下三个方面讲讲《超时空同居》带给我的意外惊喜:</p>
          <p><b>一、差异十足的 CP 设定</b></p>
          <p>《超时空同居》从片名看就知道,是一个玩时间梗的爱情电影,雷佳音饰演的陆鸣,跟佟丽娅饰演的谷小焦,因为一个突如其来的时空意外住在了同一个房间,只不过陆鸣是在世纪之交的 1999 年,谷小焦是在当下的 2018 年。</p>
          <p>这样的时间差距,就为他们两个人在形象和性格上都建立起了巨大的反差。可以想象,当土气、邋遢、过时老气的雷佳音与漂亮、时髦、现代化的佟丽娅寄居在同一个屋檐下将是一番怎样的情景。</p>
          <p><b>二、时空重叠的叙事结构</b></p>
          <p>与同样玩时间梗的《重返二十岁》不同的是,《超时空同居》并非把时空转换作为一个"起点",此后的叙事依然固定在一个时空里,它是把不断变化的时空作为一个基本背景,叙事也在不同的时空里来回穿梭。这不仅让影片的整体风格变得复杂多变,也给故事增添了浓厚的奇幻色彩。</p>
          <p><b>三、笑中带泪的成长主题</b></p>
          <p>《超时空同居》除了各种令人爆笑的情节外,它还将更多的落脚点放在了传达勇敢真诚的爱情观上,并且以主人公的"成长"为主题,让观众在笑过之后有了更多关于自身的思考。</p>
          <p>作为现代奇幻片,与古装奇幻片不同的是,在这里未来是可以被改变的。</p>
          <p>当陆鸣看到那个未来的自己虽然外表光鲜,却为了成功不择手段时,他毅然决然地选择了采取行动,从而避免了悲剧的发生。虽然他最终没能成为大屏幕上的地产大亨,却依然赢得了属于自己的爱情。</p>
          <p>同时发布会现场也呼应了影片的这一主题,当四位主创被问及最想对 19 年前的自己说什么时,佟丽娅非常"实在"地笑称,希望<b>"能多买几套房"</b>。</p>
          <p>2004 年就进入影视圈、却直到去年才"大器晚成"的雷佳音希望对自己说:<b>就坚持这么干,没错!</b></p>
          <p>1972 年出生,2012 年才开始当导演的徐峥说:<b>要抓紧时间早一点拍戏,早一点开始自己的创作,不要浪费时间。</b></p>
          <p>来自内蒙古的 80 后新锐女导演苏伦说:<b>早点离开草原,去北京。</b></p>
          <p>你呢,最想对 19 年前的自己说什么?</p>
          <p>影片将于今天 5 月 18 日上映,我想看过之后,你会给自己一个满意的答复的。</p>
        </Content>
         */}
      
        <Header>{this.props.title}</Header>
        <Content>
          {this.props.content} {/* ❗️❗️❗️ */}
        </Content>

      </DetailWrapper>
    )
  }
}


// 6️⃣-⑥:接下来,我们先定义"连接"的规则;
const mapStateToProps = (state) => { /*
																		 6️⃣-⑦:把 store 里的"数据 state"作为"参数"
                                     传递给 mapStateToProps;
                                      */
  
  return { // ❗️这个"规则"会返回一个"对象"出去!
    title: state.getIn(["detail", "title"]), /*
    																				 ❗️❗️❗️6️⃣-⑧:"规则"的具体做法为------
                                             将 store 里的 title 映射到"Detail 组件"
                                             里的 props 的 title 中去;
                                              */
    
    content: state.getIn(["detail", "content"]) // ❗️同理~
  }

}


/*❗️❗️❗️6️⃣-③:之前我们直接导出的是 Detail,可用了 React-redux 后,就不能这样写了!
export default Detail;
*/

/*6️⃣-④:取而代之,我们导出 connect 方法(
❗️注意看我们给 connect 方法传递了哪些参数!);
*/
export default connect(mapStateToProps, null)(Detail); /*
																				6️⃣-⑤:我们一共要给 connect 传递 3 个参数!
                                        Detail 表示:connect 会让"Detail 组件"和 store
                                        进行"连接"(由"6️⃣-①"可知,Detail 已经拥有"能力"
                                        连接 store);
                                        
                                        mapStateToProps 表示:"Detail 组件"和 store 
                                        进行"连接"是需要"规则"的,而具体的"规则"就在这个
                                        mapStateToProps 里边(❗️直译为:把 store 里边的
                                        "数据 state"映射到"Detail 组件"的 props 里);
                                        
                                        null 表示:这里还会接收一个名叫 
                                        mapDispatchToProps 的参数,由于这里不需要"修改"数据,
                                        故用 null 占位!
                                        							  */

返回页面查看(详情页可以正常跳转,❗️但内容部分所有的"标签"都被转义了!

❓怎样让"标签"不被转义呢? 🔗前置知识:《React 入门------④ JSX 语法细节补充》------ dangerouslySetInnerHTML 的用法~

所以,返回 detail 目录下的 index.js 文件:

jsx 复制代码
import React, {Component} from "react";

import {
  DetailWrapper,
  Header,
  Content
} from "./style.js";

import {connect} from "react-redux";

class Detail extends Component {
  render() {
    return(
      <DetailWrapper>
        <Header>{this.props.title}</Header>
      
        {/*❗️用 dangerouslySetInnerHTML 避免"转义"!*/}
        <Content 
					dangerouslySetInnerHTML={{__html: this.props.content}}
				/>
         {/* {this.props.content}  ❗️相应地,删除这里的原始写法! */}
        

      </DetailWrapper>
    )
  }
}

const mapStateToProps = (state) => { 
  
  return { // ❗️这个"规则"会返回一个"对象"出去!
    title: state.getIn(["detail", "title"]),  
    content: state.getIn(["detail", "content"]) 
  }

}

export default connect(mapStateToProps, null)(Detail);  

返回页面查看(详情页正常显示):

3 AJAX 获取详情页数据

3.1 mock "详情页"数据

在 AJAX 请求数据前,我们得自己 mock 一些数据辅助我们开发。

1️⃣在项目的 public 目录下 api 文件夹中新增一个 detailData.json 文件:

2️⃣编写 mock 数据 detailData.json 中的内容:

json 复制代码
{
	"success": true,
	"data": {
		"title": "评"超时空同居"浅议爱情电影和奇幻元素的嫁接",
		"content": "<img src='https://qdywxs.github.io/jianshu-images/detail.jpg' alt='' /><p>这部电影充分照顾到了八零后的情怀,九零后的笑点,零零后嘛,那就是奇幻爱情吧。奇幻爱情喜剧,三种元素都有,但哪种都不出彩,相对来说喜剧元素稍微强一点。</p><p>说实话,去之前我对电影《超时空同居》的期望值并不高,首先超时空已经不是什么新鲜话题,同居也不是什么热点新闻;其次导演苏伦是个新人,主演雷佳音和佟丽娅一看就不搭,两人的票房号召力也不是很大;再者《后来的我们》刚刚刷过情怀牌,而《复联 3》又以十年一战的王者气势独占鳌头,试想此情此景,一部小成本投资的奇幻喜剧爱情电影,凭什么在这种困境下胜出呢?而最终促使我前往的是监制徐峥,一个相当有才,且在电影圈呼风唤雨的全能型电影人。</p><p>可是看完影片,我就后悔了,特别想见见现实中的雷佳音是怎样的,因为这次雷大头确实雷到我了,真是应了一句话<b>"人不可貌相,海水不可斗量"</b>。</p><p>那个在《我的前半生》中一张纠结脸的前夫哥不见了,取而代之的是一个在穷困潦倒时依然积极向上,始终闪现人性光辉的阳光男。<b>豆干 + 花生就能吃出烧鸡味,馒头片 + 黑芝麻粉 + 咖啡粉就能吃出巧克力蛋糕味</b>,这种生活的智慧可不是一般人能具备的。</p><p>记得来之前朋友就跟我说雷佳音是那种演什么像什么的演员,我还不信,现在信了,也晚了,因为他正如我之前所愿,很配合地没能出现在搜狐的首映现场,唉,只能脑补生活中的他是怎样的了。</p><p>说完了雷佳音的"雷",我想再说说《超时空》的赞。</p><p>现在的电影市场上,爱情片不少,喜剧片不少,奇幻片也不少,但是要说集三者于一身的,奇幻喜剧爱情片,还真是不多见,而《超时空同居》就是这样一个集三者于一身且脑洞大开的影片。</p><p>接下来,我想从以下三个方面讲讲《超时空同居》带给我的意外惊喜:</p><p><b>一、差异十足的 CP 设定</b></p><p>《超时空同居》从片名看就知道,是一个玩时间梗的爱情电影,雷佳音饰演的陆鸣,跟佟丽娅饰演的谷小焦,因为一个突如其来的时空意外住在了同一个房间,只不过陆鸣是在世纪之交的 1999 年,谷小焦是在当下的 2018 年。</p><p>这样的时间差距,就为他们两个人在形象和性格上都建立起了巨大的反差。可以想象,当土气、邋遢、过时老气的雷佳音与漂亮、时髦、现代化的佟丽娅寄居在同一个屋檐下将是一番怎样的情景。</p><p><b>二、时空重叠的叙事结构</b></p><p>与同样玩时间梗的《重返二十岁》不同的是,《超时空同居》并非把时空转换作为一个"起点",此后的叙事依然固定在一个时空里,它是把不断变化的时空作为一个基本背景,叙事也在不同的时空里来回穿梭。这不仅让影片的整体风格变得复杂多变,也给故事增添了浓厚的奇幻色彩。</p><p><b>三、笑中带泪的成长主题</b></p><p>《超时空同居》除了各种令人爆笑的情节外,它还将更多的落脚点放在了传达勇敢真诚的爱情观上,并且以主人公的"成长"为主题,让观众在笑过之后有了更多关于自身的思考。</p><p>作为现代奇幻片,与古装奇幻片不同的是,在这里未来是可以被改变的。</p><p>当陆鸣看到那个未来的自己虽然外表光鲜,却为了成功不择手段时,他毅然决然地选择了采取行动,从而避免了悲剧的发生。虽然他最终没能成为大屏幕上的地产大亨,却依然赢得了属于自己的爱情。</p><p>同时发布会现场也呼应了影片的这一主题,当四位主创被问及最想对 19 年前的自己说什么时,佟丽娅非常"实在"地笑称,希望<b>"能多买几套房"</b>。</p><p>2004 年就进入影视圈、却直到去年才"大器晚成"的雷佳音希望对自己说:<b>就坚持这么干,没错!</b></p><p>1972 年出生,2012 年才开始当导演的徐峥说:<b>要抓紧时间早一点拍戏,早一点开始自己的创作,不要浪费时间。</b></p><p>来自内蒙古的 80 后新锐女导演苏伦说:<b>早点离开草原,去北京。</b></p><p>你呢,最想对 19 年前的自己说什么?</p><p>影片将于今天 5 月 18 日上映,我想看过之后,你会给自己一个满意的答复的。</p>"
	}
}

3️⃣既然有了 mock 数据,我们就可以将 detail 目录下 store 中的 reducer.js 里写死的"数据"去除掉了:

javascript 复制代码
import {fromJS} from "immutable"; 

const defaultState = fromJS({
  title: "",
  content: ""
});

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

  return state;
}

返回页面查看(详情页面数据都没有了):

3.2 异步获取并显示数据

🔗前置知识:《React 进阶------⑧ React 生命周期函数(下):巧用 componentDidMount 进行 AJAX 数据请求》

在上边的视频中可以看到,详情页上什么数据都没有了。但这也正符合正常的逻辑------详情页的"数据"肯定不是写死在页面上的,作为"详情页"展示的"数据",它会在"详情页"挂载完成时,去异步获取并显示"数据"。

故,这里就涉及到了"改变数据"的流程!改变什么"数据"呢------将本来的"空字符",通过异步获取到的"数据"填满。

4️⃣打开 detail 目录下的 index.js 文件:

jsx 复制代码
import React, {Component} from "react";

import {
  DetailWrapper,
  Header,
  Content
} from "./style.js";

import {connect} from "react-redux";

import {actionCreators} from "./store"; // ❗️❗️❗️引入 actionCreators!

class Detail extends Component {
  render() {
    return(
      <DetailWrapper>
        <Header>{this.props.title}</Header>
     
        <Content 
          dangerouslySetInnerHTML={{__html: this.props.content}}
        />

      </DetailWrapper>
    )
  }
  
  componentDidMount() { // ❗️❗️❗️5️⃣当组件挂载完毕,就去请求"数据";
    // 5️⃣-①:这里应该怎么去请求"数据"呢?
    
    /* 
    5️⃣-③:因此可以通过 this.props.getDetail 来调用 store 中的
    getDetail;
     */
    this.props.getDetail();
  }
}

const mapStateToProps = (state) => { 
  
  return {  
    title: state.getIn(["detail", "title"]),  
    content: state.getIn(["detail", "content"]) 
  }

}


/*
❗️4️⃣-②:接下来,我们定义哪些"用户的操作"应该
当作 action,并传给 store;
 */
const mapDispatchToProps = (dispatch) => { /*
																					 4️⃣-③:把 store 里的"dispatch 方法"
																					 作为"参数"传递给 mapDispatchToProps;
                                            */
  return {
    getDetail() { // ❗️5️⃣-②:在这里定义 getDetail 会被当作 action 传给 store;
      
    	/*
      ❗️5️⃣-④:Redux-thunk 中,"异步"代码我们是放在 action 中进行。
      这里我们仅作方法的"调用";
       */
      const action = actionCreators.getDetailData();
      
      dispatch(action)
    
    }
  }
}


/*
❗️❗️❗️4️⃣-①:给 connect 传递第三个"参数"------mapDispatchToProps,
表示------我们把 store 的 dispatch 方法"挂载"到 Detail 组件的 props 上。
即,我们可以定义哪些"用户的操作"应该当作 action,并传给 store;
 */
export default connect(mapStateToProps, mapDispatchToProps)(Detail);  

5️⃣-⑤:打开 detail 目录下 store 中的 actionCreators.js 文件,定义这个 action;

javascript 复制代码
// 5️⃣-⑦:引入 axios 模块;
import axios from "axios";

// 5️⃣-⑥:在 action 中添加 AJAX"异步"代码;
export const getDetailData = () => {
  
  // 5️⃣-⑧:编写"异步"函数;
  return(dispatch) => {
    axios.get("/api/detailData.json")
    	.then((res) => {
    		const result = res.data.data;
    	})
      .catch(() => {alert("error")})
  }
}

返回页面,查看"数据"是否成功获取到(已成功获取):

6️⃣既然"数据"已成功获取,接下来就用 AJAX 获取到的"数据"替换初始的"空字符"。又是"修改数据"的套路,那我们继续走 Redux 的流程:

6️⃣-①:打开 detail 目录下 store 中的 actionTypes.js 文件:

javascript 复制代码
export const CHANGE_DETAIL_DATA_ACTION = "get_detail_data_action"; // ❗️定义好常量~

6️⃣-②:返回 detail 目录下 store 中的 actionCreators.js 文件;

javascript 复制代码
import axios from "axios";

// 6️⃣-③:先引入"常量";
import {CHANGE_DETAIL_DATA_ACTION} from "./actionTypes";

// 6️⃣-⑤:在这里定义 action;
const changeDetailDataAction = (result) => ({
  type: CHANGE_DETAIL_DATA_ACTION,
  
  title: result.title,
  content: result.content
})


export const getDetailData = () => {
  
  return(dispatch) => {
    axios.get("/api/detailData.json")
      .then((res) => {
        const result = res.data.data;
      
        // ❗️6️⃣-④:获取到数据后,需要去替换初始的"空字符";
        const action = changeDetailDataAction(result);
      
        dispatch(action); // ❗️❗️❗️6️⃣-⑥:将这个 action 发送给 reducer!
      })
      .catch(() => {alert("error")})
  }
}

7️⃣打开 detail 目录下 store 中的 reducer.js 文件:

javascript 复制代码
import {fromJS} from "immutable";

// 7️⃣-①:先引入"常量";
import {CHANGE_DETAIL_DATA_ACTION} from "./actionTypes";

const defaultState = fromJS({
  title: "",
  content: ""
});

export default (state=defaultState, action) => {
  
  // ❗️7️⃣-②:编写替换"数据"的逻辑;
  if(action.type === CHANGE_DETAIL_DATA_ACTION) {
    return state.merge({
      title: action.title,
      content: action.content
    })
  }
  
  return state;
}

返回页面查看效果(异步获取到的"数据"正常显示出来了):

以上,我们就把"详情页"给完整写出来了。但是,在上边的视频最后 20 秒时间内,你是否也发现了一个问题:

无论我们点击 home 页的哪个数据,它都请求同一个"接口",并跳转至同一个"详情页"。

✔️实际项目中,在请求后端接口时,我们会给后端不同的"详情页" id 参数,后端根据这个" id 参数"给我们返回不同的内容。

即,"详情页"得知道它对应的上一个页面 用户所点击的各个详情的 id 是多少。

下一篇,我们专门解决这个问题。

祝好,qdywxs ♥ you!

相关推荐
mldong12 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排13 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby606114 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角15 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!15 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰15 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi16 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen17 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马18 小时前
AI源码分析:拆解模型底层实现逻辑
前端
BillKu18 小时前
全局样式变量:CSS 自定义属性(--border-color)和SCSS 变量($border-color)的说明
javascript·css·scss