转载请注明出处,未经同意,不可修改文章内容。
🔥🔥🔥"前端一万小时"两大明星专栏------"从零基础到轻松就业"、"前端面试刷题",已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。
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.js
和 actionCreators.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!