你会做出反应吗?——构建一个视频应用程序
介绍
什么是 React?
React 是由 Facebook 开源的流行 JavaScript 前端库。它允许你创建和复用 UI 元素。React 的诞生源于解决浏览器 DOM 速度慢的问题。它非常出名,并且被广泛使用。
为什么要使用 React?
可重用组件- React 是基于组件的,这意味着组件是 React UI 的构建块。组件描述了 UI 中一小部分的外观,并且可重用。复杂的应用程序可以通过将组件嵌套在其他组件中来构建。
SPA - React 的另一个重要特性是它是一个单页应用程序 (SPA)。这意味着当需要向用户显示不同的视图时,React 不会获取一个全新的网页,它只会使用从后端服务器获取的新数据重写当前网页。
使用虚拟 DOM 快速渲染- 更新 DOM 通常是 Web 性能的瓶颈。React 使用虚拟 DOM;即保存在内存中的 DOM。任何视图更改都会首先反映到虚拟 DOM,然后比较虚拟 DOM 的先前状态和当前状态,并仅对 DOM 应用必要且最小的更改。这正是 React高性能的主要原因。
本文的主要目标-
- 了解 React 如何将 HTML 渲染为 UI。
- 了解如何使用 JSX。
- 了解 React 组件、状态、道具和生命周期方法。
- 从头开始创建一个 React 应用程序(视频)。
入门 -
首先,进入终端,然后输入 -
npx create-react-app videos
此指令创建一个名为 videos 的 React 应用程序模板。
要在浏览器中的 localhost:3000 上运行您的应用程序,请在终端中输入-
cd videos
npm start
它在浏览器中的样子如下-
清理 -
打开 src 文件夹并删除以下文件 - App.test.js、logo.svg、setupTests.js
删除 App.js 和 Index.js 文件中的所有内容。
现在我们已经设置了项目并清理了不必要的文件,我们可以继续了解 React。
在 Index.js 文件中,放置以下内容-
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.querySelector('#root'));
第 1 行和第 2 行:导入 react 和 react-dom 库。react 模块用于在 JavaScript(JSX)中编写 HTML 代码,而 react-dom 用于执行渲染函数,将内容显示到页面上。
第 3 行:导入 App 组件。
第 4 行:在 ID 为 root 的容器中渲染 App。
ReactDOM.render 方法-
此方法的作用是将 App 组件插入到浏览器的 DOM 中。它接收的第一个参数是需要渲染的 React 组件,第二个参数指定我们需要将组件插入到 public/index.html 文件中的哪个位置。您会<div>
在 public/index.html 中找到一个 id 为 root 的元素。该<div>
元素是 React 容器节点。首次调用 render() 函数时,整个容器节点将被替换为 App 组件。
现在我们将编写我们的 App 组件(转到 App.js 文件),但在此之前,让我们先了解一下基础知识 -
JSX - React 支持使用 JSX(JavaScript XML)的语句,这使得我们可以在 JavaScript 中使用类似 HTML 的语句来创建 UI 元素(组件)。你会发现 JSX 的语法非常易于使用,因为它与我们编写 HTML 的方式非常相似。
组件 - 如前所述,React 是基于组件的,组件是 React 中的构建块,一组元素组成一个组件。通常,每个组件都会定义导航栏、内容选项卡、登录表单等的外观。这也有助于将 UI 部分的视觉(元素)和逻辑(功能)部分整合在一起。
将我们的项目分解成组件 -
该应用程序中将有 4 个组件。
- 搜索栏(位于顶部)
- 视频细节(左侧)
- 视频列表(右侧)
- VideoItem(VideoList 的项目)
功能组件-
定义组件的最简单方法是编写 JavaScript 函数。
在 app.js 中 -
import React from 'react';
function App(props) {
return ();
}
这个函数是一个有效的 React 组件,因为它接受一个带有数据的“props”对象参数,并返回组件渲染时需要渲染的元素。我们称这样的组件为“函数式组件”,因为它们本质上就是 JavaScript 函数。
基于类的组件-
创建 React 组件时,组件名称必须以大写字母开头。组件必须包含 extends React.Component 语句,该语句创建对 React.Component 的继承,并允许组件访问 React.Component 的函数。React 组件至少必须包含 render() 函数。render() 函数返回组件渲染时要渲染的元素。
要呈现组件“Component”,请使用<Component />
标签。
对于我们的App组件(App.js)
这相当于上面的功能组件。
import React from 'react';
class App extends React.Component {
render() {
return ();
}
}
在类组件中管理状态
- 与特定组件相关的数据可以存储为其“状态”。组件的状态只能从该特定组件进行更改。所有状态变量都state
以键值对的形式存储在类变量中。
每当状态发生变化时,React 都会重新渲染 UI。
修改状态-
- 不要直接修改状态,因为这不会重新渲染组件。
- 改为使用
setState()
。当 setState() 被调用时,React 知道状态已更改,并再次调用 render() 方法来了解屏幕上应该显示的内容。例如:
state = {selectedVideo: null}
//Wrong
this.state.selectedVideo = 'Something'
//Correct
this.setState({ selectedVideo: 'Something' })
在 JSX 语法中,我们还可以通过用括号 (“{}”) 包裹正则 JavaScript 表达式来插入它们。
例如:
<h1>{ 2 + 5 * 8 }</h1>
<p> Current Selected Video is : {this.state.selectedVideo} </p>
请注意,早期状态只能在类组件中使用,而不能在函数式组件中使用。因此,函数式组件也被称为无状态组件。
然而,在引入 React Hooks 之后,状态现在可以在类组件和函数式组件中使用。
在本项目中,我们没有使用 React Hooks,因此,如果需要状态变量,我们将使用类组件,否则我们将使用简单的函数组件。
继承属性的props
- 假设用户选择了某个视频,并将其存储在 this.state.SelectedVideo 状态变量中。为了在 VideoDetail 组件中显示该视频,我们必须将所选视频的信息传递给 VideoDetail 组件。props 或属性允许我们将数据从父组件传递到子组件。我们可以通过类似于 HTML 属性的方式将“props”值传递给单个组件。
<VideoDetail video={this.state.selectedVideo} />
video
是此处定义的 prop,包含 selectedVideo 数据。然后,我们可以像给函数传递参数一样,通过 props 传递数据:
const VideoDetail = (props) => {
// code
}
最后,我们使用点符号来访问 prop 数据并将其用作“props.video”
在类组件中,我们可以这样访问属性this.props.video
请注意,JSX 的元素只是 JavaScript 对象。这意味着 JSX 元素的 props 和 children 可以是任何可以放入 JavaScript 变量的内容——它们可以是字符串、状态变量、其他组件,甚至是函数。您将在项目中看到如何将不同的内容作为 props 传递给组件。
生命周期方法-
React 组件实例的生命周期包含三个阶段。
挂载 - 创建并插入 DOM。
更新 - 由于状态值变化而更新或重新渲染。
卸载 - 从 DOM 中移除。
每个生命周期阶段都涉及执行一组生命周期方法。
1. componentDidMount() - 在组件安装后立即调用,可用于执行需要 DOM 节点到位的初始化。在这里我们可以执行从 API 获取数据等任务。
2. componentDidUpdate() - 组件的任何 props 或 state 值发生变化时,都会更新/重新渲染。componentDidUpdate() 在组件更新后立即调用。它可以用于实现重新渲染后执行的任何逻辑。
还有其他生命周期方法,本文就不一一解释了。在本项目中,我们只会使用 componentDidMount() 。
好的!!
现在我们已经解决了这个问题,是时候开始行动了!
重要提示- 我们将使用语义 UI 来设置组件的样式。(语义 UI 是一个开发框架,它使用人性化的 HTML 来帮助创建美观、响应式的布局。)为此,请在 public/index.html 的 head 中添加以下代码 -
<link rel = 'stylesheet' href = "https://cdnjs.cloudflare.com/ajax/libs/semanticui/2.4.1/semantic.min.css"
integrity = "sha512-8bHTC73gkZ7rZ7vpqUQThUDhqcNFyYi2xgDgPDHc+GXVGHXq+xPjynxIopALmOPqzo9JZj0k6OqqewdGO3EsrQ==" crossorigin = "anonymous" />
项目文件夹结构 -
另外,请注意,本文主要关注 React 概念。因此,请忽略不相关的主题。
index.js -`
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
ReactDOM.render(<App />, document.querySelector('#root'));
apis/youtube.js
import axios from 'axios';
// create your youtube api key and place it here
const KEY = YOUR_API_KEY;
// setting up base instance in which you can define a URL and
// any other configuration elements.
// and exporting as default.
export default axios.create({
baseURL: 'https://www.googleapis.com/youtube/v3',
params: {
part: 'snippet',
maxResults: 5,
key: KEY
}
});
// we can import this instance from other files and no longer need to write
// whole URL everytime we call API
VideoItem.js
import './VideoItem.css';
import React from 'react';
// takes props video object and onVideoSelect function
// we call onVideoSelect when user clicks the video
// What does a VideoItem Component contains?
// yepp, Video Thumbnail and Video title
const VideoItem = ({ video, onVideoSelect }) => {
return (
<div onClick={() => onVideoSelect(video)} className="item video-item">
<img alt={video.snippet.title} className="ui image" src={video.snippet.thumbnails.medium.url} />
<div className="content">
<div className="header">{video.snippet.title}</div>
</div>
</div>
);
}
export default VideoItem;
视频项目.css
.video-item{
display: flex !important;
align-items: center !important;
cursor: pointer;
}
.video-item.item img{
max-width: 180px;
}
视频列表.js
import React from 'react';
import VideoItem from './VideoItem';
// takes videos (video array) and onVideoSelect function
// What does a VideoList contain?
// yepp, List of Video (specifically VideoItems)
// So we iterate over videos array and make a VideoItem for each
// Note We are passing video and OnVideoSelect as props to VideoItem
const VideoList = ({ videos, onVideoSelect }) => {
const renderedList = videos.map((video) => {
return <VideoItem key={video.id.videoId} onVideoSelect={onVideoSelect} video={video} />;
})
return <div className="ui relaxed divided list">{renderedList}</div>;
}
export default VideoList;
视频细节.js
import React from 'react';
// videoDetail takes the selectedVideo and displays its info.
const VideoDetail = ({ video }) => {
if (!video) {
return <div>Loading...</div>;
}
//This url is for fetching selectedVideo
const videoSrc = `https://www.youtube.com/embed/${video.id.videoId}`;
return (
<div>
<div className="ui embed">
<iframe title="video player" src={videoSrc} />
</div>
<div className="ui segment">
<h4 className="ui header">{video.snippet.title}</h4>
<p>{video.snippet.description}</p>
</div>
</div>
);
}
export default VideoDetail;
SearchBar.js
import React from 'react';
// state variable "term" stores what user types in searchBar
class SearchBar extends React.Component {
state = { term: '' };
// this sets the "term" to what user types in. (in sync)
onInputChange = (e) => {
this.setState({ term: e.target.value });
}
// it is called when user submits the "term"
// which in turn calls the onTermSubmit() function passed as its prop
onSearchBarSubmit = (e) => {
e.preventDefault();
this.props.onTermSubmit(this.state.term);
}
render() {
return (
<div className="ui segment search-bar" style={{ marginTop: '20px' }}>
<form onSubmit={this.onSearchBarSubmit} className="ui form">
<div className="field">
<label>Video Search</label>
<input
style={{ backgroundColor: 'whitesmoke' }}
type="text" value={this.state.term}
onChange={this.onInputChange}
/>
</div>
</form>
</div>
);
}
}
export default SearchBar;
App.js
import React from 'react';
import SearchBar from './SearchBar';
import VideoList from './VideoList';
import youtube from '../apis/youtube';
import VideoDetail from './VideoDetail';
class App extends React.Component {
state = { videos: [], selectedVideo: null };
// videos - array of videos based on term that user passed in searchbar (initally empty)
// selectedVideo - video selected to display on left
// this lifecycle method is called when App component gets mounted
componentDidMount() {
this.onTermSubmit('dev.to');
}
// Note that here 'dev.to' is initial term for which videos will be searched
// It is random
// This function is the one that accepts the term and fetches videos
// and set "videos" state variable to fetched videos and
// selectedVideo to the first video of videos
onTermSubmit = async (term) => {
const response = await youtube.get('/search', {
params: {
q: term
}
});
this.setState({ videos: response.data.items, selectedVideo: response.data.items[0] });
}
onVideoSelect = (video) => {
this.setState({ selectedVideo: video });
};
render() {
return (
<div className="ui container" style={{ backgroundColor: 'whitesmoke', padding: '40px' }}>
<SearchBar onTermSubmit={this.onTermSubmit} />
<div className="ui grid">
<div className="ui row">
<div className="eleven wide column">
<VideoDetail video={this.state.selectedVideo} />
</div>
<div className="five wide column">
<VideoList onVideoSelect={this.onVideoSelect} videos={this.state.videos} />
</div>
</div>
</div>
</div>
);
}
}
export default App;
结论
在本文中,我们学习了ReactJS的基本概念以及如何在实际项目中应用这些概念。现在你已经对 React 有了基本的了解,可以继续学习更高级的主题,例如 Hooks、Redux 等。
感谢阅读
这是我在 Dev 上的第一篇文章。希望你们喜欢,不要觉得太过冗长。如果你们喜欢这篇文章,请点赞❤️。祝你们编程愉快😊。
文章来源:https://dev.to/jaskaran/fundamentals-of-react-build-a-video-app-24j8