本文是在学习 React 的时候,根据 Learn React & Webpack by building the Hacker News front page 这篇教程来进行练习的Demo,详细文章请阅读原文。
准备工作
安装 Webpack
在此之前你应该已经安装了 node.js.
1 | npm install webpack --save-dev |
参数 -g
表示我们将全局(global)安装 webpack, 这样你就能使用 webpack 命令了.
webpack 也有一个 web 服务器 webpack-dev-server, 我们也安装上
1 | npm install webpack-dev-server --save-dev |
webpack 配置文件
webpack 使用一个名为 webpack.config.js 的配置文件, 现在在你的项目根目录下创建该文件. 我们假设我们的工程有一个入口文件 app.js, 该文件位于 app/
目录下, 并且希望 webpack 将它打包输出为 build/
目录下的 bundle.js 文件. webpack.config.js 配置如下:
1 | var path = require('path'); |
注意,如果文件的路径不是在当前目录下,则需要使用 path.resolve
来创建文件路径
现在让我们测试一下, 创建 app/app.js
文件, 填入一下内容:
1 | document.write("It works"); |
创建 build/index.html
, 填入以下内容:
1 | <!DOCTYPE html> |
其中 script 引入了 bundle.js
, 这是 webpack 打包后的输出文件.
运行 webpack 打包, 运行 webpack-dev-server
启动服务器. 访问 http://localhost:8080/build/index.html
, 如果一切顺利, 你会看到打印出了 It works.
配置 package.json
在项目根目录下运行 npm init -y
会按照默认设置生成 package.json, 修改 scripts 的键值如下:
1 | "scripts": { |
1 | 注:package.json中的脚本部分已经默认在命令前添加了node_modules/.bin路径,所以无论是全局还是局部安装的Webpack,你都不需要写前面那指明详细的 script 中运行命令所对应的路径了 |
现在执行 npm run build
相当于 webpack
, npm run start
相当于 webpack-dev-server
. 当项目变得相当复杂时, 你可以使用这种技巧隐藏其中的细节.
安装依赖
安装 React:
1 | npm install react react-dom --save-dev |
为了简化 AJAX 请求代码, 这里引入 jQuery:
1 | npm install jquery --save-dev |
安装 Babel 的 loader 以支持 ES6 语法:
1 | npm install babel-core babel-loader babel-preset-es2015 babel-preset-react --save-dev |
然后配置 webpack.config.js
来使用安装的 loader.
1 | var path = require('path'); |
接下来测试一下开发环境是否搭建完成.
打开 app.js
, 修改内容为:
1 | import $ from "jquery"; |
这里组件 HelloWorld
被装载到 id
为 content
的 DOM 元素, 所以相应的需要修改 index.html
.
1 | ... |
再次打包运行, 访问 http://localhost:8080/build/index.html
如果可以看到打印出了 Hello World 那么开发环境就算搭建完成了.
在开始写第一个组件之前
在开始写第一个组件之前, 让我们分析一下到底我们需要哪些组件.
上图是的最终效果的部分截图, 我们将其分为几个组件, 用不同颜色的线框标出
我们可以看出其中包含以下几个组件.
NewsList
(蓝色): 所有组件的容器.NewsHeader
(绿色): Logo, 标题, 导航栏等.NewsItem
(红色): 对应每条资讯.
把这些组成为一棵组件树.
- NewsList
- NewsHeader
- NewsItem * n
编写大致的模板
上一节我们知道了整个页面的组件结构, 在这一节我们根据这些结果编写出一个大致的模板. 先在 app 目录下为每个组件建立文件, NewsList.js
, NewsHeader.js
和 NewsItem.js
.
1 | cd app |
编辑 NewsHeader.js
1 | // NewsHeader.js |
NewsHeader
组件就先这样, 具体的实现现在先不去考虑, 记得我们现在只是编写一个大致的结构.
同样的, 编辑 NewsItem.js
1 | // NewsItem.js |
接着是 NewsList.js
, 因为 NewsList 是前两个组件的容器, 所以我们需要引入它们
1 | // NewsList.js |
最后修改入口文件 app.js
:
1 | import $ from "jquery"; |
这时访问 http://localhost:8080/build/index.html
可以看到下图的效果
NewsHeader
整个 NewsHeader
包含了三个部分, 左边的Logo和标题, 中间的导航栏和右边的登录入口.
让我们先从左边的 Logo 和标题开始.
Logo 和标题
先下载 Logo
在 NewsHeader
组件里新增一个方法 getLogo
, 就像下面这样.
1 | // NewsHeader.js |
这个方法返回一个包含 Logo 的子组件.
1 | getLogo() { |
这里遇到一个问题, img 的 src 填什么?
在前面几节的开发中, 还记得你是怎么引入其他的 js 文件的吗? import
. 实际上这是 ES6 的模块系统, 这里的 js 文件作为模块被其他模块引入. 但除了 js 文件, 在开发时我们还会涉及其他的资源文件, 如图像, 字体, 样式等, 它们也需要被模块化. 在这里, 如果 Logo 图片也能被模块化然后引入该多好. 我们需要再次配置 Webpack.
安装对应的 loader:
1 | npm install url-loader file-loader --save-dev |
配置 webpack.config.js
1 | //... |
然后回到 NewsHeader.js
这时候你就可以使用 import
引入图片了.
1 | import imageLogo from './y18.gif'; |
然后像这样使用.
1 | <img src={imageLogo} /> |
注意这里用 {}
包起来, 这样其中的内容会作为表达式.
getLogo
方法完成了, 再写一个 getTitle
方法.
1 | getTitle() { |
修改 render
方法, 引用我们刚刚写好的那两个方法.
1 | render() { |
最后我们需要添加样式, 还是回到刚刚的问题, 怎么引入样式?
我们也需要将样式模块化.
安装相应的 loader:
1 | npm install css-loader style-loader --save-dev |
css-loader
处理 css 文件中的 url()
表达式.
style-loader
将 css 代码插入页面中的 style 标签中.
在 webpack.config.js
中配置新的 loader.
1 | { |
新建一个 css 文件 NewsHeader.css
1 | .newsHeader{background-color: #ff6600; font-size: 10pt; padding: 2px;color: #000;} |
然后在 NewsHeader.js
中引入它
1 | import './NewsHeader.css'; |
再建立一个全局的 css 文件 app.css
1 | body { |
然后在 app.js
中引入
1 | import './app.css' |
打包运行看看吧.
导航栏
接下来是导航栏, 也就是中间那部分.
回到 NewsHeader.js
, 增加一个 getNav
方法.
1 | getNav() { |
同样, 记得在 render
方法中引用
1 | render() { |
添加样式.
1 | .newsHeader-nav{height: 20px; line-height: 20px;} |
登录入口
增加一个 getLogin
方法.
1 | getLogin() { |
在 render
中引用
1 | render() { |
更新样式
1 | .newsHeader{background-color: #ff6600; font-size: 10pt; padding: 2px;color: #000;height: 20px;clear: both;} |
至此整个 NewsHeader
就完成了, 你应该能看到如本节初所展示的效果.
NewsItem
如图, 每条资讯对应着这样一个 NewsItem
, 本节我们将编写 NewsItem
组件.
可以看到, 一个 NewsItem
包含了资讯的标题, 来源地址, 什么时候发布的以及评论数等等. 它依赖于传入的数据, 那么怎么传入数据呢?
对于父子组件间的通信, 可以使用属性传递. 子组件可以使用 this.props
访问到父组件传入的属性数据.
回到 NewsList
组件, 它作为 NewsItem
的父组件可以使用如下方式传入数据.
回到 NewsList
组件, 它作为 NewsItem
的父组件可以使用如下方式传入数据.
1 | <NewsItem item={data} /> |
NewsItem
中可以使用 this.props.item
访问 item
属性.
NewsItem 标题
先来简单点的, 第一步我们只获取并显示标题.
修改 NewsList.js
1 | export default class NewsList extends React.Component { |
这里我们声明一个 testData 作为测试数据传入 NewsItem.
修改 `NewsItem.js
1 | render() { |
在这里使用 this.props.item 访问 item 属性.
建立 NewsItem.css
1 | .newsItem{background-color: #f6f6ef;margin-top: 5px;} |
在 NewsItem.js
中引入
1 | import './NewsItem.css'; |
运行看看效果, 显示的标题应该和传入的测试数据中的一样.
NewsItem 来源地址
我们现在添加来源地址到标题的末尾. 先在 NewsItem.js
中引入 url
模块
1 | import URL from 'url'; |
然后增加一个 getDomain
方法.
1 | getDomain() { |
然后再增加一个 getTitle
方法, 这个方法会返回一个包含了标题(我们上一节做的事)和地址的组件.
1 | getTitle() { |
修改 render
1 | render() { |
增加样式
1 | .newsItem{background-color: #f6f6ef;margin-top: 5px;} |
好了, 看起来不错, 但是有个问题, 这个项目最终需要从 Hacker News 的 API 取得资讯数据, 而其中有些是没有 url
属性的, 看看我们的 getTitle()
方法, 我们似乎忽略了这个特例, 让我们做些修改.
1 | getTitle() { |
试着去掉 testData
的 url
属性, 看看是不是一切正常.
NewsItem 其余部分
我们现在加上其余部分, 你已经看过了前两节, 这节应该是没有什么难度的, 我们快速带过.
下载 grayarrow.gif
, 在 NewsItem.js
中引入
1 | import ImageGrayArrow from '../img/grayarrow.gif'; |
这里计算时间间距我们使用了 Moment, 如果你要使用你需要安装并引入它, 或者使用你喜欢的实现方法.
1 | npm install moment --save-dev |
修改 NewsItem.js
1 | getDomain() { |
NewItem.css
1 | .newsItem{background-color: #f6f6ef;padding-top: 6px;} |
NewsList
上一节中为了测试 NewsItem
, 我们定义了一个测试数据 testData
, NewsList
中也只有一个 NewsItem
, 而真实的情况不会只有一条资讯, 而应该是一组资讯, 每一条对应有一个 NewsItem
, 本节中我们来实现这个功能.
首先我们确定传入的数据是一个数组, 其中每一个元素都是一条资讯, 至于这个数据由哪里传入, 怎么生成我们先不关心, 但我们可以用 this.props.items
获取到. NewsList
对于其中的每一个元素都生成一个 NewsItem
.
下面是修改完的 render
1 | render() { |
新建样式 NewsList.css
1 | .newsList{width: 85%; margin: 0 auto;} |
目前的代码是没法运行的, 我们还没有取得数据传入给 NewsList
, 这将在下一节完善.
Hacker News API
本节中我们使用 Hacker News API 来获取数据, 具体请参考 API 文档.
app.js
1 | function get(url) { |
items
就是处理完后的数据, 一个由资讯数据组成的数组, 我们将它作为属性传入 NewsList
.
最后的效果如下:
Demo
这个小项目的 Demo 我已经传到网上去了,可以在 github 上进行下载。具体步骤如下:
安装方法是:
1 | git clone https://github.com/ShiningDan/React-hacker-news.git |
就可以在 http://localhost:8080/build/index.html 上看到项目的展示效果。因为是从 Hacker News 上获取最新的新闻,页面的显示速度和网速以及 Hacker News 的访问速度有关,请耐心等待~